dom/xslt/xpath/txPathExpr.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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.

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

mercurial