1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/xslt/xpath/txPathExpr.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,253 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "txExpr.h" 1.10 +#include "txNodeSet.h" 1.11 +#include "txNodeSetContext.h" 1.12 +#include "txSingleNodeContext.h" 1.13 +#include "txXMLUtils.h" 1.14 +#include "txXPathTreeWalker.h" 1.15 + 1.16 + //------------/ 1.17 + //- PathExpr -/ 1.18 +//------------/ 1.19 + 1.20 +/** 1.21 + * Adds the Expr to this PathExpr 1.22 + * @param expr the Expr to add to this PathExpr 1.23 +**/ 1.24 +nsresult 1.25 +PathExpr::addExpr(Expr* aExpr, PathOperator aPathOp) 1.26 +{ 1.27 + NS_ASSERTION(!mItems.IsEmpty() || aPathOp == RELATIVE_OP, 1.28 + "First step has to be relative in PathExpr"); 1.29 + PathExprItem* pxi = mItems.AppendElement(); 1.30 + if (!pxi) { 1.31 + return NS_ERROR_OUT_OF_MEMORY; 1.32 + } 1.33 + pxi->expr = aExpr; 1.34 + pxi->pathOp = aPathOp; 1.35 + 1.36 + return NS_OK; 1.37 +} 1.38 + 1.39 + //-----------------------------/ 1.40 + //- Virtual methods from Expr -/ 1.41 +//-----------------------------/ 1.42 + 1.43 +/** 1.44 + * Evaluates this Expr based on the given context node and processor state 1.45 + * @param context the context node for evaluation of this Expr 1.46 + * @param ps the ContextState containing the stack information needed 1.47 + * for evaluation 1.48 + * @return the result of the evaluation 1.49 +**/ 1.50 +nsresult 1.51 +PathExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult) 1.52 +{ 1.53 + *aResult = nullptr; 1.54 + 1.55 + // We need to evaluate the first step with the current context since it 1.56 + // can depend on the context size and position. For example: 1.57 + // key('books', concat('book', position())) 1.58 + nsRefPtr<txAExprResult> res; 1.59 + nsresult rv = mItems[0].expr->evaluate(aContext, getter_AddRefs(res)); 1.60 + NS_ENSURE_SUCCESS(rv, rv); 1.61 + 1.62 + NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET, 1.63 + NS_ERROR_XSLT_NODESET_EXPECTED); 1.64 + 1.65 + nsRefPtr<txNodeSet> nodes = static_cast<txNodeSet*> 1.66 + (static_cast<txAExprResult*> 1.67 + (res)); 1.68 + if (nodes->isEmpty()) { 1.69 + res.swap(*aResult); 1.70 + 1.71 + return NS_OK; 1.72 + } 1.73 + res = nullptr; // To allow recycling 1.74 + 1.75 + // Evaluate remaining steps 1.76 + uint32_t i, len = mItems.Length(); 1.77 + for (i = 1; i < len; ++i) { 1.78 + PathExprItem& pxi = mItems[i]; 1.79 + nsRefPtr<txNodeSet> tmpNodes; 1.80 + txNodeSetContext eContext(nodes, aContext); 1.81 + while (eContext.hasNext()) { 1.82 + eContext.next(); 1.83 + 1.84 + nsRefPtr<txNodeSet> resNodes; 1.85 + if (pxi.pathOp == DESCENDANT_OP) { 1.86 + rv = aContext->recycler()->getNodeSet(getter_AddRefs(resNodes)); 1.87 + NS_ENSURE_SUCCESS(rv, rv); 1.88 + 1.89 + rv = evalDescendants(pxi.expr, eContext.getContextNode(), 1.90 + &eContext, resNodes); 1.91 + NS_ENSURE_SUCCESS(rv, rv); 1.92 + } 1.93 + else { 1.94 + nsRefPtr<txAExprResult> res; 1.95 + rv = pxi.expr->evaluate(&eContext, getter_AddRefs(res)); 1.96 + NS_ENSURE_SUCCESS(rv, rv); 1.97 + 1.98 + if (res->getResultType() != txAExprResult::NODESET) { 1.99 + //XXX ErrorReport: report nonnodeset error 1.100 + return NS_ERROR_XSLT_NODESET_EXPECTED; 1.101 + } 1.102 + resNodes = static_cast<txNodeSet*> 1.103 + (static_cast<txAExprResult*> 1.104 + (res)); 1.105 + } 1.106 + 1.107 + if (tmpNodes) { 1.108 + if (!resNodes->isEmpty()) { 1.109 + nsRefPtr<txNodeSet> oldSet; 1.110 + oldSet.swap(tmpNodes); 1.111 + rv = aContext->recycler()-> 1.112 + getNonSharedNodeSet(oldSet, getter_AddRefs(tmpNodes)); 1.113 + NS_ENSURE_SUCCESS(rv, rv); 1.114 + 1.115 + oldSet.swap(resNodes); 1.116 + rv = aContext->recycler()-> 1.117 + getNonSharedNodeSet(oldSet, getter_AddRefs(resNodes)); 1.118 + NS_ENSURE_SUCCESS(rv, rv); 1.119 + 1.120 + tmpNodes->addAndTransfer(resNodes); 1.121 + } 1.122 + } 1.123 + else { 1.124 + tmpNodes = resNodes; 1.125 + } 1.126 + } 1.127 + nodes = tmpNodes; 1.128 + if (nodes->isEmpty()) { 1.129 + break; 1.130 + } 1.131 + } 1.132 + 1.133 + *aResult = nodes; 1.134 + NS_ADDREF(*aResult); 1.135 + 1.136 + return NS_OK; 1.137 +} //-- evaluate 1.138 + 1.139 +/** 1.140 + * Selects from the descendants of the context node 1.141 + * all nodes that match the Expr 1.142 +**/ 1.143 +nsresult 1.144 +PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode, 1.145 + txIMatchContext* aContext, txNodeSet* resNodes) 1.146 +{ 1.147 + txSingleNodeContext eContext(aNode, aContext); 1.148 + nsRefPtr<txAExprResult> res; 1.149 + nsresult rv = aStep->evaluate(&eContext, getter_AddRefs(res)); 1.150 + NS_ENSURE_SUCCESS(rv, rv); 1.151 + 1.152 + if (res->getResultType() != txAExprResult::NODESET) { 1.153 + //XXX ErrorReport: report nonnodeset error 1.154 + return NS_ERROR_XSLT_NODESET_EXPECTED; 1.155 + } 1.156 + 1.157 + txNodeSet* oldSet = static_cast<txNodeSet*> 1.158 + (static_cast<txAExprResult*>(res)); 1.159 + nsRefPtr<txNodeSet> newSet; 1.160 + rv = aContext->recycler()->getNonSharedNodeSet(oldSet, 1.161 + getter_AddRefs(newSet)); 1.162 + NS_ENSURE_SUCCESS(rv, rv); 1.163 + 1.164 + resNodes->addAndTransfer(newSet); 1.165 + 1.166 + bool filterWS = aContext->isStripSpaceAllowed(aNode); 1.167 + 1.168 + txXPathTreeWalker walker(aNode); 1.169 + if (!walker.moveToFirstChild()) { 1.170 + return NS_OK; 1.171 + } 1.172 + 1.173 + do { 1.174 + const txXPathNode& node = walker.getCurrentPosition(); 1.175 + if (!(filterWS && txXPathNodeUtils::isText(node) && 1.176 + txXPathNodeUtils::isWhitespace(node))) { 1.177 + rv = evalDescendants(aStep, node, aContext, resNodes); 1.178 + NS_ENSURE_SUCCESS(rv, rv); 1.179 + } 1.180 + } while (walker.moveToNextSibling()); 1.181 + 1.182 + return NS_OK; 1.183 +} //-- evalDescendants 1.184 + 1.185 +Expr::ExprType 1.186 +PathExpr::getType() 1.187 +{ 1.188 + return PATH_EXPR; 1.189 +} 1.190 + 1.191 +TX_IMPL_EXPR_STUBS_BASE(PathExpr, NODESET_RESULT) 1.192 + 1.193 +Expr* 1.194 +PathExpr::getSubExprAt(uint32_t aPos) 1.195 +{ 1.196 + return aPos < mItems.Length() ? mItems[aPos].expr.get() : nullptr; 1.197 +} 1.198 +void 1.199 +PathExpr::setSubExprAt(uint32_t aPos, Expr* aExpr) 1.200 +{ 1.201 + NS_ASSERTION(aPos < mItems.Length(), "setting bad subexpression index"); 1.202 + mItems[aPos].expr.forget(); 1.203 + mItems[aPos].expr = aExpr; 1.204 +} 1.205 + 1.206 + 1.207 +bool 1.208 +PathExpr::isSensitiveTo(ContextSensitivity aContext) 1.209 +{ 1.210 + if (mItems[0].expr->isSensitiveTo(aContext)) { 1.211 + return true; 1.212 + } 1.213 + 1.214 + // We're creating a new node/nodeset so we can ignore those bits. 1.215 + Expr::ContextSensitivity context = 1.216 + aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT); 1.217 + if (context == NO_CONTEXT) { 1.218 + return false; 1.219 + } 1.220 + 1.221 + uint32_t i, len = mItems.Length(); 1.222 + for (i = 0; i < len; ++i) { 1.223 + NS_ASSERTION(!mItems[i].expr->isSensitiveTo(Expr::NODESET_CONTEXT), 1.224 + "Step cannot depend on nodeset-context"); 1.225 + if (mItems[i].expr->isSensitiveTo(context)) { 1.226 + return true; 1.227 + } 1.228 + } 1.229 + 1.230 + return false; 1.231 +} 1.232 + 1.233 +#ifdef TX_TO_STRING 1.234 +void 1.235 +PathExpr::toString(nsAString& dest) 1.236 +{ 1.237 + if (!mItems.IsEmpty()) { 1.238 + NS_ASSERTION(mItems[0].pathOp == RELATIVE_OP, 1.239 + "First step should be relative"); 1.240 + mItems[0].expr->toString(dest); 1.241 + } 1.242 + 1.243 + uint32_t i, len = mItems.Length(); 1.244 + for (i = 1; i < len; ++i) { 1.245 + switch (mItems[i].pathOp) { 1.246 + case DESCENDANT_OP: 1.247 + dest.AppendLiteral("//"); 1.248 + break; 1.249 + case RELATIVE_OP: 1.250 + dest.Append(char16_t('/')); 1.251 + break; 1.252 + } 1.253 + mItems[i].expr->toString(dest); 1.254 + } 1.255 +} 1.256 +#endif