1 /*
2 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "third_party/blink/renderer/core/xml/xpath_node_set.h"
27
28 #include "third_party/blink/renderer/core/dom/attr.h"
29 #include "third_party/blink/renderer/core/dom/document.h"
30 #include "third_party/blink/renderer/core/dom/element.h"
31 #include "third_party/blink/renderer/core/dom/node_traversal.h"
32
33 namespace blink {
34 namespace xpath {
35
36 // When a node set is large, sorting it by traversing the whole document is
37 // better (we can assume that we aren't dealing with documents that we cannot
38 // even traverse in reasonable time).
39 const unsigned kTraversalSortCutoff = 10000;
40
41 typedef HeapVector<Member<Node>> NodeSetVector;
42
Create(const NodeSet & other)43 NodeSet* NodeSet::Create(const NodeSet& other) {
44 NodeSet* node_set = NodeSet::Create();
45 node_set->is_sorted_ = other.is_sorted_;
46 node_set->subtrees_are_disjoint_ = other.subtrees_are_disjoint_;
47 node_set->nodes_.AppendVector(other.nodes_);
48 return node_set;
49 }
50
ParentWithDepth(unsigned depth,const NodeSetVector & parents)51 static inline Node* ParentWithDepth(unsigned depth,
52 const NodeSetVector& parents) {
53 DCHECK_GE(parents.size(), depth + 1);
54 return parents[parents.size() - 1 - depth];
55 }
56
SortBlock(unsigned from,unsigned to,HeapVector<NodeSetVector> & parent_matrix,bool may_contain_attribute_nodes)57 static void SortBlock(unsigned from,
58 unsigned to,
59 HeapVector<NodeSetVector>& parent_matrix,
60 bool may_contain_attribute_nodes) {
61 // Should not call this function with less that two nodes to sort.
62 DCHECK_LT(from + 1, to);
63 unsigned min_depth = UINT_MAX;
64 for (unsigned i = from; i < to; ++i) {
65 unsigned depth = parent_matrix[i].size() - 1;
66 if (min_depth > depth)
67 min_depth = depth;
68 }
69
70 // Find the common ancestor.
71 unsigned common_ancestor_depth = min_depth;
72 Node* common_ancestor;
73 while (true) {
74 common_ancestor =
75 ParentWithDepth(common_ancestor_depth, parent_matrix[from]);
76 if (common_ancestor_depth == 0)
77 break;
78
79 bool all_equal = true;
80 for (unsigned i = from + 1; i < to; ++i) {
81 if (common_ancestor !=
82 ParentWithDepth(common_ancestor_depth, parent_matrix[i])) {
83 all_equal = false;
84 break;
85 }
86 }
87 if (all_equal)
88 break;
89
90 --common_ancestor_depth;
91 }
92
93 if (common_ancestor_depth == min_depth) {
94 // One of the nodes is the common ancestor => it is the first in
95 // document order. Find it and move it to the beginning.
96 for (unsigned i = from; i < to; ++i) {
97 if (common_ancestor == parent_matrix[i][0]) {
98 parent_matrix[i].swap(parent_matrix[from]);
99 if (from + 2 < to)
100 SortBlock(from + 1, to, parent_matrix, may_contain_attribute_nodes);
101 return;
102 }
103 }
104 }
105
106 if (may_contain_attribute_nodes && common_ancestor->IsElementNode()) {
107 // The attribute nodes and namespace nodes of an element occur before
108 // the children of the element. The namespace nodes are defined to occur
109 // before the attribute nodes. The relative order of namespace nodes is
110 // implementation-dependent. The relative order of attribute nodes is
111 // implementation-dependent.
112 unsigned sorted_end = from;
113 // FIXME: namespace nodes are not implemented.
114 for (unsigned i = sorted_end; i < to; ++i) {
115 Node* n = parent_matrix[i][0];
116 auto* attr = DynamicTo<Attr>(n);
117 if (attr && attr->ownerElement() == common_ancestor)
118 parent_matrix[i].swap(parent_matrix[sorted_end++]);
119 }
120 if (sorted_end != from) {
121 if (to - sorted_end > 1)
122 SortBlock(sorted_end, to, parent_matrix, may_contain_attribute_nodes);
123 return;
124 }
125 }
126
127 // Children nodes of the common ancestor induce a subdivision of our
128 // node-set. Sort it according to this subdivision, and recursively sort
129 // each group.
130 HeapHashSet<Member<Node>> parent_nodes;
131 for (unsigned i = from; i < to; ++i)
132 parent_nodes.insert(
133 ParentWithDepth(common_ancestor_depth + 1, parent_matrix[i]));
134
135 unsigned previous_group_end = from;
136 unsigned group_end = from;
137 for (Node* n = common_ancestor->firstChild(); n; n = n->nextSibling()) {
138 // If parentNodes contains the node, perform a linear search to move its
139 // children in the node-set to the beginning.
140 if (parent_nodes.Contains(n)) {
141 for (unsigned i = group_end; i < to; ++i) {
142 if (ParentWithDepth(common_ancestor_depth + 1, parent_matrix[i]) == n)
143 parent_matrix[i].swap(parent_matrix[group_end++]);
144 }
145
146 if (group_end - previous_group_end > 1)
147 SortBlock(previous_group_end, group_end, parent_matrix,
148 may_contain_attribute_nodes);
149
150 DCHECK_NE(previous_group_end, group_end);
151 previous_group_end = group_end;
152 #if DCHECK_IS_ON()
153 parent_nodes.erase(n);
154 #endif
155 }
156 }
157
158 DCHECK(parent_nodes.IsEmpty());
159 }
160
Sort() const161 void NodeSet::Sort() const {
162 if (is_sorted_)
163 return;
164
165 unsigned node_count = nodes_.size();
166 if (node_count < 2) {
167 const_cast<bool&>(is_sorted_) = true;
168 return;
169 }
170
171 if (node_count > kTraversalSortCutoff) {
172 TraversalSort();
173 return;
174 }
175
176 bool contains_attribute_nodes = false;
177
178 HeapVector<NodeSetVector> parent_matrix(node_count);
179 for (unsigned i = 0; i < node_count; ++i) {
180 NodeSetVector& parents_vector = parent_matrix[i];
181 Node* n = nodes_[i].Get();
182 parents_vector.push_back(n);
183 if (auto* attr = DynamicTo<Attr>(n)) {
184 n = attr->ownerElement();
185 parents_vector.push_back(n);
186 contains_attribute_nodes = true;
187 }
188 for (n = n->parentNode(); n; n = n->parentNode())
189 parents_vector.push_back(n);
190 }
191 SortBlock(0, node_count, parent_matrix, contains_attribute_nodes);
192
193 // It is not possible to just assign the result to m_nodes, because some
194 // nodes may get dereferenced and destroyed.
195 HeapVector<Member<Node>> sorted_nodes;
196 sorted_nodes.ReserveInitialCapacity(node_count);
197 for (unsigned i = 0; i < node_count; ++i)
198 sorted_nodes.push_back(parent_matrix[i][0]);
199
200 const_cast<HeapVector<Member<Node>>&>(nodes_).swap(sorted_nodes);
201 }
202
FindRootNode(Node * node)203 static Node* FindRootNode(Node* node) {
204 if (auto* attr = DynamicTo<Attr>(node))
205 node = attr->ownerElement();
206 if (node->isConnected()) {
207 node = &node->GetDocument();
208 } else {
209 while (Node* parent = node->parentNode())
210 node = parent;
211 }
212 return node;
213 }
214
TraversalSort() const215 void NodeSet::TraversalSort() const {
216 HeapHashSet<Member<Node>> nodes;
217 bool contains_attribute_nodes = false;
218
219 unsigned node_count = nodes_.size();
220 DCHECK_GT(node_count, 1u);
221 for (unsigned i = 0; i < node_count; ++i) {
222 Node* node = nodes_[i].Get();
223 nodes.insert(node);
224 if (node->IsAttributeNode())
225 contains_attribute_nodes = true;
226 }
227
228 HeapVector<Member<Node>> sorted_nodes;
229 sorted_nodes.ReserveInitialCapacity(node_count);
230
231 for (Node& n : NodeTraversal::StartsAt(*FindRootNode(nodes_.front()))) {
232 if (nodes.Contains(&n))
233 sorted_nodes.push_back(&n);
234
235 auto* element = DynamicTo<Element>(&n);
236 if (!element || !contains_attribute_nodes)
237 continue;
238
239 AttributeCollection attributes = element->Attributes();
240 for (auto& attribute : attributes) {
241 Attr* attr = element->AttrIfExists(attribute.GetName());
242 if (attr && nodes.Contains(attr))
243 sorted_nodes.push_back(attr);
244 }
245 }
246
247 DCHECK_EQ(sorted_nodes.size(), node_count);
248 const_cast<HeapVector<Member<Node>>&>(nodes_).swap(sorted_nodes);
249 }
250
Reverse()251 void NodeSet::Reverse() {
252 if (nodes_.IsEmpty())
253 return;
254
255 unsigned from = 0;
256 unsigned to = nodes_.size() - 1;
257 while (from < to) {
258 nodes_[from].Swap(nodes_[to]);
259 ++from;
260 --to;
261 }
262 }
263
FirstNode() const264 Node* NodeSet::FirstNode() const {
265 if (IsEmpty())
266 return nullptr;
267
268 // FIXME: fully sorting the node-set just to find its first node is
269 // wasteful.
270 Sort();
271 return nodes_.at(0).Get();
272 }
273
AnyNode() const274 Node* NodeSet::AnyNode() const {
275 if (IsEmpty())
276 return nullptr;
277
278 return nodes_.at(0).Get();
279 }
280
281 } // namespace xpath
282 } // namespace blink
283