tor-browser

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

ShadowIncludingTreeIterator.h (3897B)


      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 /**
      8 * Implementation of
      9 * https://dom.spec.whatwg.org/#concept-shadow-including-tree-order in iterator
     10 * form.  This can and should be used to avoid recursion on the stack and lots
     11 * of function calls during shadow-including tree iteration.
     12 */
     13 
     14 #ifndef mozilla_dom_ShadowIncludingTreeIterator_h
     15 #define mozilla_dom_ShadowIncludingTreeIterator_h
     16 
     17 #include "mozilla/dom/Element.h"
     18 #include "mozilla/dom/ShadowRoot.h"
     19 #include "nsINode.h"
     20 #include "nsTArray.h"
     21 
     22 namespace mozilla::dom {
     23 
     24 class ShadowIncludingTreeIterator {
     25 public:
     26  /**
     27   * Initialize an iterator with aRoot.  After that it can be iterated with a
     28   * range-based for loop.  At the moment, that's the only supported form of use
     29   * for this iterator.
     30   */
     31  explicit ShadowIncludingTreeIterator(nsINode& aRoot) : mCurrent(&aRoot) {
     32    mRoots.AppendElement(&aRoot);
     33  }
     34 
     35 #ifdef DEBUG
     36  ~ShadowIncludingTreeIterator() {
     37    MOZ_ASSERT(
     38        !mMutationGuard.Mutated(0),
     39        "Don't mutate the DOM while using a ShadowIncludingTreeIterator");
     40  }
     41 #endif  // DEBUG
     42 
     43  // Basic support for range-based for loops.  This will modify the iterator as
     44  // it goes.
     45  ShadowIncludingTreeIterator& begin() { return *this; }
     46 
     47  std::nullptr_t end() const { return nullptr; }
     48 
     49  bool operator!=(std::nullptr_t) const { return !!mCurrent; }
     50 
     51  explicit operator bool() const { return !!mCurrent; }
     52 
     53  void operator++() { Next(); }
     54 
     55  void SkipChildren() {
     56    MOZ_ASSERT(mCurrent, "Shouldn't be at end");
     57    mCurrent = mCurrent->GetNextNonChildNode(mRoots.LastElement());
     58    WalkOutOfShadowRootsIfNeeded();
     59  }
     60 
     61  nsINode* operator*() { return mCurrent; }
     62 
     63 private:
     64  void Next() {
     65    MOZ_ASSERT(mCurrent, "Don't call Next() after we have no current node");
     66 
     67    // We walk shadow roots immediately after their shadow host.
     68    if (Element* element = Element::FromNode(mCurrent)) {
     69      if (ShadowRoot* shadowRoot = element->GetShadowRoot()) {
     70        mCurrent = shadowRoot;
     71        mRoots.AppendElement(shadowRoot);
     72        return;
     73      }
     74    }
     75 
     76    mCurrent = mCurrent->GetNextNode(mRoots.LastElement());
     77    WalkOutOfShadowRootsIfNeeded();
     78  }
     79 
     80  void WalkOutOfShadowRootsIfNeeded() {
     81    while (!mCurrent) {
     82      // Nothing left under this root.  Keep trying to pop the stack until we
     83      // find a node or run out of stack.
     84      nsINode* root = mRoots.PopLastElement();
     85      if (mRoots.IsEmpty()) {
     86        // No more roots to step out of; we're done.  mCurrent is already set to
     87        // null.
     88        return;
     89      }
     90      mCurrent =
     91          ShadowRoot::FromNode(root)->Host()->GetNextNode(mRoots.LastElement());
     92    }
     93  }
     94 
     95  // The current node we're at.
     96  nsINode* mCurrent;
     97 
     98  // Stack of roots that we're inside of right now.  An empty stack can only
     99  // happen when mCurrent is null (and hence we are done iterating).
    100  //
    101  // The default array size here is picked based on gut feeling.  We want at
    102  // least 1, since we will always add something to it in our constructor.
    103  // Having a few more entries probably makes sense, because this is commonly
    104  // used in cases when we know we have custom elements, and hence likely have
    105  // shadow DOMs.  But the exact value "4" was just picked because it sounded
    106  // not too big, not too small.  Feel free to replace it with something else
    107  // based on actual data.
    108  CopyableAutoTArray<nsINode*, 4> mRoots;
    109 
    110 #ifdef DEBUG
    111  // Make sure no one mutates the DOM while we're walking over it.
    112  nsMutationGuard mMutationGuard;
    113 #endif  // DEBUG
    114 };
    115 
    116 }  // namespace mozilla::dom
    117 
    118 #endif  // mozilla_dom_ShadowIncludingTreeIterator_h