1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "txExpr.h"
7 #include "txNodeSet.h"
8 #include "txNodeSetContext.h"
9 #include "txSingleNodeContext.h"
10 #include "txXMLUtils.h"
11 #include "txXPathTreeWalker.h"
12 
13 //------------/
14 //- PathExpr -/
15 //------------/
16 
17 /**
18  * Adds the Expr to this PathExpr
19  * @param expr the Expr to add to this PathExpr
20  **/
addExpr(Expr * aExpr,PathOperator aPathOp)21 nsresult PathExpr::addExpr(Expr* aExpr, PathOperator aPathOp) {
22   NS_ASSERTION(!mItems.IsEmpty() || aPathOp == RELATIVE_OP,
23                "First step has to be relative in PathExpr");
24   PathExprItem* pxi = mItems.AppendElement();
25   if (!pxi) {
26     return NS_ERROR_OUT_OF_MEMORY;
27   }
28   pxi->expr = aExpr;
29   pxi->pathOp = aPathOp;
30 
31   return NS_OK;
32 }
33 
34 //-----------------------------/
35 //- Virtual methods from Expr -/
36 //-----------------------------/
37 
38 /**
39  * Evaluates this Expr based on the given context node and processor state
40  * @param context the context node for evaluation of this Expr
41  * @param ps the ContextState containing the stack information needed
42  * for evaluation
43  * @return the result of the evaluation
44  **/
evaluate(txIEvalContext * aContext,txAExprResult ** aResult)45 nsresult PathExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult) {
46   *aResult = nullptr;
47 
48   // We need to evaluate the first step with the current context since it
49   // can depend on the context size and position. For example:
50   // key('books', concat('book', position()))
51   RefPtr<txAExprResult> res;
52   nsresult rv = mItems[0].expr->evaluate(aContext, getter_AddRefs(res));
53   NS_ENSURE_SUCCESS(rv, rv);
54 
55   NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET,
56                  NS_ERROR_XSLT_NODESET_EXPECTED);
57 
58   RefPtr<txNodeSet> nodes =
59       static_cast<txNodeSet*>(static_cast<txAExprResult*>(res));
60   if (nodes->isEmpty()) {
61     res.forget(aResult);
62 
63     return NS_OK;
64   }
65   res = nullptr;  // To allow recycling
66 
67   // Evaluate remaining steps
68   uint32_t i, len = mItems.Length();
69   for (i = 1; i < len; ++i) {
70     PathExprItem& pxi = mItems[i];
71     RefPtr<txNodeSet> tmpNodes;
72     txNodeSetContext eContext(nodes, aContext);
73     while (eContext.hasNext()) {
74       eContext.next();
75 
76       RefPtr<txNodeSet> resNodes;
77       if (pxi.pathOp == DESCENDANT_OP) {
78         rv = aContext->recycler()->getNodeSet(getter_AddRefs(resNodes));
79         NS_ENSURE_SUCCESS(rv, rv);
80 
81         rv = evalDescendants(pxi.expr, eContext.getContextNode(), &eContext,
82                              resNodes);
83         NS_ENSURE_SUCCESS(rv, rv);
84       } else {
85         RefPtr<txAExprResult> res;
86         rv = pxi.expr->evaluate(&eContext, getter_AddRefs(res));
87         NS_ENSURE_SUCCESS(rv, rv);
88 
89         if (res->getResultType() != txAExprResult::NODESET) {
90           // XXX ErrorReport: report nonnodeset error
91           return NS_ERROR_XSLT_NODESET_EXPECTED;
92         }
93         resNodes = static_cast<txNodeSet*>(static_cast<txAExprResult*>(res));
94       }
95 
96       if (tmpNodes) {
97         if (!resNodes->isEmpty()) {
98           RefPtr<txNodeSet> oldSet;
99           oldSet.swap(tmpNodes);
100           rv = aContext->recycler()->getNonSharedNodeSet(
101               oldSet, getter_AddRefs(tmpNodes));
102           NS_ENSURE_SUCCESS(rv, rv);
103 
104           oldSet.swap(resNodes);
105           rv = aContext->recycler()->getNonSharedNodeSet(
106               oldSet, getter_AddRefs(resNodes));
107           NS_ENSURE_SUCCESS(rv, rv);
108 
109           tmpNodes->addAndTransfer(resNodes);
110         }
111       } else {
112         tmpNodes = resNodes;
113       }
114     }
115     nodes = tmpNodes;
116     if (nodes->isEmpty()) {
117       break;
118     }
119   }
120 
121   *aResult = nodes;
122   NS_ADDREF(*aResult);
123 
124   return NS_OK;
125 }  //-- evaluate
126 
127 /**
128  * Selects from the descendants of the context node
129  * all nodes that match the Expr
130  **/
evalDescendants(Expr * aStep,const txXPathNode & aNode,txIMatchContext * aContext,txNodeSet * resNodes)131 nsresult PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode,
132                                    txIMatchContext* aContext,
133                                    txNodeSet* resNodes) {
134   txSingleNodeContext eContext(aNode, aContext);
135   RefPtr<txAExprResult> res;
136   nsresult rv = aStep->evaluate(&eContext, getter_AddRefs(res));
137   NS_ENSURE_SUCCESS(rv, rv);
138 
139   if (res->getResultType() != txAExprResult::NODESET) {
140     // XXX ErrorReport: report nonnodeset error
141     return NS_ERROR_XSLT_NODESET_EXPECTED;
142   }
143 
144   txNodeSet* oldSet = static_cast<txNodeSet*>(static_cast<txAExprResult*>(res));
145   RefPtr<txNodeSet> newSet;
146   rv =
147       aContext->recycler()->getNonSharedNodeSet(oldSet, getter_AddRefs(newSet));
148   NS_ENSURE_SUCCESS(rv, rv);
149 
150   resNodes->addAndTransfer(newSet);
151 
152   bool filterWS;
153   rv = aContext->isStripSpaceAllowed(aNode, filterWS);
154   NS_ENSURE_SUCCESS(rv, rv);
155 
156   txXPathTreeWalker walker(aNode);
157   if (!walker.moveToFirstChild()) {
158     return NS_OK;
159   }
160 
161   do {
162     const txXPathNode& node = walker.getCurrentPosition();
163     if (!(filterWS && txXPathNodeUtils::isText(node) &&
164           txXPathNodeUtils::isWhitespace(node))) {
165       rv = evalDescendants(aStep, node, aContext, resNodes);
166       NS_ENSURE_SUCCESS(rv, rv);
167     }
168   } while (walker.moveToNextSibling());
169 
170   return NS_OK;
171 }  //-- evalDescendants
172 
getType()173 Expr::ExprType PathExpr::getType() { return PATH_EXPR; }
174 
TX_IMPL_EXPR_STUBS_BASE(PathExpr,NODESET_RESULT)175 TX_IMPL_EXPR_STUBS_BASE(PathExpr, NODESET_RESULT)
176 
177 Expr* PathExpr::getSubExprAt(uint32_t aPos) {
178   return aPos < mItems.Length() ? mItems[aPos].expr.get() : nullptr;
179 }
setSubExprAt(uint32_t aPos,Expr * aExpr)180 void PathExpr::setSubExprAt(uint32_t aPos, Expr* aExpr) {
181   NS_ASSERTION(aPos < mItems.Length(), "setting bad subexpression index");
182   mItems[aPos].expr.forget();
183   mItems[aPos].expr = aExpr;
184 }
185 
isSensitiveTo(ContextSensitivity aContext)186 bool PathExpr::isSensitiveTo(ContextSensitivity aContext) {
187   if (mItems[0].expr->isSensitiveTo(aContext)) {
188     return true;
189   }
190 
191   // We're creating a new node/nodeset so we can ignore those bits.
192   Expr::ContextSensitivity context =
193       aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT);
194   if (context == NO_CONTEXT) {
195     return false;
196   }
197 
198   uint32_t i, len = mItems.Length();
199   for (i = 0; i < len; ++i) {
200     NS_ASSERTION(!mItems[i].expr->isSensitiveTo(Expr::NODESET_CONTEXT),
201                  "Step cannot depend on nodeset-context");
202     if (mItems[i].expr->isSensitiveTo(context)) {
203       return true;
204     }
205   }
206 
207   return false;
208 }
209 
210 #ifdef TX_TO_STRING
toString(nsAString & dest)211 void PathExpr::toString(nsAString& dest) {
212   if (!mItems.IsEmpty()) {
213     NS_ASSERTION(mItems[0].pathOp == RELATIVE_OP,
214                  "First step should be relative");
215     mItems[0].expr->toString(dest);
216   }
217 
218   uint32_t i, len = mItems.Length();
219   for (i = 1; i < len; ++i) {
220     switch (mItems[i].pathOp) {
221       case DESCENDANT_OP:
222         dest.AppendLiteral("//");
223         break;
224       case RELATIVE_OP:
225         dest.Append(char16_t('/'));
226         break;
227     }
228     mItems[i].expr->toString(dest);
229   }
230 }
231 #endif
232