dom/xslt/xpath/txXPathOptimizer.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.

     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 "txXPathOptimizer.h"
     7 #include "txExprResult.h"
     8 #include "nsIAtom.h"
     9 #include "nsGkAtoms.h"
    10 #include "txXPathNode.h"
    11 #include "txExpr.h"
    12 #include "txIXPathContext.h"
    14 class txEarlyEvalContext : public txIEvalContext
    15 {
    16 public:
    17     txEarlyEvalContext(txResultRecycler* aRecycler)
    18         : mRecycler(aRecycler)
    19     {
    20     }
    22     // txIEvalContext
    23     nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
    24                          txAExprResult*& aResult)
    25     {
    26         NS_NOTREACHED("shouldn't depend on this context");
    27         return NS_ERROR_FAILURE;
    28     }
    29     bool isStripSpaceAllowed(const txXPathNode& aNode)
    30     {
    31         NS_NOTREACHED("shouldn't depend on this context");
    32         return false;
    33     }
    34     void* getPrivateContext()
    35     {
    36         NS_NOTREACHED("shouldn't depend on this context");
    37         return nullptr;
    38     }
    39     txResultRecycler* recycler()
    40     {
    41         return mRecycler;
    42     }
    43     void receiveError(const nsAString& aMsg, nsresult aRes)
    44     {
    45     }
    46     const txXPathNode& getContextNode()
    47     {
    48         NS_NOTREACHED("shouldn't depend on this context");
    50         // This will return an invalid node, but we should never
    51         // get here so that's fine.
    53         return *static_cast<txXPathNode*>(nullptr);
    54     }
    55     uint32_t size()
    56     {
    57         NS_NOTREACHED("shouldn't depend on this context");
    58         return 1;
    59     }
    60     uint32_t position()
    61     {
    62         NS_NOTREACHED("shouldn't depend on this context");
    63         return 1;
    64     }
    66 private:
    67     txResultRecycler* mRecycler;
    68 };
    71 nsresult
    72 txXPathOptimizer::optimize(Expr* aInExpr, Expr** aOutExpr)
    73 {
    74     *aOutExpr = nullptr;
    75     nsresult rv = NS_OK;
    77     // First check if the expression will produce the same result
    78     // under any context.
    79     Expr::ExprType exprType = aInExpr->getType();
    80     if (exprType != Expr::LITERAL_EXPR &&
    81         !aInExpr->isSensitiveTo(Expr::ANY_CONTEXT)) {
    82         nsRefPtr<txResultRecycler> recycler = new txResultRecycler;
    83         NS_ENSURE_TRUE(recycler, NS_ERROR_OUT_OF_MEMORY);
    85         rv = recycler->init();
    86         NS_ENSURE_SUCCESS(rv, rv);
    88         txEarlyEvalContext context(recycler);
    89         nsRefPtr<txAExprResult> exprRes;
    91         // Don't throw if this fails since it could be that the expression
    92         // is or contains an error-expression.
    93         rv = aInExpr->evaluate(&context, getter_AddRefs(exprRes));
    94         if (NS_SUCCEEDED(rv)) {
    95             *aOutExpr = new txLiteralExpr(exprRes);
    96         }
    98         return NS_OK;
    99     }
   101     // Then optimize sub expressions
   102     uint32_t i = 0;
   103     Expr* subExpr;
   104     while ((subExpr = aInExpr->getSubExprAt(i))) {
   105         Expr* newExpr = nullptr;
   106         rv = optimize(subExpr, &newExpr);
   107         NS_ENSURE_SUCCESS(rv, rv);
   108         if (newExpr) {
   109             delete subExpr;
   110             aInExpr->setSubExprAt(i, newExpr);
   111         }
   113         ++i;
   114     }
   116     // Finally see if current expression can be optimized
   117     switch (exprType) {
   118         case Expr::LOCATIONSTEP_EXPR:
   119             return optimizeStep(aInExpr, aOutExpr);
   121         case Expr::PATH_EXPR:
   122             return optimizePath(aInExpr, aOutExpr);
   124         case Expr::UNION_EXPR:
   125             return optimizeUnion(aInExpr, aOutExpr);
   127         default:
   128             break;
   129     }
   131     return NS_OK;
   132 }
   134 nsresult
   135 txXPathOptimizer::optimizeStep(Expr* aInExpr, Expr** aOutExpr)
   136 {
   137     LocationStep* step = static_cast<LocationStep*>(aInExpr);
   139     if (step->getAxisIdentifier() == LocationStep::ATTRIBUTE_AXIS) {
   140         // Test for @foo type steps.
   141         txNameTest* nameTest = nullptr;
   142         if (!step->getSubExprAt(0) &&
   143             step->getNodeTest()->getType() == txNameTest::NAME_TEST &&
   144             (nameTest = static_cast<txNameTest*>(step->getNodeTest()))->
   145                 mLocalName != nsGkAtoms::_asterix) {
   147             *aOutExpr = new txNamedAttributeStep(nameTest->mNamespace,
   148                                                  nameTest->mPrefix,
   149                                                  nameTest->mLocalName);
   150             NS_ENSURE_TRUE(*aOutExpr, NS_ERROR_OUT_OF_MEMORY);
   152             return NS_OK; // return since we no longer have a step-object.
   153         }
   154     }
   156     // Test for predicates that can be combined into the nodetest
   157     Expr* pred;
   158     while ((pred = step->getSubExprAt(0)) &&
   159            !pred->canReturnType(Expr::NUMBER_RESULT) &&
   160            !pred->isSensitiveTo(Expr::NODESET_CONTEXT)) {
   161         txNodeTest* predTest = new txPredicatedNodeTest(step->getNodeTest(), pred);
   162         NS_ENSURE_TRUE(predTest, NS_ERROR_OUT_OF_MEMORY);
   164         step->dropFirst();
   165         step->setNodeTest(predTest);
   166     }
   168     return NS_OK;
   169 }
   171 nsresult
   172 txXPathOptimizer::optimizePath(Expr* aInExpr, Expr** aOutExpr)
   173 {
   174     PathExpr* path = static_cast<PathExpr*>(aInExpr);
   176     uint32_t i;
   177     Expr* subExpr;
   178     // look for steps like "//foo" that can be turned into "/descendant::foo"
   179     // and "//." that can be turned into "/descendant-or-self::node()"
   180     for (i = 0; (subExpr = path->getSubExprAt(i)); ++i) {
   181         if (path->getPathOpAt(i) == PathExpr::DESCENDANT_OP &&
   182             subExpr->getType() == Expr::LOCATIONSTEP_EXPR &&
   183             !subExpr->getSubExprAt(0)) {
   184             LocationStep* step = static_cast<LocationStep*>(subExpr);
   185             if (step->getAxisIdentifier() == LocationStep::CHILD_AXIS) {
   186                 step->setAxisIdentifier(LocationStep::DESCENDANT_AXIS);
   187                 path->setPathOpAt(i, PathExpr::RELATIVE_OP);
   188             }
   189             else if (step->getAxisIdentifier() == LocationStep::SELF_AXIS) {
   190                 step->setAxisIdentifier(LocationStep::DESCENDANT_OR_SELF_AXIS);
   191                 path->setPathOpAt(i, PathExpr::RELATIVE_OP);
   192             }
   193         }
   194     }
   196     // look for expressions that start with a "./"
   197     subExpr = path->getSubExprAt(0);
   198     LocationStep* step;
   199     if (subExpr->getType() == Expr::LOCATIONSTEP_EXPR &&
   200         path->getSubExprAt(1) &&
   201         path->getPathOpAt(1) != PathExpr::DESCENDANT_OP) {
   202         step = static_cast<LocationStep*>(subExpr);
   203         if (step->getAxisIdentifier() == LocationStep::SELF_AXIS &&
   204             !step->getSubExprAt(0)) {
   205             txNodeTest* test = step->getNodeTest();
   206             txNodeTypeTest* typeTest;
   207             if (test->getType() == txNodeTest::NODETYPE_TEST &&
   208                 (typeTest = static_cast<txNodeTypeTest*>(test))->
   209                   getNodeTestType() == txNodeTypeTest::NODE_TYPE) {
   210                 // We have a '.' as first step followed by a single '/'.
   212                 // Check if there are only two steps. If so, return the second
   213                 // as resulting expression.
   214                 if (!path->getSubExprAt(2)) {
   215                     *aOutExpr = path->getSubExprAt(1);
   216                     path->setSubExprAt(1, nullptr);
   218                     return NS_OK;
   219                 }
   221                 // Just delete the '.' step and leave the rest of the PathExpr
   222                 path->deleteExprAt(0);
   223             }
   224         }
   225     }
   227     return NS_OK;
   228 }
   230 nsresult
   231 txXPathOptimizer::optimizeUnion(Expr* aInExpr, Expr** aOutExpr)
   232 {
   233     UnionExpr* uni = static_cast<UnionExpr*>(aInExpr);
   235     // Check for expressions like "foo | bar" and
   236     // "descendant::foo | descendant::bar"
   238     nsresult rv;
   239     uint32_t current;
   240     Expr* subExpr;
   241     for (current = 0; (subExpr = uni->getSubExprAt(current)); ++current) {
   242         if (subExpr->getType() != Expr::LOCATIONSTEP_EXPR ||
   243             subExpr->getSubExprAt(0)) {
   244             continue;
   245         }
   247         LocationStep* currentStep = static_cast<LocationStep*>(subExpr);
   248         LocationStep::LocationStepType axis = currentStep->getAxisIdentifier();
   250         txUnionNodeTest* unionTest = nullptr;
   252         // Check if there are any other steps with the same axis and merge
   253         // them with currentStep
   254         uint32_t i;
   255         for (i = current + 1; (subExpr = uni->getSubExprAt(i)); ++i) {
   256             if (subExpr->getType() != Expr::LOCATIONSTEP_EXPR ||
   257                 subExpr->getSubExprAt(0)) {
   258                 continue;
   259             }
   261             LocationStep* step = static_cast<LocationStep*>(subExpr);
   262             if (step->getAxisIdentifier() != axis) {
   263                 continue;
   264             }
   266             // Create a txUnionNodeTest if needed
   267             if (!unionTest) {
   268                 nsAutoPtr<txNodeTest> owner(unionTest = new txUnionNodeTest);
   269                 NS_ENSURE_TRUE(unionTest, NS_ERROR_OUT_OF_MEMORY);
   271                 rv = unionTest->addNodeTest(currentStep->getNodeTest());
   272                 NS_ENSURE_SUCCESS(rv, rv);
   274                 currentStep->setNodeTest(unionTest);
   275                 owner.forget();
   276             }
   278             // Merge the nodetest into the union
   279             rv = unionTest->addNodeTest(step->getNodeTest());
   280             NS_ENSURE_SUCCESS(rv, rv);
   282             step->setNodeTest(nullptr);
   284             // Remove the step from the UnionExpr
   285             uni->deleteExprAt(i);
   286             --i;
   287         }
   289         // Check if all expressions were merged into a single step. If so,
   290         // return the step as the new expression.
   291         if (unionTest && current == 0 && !uni->getSubExprAt(1)) {
   292             // Make sure the step doesn't get deleted when the UnionExpr is
   293             uni->setSubExprAt(0, nullptr);
   294             *aOutExpr = currentStep;
   296             // Return right away since we no longer have a union            
   297             return NS_OK;
   298         }
   299     }
   301     return NS_OK;
   302 }

mercurial