tor-browser

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

inDeepTreeWalker.cpp (9171B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "inDeepTreeWalker.h"
      8 
      9 #include "ChildIterator.h"
     10 #include "inLayoutUtils.h"
     11 #include "mozilla/Try.h"
     12 #include "mozilla/dom/CSSStyleRule.h"
     13 #include "mozilla/dom/Document.h"
     14 #include "mozilla/dom/Element.h"
     15 #include "mozilla/dom/InspectorUtils.h"
     16 #include "mozilla/dom/NodeFilterBinding.h"
     17 #include "nsIContent.h"
     18 #include "nsServiceManagerUtils.h"
     19 #include "nsString.h"
     20 
     21 using mozilla::dom::InspectorUtils;
     22 
     23 /*****************************************************************************
     24 * This implementation does not currently operaate according to the W3C spec.
     25 * In particular it does NOT handle DOM mutations during the walk.  It also
     26 * ignores whatToShow and the filter.
     27 *****************************************************************************/
     28 
     29 ////////////////////////////////////////////////////
     30 
     31 inDeepTreeWalker::inDeepTreeWalker() = default;
     32 inDeepTreeWalker::~inDeepTreeWalker() = default;
     33 
     34 NS_IMPL_ISUPPORTS(inDeepTreeWalker, inIDeepTreeWalker)
     35 
     36 ////////////////////////////////////////////////////
     37 // inIDeepTreeWalker
     38 
     39 NS_IMETHODIMP
     40 inDeepTreeWalker::GetShowAnonymousContent(bool* aShowAnonymousContent) {
     41  *aShowAnonymousContent = mShowAnonymousContent;
     42  return NS_OK;
     43 }
     44 
     45 NS_IMETHODIMP
     46 inDeepTreeWalker::SetShowAnonymousContent(bool aShowAnonymousContent) {
     47  mShowAnonymousContent = aShowAnonymousContent;
     48  return NS_OK;
     49 }
     50 
     51 NS_IMETHODIMP
     52 inDeepTreeWalker::GetShowSubDocuments(bool* aShowSubDocuments) {
     53  *aShowSubDocuments = mShowSubDocuments;
     54  return NS_OK;
     55 }
     56 
     57 NS_IMETHODIMP
     58 inDeepTreeWalker::SetShowSubDocuments(bool aShowSubDocuments) {
     59  mShowSubDocuments = aShowSubDocuments;
     60  return NS_OK;
     61 }
     62 
     63 NS_IMETHODIMP
     64 inDeepTreeWalker::GetShowDocumentsAsNodes(bool* aShowDocumentsAsNodes) {
     65  *aShowDocumentsAsNodes = mShowDocumentsAsNodes;
     66  return NS_OK;
     67 }
     68 
     69 NS_IMETHODIMP
     70 inDeepTreeWalker::SetShowDocumentsAsNodes(bool aShowDocumentsAsNodes) {
     71  mShowDocumentsAsNodes = aShowDocumentsAsNodes;
     72  return NS_OK;
     73 }
     74 
     75 NS_IMETHODIMP
     76 inDeepTreeWalker::Init(nsINode* aRoot) {
     77  if (!aRoot) {
     78    return NS_ERROR_INVALID_ARG;
     79  }
     80 
     81  mRoot = aRoot;
     82  mCurrentNode = aRoot;
     83 
     84  return NS_OK;
     85 }
     86 
     87 ////////////////////////////////////////////////////
     88 
     89 NS_IMETHODIMP
     90 inDeepTreeWalker::GetRoot(nsINode** aRoot) {
     91  *aRoot = mRoot;
     92  NS_IF_ADDREF(*aRoot);
     93  return NS_OK;
     94 }
     95 
     96 NS_IMETHODIMP
     97 inDeepTreeWalker::GetCurrentNode(nsINode** aCurrentNode) {
     98  *aCurrentNode = mCurrentNode;
     99  NS_IF_ADDREF(*aCurrentNode);
    100  return NS_OK;
    101 }
    102 
    103 already_AddRefed<nsINode> inDeepTreeWalker::GetParent() {
    104  MOZ_ASSERT(mCurrentNode);
    105 
    106  if (mCurrentNode == mRoot) {
    107    return nullptr;
    108  }
    109 
    110  nsINode* parentNode =
    111      InspectorUtils::GetParentForNode(*mCurrentNode, mShowAnonymousContent);
    112  if (!parentNode) {
    113    return nullptr;
    114  }
    115 
    116  // For compatibility reasons by default we skip the document nodes
    117  // from the walk.
    118  if (!mShowDocumentsAsNodes && parentNode->IsDocument() &&
    119      parentNode != mRoot) {
    120    parentNode =
    121        InspectorUtils::GetParentForNode(*parentNode, mShowAnonymousContent);
    122  }
    123 
    124  return do_AddRef(parentNode);
    125 }
    126 
    127 void inDeepTreeWalker::GetChildren(nsINode& aParent, ChildList& aChildList) {
    128  aChildList.ClearAndRetainStorage();
    129  InspectorUtils::GetChildrenForNode(aParent, mShowAnonymousContent,
    130                                     /* aIncludeAssignedNodes = */ false,
    131                                     mShowSubDocuments, aChildList);
    132  if (aChildList.Length() == 1 && aChildList.ElementAt(0)->IsDocument() &&
    133      !mShowDocumentsAsNodes) {
    134    RefPtr parent = aChildList.ElementAt(0);
    135    aChildList.ClearAndRetainStorage();
    136    InspectorUtils::GetChildrenForNode(*parent, mShowAnonymousContent,
    137                                       /* aIncludeAssignedNodes = */ false,
    138                                       mShowSubDocuments, aChildList);
    139  }
    140 }
    141 
    142 NS_IMETHODIMP
    143 inDeepTreeWalker::SetCurrentNode(nsINode* aCurrentNode) {
    144  // mCurrentNode can only be null if init either failed, or has not been called
    145  // yet.
    146  if (!mCurrentNode || !aCurrentNode) {
    147    return NS_ERROR_FAILURE;
    148  }
    149 
    150  // If Document nodes are skipped by the walk, we should not allow one to set
    151  // one as the current node either.
    152  if (!mShowDocumentsAsNodes) {
    153    if (aCurrentNode->IsDocument()) {
    154      return NS_ERROR_FAILURE;
    155    }
    156  }
    157 
    158  // We want to store the original state so in case of error
    159  // we can restore that.
    160  ChildList oldSiblings;
    161  mSiblings.SwapElements(oldSiblings);
    162  nsCOMPtr<nsINode> oldCurrent = std::move(mCurrentNode);
    163 
    164  mCurrentNode = aCurrentNode;
    165  if (RefPtr<nsINode> parent = GetParent()) {
    166    GetChildren(*parent, mSiblings);
    167    // We cached all the siblings (if there are any) of the current node, but we
    168    // still have to set the index too, to be able to iterate over them.
    169    int32_t index = mSiblings.IndexOf(mCurrentNode);
    170    if (index < 0) {
    171      // If someone tries to set current node to some value that is not
    172      // reachable otherwise, let's throw. (For example mShowAnonymousContent is
    173      // false and some NAC was passed in).
    174      // Restore state first.
    175      mCurrentNode = std::move(oldCurrent);
    176      oldSiblings.SwapElements(mSiblings);
    177      return NS_ERROR_INVALID_ARG;
    178    }
    179    mCurrentIndex = index;
    180  } else {
    181    mCurrentIndex = -1;
    182  }
    183  return NS_OK;
    184 }
    185 
    186 NS_IMETHODIMP
    187 inDeepTreeWalker::ParentNode(nsINode** _retval) {
    188  *_retval = nullptr;
    189  if (!mCurrentNode || mCurrentNode == mRoot) {
    190    return NS_OK;
    191  }
    192 
    193  nsCOMPtr<nsINode> parent = GetParent();
    194  if (!parent) {
    195    return NS_OK;
    196  }
    197 
    198  MOZ_TRY(SetCurrentNode(parent));
    199 
    200  parent.forget(_retval);
    201  return NS_OK;
    202 }
    203 
    204 // FirstChild and LastChild are very similar methods, this is the generic
    205 // version for internal use. With aReverse = true it returns the LastChild.
    206 nsresult inDeepTreeWalker::EdgeChild(nsINode** _retval, bool aFront) {
    207  if (!mCurrentNode) {
    208    return NS_ERROR_FAILURE;
    209  }
    210 
    211  *_retval = nullptr;
    212 
    213  ChildList children;
    214  GetChildren(*mCurrentNode, children);
    215  if (children.IsEmpty()) {
    216    return NS_OK;
    217  }
    218  mSiblings = std::move(children);
    219  mCurrentIndex = aFront ? 0 : mSiblings.Length() - 1;
    220  mCurrentNode = mSiblings.ElementAt(mCurrentIndex);
    221  NS_ADDREF(*_retval = mCurrentNode);
    222  return NS_OK;
    223 }
    224 
    225 NS_IMETHODIMP
    226 inDeepTreeWalker::FirstChild(nsINode** _retval) {
    227  return EdgeChild(_retval, /* aFront = */ true);
    228 }
    229 
    230 NS_IMETHODIMP
    231 inDeepTreeWalker::LastChild(nsINode** _retval) {
    232  return EdgeChild(_retval, /* aFront = */ false);
    233 }
    234 
    235 NS_IMETHODIMP
    236 inDeepTreeWalker::PreviousSibling(nsINode** _retval) {
    237  *_retval = nullptr;
    238  if (!mCurrentNode || mCurrentIndex < 1) {
    239    return NS_OK;
    240  }
    241 
    242  nsINode* prev = mSiblings.ElementAt(--mCurrentIndex);
    243  mCurrentNode = prev;
    244  NS_ADDREF(*_retval = mCurrentNode);
    245  return NS_OK;
    246 }
    247 
    248 NS_IMETHODIMP
    249 inDeepTreeWalker::NextSibling(nsINode** _retval) {
    250  *_retval = nullptr;
    251  if (!mCurrentNode || mCurrentIndex + 1 >= (int32_t)mSiblings.Length()) {
    252    return NS_OK;
    253  }
    254 
    255  nsINode* next = mSiblings.ElementAt(++mCurrentIndex);
    256  mCurrentNode = next;
    257  NS_ADDREF(*_retval = mCurrentNode);
    258  return NS_OK;
    259 }
    260 
    261 NS_IMETHODIMP
    262 inDeepTreeWalker::PreviousNode(nsINode** _retval) {
    263  if (!mCurrentNode || mCurrentNode == mRoot) {
    264    // Nowhere to go from here
    265    *_retval = nullptr;
    266    return NS_OK;
    267  }
    268 
    269  nsCOMPtr<nsINode> node;
    270  PreviousSibling(getter_AddRefs(node));
    271 
    272  if (!node) {
    273    return ParentNode(_retval);
    274  }
    275 
    276  // Now we're positioned at our previous sibling.  But since the DOM tree
    277  // traversal is depth-first, the previous node is its most deeply nested last
    278  // child.  Just loop until LastChild() returns null; since the LastChild()
    279  // call that returns null won't affect our position, we will then be
    280  // positioned at the correct node.
    281  while (node) {
    282    LastChild(getter_AddRefs(node));
    283  }
    284 
    285  NS_ADDREF(*_retval = mCurrentNode);
    286  return NS_OK;
    287 }
    288 
    289 NS_IMETHODIMP
    290 inDeepTreeWalker::NextNode(nsINode** _retval) {
    291  if (!mCurrentNode) {
    292    return NS_OK;
    293  }
    294 
    295  // First try our kids
    296  FirstChild(_retval);
    297 
    298  if (*_retval) {
    299    return NS_OK;
    300  }
    301 
    302  // Now keep trying next siblings up the parent chain, but if we
    303  // discover there's nothing else restore our state.
    304 #ifdef DEBUG
    305  nsINode* origCurrentNode = mCurrentNode;
    306 #endif
    307  uint32_t lastChildCallsToMake = 0;
    308  while (1) {
    309    NextSibling(_retval);
    310 
    311    if (*_retval) {
    312      return NS_OK;
    313    }
    314 
    315    nsCOMPtr<nsINode> parent;
    316    ParentNode(getter_AddRefs(parent));
    317    if (!parent) {
    318      // Nowhere else to go; we're done.  Restore our state.
    319      while (lastChildCallsToMake--) {
    320        nsCOMPtr<nsINode> dummy;
    321        LastChild(getter_AddRefs(dummy));
    322      }
    323      NS_ASSERTION(mCurrentNode == origCurrentNode,
    324                   "Didn't go back to the right node?");
    325      *_retval = nullptr;
    326      return NS_OK;
    327    }
    328    ++lastChildCallsToMake;
    329  }
    330 
    331  MOZ_ASSERT_UNREACHABLE("how did we get here?");
    332  return NS_OK;
    333 }