Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
6 #include "txExpr.h"
7 #include "txNodeSet.h"
8 #include "txNodeSetContext.h"
9 #include "txSingleNodeContext.h"
10 #include "txXMLUtils.h"
11 #include "txXPathTreeWalker.h"
13 //------------/
14 //- PathExpr -/
15 //------------/
17 /**
18 * Adds the Expr to this PathExpr
19 * @param expr the Expr to add to this PathExpr
20 **/
21 nsresult
22 PathExpr::addExpr(Expr* aExpr, PathOperator aPathOp)
23 {
24 NS_ASSERTION(!mItems.IsEmpty() || aPathOp == RELATIVE_OP,
25 "First step has to be relative in PathExpr");
26 PathExprItem* pxi = mItems.AppendElement();
27 if (!pxi) {
28 return NS_ERROR_OUT_OF_MEMORY;
29 }
30 pxi->expr = aExpr;
31 pxi->pathOp = aPathOp;
33 return NS_OK;
34 }
36 //-----------------------------/
37 //- Virtual methods from Expr -/
38 //-----------------------------/
40 /**
41 * Evaluates this Expr based on the given context node and processor state
42 * @param context the context node for evaluation of this Expr
43 * @param ps the ContextState containing the stack information needed
44 * for evaluation
45 * @return the result of the evaluation
46 **/
47 nsresult
48 PathExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
49 {
50 *aResult = nullptr;
52 // We need to evaluate the first step with the current context since it
53 // can depend on the context size and position. For example:
54 // key('books', concat('book', position()))
55 nsRefPtr<txAExprResult> res;
56 nsresult rv = mItems[0].expr->evaluate(aContext, getter_AddRefs(res));
57 NS_ENSURE_SUCCESS(rv, rv);
59 NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET,
60 NS_ERROR_XSLT_NODESET_EXPECTED);
62 nsRefPtr<txNodeSet> nodes = static_cast<txNodeSet*>
63 (static_cast<txAExprResult*>
64 (res));
65 if (nodes->isEmpty()) {
66 res.swap(*aResult);
68 return NS_OK;
69 }
70 res = nullptr; // To allow recycling
72 // Evaluate remaining steps
73 uint32_t i, len = mItems.Length();
74 for (i = 1; i < len; ++i) {
75 PathExprItem& pxi = mItems[i];
76 nsRefPtr<txNodeSet> tmpNodes;
77 txNodeSetContext eContext(nodes, aContext);
78 while (eContext.hasNext()) {
79 eContext.next();
81 nsRefPtr<txNodeSet> resNodes;
82 if (pxi.pathOp == DESCENDANT_OP) {
83 rv = aContext->recycler()->getNodeSet(getter_AddRefs(resNodes));
84 NS_ENSURE_SUCCESS(rv, rv);
86 rv = evalDescendants(pxi.expr, eContext.getContextNode(),
87 &eContext, resNodes);
88 NS_ENSURE_SUCCESS(rv, rv);
89 }
90 else {
91 nsRefPtr<txAExprResult> res;
92 rv = pxi.expr->evaluate(&eContext, getter_AddRefs(res));
93 NS_ENSURE_SUCCESS(rv, rv);
95 if (res->getResultType() != txAExprResult::NODESET) {
96 //XXX ErrorReport: report nonnodeset error
97 return NS_ERROR_XSLT_NODESET_EXPECTED;
98 }
99 resNodes = static_cast<txNodeSet*>
100 (static_cast<txAExprResult*>
101 (res));
102 }
104 if (tmpNodes) {
105 if (!resNodes->isEmpty()) {
106 nsRefPtr<txNodeSet> oldSet;
107 oldSet.swap(tmpNodes);
108 rv = aContext->recycler()->
109 getNonSharedNodeSet(oldSet, getter_AddRefs(tmpNodes));
110 NS_ENSURE_SUCCESS(rv, rv);
112 oldSet.swap(resNodes);
113 rv = aContext->recycler()->
114 getNonSharedNodeSet(oldSet, getter_AddRefs(resNodes));
115 NS_ENSURE_SUCCESS(rv, rv);
117 tmpNodes->addAndTransfer(resNodes);
118 }
119 }
120 else {
121 tmpNodes = resNodes;
122 }
123 }
124 nodes = tmpNodes;
125 if (nodes->isEmpty()) {
126 break;
127 }
128 }
130 *aResult = nodes;
131 NS_ADDREF(*aResult);
133 return NS_OK;
134 } //-- evaluate
136 /**
137 * Selects from the descendants of the context node
138 * all nodes that match the Expr
139 **/
140 nsresult
141 PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode,
142 txIMatchContext* aContext, txNodeSet* resNodes)
143 {
144 txSingleNodeContext eContext(aNode, aContext);
145 nsRefPtr<txAExprResult> res;
146 nsresult rv = aStep->evaluate(&eContext, getter_AddRefs(res));
147 NS_ENSURE_SUCCESS(rv, rv);
149 if (res->getResultType() != txAExprResult::NODESET) {
150 //XXX ErrorReport: report nonnodeset error
151 return NS_ERROR_XSLT_NODESET_EXPECTED;
152 }
154 txNodeSet* oldSet = static_cast<txNodeSet*>
155 (static_cast<txAExprResult*>(res));
156 nsRefPtr<txNodeSet> newSet;
157 rv = aContext->recycler()->getNonSharedNodeSet(oldSet,
158 getter_AddRefs(newSet));
159 NS_ENSURE_SUCCESS(rv, rv);
161 resNodes->addAndTransfer(newSet);
163 bool filterWS = aContext->isStripSpaceAllowed(aNode);
165 txXPathTreeWalker walker(aNode);
166 if (!walker.moveToFirstChild()) {
167 return NS_OK;
168 }
170 do {
171 const txXPathNode& node = walker.getCurrentPosition();
172 if (!(filterWS && txXPathNodeUtils::isText(node) &&
173 txXPathNodeUtils::isWhitespace(node))) {
174 rv = evalDescendants(aStep, node, aContext, resNodes);
175 NS_ENSURE_SUCCESS(rv, rv);
176 }
177 } while (walker.moveToNextSibling());
179 return NS_OK;
180 } //-- evalDescendants
182 Expr::ExprType
183 PathExpr::getType()
184 {
185 return PATH_EXPR;
186 }
188 TX_IMPL_EXPR_STUBS_BASE(PathExpr, NODESET_RESULT)
190 Expr*
191 PathExpr::getSubExprAt(uint32_t aPos)
192 {
193 return aPos < mItems.Length() ? mItems[aPos].expr.get() : nullptr;
194 }
195 void
196 PathExpr::setSubExprAt(uint32_t aPos, Expr* aExpr)
197 {
198 NS_ASSERTION(aPos < mItems.Length(), "setting bad subexpression index");
199 mItems[aPos].expr.forget();
200 mItems[aPos].expr = aExpr;
201 }
204 bool
205 PathExpr::isSensitiveTo(ContextSensitivity aContext)
206 {
207 if (mItems[0].expr->isSensitiveTo(aContext)) {
208 return true;
209 }
211 // We're creating a new node/nodeset so we can ignore those bits.
212 Expr::ContextSensitivity context =
213 aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT);
214 if (context == NO_CONTEXT) {
215 return false;
216 }
218 uint32_t i, len = mItems.Length();
219 for (i = 0; i < len; ++i) {
220 NS_ASSERTION(!mItems[i].expr->isSensitiveTo(Expr::NODESET_CONTEXT),
221 "Step cannot depend on nodeset-context");
222 if (mItems[i].expr->isSensitiveTo(context)) {
223 return true;
224 }
225 }
227 return false;
228 }
230 #ifdef TX_TO_STRING
231 void
232 PathExpr::toString(nsAString& dest)
233 {
234 if (!mItems.IsEmpty()) {
235 NS_ASSERTION(mItems[0].pathOp == RELATIVE_OP,
236 "First step should be relative");
237 mItems[0].expr->toString(dest);
238 }
240 uint32_t i, len = mItems.Length();
241 for (i = 1; i < len; ++i) {
242 switch (mItems[i].pathOp) {
243 case DESCENDANT_OP:
244 dest.AppendLiteral("//");
245 break;
246 case RELATIVE_OP:
247 dest.Append(char16_t('/'));
248 break;
249 }
250 mItems[i].expr->toString(dest);
251 }
252 }
253 #endif