Value LocationPath::evaluate() const { EvaluationContext& evaluationContext = Expression::evaluationContext(); EvaluationContext backupContext = evaluationContext; // http://www.w3.org/TR/xpath/ // Section 2, Location Paths: // "/ selects the document root (which is always the parent of the document element)" // "A / by itself selects the root node of the document containing the context node." // In the case of a tree that is detached from the document, we violate // the spec and treat / as the root node of the detached tree. // This is for compatibility with Firefox, and also seems like a more // logical treatment of where you would expect the "root" to be. Node* context = evaluationContext.node.get(); if (m_absolute && context->nodeType() != Node::DOCUMENT_NODE) { if (context->inDocument()) context = context->ownerDocument(); else context = context->highestAncestor(); } NodeSet nodes; nodes.append(context); evaluate(nodes); evaluationContext = backupContext; return Value(nodes, Value::adopt); }
void Step::evaluate(Node* context, NodeSet& nodes) const { EvaluationContext& evaluationContext = Expression::evaluationContext(); evaluationContext.position = 0; nodesInAxis(context, nodes); // Check predicates that couldn't be merged into node test. for (unsigned i = 0; i < m_predicates.size(); i++) { Predicate* predicate = m_predicates[i].get(); NodeSet newNodes; if (!nodes.isSorted()) newNodes.markSorted(false); for (unsigned j = 0; j < nodes.size(); j++) { Node* node = nodes[j]; evaluationContext.node = node; evaluationContext.size = nodes.size(); evaluationContext.position = j + 1; if (predicate->evaluate()) newNodes.append(node); } nodes.swap(newNodes); } }
Value Filter::evaluate() const { Value v = m_expr->evaluate(); NodeSet& nodes = v.modifiableNodeSet(); nodes.sort(); EvaluationContext& evaluationContext = Expression::evaluationContext(); for (unsigned i = 0; i < m_predicates.size(); i++) { NodeSet newNodes; evaluationContext.size = nodes.size(); evaluationContext.position = 0; for (unsigned j = 0; j < nodes.size(); j++) { Node* node = nodes[j]; evaluationContext.node = node; ++evaluationContext.position; if (m_predicates[i]->evaluate()) newNodes.append(node); } nodes.swap(newNodes); } return v; }
void Step::evaluate(Node* context, NodeSet& nodes) const { nodesInAxis(context, nodes); EvaluationContext& evaluationContext = Expression::evaluationContext(); for (unsigned i = 0; i < m_predicates.size(); i++) { Predicate* predicate = m_predicates[i]; NodeSet newNodes; if (!nodes.isSorted()) newNodes.markSorted(false); evaluationContext.size = nodes.size(); evaluationContext.position = 1; for (unsigned j = 0; j < nodes.size(); j++) { Node* node = nodes[j]; Expression::evaluationContext().node = node; EvaluationContext backupCtx = evaluationContext; if (predicate->evaluate()) newNodes.append(node); evaluationContext = backupCtx; ++evaluationContext.position; } nodes.swap(newNodes); } }
Value Filter::evaluate() const { Value result = m_expression->evaluate(); NodeSet& nodes = result.modifiableNodeSet(); nodes.sort(); EvaluationContext& evaluationContext = Expression::evaluationContext(); for (unsigned i = 0; i < m_predicates.size(); i++) { NodeSet newNodes; evaluationContext.size = nodes.size(); evaluationContext.position = 0; for (unsigned j = 0; j < nodes.size(); j++) { Node* node = nodes[j]; evaluationContext.node = node; ++evaluationContext.position; if (evaluatePredicate(*m_predicates[i])) newNodes.append(node); } nodes = WTF::move(newNodes); } return result; }
Value Filter::evaluate() const { Value result = m_expression->evaluate(); NodeSet& nodes = result.modifiableNodeSet(); nodes.sort(); EvaluationContext& evaluationContext = Expression::evaluationContext(); for (auto& predicate : m_predicates) { NodeSet newNodes; evaluationContext.size = nodes.size(); evaluationContext.position = 0; for (auto& node : nodes) { evaluationContext.node = node; ++evaluationContext.position; if (evaluatePredicate(*predicate)) newNodes.append(node.copyRef()); } nodes = WTFMove(newNodes); } return result; }
Value LocationPath::evaluate() const { EvaluationContext& evaluationContext = Expression::evaluationContext(); EvaluationContext backupContext = evaluationContext; // For absolute location paths, the context node is ignored - the // document's root node is used instead. Node* context = evaluationContext.node.get(); if (m_absolute && context->nodeType() != Node::DOCUMENT_NODE) context = context->ownerDocument(); NodeSet nodes; nodes.append(context); evaluate(nodes); evaluationContext = backupContext; return Value(nodes, Value::adopt); }
Value FunId::evaluate() const { Value a = argument(0).evaluate(); StringBuilder idList; // A whitespace-separated list of IDs if (!a.isNodeSet()) idList.append(a.toString()); else { for (auto& node : a.toNodeSet()) { idList.append(stringValue(node.get())); idList.append(' '); } } TreeScope& contextScope = evaluationContext().node->treeScope(); NodeSet result; HashSet<Node*> resultSet; unsigned startPos = 0; unsigned length = idList.length(); while (true) { while (startPos < length && isWhitespace(idList[startPos])) ++startPos; if (startPos == length) break; size_t endPos = startPos; while (endPos < length && !isWhitespace(idList[endPos])) ++endPos; // If there are several nodes with the same id, id() should return the first one. // In WebKit, getElementById behaves so, too, although its behavior in this case is formally undefined. Node* node = contextScope.getElementById(atomicSubstring(idList, startPos, endPos - startPos)); if (node && resultSet.add(node).isNewEntry) result.append(node); startPos = endPos; } result.markSorted(false); return Value(WTFMove(result)); }
void LocationPath::evaluate(NodeSet& nodes) const { bool resultIsSorted = nodes.isSorted(); for (unsigned i = 0; i < m_steps.size(); i++) { Step* step = m_steps[i]; NodeSet newNodes; HashSet<Node*> newNodesSet; bool needToCheckForDuplicateNodes = !nodes.subtreesAreDisjoint() || (step->axis() != Step::ChildAxis && step->axis() != Step::SelfAxis && step->axis() != Step::DescendantAxis && step->axis() != Step::DescendantOrSelfAxis && step->axis() != Step::AttributeAxis); if (needToCheckForDuplicateNodes) resultIsSorted = false; // This is a simplified check that can be improved to handle more cases. if (nodes.subtreesAreDisjoint() && (step->axis() == Step::ChildAxis || step->axis() == Step::SelfAxis)) newNodes.markSubtreesDisjoint(true); for (unsigned j = 0; j < nodes.size(); j++) { NodeSet matches; step->evaluate(nodes[j], matches); if (!matches.isSorted()) resultIsSorted = false; for (size_t nodeIndex = 0; nodeIndex < matches.size(); ++nodeIndex) { Node* node = matches[nodeIndex]; if (!needToCheckForDuplicateNodes || newNodesSet.add(node).isNewEntry) newNodes.append(node); } } nodes.swap(newNodes); } nodes.markSorted(resultIsSorted); }
Value LocationPath::evaluate() const { EvaluationContext& evaluationContext = Expression::evaluationContext(); EvaluationContext backupContext = evaluationContext; // http://www.w3.org/TR/xpath/ // Section 2, Location Paths: // "/ selects the document root (which is always the parent of the document element)" // "A / by itself selects the root node of the document containing the context node." // In the case of a tree that is detached from the document, we violate // the spec and treat / as the root node of the detached tree. // This is for compatibility with Firefox, and also seems like a more // logical treatment of where you would expect the "root" to be. Node* context = evaluationContext.node.get(); if (m_isAbsolute && !context->isDocumentNode()) context = &context->rootNode(); NodeSet nodes; nodes.append(context); evaluate(nodes); evaluationContext = backupContext; return Value(WTFMove(nodes)); }
void LocationPath::evaluate(NodeSet& nodes) const { bool resultIsSorted = nodes.isSorted(); for (auto& step : m_steps) { NodeSet newNodes; HashSet<Node*> newNodesSet; bool needToCheckForDuplicateNodes = !nodes.subtreesAreDisjoint() || (step->axis() != Step::ChildAxis && step->axis() != Step::SelfAxis && step->axis() != Step::DescendantAxis && step->axis() != Step::DescendantOrSelfAxis && step->axis() != Step::AttributeAxis); if (needToCheckForDuplicateNodes) resultIsSorted = false; // This is a simplified check that can be improved to handle more cases. if (nodes.subtreesAreDisjoint() && (step->axis() == Step::ChildAxis || step->axis() == Step::SelfAxis)) newNodes.markSubtreesDisjoint(true); for (auto& node : nodes) { NodeSet matches; step->evaluate(*node, matches); if (!matches.isSorted()) resultIsSorted = false; for (auto& match : matches) { if (!needToCheckForDuplicateNodes || newNodesSet.add(match.get()).isNewEntry) newNodes.append(match.copyRef()); } } nodes = WTFMove(newNodes); } nodes.markSorted(resultIsSorted); }
// Result nodes are ordered in axis order. Node test (including merged predicates) is applied. void Step::nodesInAxis(Node* context, NodeSet& nodes) const { ASSERT(nodes.isEmpty()); switch (m_axis) { case ChildAxis: if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. return; for (Node* n = context->firstChild(); n; n = n->nextSibling()) if (nodeMatches(n, ChildAxis, m_nodeTest)) nodes.append(n); return; case DescendantAxis: if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. return; for (Node* n = context->firstChild(); n; n = NodeTraversal::next(*n, context)) if (nodeMatches(n, DescendantAxis, m_nodeTest)) nodes.append(n); return; case ParentAxis: if (context->isAttributeNode()) { Element* n = toAttr(context)->ownerElement(); if (nodeMatches(n, ParentAxis, m_nodeTest)) nodes.append(n); } else { ContainerNode* n = context->parentNode(); if (n && nodeMatches(n, ParentAxis, m_nodeTest)) nodes.append(n); } return; case AncestorAxis: { Node* n = context; if (context->isAttributeNode()) { n = toAttr(context)->ownerElement(); if (nodeMatches(n, AncestorAxis, m_nodeTest)) nodes.append(n); } for (n = n->parentNode(); n; n = n->parentNode()) if (nodeMatches(n, AncestorAxis, m_nodeTest)) nodes.append(n); nodes.markSorted(false); return; } case FollowingSiblingAxis: if (context->nodeType() == Node::ATTRIBUTE_NODE || context->nodeType() == Node::XPATH_NAMESPACE_NODE) return; for (Node* n = context->nextSibling(); n; n = n->nextSibling()) if (nodeMatches(n, FollowingSiblingAxis, m_nodeTest)) nodes.append(n); return; case PrecedingSiblingAxis: if (context->nodeType() == Node::ATTRIBUTE_NODE || context->nodeType() == Node::XPATH_NAMESPACE_NODE) return; for (Node* n = context->previousSibling(); n; n = n->previousSibling()) if (nodeMatches(n, PrecedingSiblingAxis, m_nodeTest)) nodes.append(n); nodes.markSorted(false); return; case FollowingAxis: if (context->isAttributeNode()) { Node* p = toAttr(context)->ownerElement(); while ((p = NodeTraversal::next(*p))) { if (nodeMatches(p, FollowingAxis, m_nodeTest)) nodes.append(p); } } else { for (Node* p = context; !isRootDomNode(p); p = p->parentNode()) { for (Node* n = p->nextSibling(); n; n = n->nextSibling()) { if (nodeMatches(n, FollowingAxis, m_nodeTest)) nodes.append(n); for (Node* c = n->firstChild(); c; c = NodeTraversal::next(*c, n)) if (nodeMatches(c, FollowingAxis, m_nodeTest)) nodes.append(c); } } } return; case PrecedingAxis: { if (context->isAttributeNode()) context = toAttr(context)->ownerElement(); Node* n = context; while (ContainerNode* parent = n->parentNode()) { for (n = NodeTraversal::previous(*n); n != parent; n = NodeTraversal::previous(*n)) if (nodeMatches(n, PrecedingAxis, m_nodeTest)) nodes.append(n); n = parent; } nodes.markSorted(false); return; } case AttributeAxis: { if (!context->isElementNode()) return; Element* contextElement = toElement(context); // Avoid lazily creating attribute nodes for attributes that we do not need anyway. if (m_nodeTest.kind() == NodeTest::NameTest && m_nodeTest.data() != starAtom) { RefPtr<Node> n = contextElement->getAttributeNodeNS(m_nodeTest.namespaceURI(), m_nodeTest.data()); if (n && n->namespaceURI() != XMLNSNames::xmlnsNamespaceURI) { // In XPath land, namespace nodes are not accessible on the attribute axis. if (nodeMatches(n.get(), AttributeAxis, m_nodeTest)) // Still need to check merged predicates. nodes.append(n.release()); } return; } if (!contextElement->hasAttributes()) return; for (unsigned i = 0; i < contextElement->attributeCount(); ++i) { RefPtr<Attr> attr = contextElement->ensureAttr(contextElement->attributeItem(i)->name()); if (nodeMatches(attr.get(), AttributeAxis, m_nodeTest)) nodes.append(attr.release()); } return; } case NamespaceAxis: // XPath namespace nodes are not implemented yet. return; case SelfAxis: if (nodeMatches(context, SelfAxis, m_nodeTest)) nodes.append(context); return; case DescendantOrSelfAxis: if (nodeMatches(context, DescendantOrSelfAxis, m_nodeTest)) nodes.append(context); if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. return; for (Node* n = context->firstChild(); n; n = NodeTraversal::next(*n, context)) if (nodeMatches(n, DescendantOrSelfAxis, m_nodeTest)) nodes.append(n); return; case AncestorOrSelfAxis: { if (nodeMatches(context, AncestorOrSelfAxis, m_nodeTest)) nodes.append(context); Node* n = context; if (context->isAttributeNode()) { n = toAttr(context)->ownerElement(); if (nodeMatches(n, AncestorOrSelfAxis, m_nodeTest)) nodes.append(n); } for (n = n->parentNode(); n; n = n->parentNode()) if (nodeMatches(n, AncestorOrSelfAxis, m_nodeTest)) nodes.append(n); nodes.markSorted(false); return; } } ASSERT_NOT_REACHED(); }
// Result nodes are ordered in axis order. Node test (including merged // predicates) is applied. void Step::nodesInAxis(EvaluationContext& evaluationContext, Node* context, NodeSet& nodes) const { ASSERT(nodes.isEmpty()); switch (m_axis) { case ChildAxis: // In XPath model, attribute nodes do not have children. if (context->isAttributeNode()) return; for (Node* n = context->firstChild(); n; n = n->nextSibling()) { if (nodeMatches(evaluationContext, n, ChildAxis, nodeTest())) nodes.append(n); } return; case DescendantAxis: // In XPath model, attribute nodes do not have children. if (context->isAttributeNode()) return; for (Node* n = context->firstChild(); n; n = NodeTraversal::next(*n, context)) { if (nodeMatches(evaluationContext, n, DescendantAxis, nodeTest())) nodes.append(n); } return; case ParentAxis: if (context->isAttributeNode()) { Element* n = toAttr(context)->ownerElement(); if (nodeMatches(evaluationContext, n, ParentAxis, nodeTest())) nodes.append(n); } else { ContainerNode* n = context->parentNode(); if (n && nodeMatches(evaluationContext, n, ParentAxis, nodeTest())) nodes.append(n); } return; case AncestorAxis: { Node* n = context; if (context->isAttributeNode()) { n = toAttr(context)->ownerElement(); if (nodeMatches(evaluationContext, n, AncestorAxis, nodeTest())) nodes.append(n); } for (n = n->parentNode(); n; n = n->parentNode()) { if (nodeMatches(evaluationContext, n, AncestorAxis, nodeTest())) nodes.append(n); } nodes.markSorted(false); return; } case FollowingSiblingAxis: if (context->nodeType() == Node::ATTRIBUTE_NODE) return; for (Node* n = context->nextSibling(); n; n = n->nextSibling()) { if (nodeMatches(evaluationContext, n, FollowingSiblingAxis, nodeTest())) nodes.append(n); } return; case PrecedingSiblingAxis: if (context->nodeType() == Node::ATTRIBUTE_NODE) return; for (Node* n = context->previousSibling(); n; n = n->previousSibling()) { if (nodeMatches(evaluationContext, n, PrecedingSiblingAxis, nodeTest())) nodes.append(n); } nodes.markSorted(false); return; case FollowingAxis: if (context->isAttributeNode()) { for (Node* p = NodeTraversal::next(*toAttr(context)->ownerElement()); p; p = NodeTraversal::next(*p)) { if (nodeMatches(evaluationContext, p, FollowingAxis, nodeTest())) nodes.append(p); } } else { for (Node* p = context; !isRootDomNode(p); p = p->parentNode()) { for (Node* n = p->nextSibling(); n; n = n->nextSibling()) { if (nodeMatches(evaluationContext, n, FollowingAxis, nodeTest())) nodes.append(n); for (Node* c = n->firstChild(); c; c = NodeTraversal::next(*c, n)) { if (nodeMatches(evaluationContext, c, FollowingAxis, nodeTest())) nodes.append(c); } } } } return; case PrecedingAxis: { if (context->isAttributeNode()) context = toAttr(context)->ownerElement(); Node* n = context; while (ContainerNode* parent = n->parentNode()) { for (n = NodeTraversal::previous(*n); n != parent; n = NodeTraversal::previous(*n)) { if (nodeMatches(evaluationContext, n, PrecedingAxis, nodeTest())) nodes.append(n); } n = parent; } nodes.markSorted(false); return; } case AttributeAxis: { if (!context->isElementNode()) return; Element* contextElement = toElement(context); // Avoid lazily creating attribute nodes for attributes that we do not // need anyway. if (nodeTest().kind() == NodeTest::NameTest && nodeTest().data() != starAtom) { RefPtrWillBeRawPtr<Node> n = contextElement->getAttributeNodeNS(nodeTest().namespaceURI(), nodeTest().data()); // In XPath land, namespace nodes are not accessible on the attribute axis. if (n && n->namespaceURI() != XMLNSNames::xmlnsNamespaceURI) { // Still need to check merged predicates. if (nodeMatches(evaluationContext, n.get(), AttributeAxis, nodeTest())) nodes.append(n.release()); } return; } AttributeCollection attributes = contextElement->attributes(); AttributeCollection::iterator end = attributes.end(); for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) { RefPtrWillBeRawPtr<Attr> attr = contextElement->ensureAttr(it->name()); if (nodeMatches(evaluationContext, attr.get(), AttributeAxis, nodeTest())) nodes.append(attr.release()); } return; } case NamespaceAxis: // XPath namespace nodes are not implemented. return; case SelfAxis: if (nodeMatches(evaluationContext, context, SelfAxis, nodeTest())) nodes.append(context); return; case DescendantOrSelfAxis: if (nodeMatches(evaluationContext, context, DescendantOrSelfAxis, nodeTest())) nodes.append(context); // In XPath model, attribute nodes do not have children. if (context->isAttributeNode()) return; for (Node* n = context->firstChild(); n; n = NodeTraversal::next(*n, context)) { if (nodeMatches(evaluationContext, n, DescendantOrSelfAxis, nodeTest())) nodes.append(n); } return; case AncestorOrSelfAxis: { if (nodeMatches(evaluationContext, context, AncestorOrSelfAxis, nodeTest())) nodes.append(context); Node* n = context; if (context->isAttributeNode()) { n = toAttr(context)->ownerElement(); if (nodeMatches(evaluationContext, n, AncestorOrSelfAxis, nodeTest())) nodes.append(n); } for (n = n->parentNode(); n; n = n->parentNode()) { if (nodeMatches(evaluationContext, n, AncestorOrSelfAxis, nodeTest())) nodes.append(n); } nodes.markSorted(false); return; } } ASSERT_NOT_REACHED(); }
void Step::nodesInAxis(Node* context, NodeSet& nodes) const { ASSERT(nodes.isEmpty()); switch (m_axis) { case ChildAxis: if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. return; for (Node* n = context->firstChild(); n; n = n->nextSibling()) if (nodeMatches(n)) nodes.append(n); return; case DescendantAxis: if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. return; for (Node* n = context->firstChild(); n; n = n->traverseNextNode(context)) if (nodeMatches(n)) nodes.append(n); return; case ParentAxis: if (context->isAttributeNode()) { Node* n = static_cast<Attr*>(context)->ownerElement(); if (nodeMatches(n)) nodes.append(n); } else { Node* n = context->parentNode(); if (n && nodeMatches(n)) nodes.append(n); } return; case AncestorAxis: { Node* n = context; if (context->isAttributeNode()) { n = static_cast<Attr*>(context)->ownerElement(); if (nodeMatches(n)) nodes.append(n); } for (n = n->parentNode(); n; n = n->parentNode()) if (nodeMatches(n)) nodes.append(n); nodes.reverse(); return; } case FollowingSiblingAxis: if (context->nodeType() == Node::ATTRIBUTE_NODE || context->nodeType() == Node::XPATH_NAMESPACE_NODE) return; for (Node* n = context->nextSibling(); n; n = n->nextSibling()) if (nodeMatches(n)) nodes.append(n); return; case PrecedingSiblingAxis: if (context->nodeType() == Node::ATTRIBUTE_NODE || context->nodeType() == Node::XPATH_NAMESPACE_NODE) return; for (Node* n = context->previousSibling(); n; n = n->previousSibling()) if (nodeMatches(n)) nodes.append(n); nodes.reverse(); return; case FollowingAxis: if (context->isAttributeNode()) { Node* p = static_cast<Attr*>(context)->ownerElement(); while ((p = p->traverseNextNode())) if (nodeMatches(p)) nodes.append(p); } else { for (Node* p = context; !isRootDomNode(p); p = p->parentNode()) { for (Node* n = p->nextSibling(); n; n = n->nextSibling()) { if (nodeMatches(n)) nodes.append(n); for (Node* c = n->firstChild(); c; c = c->traverseNextNode(n)) if (nodeMatches(c)) nodes.append(c); } } } return; case PrecedingAxis: if (context->isAttributeNode()) context = static_cast<Attr*>(context)->ownerElement(); for (Node* p = context; !isRootDomNode(p); p = p->parentNode()) { for (Node* n = p->previousSibling(); n ; n = n->previousSibling()) { if (nodeMatches(n)) nodes.append(n); for (Node* c = n->firstChild(); c; c = c->traverseNextNode(n)) if (nodeMatches(c)) nodes.append(c); } } nodes.markSorted(false); return; case AttributeAxis: { if (context->nodeType() != Node::ELEMENT_NODE) return; // Avoid lazily creating attribute nodes for attributes that we do not need anyway. if (m_nodeTest.kind() == NodeTest::NameTest && m_nodeTest.data() != "*") { RefPtr<Node> n = static_cast<Element*>(context)->getAttributeNodeNS(m_nodeTest.namespaceURI(), m_nodeTest.data()); if (n && n->namespaceURI() != "http://www.w3.org/2000/xmlns/") // In XPath land, namespace nodes are not accessible on the attribute axis. nodes.append(n.release()); return; } NamedAttrMap* attrs = context->attributes(); if (!attrs) return; for (unsigned long i = 0; i < attrs->length(); ++i) { RefPtr<Node> n = attrs->item(i); if (nodeMatches(n.get())) nodes.append(n.release()); } return; } case NamespaceAxis: // XPath namespace nodes are not implemented yet. return; case SelfAxis: if (nodeMatches(context)) nodes.append(context); return; case DescendantOrSelfAxis: if (nodeMatches(context)) nodes.append(context); if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. return; for (Node* n = context->firstChild(); n; n = n->traverseNextNode(context)) if (nodeMatches(n)) nodes.append(n); return; case AncestorOrSelfAxis: { if (nodeMatches(context)) nodes.append(context); Node* n = context; if (context->isAttributeNode()) { n = static_cast<Attr*>(context)->ownerElement(); if (nodeMatches(n)) nodes.append(n); } for (n = n->parentNode(); n; n = n->parentNode()) if (nodeMatches(n)) nodes.append(n); nodes.reverse(); return; } } ASSERT_NOT_REACHED(); }