tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

txPathExpr.cpp (6817B)


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