tor-browser

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

txMozillaXPathTreeWalker.cpp (16977B)


      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 <stdint.h>
      7 
      8 #include <algorithm>
      9 
     10 #include "mozilla/Maybe.h"
     11 #include "mozilla/dom/Attr.h"
     12 #include "mozilla/dom/CharacterData.h"
     13 #include "mozilla/dom/Element.h"
     14 #include "nsAtom.h"
     15 #include "nsAttrName.h"
     16 #include "nsINode.h"
     17 #include "nsNameSpaceManager.h"
     18 #include "nsPrintfCString.h"
     19 #include "nsReadableUtils.h"
     20 #include "nsString.h"
     21 #include "nsTArray.h"
     22 #include "nsUnicharUtils.h"
     23 #include "txLog.h"
     24 #include "txXMLUtils.h"
     25 #include "txXPathTreeWalker.h"
     26 
     27 using namespace mozilla;
     28 using namespace mozilla::dom;
     29 
     30 txXPathTreeWalker::txXPathTreeWalker(const txXPathTreeWalker& aOther) = default;
     31 
     32 txXPathTreeWalker::txXPathTreeWalker(const txXPathNode& aNode)
     33    : mPosition(aNode) {}
     34 
     35 void txXPathTreeWalker::moveToRoot() {
     36  if (mPosition.isDocument()) {
     37    return;
     38  }
     39 
     40  Document* root = mPosition.mNode->GetUncomposedDoc();
     41  if (root) {
     42    mPosition.mIndex = txXPathNode::eDocument;
     43    mPosition.mNode = root;
     44  } else {
     45    nsINode* rootNode = mPosition.Root();
     46 
     47    NS_ASSERTION(rootNode->IsContent(), "root of subtree wasn't an nsIContent");
     48 
     49    mPosition.mIndex = txXPathNode::eContent;
     50    mPosition.mNode = rootNode;
     51  }
     52 }
     53 
     54 bool txXPathTreeWalker::moveToElementById(const nsAString& aID) {
     55  if (aID.IsEmpty()) {
     56    return false;
     57  }
     58 
     59  Document* doc = mPosition.mNode->GetUncomposedDoc();
     60 
     61  nsCOMPtr<nsIContent> content;
     62  if (doc) {
     63    content = doc->GetElementById(aID);
     64  } else {
     65    // We're in a disconnected subtree, search only that subtree.
     66    nsINode* rootNode = mPosition.Root();
     67 
     68    NS_ASSERTION(rootNode->IsContent(), "root of subtree wasn't an nsIContent");
     69 
     70    content =
     71        nsContentUtils::MatchElementId(static_cast<nsIContent*>(rootNode), aID);
     72  }
     73 
     74  if (!content) {
     75    return false;
     76  }
     77 
     78  mPosition.mIndex = txXPathNode::eContent;
     79  mPosition.mNode = content;
     80 
     81  return true;
     82 }
     83 
     84 bool txXPathTreeWalker::moveToFirstAttribute() {
     85  if (!mPosition.isContent()) {
     86    return false;
     87  }
     88 
     89  return moveToValidAttribute(0);
     90 }
     91 
     92 bool txXPathTreeWalker::moveToNextAttribute() {
     93  // XXX an assertion should be enough here with the current code
     94  if (!mPosition.isAttribute()) {
     95    return false;
     96  }
     97 
     98  return moveToValidAttribute(mPosition.mIndex + 1);
     99 }
    100 
    101 bool txXPathTreeWalker::moveToValidAttribute(uint32_t aStartIndex) {
    102  NS_ASSERTION(!mPosition.isDocument(), "documents doesn't have attrs");
    103 
    104  if (!mPosition.Content()->IsElement()) {
    105    return false;
    106  }
    107 
    108  Element* element = mPosition.Content()->AsElement();
    109  uint32_t total = element->GetAttrCount();
    110  if (aStartIndex >= total) {
    111    return false;
    112  }
    113 
    114  uint32_t index;
    115  for (index = aStartIndex; index < total; ++index) {
    116    const nsAttrName* name = element->GetAttrNameAt(index);
    117 
    118    // We need to ignore XMLNS attributes.
    119    if (name->NamespaceID() != kNameSpaceID_XMLNS) {
    120      mPosition.mIndex = index;
    121 
    122      return true;
    123    }
    124  }
    125  return false;
    126 }
    127 
    128 bool txXPathTreeWalker::moveToNamedAttribute(nsAtom* aLocalName,
    129                                             int32_t aNSID) {
    130  if (!mPosition.isContent() || !mPosition.Content()->IsElement()) {
    131    return false;
    132  }
    133 
    134  Element* element = mPosition.Content()->AsElement();
    135 
    136  const nsAttrName* name;
    137  uint32_t i;
    138  for (i = 0; element->GetAttrNameAt(i, &name); ++i) {
    139    if (name->Equals(aLocalName, aNSID)) {
    140      mPosition.mIndex = i;
    141 
    142      return true;
    143    }
    144  }
    145  return false;
    146 }
    147 
    148 bool txXPathTreeWalker::moveToFirstChild() {
    149  if (mPosition.isAttribute()) {
    150    return false;
    151  }
    152 
    153  nsIContent* child = mPosition.mNode->GetFirstChild();
    154  if (!child) {
    155    return false;
    156  }
    157  mPosition.mIndex = txXPathNode::eContent;
    158  mPosition.mNode = child;
    159 
    160  return true;
    161 }
    162 
    163 bool txXPathTreeWalker::moveToLastChild() {
    164  if (mPosition.isAttribute()) {
    165    return false;
    166  }
    167 
    168  nsIContent* child = mPosition.mNode->GetLastChild();
    169  if (!child) {
    170    return false;
    171  }
    172 
    173  mPosition.mIndex = txXPathNode::eContent;
    174  mPosition.mNode = child;
    175 
    176  return true;
    177 }
    178 
    179 bool txXPathTreeWalker::moveToNextSibling() {
    180  if (!mPosition.isContent()) {
    181    return false;
    182  }
    183 
    184  nsINode* sibling = mPosition.mNode->GetNextSibling();
    185  if (!sibling) {
    186    return false;
    187  }
    188 
    189  mPosition.mNode = sibling;
    190 
    191  return true;
    192 }
    193 
    194 bool txXPathTreeWalker::moveToPreviousSibling() {
    195  if (!mPosition.isContent()) {
    196    return false;
    197  }
    198 
    199  nsINode* sibling = mPosition.mNode->GetPreviousSibling();
    200  if (!sibling) {
    201    return false;
    202  }
    203 
    204  mPosition.mNode = sibling;
    205 
    206  return true;
    207 }
    208 
    209 bool txXPathTreeWalker::moveToParent() {
    210  if (mPosition.isDocument()) {
    211    return false;
    212  }
    213 
    214  if (mPosition.isAttribute()) {
    215    mPosition.mIndex = txXPathNode::eContent;
    216 
    217    return true;
    218  }
    219 
    220  nsINode* parent = mPosition.mNode->GetParentNode();
    221  if (!parent) {
    222    return false;
    223  }
    224 
    225  mPosition.mIndex = mPosition.mNode->GetParent() ? txXPathNode::eContent
    226                                                  : txXPathNode::eDocument;
    227  mPosition.mNode = parent;
    228 
    229  return true;
    230 }
    231 
    232 /* static */
    233 bool txXPathNodeUtils::getAttr(const txXPathNode& aNode, nsAtom* aLocalName,
    234                               int32_t aNSID, nsAString& aValue) {
    235  if (aNode.isDocument() || aNode.isAttribute() ||
    236      !aNode.Content()->IsElement()) {
    237    return false;
    238  }
    239 
    240  return aNode.Content()->AsElement()->GetAttr(aNSID, aLocalName, aValue);
    241 }
    242 
    243 /* static */
    244 already_AddRefed<nsAtom> txXPathNodeUtils::getLocalName(
    245    const txXPathNode& aNode) {
    246  if (aNode.isDocument()) {
    247    return nullptr;
    248  }
    249 
    250  if (aNode.isContent()) {
    251    if (aNode.mNode->IsElement()) {
    252      RefPtr<nsAtom> localName = aNode.Content()->NodeInfo()->NameAtom();
    253      return localName.forget();
    254    }
    255 
    256    if (aNode.mNode->IsProcessingInstruction()) {
    257      return NS_Atomize(aNode.mNode->NodeName());
    258    }
    259 
    260    return nullptr;
    261  }
    262 
    263  // This is an attribute node, so we necessarily come from an element.
    264  RefPtr<nsAtom> localName =
    265      aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex)->LocalName();
    266 
    267  return localName.forget();
    268 }
    269 
    270 nsAtom* txXPathNodeUtils::getPrefix(const txXPathNode& aNode) {
    271  if (aNode.isDocument()) {
    272    return nullptr;
    273  }
    274 
    275  if (aNode.isContent()) {
    276    // All other nsIContent node types but elements have a null prefix
    277    // which is what we want here.
    278    return aNode.Content()->NodeInfo()->GetPrefixAtom();
    279  }
    280 
    281  return aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex)->GetPrefix();
    282 }
    283 
    284 /* static */
    285 void txXPathNodeUtils::getLocalName(const txXPathNode& aNode,
    286                                    nsAString& aLocalName) {
    287  if (aNode.isDocument()) {
    288    aLocalName.Truncate();
    289 
    290    return;
    291  }
    292 
    293  if (aNode.isContent()) {
    294    if (aNode.mNode->IsElement()) {
    295      mozilla::dom::NodeInfo* nodeInfo = aNode.Content()->NodeInfo();
    296      nodeInfo->GetName(aLocalName);
    297      return;
    298    }
    299 
    300    if (aNode.mNode->IsProcessingInstruction()) {
    301      // PIs don't have a nodeinfo but do have a name
    302      // XXXbz Not actually true, but this function looks like it wants
    303      // different things from elements and PIs for "local name"...
    304      aLocalName = aNode.mNode->NodeName();
    305      return;
    306    }
    307 
    308    aLocalName.Truncate();
    309 
    310    return;
    311  }
    312 
    313  aNode.Content()
    314      ->AsElement()
    315      ->GetAttrNameAt(aNode.mIndex)
    316      ->LocalName()
    317      ->ToString(aLocalName);
    318 
    319  // Check for html
    320  if (aNode.Content()->NodeInfo()->NamespaceEquals(kNameSpaceID_None) &&
    321      aNode.Content()->IsHTMLElement()) {
    322    nsContentUtils::ASCIIToUpper(aLocalName);
    323  }
    324 }
    325 
    326 /* static */
    327 void txXPathNodeUtils::getNodeName(const txXPathNode& aNode, nsAString& aName) {
    328  if (aNode.isDocument()) {
    329    aName.Truncate();
    330 
    331    return;
    332  }
    333 
    334  if (aNode.isContent()) {
    335    // Elements and PIs have a name
    336    if (aNode.mNode->IsElement() ||
    337        aNode.mNode->NodeType() == nsINode::PROCESSING_INSTRUCTION_NODE) {
    338      aName = aNode.Content()->NodeName();
    339      return;
    340    }
    341 
    342    aName.Truncate();
    343 
    344    return;
    345  }
    346 
    347  aNode.Content()
    348      ->AsElement()
    349      ->GetAttrNameAt(aNode.mIndex)
    350      ->GetQualifiedName(aName);
    351 }
    352 
    353 /* static */
    354 int32_t txXPathNodeUtils::getNamespaceID(const txXPathNode& aNode) {
    355  if (aNode.isDocument()) {
    356    return kNameSpaceID_None;
    357  }
    358 
    359  if (aNode.isContent()) {
    360    return aNode.Content()->GetNameSpaceID();
    361  }
    362 
    363  return aNode.Content()
    364      ->AsElement()
    365      ->GetAttrNameAt(aNode.mIndex)
    366      ->NamespaceID();
    367 }
    368 
    369 /* static */
    370 void txXPathNodeUtils::getNamespaceURI(const txXPathNode& aNode,
    371                                       nsAString& aURI) {
    372  nsNameSpaceManager::GetInstance()->GetNameSpaceURI(getNamespaceID(aNode),
    373                                                     aURI);
    374 }
    375 
    376 /* static */
    377 uint16_t txXPathNodeUtils::getNodeType(const txXPathNode& aNode) {
    378  if (aNode.isDocument()) {
    379    return txXPathNodeType::DOCUMENT_NODE;
    380  }
    381 
    382  if (aNode.isContent()) {
    383    return aNode.mNode->NodeType();
    384  }
    385 
    386  return txXPathNodeType::ATTRIBUTE_NODE;
    387 }
    388 
    389 /* static */
    390 void txXPathNodeUtils::appendNodeValue(const txXPathNode& aNode,
    391                                       nsAString& aResult) {
    392  if (aNode.isAttribute()) {
    393    const nsAttrName* name =
    394        aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex);
    395 
    396    if (aResult.IsEmpty()) {
    397      aNode.Content()->AsElement()->GetAttr(name->NamespaceID(),
    398                                            name->LocalName(), aResult);
    399    } else {
    400      nsAutoString result;
    401      aNode.Content()->AsElement()->GetAttr(name->NamespaceID(),
    402                                            name->LocalName(), result);
    403      aResult.Append(result);
    404    }
    405 
    406    return;
    407  }
    408 
    409  if (aNode.isDocument() || aNode.mNode->IsElement() ||
    410      aNode.mNode->IsDocumentFragment()) {
    411    nsContentUtils::AppendNodeTextContent(aNode.mNode, true, aResult,
    412                                          mozilla::fallible);
    413 
    414    return;
    415  }
    416 
    417  MOZ_ASSERT(aNode.mNode->IsCharacterData());
    418  static_cast<CharacterData*>(aNode.Content())->AppendTextTo(aResult);
    419 }
    420 
    421 /* static */
    422 bool txXPathNodeUtils::isWhitespace(const txXPathNode& aNode) {
    423  NS_ASSERTION(aNode.isContent() && isText(aNode), "Wrong type!");
    424 
    425  return aNode.Content()->TextIsOnlyWhitespace();
    426 }
    427 
    428 /* static */
    429 txXPathNode txXPathNodeUtils::getOwnerDocument(const txXPathNode& aNode) {
    430  return txXPathNode(aNode.mNode->OwnerDoc());
    431 }
    432 
    433 const char gPrintfFmt[] = "id0x%" PRIxPTR;
    434 const char gPrintfFmtAttr[] = "id0x%" PRIxPTR "-%010i";
    435 
    436 /* static */
    437 nsresult txXPathNodeUtils::getXSLTId(const txXPathNode& aNode,
    438                                     const txXPathNode& aBase,
    439                                     nsAString& aResult) {
    440  uintptr_t nodeid =
    441      ((uintptr_t)aNode.mNode.get()) - ((uintptr_t)aBase.mNode.get());
    442  if (!aNode.isAttribute()) {
    443    CopyASCIItoUTF16(nsPrintfCString(gPrintfFmt, nodeid), aResult);
    444  } else {
    445    CopyASCIItoUTF16(nsPrintfCString(gPrintfFmtAttr, nodeid, aNode.mIndex),
    446                     aResult);
    447  }
    448 
    449  return NS_OK;
    450 }
    451 
    452 /* static */
    453 nsresult txXPathNodeUtils::getBaseURI(const txXPathNode& aNode,
    454                                      nsAString& aURI) {
    455  return aNode.mNode->GetBaseURI(aURI);
    456 }
    457 
    458 /* static */
    459 int txXPathNodeUtils::comparePosition(const txXPathNode& aNode,
    460                                      const txXPathNode& aOtherNode) {
    461  // First check for equal nodes or attribute-nodes on the same element.
    462  if (aNode.mNode == aOtherNode.mNode) {
    463    if (aNode.mIndex == aOtherNode.mIndex) {
    464      return 0;
    465    }
    466 
    467    NS_ASSERTION(!aNode.isDocument() && !aOtherNode.isDocument(),
    468                 "documents should always have a set index");
    469 
    470    if (aNode.isContent() ||
    471        (!aOtherNode.isContent() && aNode.mIndex < aOtherNode.mIndex)) {
    472      return -1;
    473    }
    474 
    475    return 1;
    476  }
    477 
    478  // Get document for both nodes.
    479  Document* document = aNode.mNode->GetUncomposedDoc();
    480  Document* otherDocument = aOtherNode.mNode->GetUncomposedDoc();
    481 
    482  // If the nodes have different current documents, compare the document
    483  // pointers.
    484  if (document != otherDocument) {
    485    return document < otherDocument ? -1 : 1;
    486  }
    487 
    488  // Now either both nodes are in orphan trees, or they are both in the
    489  // same tree.
    490 
    491  // Get parents up the tree.
    492  AutoTArray<nsINode*, 8> parents, otherParents;
    493  nsINode* node = aNode.mNode;
    494  nsINode* otherNode = aOtherNode.mNode;
    495  nsINode* parent;
    496  nsINode* otherParent;
    497  while (node && otherNode) {
    498    parent = node->GetParentNode();
    499    otherParent = otherNode->GetParentNode();
    500 
    501    // Hopefully this is a common case.
    502    if (parent == otherParent) {
    503      if (!parent) {
    504        // Both node and otherNode are root nodes in respective orphan
    505        // tree.
    506        return node < otherNode ? -1 : 1;
    507      }
    508 
    509      const Maybe<uint32_t> indexOfNode = parent->ComputeIndexOf(node);
    510      const Maybe<uint32_t> indexOfOtherNode =
    511          parent->ComputeIndexOf(otherNode);
    512      if (MOZ_LIKELY(indexOfNode.isSome() && indexOfOtherNode.isSome())) {
    513        return *indexOfNode < *indexOfOtherNode ? -1 : 1;
    514      }
    515      // XXX Keep the odd traditional behavior for now.
    516      return indexOfNode.isNothing() && indexOfOtherNode.isSome() ? -1 : 1;
    517    }
    518 
    519    parents.AppendElement(node);
    520    otherParents.AppendElement(otherNode);
    521    node = parent;
    522    otherNode = otherParent;
    523  }
    524 
    525  while (node) {
    526    parents.AppendElement(node);
    527    node = node->GetParentNode();
    528  }
    529  while (otherNode) {
    530    otherParents.AppendElement(otherNode);
    531    otherNode = otherNode->GetParentNode();
    532  }
    533 
    534  // Walk back down along the parent-chains until we find where they split.
    535  int32_t total = parents.Length() - 1;
    536  int32_t otherTotal = otherParents.Length() - 1;
    537  NS_ASSERTION(total != otherTotal, "Can't have same number of parents");
    538 
    539  int32_t lastIndex = std::min(total, otherTotal);
    540  int32_t i;
    541  parent = nullptr;
    542  for (i = 0; i <= lastIndex; ++i) {
    543    node = parents.ElementAt(total - i);
    544    otherNode = otherParents.ElementAt(otherTotal - i);
    545    if (node != otherNode) {
    546      if (!parent) {
    547        // The two nodes are in different orphan subtrees.
    548        NS_ASSERTION(i == 0, "this shouldn't happen");
    549        return node < otherNode ? -1 : 1;
    550      }
    551 
    552      const Maybe<uint32_t> index = parent->ComputeIndexOf(node);
    553      const Maybe<uint32_t> otherIndex = parent->ComputeIndexOf(otherNode);
    554      if (MOZ_LIKELY(index.isSome() && otherIndex.isSome())) {
    555        NS_ASSERTION(*index != *otherIndex, "invalid index in comparePosition");
    556        return *index < *otherIndex ? -1 : 1;
    557      }
    558      NS_ASSERTION(false, "invalid index in comparePosition");
    559      // XXX Keep the odd traditional behavior for now.
    560      return index.isNothing() && otherIndex.isSome() ? -1 : 1;
    561    }
    562 
    563    parent = node;
    564  }
    565 
    566  // One node is a descendant of the other. The one with the shortest
    567  // parent-chain is first in the document.
    568  return total < otherTotal ? -1 : 1;
    569 }
    570 
    571 /* static */
    572 Maybe<txXPathNode> txXPathNativeNode::createXPathNode(nsINode* aNode) {
    573  uint16_t nodeType = aNode->NodeType();
    574  if (nodeType == nsINode::ATTRIBUTE_NODE) {
    575    auto* attr = static_cast<Attr*>(aNode);
    576 
    577    NodeInfo* nodeInfo = attr->NodeInfo();
    578    Element* parent = attr->GetElement();
    579    if (!parent) {
    580      return Nothing();
    581    }
    582 
    583    uint32_t i, total = parent->GetAttrCount();
    584    for (i = 0; i < total; ++i) {
    585      const nsAttrName* name = parent->GetAttrNameAt(i);
    586      if (nodeInfo->Equals(name->LocalName(), name->NamespaceID())) {
    587        return Some(txXPathNode(parent, i));
    588      }
    589    }
    590 
    591    NS_ERROR("Couldn't find the attribute in its parent!");
    592 
    593    return Nothing();
    594  }
    595 
    596  uint32_t index;
    597  if (nodeType == nsINode::DOCUMENT_NODE) {
    598    index = txXPathNode::eDocument;
    599  } else {
    600    index = txXPathNode::eContent;
    601  }
    602 
    603  return Some(txXPathNode(aNode, index));
    604 }
    605 
    606 /* static */
    607 nsINode* txXPathNativeNode::getNode(const txXPathNode& aNode) {
    608  if (!aNode.isAttribute()) {
    609    return aNode.mNode;
    610  }
    611 
    612  const nsAttrName* name =
    613      aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex);
    614 
    615  nsAutoString namespaceURI;
    616  nsNameSpaceManager::GetInstance()->GetNameSpaceURI(name->NamespaceID(),
    617                                                     namespaceURI);
    618 
    619  nsCOMPtr<Element> element = do_QueryInterface(aNode.mNode);
    620  nsDOMAttributeMap* map = element->Attributes();
    621  return map->GetNamedItemNS(namespaceURI,
    622                             nsDependentAtomString(name->LocalName()));
    623 }
    624 
    625 /* static */
    626 nsIContent* txXPathNativeNode::getContent(const txXPathNode& aNode) {
    627  NS_ASSERTION(aNode.isContent(),
    628               "Only call getContent on nsIContent wrappers!");
    629  return aNode.Content();
    630 }
    631 
    632 /* static */
    633 Document* txXPathNativeNode::getDocument(const txXPathNode& aNode) {
    634  NS_ASSERTION(aNode.isDocument(),
    635               "Only call getDocument on Document wrappers!");
    636  return aNode.Document();
    637 }