1 /* 2 * Copyright (C) 2005 Frerich Raabe <raabe (at) kde.org> 3 * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. 4 * Copyright (C) 2007 Alexey Proskuryakov <ap (at) 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 "core/xml/XPathStep.h" 30 31 #include "core/XMLNSNames.h" 32 #include "core/dom/Attr.h" 33 #include "core/dom/Document.h" 34 #include "core/dom/Element.h" 35 #include "core/dom/NodeTraversal.h" 36 #include "core/xml/XPathParser.h" 37 #include "core/xml/XPathUtil.h" 38 39 namespace WebCore { 40 namespace XPath { 41 42 Step::Step(Axis axis, const NodeTest& nodeTest) 43 : m_axis(axis) 44 , m_nodeTest(adoptPtrWillBeNoop(new NodeTest(nodeTest))) 45 { 46 } 47 48 Step::Step(Axis axis, const NodeTest& nodeTest, WillBeHeapVector<OwnPtrWillBeMember<Predicate> >& predicates) 49 : m_axis(axis) 50 , m_nodeTest(adoptPtrWillBeNoop(new NodeTest(nodeTest))) 51 { 52 m_predicates.swap(predicates); 53 } 54 55 Step::~Step() 56 { 57 } 58 59 void Step::trace(Visitor* visitor) 60 { 61 visitor->trace(m_nodeTest); 62 visitor->trace(m_predicates); 63 ParseNode::trace(visitor); 64 } 65 66 void Step::optimize() 67 { 68 // Evaluate predicates as part of node test if possible to avoid building 69 // unnecessary NodeSets. 70 // E.g., there is no need to build a set of all "foo" nodes to evaluate 71 // "foo[@bar]", we can check the predicate while enumerating. 72 // This optimization can be applied to predicates that are not context node 73 // list sensitive, or to first predicate that is only context position 74 // sensitive, e.g. foo[position() mod 2 = 0]. 75 WillBeHeapVector<OwnPtrWillBeMember<Predicate> > remainingPredicates; 76 for (size_t i = 0; i < m_predicates.size(); ++i) { 77 OwnPtrWillBeRawPtr<Predicate> predicate(m_predicates[i].release()); 78 if ((!predicate->isContextPositionSensitive() || nodeTest().mergedPredicates().isEmpty()) && !predicate->isContextSizeSensitive() && remainingPredicates.isEmpty()) { 79 nodeTest().mergedPredicates().append(predicate.release()); 80 } else { 81 remainingPredicates.append(predicate.release()); 82 } 83 } 84 swap(remainingPredicates, m_predicates); 85 } 86 87 void optimizeStepPair(Step* first, Step* second, bool& dropSecondStep) 88 { 89 dropSecondStep = false; 90 91 if (first->m_axis == Step::DescendantOrSelfAxis 92 && first->nodeTest().kind() == Step::NodeTest::AnyNodeTest 93 && !first->m_predicates.size() 94 && !first->nodeTest().mergedPredicates().size()) { 95 96 ASSERT(first->nodeTest().data().isEmpty()); 97 ASSERT(first->nodeTest().namespaceURI().isEmpty()); 98 99 // Optimize the common case of "//" AKA 100 // /descendant-or-self::node()/child::NodeTest to /descendant::NodeTest. 101 if (second->m_axis == Step::ChildAxis && second->predicatesAreContextListInsensitive()) { 102 first->m_axis = Step::DescendantAxis; 103 first->nodeTest() = Step::NodeTest(second->nodeTest().kind(), second->nodeTest().data(), second->nodeTest().namespaceURI()); 104 swap(second->nodeTest().mergedPredicates(), first->nodeTest().mergedPredicates()); 105 swap(second->m_predicates, first->m_predicates); 106 first->optimize(); 107 dropSecondStep = true; 108 } 109 } 110 } 111 112 bool Step::predicatesAreContextListInsensitive() const 113 { 114 for (size_t i = 0; i < m_predicates.size(); ++i) { 115 Predicate* predicate = m_predicates[i].get(); 116 if (predicate->isContextPositionSensitive() || predicate->isContextSizeSensitive()) 117 return false; 118 } 119 120 for (size_t i = 0; i < nodeTest().mergedPredicates().size(); ++i) { 121 Predicate* predicate = nodeTest().mergedPredicates()[i].get(); 122 if (predicate->isContextPositionSensitive() || predicate->isContextSizeSensitive()) 123 return false; 124 } 125 126 return true; 127 } 128 129 void Step::evaluate(Node* context, NodeSet& nodes) const 130 { 131 EvaluationContext& evaluationContext = Expression::evaluationContext(); 132 evaluationContext.position = 0; 133 134 nodesInAxis(context, nodes); 135 136 // Check predicates that couldn't be merged into node test. 137 for (unsigned i = 0; i < m_predicates.size(); i++) { 138 Predicate* predicate = m_predicates[i].get(); 139 140 OwnPtrWillBeRawPtr<NodeSet> newNodes(NodeSet::create()); 141 if (!nodes.isSorted()) 142 newNodes->markSorted(false); 143 144 for (unsigned j = 0; j < nodes.size(); j++) { 145 Node* node = nodes[j]; 146 147 evaluationContext.node = node; 148 evaluationContext.size = nodes.size(); 149 evaluationContext.position = j + 1; 150 if (predicate->evaluate()) 151 newNodes->append(node); 152 } 153 154 nodes.swap(*newNodes); 155 } 156 } 157 158 #if ASSERT_ENABLED 159 static inline Node::NodeType primaryNodeType(Step::Axis axis) 160 { 161 switch (axis) { 162 case Step::AttributeAxis: 163 return Node::ATTRIBUTE_NODE; 164 default: 165 return Node::ELEMENT_NODE; 166 } 167 } 168 #endif 169 170 // Evaluate NodeTest without considering merged predicates. 171 static inline bool nodeMatchesBasicTest(Node* node, Step::Axis axis, const Step::NodeTest& nodeTest) 172 { 173 switch (nodeTest.kind()) { 174 case Step::NodeTest::TextNodeTest: { 175 Node::NodeType type = node->nodeType(); 176 return type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE; 177 } 178 case Step::NodeTest::CommentNodeTest: 179 return node->nodeType() == Node::COMMENT_NODE; 180 case Step::NodeTest::ProcessingInstructionNodeTest: { 181 const AtomicString& name = nodeTest.data(); 182 return node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE && (name.isEmpty() || node->nodeName() == name); 183 } 184 case Step::NodeTest::AnyNodeTest: 185 return true; 186 case Step::NodeTest::NameTest: { 187 const AtomicString& name = nodeTest.data(); 188 const AtomicString& namespaceURI = nodeTest.namespaceURI(); 189 190 if (axis == Step::AttributeAxis) { 191 ASSERT(node->isAttributeNode()); 192 193 // In XPath land, namespace nodes are not accessible on the 194 // attribute axis. 195 if (node->namespaceURI() == XMLNSNames::xmlnsNamespaceURI) 196 return false; 197 198 if (name == starAtom) 199 return namespaceURI.isEmpty() || node->namespaceURI() == namespaceURI; 200 201 return node->localName() == name && node->namespaceURI() == namespaceURI; 202 } 203 204 // Node test on the namespace axis is not implemented yet, the caller 205 // has a check for it. 206 ASSERT(axis != Step::NamespaceAxis); 207 208 // For other axes, the principal node type is element. 209 ASSERT(primaryNodeType(axis) == Node::ELEMENT_NODE); 210 if (!node->isElementNode()) 211 return false; 212 Element& element = toElement(*node); 213 214 if (name == starAtom) 215 return namespaceURI.isEmpty() || namespaceURI == element.namespaceURI(); 216 217 if (element.document().isHTMLDocument()) { 218 if (element.isHTMLElement()) { 219 // Paths without namespaces should match HTML elements in HTML 220 // documents despite those having an XHTML namespace. Names are 221 // compared case-insensitively. 222 return equalIgnoringCase(element.localName(), name) && (namespaceURI.isNull() || namespaceURI == element.namespaceURI()); 223 } 224 // An expression without any prefix shouldn't match no-namespace 225 // nodes (because HTML5 says so). 226 return element.hasLocalName(name) && namespaceURI == element.namespaceURI() && !namespaceURI.isNull(); 227 } 228 return element.hasLocalName(name) && namespaceURI == element.namespaceURI(); 229 } 230 } 231 ASSERT_NOT_REACHED(); 232 return false; 233 } 234 235 static inline bool nodeMatches(Node* node, Step::Axis axis, const Step::NodeTest& nodeTest) 236 { 237 if (!nodeMatchesBasicTest(node, axis, nodeTest)) 238 return false; 239 240 EvaluationContext& evaluationContext = Expression::evaluationContext(); 241 242 // Only the first merged predicate may depend on position. 243 ++evaluationContext.position; 244 245 const WillBeHeapVector<OwnPtrWillBeMember<Predicate> >& mergedPredicates = nodeTest.mergedPredicates(); 246 for (unsigned i = 0; i < mergedPredicates.size(); i++) { 247 Predicate* predicate = mergedPredicates[i].get(); 248 249 evaluationContext.node = node; 250 // No need to set context size - we only get here when evaluating 251 // predicates that do not depend on it. 252 if (!predicate->evaluate()) 253 return false; 254 } 255 256 return true; 257 } 258 259 // Result nodes are ordered in axis order. Node test (including merged 260 // predicates) is applied. 261 void Step::nodesInAxis(Node* context, NodeSet& nodes) const 262 { 263 ASSERT(nodes.isEmpty()); 264 switch (m_axis) { 265 case ChildAxis: 266 // In XPath model, attribute nodes do not have children. 267 if (context->isAttributeNode()) 268 return; 269 270 for (Node* n = context->firstChild(); n; n = n->nextSibling()) { 271 if (nodeMatches(n, ChildAxis, nodeTest())) 272 nodes.append(n); 273 } 274 return; 275 276 case DescendantAxis: 277 // In XPath model, attribute nodes do not have children. 278 if (context->isAttributeNode()) 279 return; 280 281 for (Node* n = context->firstChild(); n; n = NodeTraversal::next(*n, context)) { 282 if (nodeMatches(n, DescendantAxis, nodeTest())) 283 nodes.append(n); 284 } 285 return; 286 287 case ParentAxis: 288 if (context->isAttributeNode()) { 289 Element* n = toAttr(context)->ownerElement(); 290 if (nodeMatches(n, ParentAxis, nodeTest())) 291 nodes.append(n); 292 } else { 293 ContainerNode* n = context->parentNode(); 294 if (n && nodeMatches(n, ParentAxis, nodeTest())) 295 nodes.append(n); 296 } 297 return; 298 299 case AncestorAxis: { 300 Node* n = context; 301 if (context->isAttributeNode()) { 302 n = toAttr(context)->ownerElement(); 303 if (nodeMatches(n, AncestorAxis, nodeTest())) 304 nodes.append(n); 305 } 306 for (n = n->parentNode(); n; n = n->parentNode()) { 307 if (nodeMatches(n, AncestorAxis, nodeTest())) 308 nodes.append(n); 309 } 310 nodes.markSorted(false); 311 return; 312 } 313 314 case FollowingSiblingAxis: 315 if (context->nodeType() == Node::ATTRIBUTE_NODE) 316 return; 317 318 for (Node* n = context->nextSibling(); n; n = n->nextSibling()) { 319 if (nodeMatches(n, FollowingSiblingAxis, nodeTest())) 320 nodes.append(n); 321 } 322 return; 323 324 case PrecedingSiblingAxis: 325 if (context->nodeType() == Node::ATTRIBUTE_NODE) 326 return; 327 328 for (Node* n = context->previousSibling(); n; n = n->previousSibling()) { 329 if (nodeMatches(n, PrecedingSiblingAxis, nodeTest())) 330 nodes.append(n); 331 } 332 nodes.markSorted(false); 333 return; 334 335 case FollowingAxis: 336 if (context->isAttributeNode()) { 337 Node* p = toAttr(context)->ownerElement(); 338 while ((p = NodeTraversal::next(*p))) { 339 if (nodeMatches(p, FollowingAxis, nodeTest())) 340 nodes.append(p); 341 } 342 } else { 343 for (Node* p = context; !isRootDomNode(p); p = p->parentNode()) { 344 for (Node* n = p->nextSibling(); n; n = n->nextSibling()) { 345 if (nodeMatches(n, FollowingAxis, nodeTest())) 346 nodes.append(n); 347 for (Node* c = n->firstChild(); c; c = NodeTraversal::next(*c, n)) { 348 if (nodeMatches(c, FollowingAxis, nodeTest())) 349 nodes.append(c); 350 } 351 } 352 } 353 } 354 return; 355 356 case PrecedingAxis: { 357 if (context->isAttributeNode()) 358 context = toAttr(context)->ownerElement(); 359 360 Node* n = context; 361 while (ContainerNode* parent = n->parentNode()) { 362 for (n = NodeTraversal::previous(*n); n != parent; n = NodeTraversal::previous(*n)) { 363 if (nodeMatches(n, PrecedingAxis, nodeTest())) 364 nodes.append(n); 365 } 366 n = parent; 367 } 368 nodes.markSorted(false); 369 return; 370 } 371 372 case AttributeAxis: { 373 if (!context->isElementNode()) 374 return; 375 376 Element* contextElement = toElement(context); 377 // Avoid lazily creating attribute nodes for attributes that we do not 378 // need anyway. 379 if (nodeTest().kind() == NodeTest::NameTest && nodeTest().data() != starAtom) { 380 RefPtrWillBeRawPtr<Node> n = contextElement->getAttributeNodeNS(nodeTest().namespaceURI(), nodeTest().data()); 381 // In XPath land, namespace nodes are not accessible on the attribute axis. 382 if (n && n->namespaceURI() != XMLNSNames::xmlnsNamespaceURI) { 383 // Still need to check merged predicates. 384 if (nodeMatches(n.get(), AttributeAxis, nodeTest())) 385 nodes.append(n.release()); 386 } 387 return; 388 } 389 390 if (!contextElement->hasAttributes()) 391 return; 392 393 AttributeCollection attributes = contextElement->attributes(); 394 AttributeCollection::const_iterator end = attributes.end(); 395 for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) { 396 RefPtrWillBeRawPtr<Attr> attr = contextElement->ensureAttr(it->name()); 397 if (nodeMatches(attr.get(), AttributeAxis, nodeTest())) 398 nodes.append(attr.release()); 399 } 400 return; 401 } 402 403 case NamespaceAxis: 404 // XPath namespace nodes are not implemented. 405 return; 406 407 case SelfAxis: 408 if (nodeMatches(context, SelfAxis, nodeTest())) 409 nodes.append(context); 410 return; 411 412 case DescendantOrSelfAxis: 413 if (nodeMatches(context, DescendantOrSelfAxis, nodeTest())) 414 nodes.append(context); 415 // In XPath model, attribute nodes do not have children. 416 if (context->isAttributeNode()) 417 return; 418 419 for (Node* n = context->firstChild(); n; n = NodeTraversal::next(*n, context)) { 420 if (nodeMatches(n, DescendantOrSelfAxis, nodeTest())) 421 nodes.append(n); 422 } 423 return; 424 425 case AncestorOrSelfAxis: { 426 if (nodeMatches(context, AncestorOrSelfAxis, nodeTest())) 427 nodes.append(context); 428 Node* n = context; 429 if (context->isAttributeNode()) { 430 n = toAttr(context)->ownerElement(); 431 if (nodeMatches(n, AncestorOrSelfAxis, nodeTest())) 432 nodes.append(n); 433 } 434 for (n = n->parentNode(); n; n = n->parentNode()) { 435 if (nodeMatches(n, AncestorOrSelfAxis, nodeTest())) 436 nodes.append(n); 437 } 438 nodes.markSorted(false); 439 return; 440 } 441 } 442 ASSERT_NOT_REACHED(); 443 } 444 445 } 446 447 } 448