|
1 /* |
|
2 * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> |
|
3 * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. |
|
4 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions |
|
8 * are met: |
|
9 * |
|
10 * 1. Redistributions of source code must retain the above copyright |
|
11 * notice, this list of conditions and the following disclaimer. |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
26 */ |
|
27 |
|
28 #include "config.h" |
|
29 #include "XPathStep.h" |
|
30 |
|
31 #if ENABLE(XPATH) |
|
32 |
|
33 #include "Attr.h" |
|
34 #include "Document.h" |
|
35 #include "Element.h" |
|
36 #include "NamedNodeMap.h" |
|
37 #include "XMLNSNames.h" |
|
38 #include "XPathParser.h" |
|
39 #include "XPathUtil.h" |
|
40 |
|
41 namespace WebCore { |
|
42 namespace XPath { |
|
43 |
|
44 Step::Step(Axis axis, const NodeTest& nodeTest, const Vector<Predicate*>& predicates) |
|
45 : m_axis(axis) |
|
46 , m_nodeTest(nodeTest) |
|
47 , m_predicates(predicates) |
|
48 { |
|
49 } |
|
50 |
|
51 Step::~Step() |
|
52 { |
|
53 deleteAllValues(m_predicates); |
|
54 deleteAllValues(m_nodeTest.mergedPredicates()); |
|
55 } |
|
56 |
|
57 void Step::optimize() |
|
58 { |
|
59 // Evaluate predicates as part of node test if possible to avoid building unnecessary NodeSets. |
|
60 // E.g., there is no need to build a set of all "foo" nodes to evaluate "foo[@bar]", we can check the predicate while enumerating. |
|
61 // This optimization can be applied to predicates that are not context node list sensitive, or to first predicate that is only context position sensitive, e.g. foo[position() mod 2 = 0]. |
|
62 Vector<Predicate*> remainingPredicates; |
|
63 for (size_t i = 0; i < m_predicates.size(); ++i) { |
|
64 Predicate* predicate = m_predicates[i]; |
|
65 if ((!predicate->isContextPositionSensitive() || m_nodeTest.mergedPredicates().isEmpty()) && !predicate->isContextSizeSensitive() && remainingPredicates.isEmpty()) { |
|
66 m_nodeTest.mergedPredicates().append(predicate); |
|
67 } else |
|
68 remainingPredicates.append(predicate); |
|
69 } |
|
70 swap(remainingPredicates, m_predicates); |
|
71 } |
|
72 |
|
73 void optimizeStepPair(Step* first, Step* second, bool& dropSecondStep) |
|
74 { |
|
75 dropSecondStep = false; |
|
76 |
|
77 if (first->m_axis == Step::DescendantOrSelfAxis |
|
78 && first->m_nodeTest.kind() == Step::NodeTest::AnyNodeTest |
|
79 && !first->m_predicates.size() |
|
80 && !first->m_nodeTest.mergedPredicates().size()) { |
|
81 |
|
82 ASSERT(first->m_nodeTest.data().isEmpty()); |
|
83 ASSERT(first->m_nodeTest.namespaceURI().isEmpty()); |
|
84 |
|
85 // Optimize the common case of "//" AKA /descendant-or-self::node()/child::NodeTest to /descendant::NodeTest. |
|
86 if (second->m_axis == Step::ChildAxis && second->predicatesAreContextListInsensitive()) { |
|
87 first->m_axis = Step::DescendantAxis; |
|
88 first->m_nodeTest = Step::NodeTest(second->m_nodeTest.kind(), second->m_nodeTest.data(), second->m_nodeTest.namespaceURI()); |
|
89 swap(second->m_nodeTest.mergedPredicates(), first->m_nodeTest.mergedPredicates()); |
|
90 swap(second->m_predicates, first->m_predicates); |
|
91 first->optimize(); |
|
92 dropSecondStep = true; |
|
93 } |
|
94 } |
|
95 } |
|
96 |
|
97 bool Step::predicatesAreContextListInsensitive() const |
|
98 { |
|
99 for (size_t i = 0; i < m_predicates.size(); ++i) { |
|
100 Predicate* predicate = m_predicates[i]; |
|
101 if (predicate->isContextPositionSensitive() || predicate->isContextSizeSensitive()) |
|
102 return false; |
|
103 } |
|
104 |
|
105 for (size_t i = 0; i < m_nodeTest.mergedPredicates().size(); ++i) { |
|
106 Predicate* predicate = m_nodeTest.mergedPredicates()[i]; |
|
107 if (predicate->isContextPositionSensitive() || predicate->isContextSizeSensitive()) |
|
108 return false; |
|
109 } |
|
110 |
|
111 return true; |
|
112 } |
|
113 |
|
114 void Step::evaluate(Node* context, NodeSet& nodes) const |
|
115 { |
|
116 EvaluationContext& evaluationContext = Expression::evaluationContext(); |
|
117 evaluationContext.position = 0; |
|
118 |
|
119 nodesInAxis(context, nodes); |
|
120 |
|
121 // Check predicates that couldn't be merged into node test. |
|
122 for (unsigned i = 0; i < m_predicates.size(); i++) { |
|
123 Predicate* predicate = m_predicates[i]; |
|
124 |
|
125 NodeSet newNodes; |
|
126 if (!nodes.isSorted()) |
|
127 newNodes.markSorted(false); |
|
128 |
|
129 for (unsigned j = 0; j < nodes.size(); j++) { |
|
130 Node* node = nodes[j]; |
|
131 |
|
132 evaluationContext.node = node; |
|
133 evaluationContext.size = nodes.size(); |
|
134 evaluationContext.position = j + 1; |
|
135 if (predicate->evaluate()) |
|
136 newNodes.append(node); |
|
137 } |
|
138 |
|
139 nodes.swap(newNodes); |
|
140 } |
|
141 } |
|
142 |
|
143 static inline Node::NodeType primaryNodeType(Step::Axis axis) |
|
144 { |
|
145 switch (axis) { |
|
146 case Step::AttributeAxis: |
|
147 return Node::ATTRIBUTE_NODE; |
|
148 case Step::NamespaceAxis: |
|
149 return Node::XPATH_NAMESPACE_NODE; |
|
150 default: |
|
151 return Node::ELEMENT_NODE; |
|
152 } |
|
153 } |
|
154 |
|
155 // Evaluate NodeTest without considering merged predicates. |
|
156 static inline bool nodeMatchesBasicTest(Node* node, Step::Axis axis, const Step::NodeTest& nodeTest) |
|
157 { |
|
158 switch (nodeTest.kind()) { |
|
159 case Step::NodeTest::TextNodeTest: |
|
160 return node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE; |
|
161 case Step::NodeTest::CommentNodeTest: |
|
162 return node->nodeType() == Node::COMMENT_NODE; |
|
163 case Step::NodeTest::ProcessingInstructionNodeTest: { |
|
164 const AtomicString& name = nodeTest.data(); |
|
165 return node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE && (name.isEmpty() || node->nodeName() == name); |
|
166 } |
|
167 case Step::NodeTest::AnyNodeTest: |
|
168 return true; |
|
169 case Step::NodeTest::NameTest: { |
|
170 const AtomicString& name = nodeTest.data(); |
|
171 const AtomicString& namespaceURI = nodeTest.namespaceURI(); |
|
172 |
|
173 if (axis == Step::AttributeAxis) { |
|
174 ASSERT(node->isAttributeNode()); |
|
175 |
|
176 // In XPath land, namespace nodes are not accessible on the attribute axis. |
|
177 if (node->namespaceURI() == XMLNSNames::xmlnsNamespaceURI) |
|
178 return false; |
|
179 |
|
180 if (name == starAtom) |
|
181 return namespaceURI.isEmpty() || node->namespaceURI() == namespaceURI; |
|
182 |
|
183 return node->localName() == name && node->namespaceURI() == namespaceURI; |
|
184 } |
|
185 |
|
186 // Node test on the namespace axis is not implemented yet, the caller has a check for it. |
|
187 ASSERT(axis != Step::NamespaceAxis); |
|
188 |
|
189 // For other axes, the principal node type is element. |
|
190 ASSERT(primaryNodeType(axis) == Node::ELEMENT_NODE); |
|
191 if (node->nodeType() != Node::ELEMENT_NODE) |
|
192 return false; |
|
193 |
|
194 if (name == starAtom) |
|
195 return namespaceURI.isEmpty() || namespaceURI == node->namespaceURI(); |
|
196 |
|
197 if (node->document()->isHTMLDocument()) { |
|
198 if (node->isHTMLElement()) { |
|
199 // Paths without namespaces should match HTML elements in HTML documents despite those having an XHTML namespace. Names are compared case-insensitively. |
|
200 return equalIgnoringCase(static_cast<Element*>(node)->localName(), name) && (namespaceURI.isNull() || namespaceURI == node->namespaceURI()); |
|
201 } |
|
202 // An expression without any prefix shouldn't match no-namespace nodes (because HTML5 says so). |
|
203 return static_cast<Element*>(node)->hasLocalName(name) && namespaceURI == node->namespaceURI() && !namespaceURI.isNull(); |
|
204 } |
|
205 return static_cast<Element*>(node)->hasLocalName(name) && namespaceURI == node->namespaceURI(); |
|
206 } |
|
207 } |
|
208 ASSERT_NOT_REACHED(); |
|
209 return false; |
|
210 } |
|
211 |
|
212 static inline bool nodeMatches(Node* node, Step::Axis axis, const Step::NodeTest& nodeTest) |
|
213 { |
|
214 if (!nodeMatchesBasicTest(node, axis, nodeTest)) |
|
215 return false; |
|
216 |
|
217 EvaluationContext& evaluationContext = Expression::evaluationContext(); |
|
218 |
|
219 // Only the first merged predicate may depend on position. |
|
220 ++evaluationContext.position; |
|
221 |
|
222 const Vector<Predicate*>& mergedPredicates = nodeTest.mergedPredicates(); |
|
223 for (unsigned i = 0; i < mergedPredicates.size(); i++) { |
|
224 Predicate* predicate = mergedPredicates[i]; |
|
225 |
|
226 evaluationContext.node = node; |
|
227 // No need to set context size - we only get here when evaluating predicates that do not depend on it. |
|
228 if (!predicate->evaluate()) |
|
229 return false; |
|
230 } |
|
231 |
|
232 return true; |
|
233 } |
|
234 |
|
235 // Result nodes are ordered in axis order. Node test (including merged predicates) is applied. |
|
236 void Step::nodesInAxis(Node* context, NodeSet& nodes) const |
|
237 { |
|
238 ASSERT(nodes.isEmpty()); |
|
239 switch (m_axis) { |
|
240 case ChildAxis: |
|
241 if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. |
|
242 return; |
|
243 |
|
244 for (Node* n = context->firstChild(); n; n = n->nextSibling()) |
|
245 if (nodeMatches(n, ChildAxis, m_nodeTest)) |
|
246 nodes.append(n); |
|
247 return; |
|
248 case DescendantAxis: |
|
249 if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. |
|
250 return; |
|
251 |
|
252 for (Node* n = context->firstChild(); n; n = n->traverseNextNode(context)) |
|
253 if (nodeMatches(n, DescendantAxis, m_nodeTest)) |
|
254 nodes.append(n); |
|
255 return; |
|
256 case ParentAxis: |
|
257 if (context->isAttributeNode()) { |
|
258 Node* n = static_cast<Attr*>(context)->ownerElement(); |
|
259 if (nodeMatches(n, ParentAxis, m_nodeTest)) |
|
260 nodes.append(n); |
|
261 } else { |
|
262 Node* n = context->parentNode(); |
|
263 if (n && nodeMatches(n, ParentAxis, m_nodeTest)) |
|
264 nodes.append(n); |
|
265 } |
|
266 return; |
|
267 case AncestorAxis: { |
|
268 Node* n = context; |
|
269 if (context->isAttributeNode()) { |
|
270 n = static_cast<Attr*>(context)->ownerElement(); |
|
271 if (nodeMatches(n, AncestorAxis, m_nodeTest)) |
|
272 nodes.append(n); |
|
273 } |
|
274 for (n = n->parentNode(); n; n = n->parentNode()) |
|
275 if (nodeMatches(n, AncestorAxis, m_nodeTest)) |
|
276 nodes.append(n); |
|
277 nodes.markSorted(false); |
|
278 return; |
|
279 } |
|
280 case FollowingSiblingAxis: |
|
281 if (context->nodeType() == Node::ATTRIBUTE_NODE || |
|
282 context->nodeType() == Node::XPATH_NAMESPACE_NODE) |
|
283 return; |
|
284 |
|
285 for (Node* n = context->nextSibling(); n; n = n->nextSibling()) |
|
286 if (nodeMatches(n, FollowingSiblingAxis, m_nodeTest)) |
|
287 nodes.append(n); |
|
288 return; |
|
289 case PrecedingSiblingAxis: |
|
290 if (context->nodeType() == Node::ATTRIBUTE_NODE || |
|
291 context->nodeType() == Node::XPATH_NAMESPACE_NODE) |
|
292 return; |
|
293 |
|
294 for (Node* n = context->previousSibling(); n; n = n->previousSibling()) |
|
295 if (nodeMatches(n, PrecedingSiblingAxis, m_nodeTest)) |
|
296 nodes.append(n); |
|
297 |
|
298 nodes.markSorted(false); |
|
299 return; |
|
300 case FollowingAxis: |
|
301 if (context->isAttributeNode()) { |
|
302 Node* p = static_cast<Attr*>(context)->ownerElement(); |
|
303 while ((p = p->traverseNextNode())) |
|
304 if (nodeMatches(p, FollowingAxis, m_nodeTest)) |
|
305 nodes.append(p); |
|
306 } else { |
|
307 for (Node* p = context; !isRootDomNode(p); p = p->parentNode()) { |
|
308 for (Node* n = p->nextSibling(); n; n = n->nextSibling()) { |
|
309 if (nodeMatches(n, FollowingAxis, m_nodeTest)) |
|
310 nodes.append(n); |
|
311 for (Node* c = n->firstChild(); c; c = c->traverseNextNode(n)) |
|
312 if (nodeMatches(c, FollowingAxis, m_nodeTest)) |
|
313 nodes.append(c); |
|
314 } |
|
315 } |
|
316 } |
|
317 return; |
|
318 case PrecedingAxis: { |
|
319 if (context->isAttributeNode()) |
|
320 context = static_cast<Attr*>(context)->ownerElement(); |
|
321 |
|
322 Node* n = context; |
|
323 while (Node* parent = n->parent()) { |
|
324 for (n = n->traversePreviousNode(); n != parent; n = n->traversePreviousNode()) |
|
325 if (nodeMatches(n, PrecedingAxis, m_nodeTest)) |
|
326 nodes.append(n); |
|
327 n = parent; |
|
328 } |
|
329 nodes.markSorted(false); |
|
330 return; |
|
331 } |
|
332 case AttributeAxis: { |
|
333 if (context->nodeType() != Node::ELEMENT_NODE) |
|
334 return; |
|
335 |
|
336 // Avoid lazily creating attribute nodes for attributes that we do not need anyway. |
|
337 if (m_nodeTest.kind() == NodeTest::NameTest && m_nodeTest.data() != starAtom) { |
|
338 RefPtr<Node> n = static_cast<Element*>(context)->getAttributeNodeNS(m_nodeTest.namespaceURI(), m_nodeTest.data()); |
|
339 if (n && n->namespaceURI() != XMLNSNames::xmlnsNamespaceURI) { // In XPath land, namespace nodes are not accessible on the attribute axis. |
|
340 if (nodeMatches(n.get(), AttributeAxis, m_nodeTest)) // Still need to check merged predicates. |
|
341 nodes.append(n.release()); |
|
342 } |
|
343 return; |
|
344 } |
|
345 |
|
346 NamedNodeMap* attrs = context->attributes(); |
|
347 if (!attrs) |
|
348 return; |
|
349 |
|
350 for (unsigned i = 0; i < attrs->length(); ++i) { |
|
351 RefPtr<Attr> attr = attrs->attributeItem(i)->createAttrIfNeeded(static_cast<Element*>(context)); |
|
352 if (nodeMatches(attr.get(), AttributeAxis, m_nodeTest)) |
|
353 nodes.append(attr.release()); |
|
354 } |
|
355 return; |
|
356 } |
|
357 case NamespaceAxis: |
|
358 // XPath namespace nodes are not implemented yet. |
|
359 return; |
|
360 case SelfAxis: |
|
361 if (nodeMatches(context, SelfAxis, m_nodeTest)) |
|
362 nodes.append(context); |
|
363 return; |
|
364 case DescendantOrSelfAxis: |
|
365 if (nodeMatches(context, DescendantOrSelfAxis, m_nodeTest)) |
|
366 nodes.append(context); |
|
367 if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. |
|
368 return; |
|
369 |
|
370 for (Node* n = context->firstChild(); n; n = n->traverseNextNode(context)) |
|
371 if (nodeMatches(n, DescendantOrSelfAxis, m_nodeTest)) |
|
372 nodes.append(n); |
|
373 return; |
|
374 case AncestorOrSelfAxis: { |
|
375 if (nodeMatches(context, AncestorOrSelfAxis, m_nodeTest)) |
|
376 nodes.append(context); |
|
377 Node* n = context; |
|
378 if (context->isAttributeNode()) { |
|
379 n = static_cast<Attr*>(context)->ownerElement(); |
|
380 if (nodeMatches(n, AncestorOrSelfAxis, m_nodeTest)) |
|
381 nodes.append(n); |
|
382 } |
|
383 for (n = n->parentNode(); n; n = n->parentNode()) |
|
384 if (nodeMatches(n, AncestorOrSelfAxis, m_nodeTest)) |
|
385 nodes.append(n); |
|
386 |
|
387 nodes.markSorted(false); |
|
388 return; |
|
389 } |
|
390 } |
|
391 ASSERT_NOT_REACHED(); |
|
392 } |
|
393 |
|
394 |
|
395 } |
|
396 } |
|
397 |
|
398 #endif // ENABLE(XPATH) |