tor-browser

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

FilteredContentIterator.cpp (10769B)


      1 /* -*- Mode: C++; tab-width: 2; 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 "FilteredContentIterator.h"
      7 
      8 #include <utility>
      9 
     10 #include "mozilla/ContentIterator.h"
     11 #include "mozilla/dom/AbstractRange.h"
     12 #include "mozilla/Maybe.h"
     13 #include "mozilla/mozalloc.h"
     14 #include "nsAtom.h"
     15 #include "nsComponentManagerUtils.h"
     16 #include "nsComposeTxtSrvFilter.h"
     17 #include "nsContentUtils.h"
     18 #include "nsDebug.h"
     19 #include "nsError.h"
     20 #include "nsIContent.h"
     21 #include "nsINode.h"
     22 #include "nsISupports.h"
     23 #include "nsISupportsUtils.h"
     24 #include "nsRange.h"
     25 
     26 namespace mozilla {
     27 
     28 using namespace dom;
     29 
     30 FilteredContentIterator::FilteredContentIterator(
     31    UniquePtr<nsComposeTxtSrvFilter> aFilter)
     32    : mCurrentIterator(nullptr),
     33      mFilter(std::move(aFilter)),
     34      mDidSkip(false),
     35      mIsOutOfRange(false),
     36      mDirection(eDirNotSet) {}
     37 
     38 FilteredContentIterator::~FilteredContentIterator() {}
     39 
     40 NS_IMPL_CYCLE_COLLECTION(FilteredContentIterator, mPostIterator, mPreIterator,
     41                         mRange)
     42 
     43 nsresult FilteredContentIterator::Init(nsINode* aRoot) {
     44  NS_ENSURE_ARG_POINTER(aRoot);
     45  mIsOutOfRange = false;
     46  mDirection = eForward;
     47  mCurrentIterator = &mPreIterator;
     48 
     49  mRange = nsRange::Create(aRoot);
     50  mRange->SelectNode(*aRoot, IgnoreErrors());
     51 
     52  nsresult rv = mPreIterator.Init(mRange);
     53  NS_ENSURE_SUCCESS(rv, rv);
     54  return mPostIterator.Init(mRange);
     55 }
     56 
     57 nsresult FilteredContentIterator::Init(const AbstractRange* aAbstractRange) {
     58  if (NS_WARN_IF(!aAbstractRange)) {
     59    return NS_ERROR_INVALID_ARG;
     60  }
     61 
     62  if (NS_WARN_IF(!aAbstractRange->IsPositioned())) {
     63    return NS_ERROR_INVALID_ARG;
     64  }
     65 
     66  mRange = nsRange::Create(aAbstractRange, IgnoreErrors());
     67  if (NS_WARN_IF(!mRange)) {
     68    return NS_ERROR_FAILURE;
     69  }
     70  return InitWithRange();
     71 }
     72 
     73 nsresult FilteredContentIterator::Init(nsINode* aStartContainer,
     74                                       uint32_t aStartOffset,
     75                                       nsINode* aEndContainer,
     76                                       uint32_t aEndOffset) {
     77  return Init(RawRangeBoundary(aStartContainer, aStartOffset),
     78              RawRangeBoundary(aEndContainer, aEndOffset));
     79 }
     80 
     81 nsresult FilteredContentIterator::Init(const RawRangeBoundary& aStartBoundary,
     82                                       const RawRangeBoundary& aEndBoundary) {
     83  RefPtr<nsRange> range =
     84      nsRange::Create(aStartBoundary, aEndBoundary, IgnoreErrors());
     85  if (NS_WARN_IF(!range) || NS_WARN_IF(!range->IsPositioned())) {
     86    return NS_ERROR_INVALID_ARG;
     87  }
     88 
     89  MOZ_ASSERT(range->StartRef() == aStartBoundary);
     90  MOZ_ASSERT(range->EndRef() == aEndBoundary);
     91 
     92  mRange = std::move(range);
     93 
     94  return InitWithRange();
     95 }
     96 
     97 nsresult FilteredContentIterator::InitWithRange() {
     98  MOZ_ASSERT(mRange);
     99  MOZ_ASSERT(mRange->IsPositioned());
    100 
    101  mIsOutOfRange = false;
    102  mDirection = eForward;
    103  mCurrentIterator = &mPreIterator;
    104 
    105  nsresult rv = mPreIterator.Init(mRange);
    106  if (NS_WARN_IF(NS_FAILED(rv))) {
    107    return rv;
    108  }
    109  return mPostIterator.Init(mRange);
    110 }
    111 
    112 nsresult FilteredContentIterator::SwitchDirections(bool aChangeToForward) {
    113  nsINode* node = mCurrentIterator->GetCurrentNode();
    114 
    115  if (aChangeToForward) {
    116    mCurrentIterator = &mPreIterator;
    117    mDirection = eForward;
    118  } else {
    119    mCurrentIterator = &mPostIterator;
    120    mDirection = eBackward;
    121  }
    122 
    123  if (node) {
    124    nsresult rv = mCurrentIterator->PositionAt(node);
    125    if (NS_FAILED(rv)) {
    126      mIsOutOfRange = true;
    127      return rv;
    128    }
    129  }
    130  return NS_OK;
    131 }
    132 
    133 void FilteredContentIterator::First() {
    134  if (!mCurrentIterator) {
    135    NS_ERROR("Missing iterator!");
    136 
    137    return;
    138  }
    139 
    140  // If we are switching directions then
    141  // we need to switch how we process the nodes
    142  if (mDirection != eForward) {
    143    mCurrentIterator = &mPreIterator;
    144    mDirection = eForward;
    145    mIsOutOfRange = false;
    146  }
    147 
    148  mCurrentIterator->First();
    149 
    150  if (mCurrentIterator->IsDone()) {
    151    return;
    152  }
    153 
    154  nsINode* currentNode = mCurrentIterator->GetCurrentNode();
    155 
    156  bool didCross;
    157  CheckAdvNode(currentNode, didCross, eForward);
    158 }
    159 
    160 void FilteredContentIterator::Last() {
    161  if (!mCurrentIterator) {
    162    NS_ERROR("Missing iterator!");
    163 
    164    return;
    165  }
    166 
    167  // If we are switching directions then
    168  // we need to switch how we process the nodes
    169  if (mDirection != eBackward) {
    170    mCurrentIterator = &mPostIterator;
    171    mDirection = eBackward;
    172    mIsOutOfRange = false;
    173  }
    174 
    175  mCurrentIterator->Last();
    176 
    177  if (mCurrentIterator->IsDone()) {
    178    return;
    179  }
    180 
    181  nsINode* currentNode = mCurrentIterator->GetCurrentNode();
    182 
    183  bool didCross;
    184  CheckAdvNode(currentNode, didCross, eBackward);
    185 }
    186 
    187 ///////////////////////////////////////////////////////////////////////////
    188 // ContentIsInTraversalRange: returns true if content is visited during
    189 // the traversal of the range in the specified mode.
    190 //
    191 static bool ContentIsInTraversalRange(nsIContent* aContent, bool aIsPreMode,
    192                                      const RangeBoundary& aStartBoundary,
    193                                      const RangeBoundary& aEndBoundary) {
    194  NS_ENSURE_TRUE(aStartBoundary.IsSet() && aEndBoundary.IsSet() && aContent,
    195                 false);
    196 
    197  nsIContent* parentContent = aContent->GetParent();
    198  if (NS_WARN_IF(!parentContent) ||
    199      NS_WARN_IF(parentContent->IsRootOfNativeAnonymousSubtree())) {
    200    return false;
    201  }
    202  const RawRangeBoundary compPoint(
    203      parentContent, aIsPreMode ? aContent->GetPreviousSibling() : aContent);
    204 
    205  const Maybe<int32_t> startRes =
    206      nsContentUtils::ComparePoints(aStartBoundary, compPoint);
    207  if (NS_WARN_IF(!startRes)) {
    208    return false;
    209  }
    210  const Maybe<int32_t> endRes =
    211      nsContentUtils::ComparePoints(aEndBoundary, compPoint);
    212  if (NS_WARN_IF(!endRes)) {
    213    return false;
    214  }
    215  return *startRes <= 0 && *endRes >= 0;
    216 }
    217 
    218 static bool ContentIsInTraversalRange(nsRange* aRange, nsIContent* aNextContent,
    219                                      bool aIsPreMode) {
    220  // XXXbz we have a caller below (in AdvanceNode) who passes null for
    221  // aNextContent!
    222  NS_ENSURE_TRUE(aNextContent && aRange, false);
    223 
    224  return ContentIsInTraversalRange(aNextContent, aIsPreMode, aRange->StartRef(),
    225                                   aRange->EndRef());
    226 }
    227 
    228 // Helper function to advance to the next or previous node
    229 nsresult FilteredContentIterator::AdvanceNode(nsINode* aNode,
    230                                              nsINode*& aNewNode,
    231                                              eDirectionType aDir) {
    232  nsCOMPtr<nsIContent> nextNode;
    233  if (aDir == eForward) {
    234    nextNode = aNode->GetNextSibling();
    235  } else {
    236    nextNode = aNode->GetPreviousSibling();
    237  }
    238 
    239  if (nextNode) {
    240    // If we got here, that means we found the nxt/prv node
    241    // make sure it is in our DOMRange
    242    bool intersects =
    243        ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
    244    if (intersects) {
    245      aNewNode = nextNode;
    246      NS_ADDREF(aNewNode);
    247      return NS_OK;
    248    }
    249  } else {
    250    // The next node was null so we need to walk up the parent(s)
    251    nsCOMPtr<nsINode> parent = aNode->GetParentNode();
    252    NS_ASSERTION(parent, "parent can't be nullptr");
    253 
    254    // Make sure the parent is in the DOMRange before going further
    255    // XXXbz why are we passing nextNode, not the parent???  If this gets fixed,
    256    // then ContentIsInTraversalRange can stop null-checking its second arg.
    257    bool intersects =
    258        ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
    259    if (intersects) {
    260      // Now find the nxt/prv node after/before this node
    261      nsresult rv = AdvanceNode(parent, aNewNode, aDir);
    262      if (NS_SUCCEEDED(rv) && aNewNode) {
    263        return NS_OK;
    264      }
    265    }
    266  }
    267 
    268  // if we get here it pretty much means
    269  // we went out of the DOM Range
    270  mIsOutOfRange = true;
    271 
    272  return NS_ERROR_FAILURE;
    273 }
    274 
    275 // Helper function to see if the next/prev node should be skipped
    276 void FilteredContentIterator::CheckAdvNode(nsINode* aNode, bool& aDidSkip,
    277                                           eDirectionType aDir) {
    278  aDidSkip = false;
    279  mIsOutOfRange = false;
    280 
    281  if (aNode && mFilter) {
    282    nsCOMPtr<nsINode> currentNode = aNode;
    283    while (1) {
    284      if (mFilter->Skip(aNode)) {
    285        aDidSkip = true;
    286        // Get the next/prev node and then
    287        // see if we should skip that
    288        nsCOMPtr<nsINode> advNode;
    289        nsresult rv = AdvanceNode(aNode, *getter_AddRefs(advNode), aDir);
    290        if (NS_SUCCEEDED(rv) && advNode) {
    291          aNode = advNode;
    292        } else {
    293          return;  // fell out of range
    294        }
    295      } else {
    296        if (aNode != currentNode) {
    297          nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
    298          (void)mCurrentIterator->PositionAt(content);
    299        }
    300        return;  // found something
    301      }
    302    }
    303  }
    304 }
    305 
    306 void FilteredContentIterator::Next() {
    307  if (mIsOutOfRange || !mCurrentIterator) {
    308    NS_ASSERTION(mCurrentIterator, "Missing iterator!");
    309 
    310    return;
    311  }
    312 
    313  // If we are switching directions then
    314  // we need to switch how we process the nodes
    315  if (mDirection != eForward) {
    316    nsresult rv = SwitchDirections(true);
    317    if (NS_FAILED(rv)) {
    318      return;
    319    }
    320  }
    321 
    322  mCurrentIterator->Next();
    323 
    324  if (mCurrentIterator->IsDone()) {
    325    return;
    326  }
    327 
    328  // If we can't get the current node then
    329  // don't check to see if we can skip it
    330  nsINode* currentNode = mCurrentIterator->GetCurrentNode();
    331 
    332  CheckAdvNode(currentNode, mDidSkip, eForward);
    333 }
    334 
    335 void FilteredContentIterator::Prev() {
    336  if (mIsOutOfRange || !mCurrentIterator) {
    337    NS_ASSERTION(mCurrentIterator, "Missing iterator!");
    338 
    339    return;
    340  }
    341 
    342  // If we are switching directions then
    343  // we need to switch how we process the nodes
    344  if (mDirection != eBackward) {
    345    nsresult rv = SwitchDirections(false);
    346    if (NS_FAILED(rv)) {
    347      return;
    348    }
    349  }
    350 
    351  mCurrentIterator->Prev();
    352 
    353  if (mCurrentIterator->IsDone()) {
    354    return;
    355  }
    356 
    357  // If we can't get the current node then
    358  // don't check to see if we can skip it
    359  nsINode* currentNode = mCurrentIterator->GetCurrentNode();
    360 
    361  CheckAdvNode(currentNode, mDidSkip, eBackward);
    362 }
    363 
    364 nsINode* FilteredContentIterator::GetCurrentNode() {
    365  if (mIsOutOfRange || !mCurrentIterator) {
    366    return nullptr;
    367  }
    368 
    369  return mCurrentIterator->GetCurrentNode();
    370 }
    371 
    372 bool FilteredContentIterator::IsDone() {
    373  if (mIsOutOfRange || !mCurrentIterator) {
    374    return true;
    375  }
    376 
    377  return mCurrentIterator->IsDone();
    378 }
    379 
    380 nsresult FilteredContentIterator::PositionAt(nsINode* aCurNode) {
    381  NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE);
    382  mIsOutOfRange = false;
    383  return mCurrentIterator->PositionAt(aCurNode);
    384 }
    385 
    386 }  // namespace mozilla