tor-browser

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

AbstractRange.cpp (24370B)


      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 "mozilla/dom/AbstractRange.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/RangeUtils.h"
     11 #include "mozilla/SelectionMovementUtils.h"
     12 #include "mozilla/dom/AbstractRangeBinding.h"
     13 #include "mozilla/dom/ChildIterator.h"
     14 #include "mozilla/dom/CrossShadowBoundaryRange.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/DocumentInlines.h"
     17 #include "mozilla/dom/Selection.h"
     18 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
     19 #include "mozilla/dom/StaticRange.h"
     20 #include "mozilla/dom/TreeIterator.h"
     21 #include "nsContentUtils.h"
     22 #include "nsCycleCollectionParticipant.h"
     23 #include "nsGkAtoms.h"
     24 #include "nsINode.h"
     25 #include "nsRange.h"
     26 #include "nsTArray.h"
     27 
     28 namespace mozilla::dom {
     29 
     30 template nsresult AbstractRange::SetStartAndEndInternal(
     31    const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
     32    nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     33 template nsresult AbstractRange::SetStartAndEndInternal(
     34    const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
     35    nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     36 template nsresult AbstractRange::SetStartAndEndInternal(
     37    const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
     38    nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     39 template nsresult AbstractRange::SetStartAndEndInternal(
     40    const RawRangeBoundary& aStartBoundary,
     41    const RawRangeBoundary& aEndBoundary, nsRange* aRange,
     42    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     43 template nsresult AbstractRange::SetStartAndEndInternal(
     44    const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
     45    StaticRange* aRange,
     46    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     47 template nsresult AbstractRange::SetStartAndEndInternal(
     48    const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
     49    StaticRange* aRange,
     50    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     51 template nsresult AbstractRange::SetStartAndEndInternal(
     52    const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
     53    StaticRange* aRange,
     54    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     55 template nsresult AbstractRange::SetStartAndEndInternal(
     56    const RawRangeBoundary& aStartBoundary,
     57    const RawRangeBoundary& aEndBoundary, StaticRange* aRange,
     58    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     59 template bool AbstractRange::MaybeCacheToReuse(nsRange& aInstance);
     60 template bool AbstractRange::MaybeCacheToReuse(StaticRange& aInstance);
     61 template bool AbstractRange::MaybeCacheToReuse(
     62    CrossShadowBoundaryRange& aInstance);
     63 
     64 bool AbstractRange::sHasShutDown = false;
     65 
     66 NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractRange)
     67 NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractRange)
     68 
     69 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbstractRange)
     70  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     71  NS_INTERFACE_MAP_ENTRY(nsISupports)
     72 NS_INTERFACE_MAP_END
     73 
     74 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(AbstractRange)
     75 
     76 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractRange)
     77  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
     78  // mStart and mEnd may depend on or be depended on some other members in
     79  // concrete classes so that they should be unlinked in sub classes.
     80  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     81  tmp->mSelections.Clear();
     82  // Unregistering of the common inclusive ancestors would by design
     83  // also happen when the actual implementations unlink `mStart`/`mEnd`.
     84  // This may introduce additional overhead which is not needed when unlinking,
     85  // therefore this is done here beforehand.
     86  if (tmp->mRegisteredClosestCommonInclusiveAncestor) {
     87    tmp->UnregisterClosestCommonInclusiveAncestor(IsUnlinking::Yes);
     88  }
     89  MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(),
     90                        "Shouldn't be registered now that we're unlinking");
     91 
     92 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     93 
     94 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractRange)
     95  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
     96  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
     97  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
     98  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegisteredClosestCommonInclusiveAncestor)
     99 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    100 
    101 static void UpdateDescendantsInSameTree(const nsINode& aNode,
    102                                        bool aMarkDesendants) {
    103  MOZ_ASSERT(!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled());
    104  // don't set the Descendant bit on |aNode| itself
    105  nsINode* node = aNode.GetNextNode(&aNode);
    106  while (node) {
    107    if (aMarkDesendants) {
    108      node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    109    } else {
    110      node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    111    }
    112 
    113    if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
    114      node = node->GetNextNode(&aNode);
    115    } else {
    116      // We found an ancestor of an overlapping range, skip its descendants.
    117      node = node->GetNextNonChildNode(&aNode);
    118    }
    119  }
    120 }
    121 
    122 void AbstractRange::UpdateDescendantsInFlattenedTree(nsINode& aNode,
    123                                                     bool aMarkDescendants) {
    124  MOZ_ASSERT(StaticPrefs::dom_shadowdom_selection_across_boundary_enabled());
    125 
    126  auto UpdateDescendant = [aMarkDescendants](nsINode* node) {
    127    if (aMarkDescendants) {
    128      node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    129    } else {
    130      node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    131    }
    132  };
    133 
    134  nsINode* target = &aNode;
    135 
    136  if (target->IsDocument()) {
    137    if (auto* rootElement = aNode.AsDocument()->GetRootElement()) {
    138      target = rootElement;
    139      UpdateDescendant(target);
    140    }
    141  }
    142 
    143  if (!target || !target->IsContent()) {
    144    return;
    145  }
    146 
    147  TreeIterator<FlattenedChildIterator> iter(*target->AsContent());
    148  iter.GetNext();  // Skip aNode itself.
    149  while (nsIContent* curNode = iter.GetCurrent()) {
    150    UpdateDescendant(curNode);
    151    if (curNode->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
    152      iter.GetNextSkippingChildren();
    153    } else {
    154      iter.GetNext();
    155    }
    156  }
    157 }
    158 
    159 void AbstractRange::MarkDescendants(nsINode& aNode) {
    160  // Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on
    161  // aNode's descendants unless aNode is already marked as a range common
    162  // ancestor or a descendant of one, in which case all of our descendants have
    163  // the bit set already.
    164  if (!aNode.IsMaybeSelected()) {
    165    // If aNode has a web-exposed shadow root, use this shadow tree and ignore
    166    // the children of aNode.
    167 
    168    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
    169      UpdateDescendantsInFlattenedTree(aNode, true /* aMarkDescendants */);
    170    } else {
    171      UpdateDescendantsInSameTree(aNode, true /* aMarkDescendants */);
    172    }
    173  }
    174 }
    175 
    176 void AbstractRange::UnmarkDescendants(nsINode& aNode) {
    177  // Unset NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection
    178  // on aNode's descendants unless aNode is a descendant of another range common
    179  // ancestor. Also, exclude descendants of range common ancestors (but not the
    180  // common ancestor itself).
    181  if (!aNode
    182           .IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
    183    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
    184      UpdateDescendantsInFlattenedTree(aNode, false /* aMarkDescendants */);
    185    } else {
    186      UpdateDescendantsInSameTree(aNode, false /* aMarkDescendants */);
    187    }
    188  }
    189 }
    190 
    191 // NOTE: If you need to change default value of members of AbstractRange,
    192 //       update nsRange::Create(nsINode* aNode) and ClearForReuse() too.
    193 AbstractRange::AbstractRange(nsINode* aNode, bool aIsDynamicRange,
    194                             TreeKind aBoundaryTreeKind)
    195    : mStart(aBoundaryTreeKind),
    196      mEnd(aBoundaryTreeKind),
    197      mRegisteredClosestCommonInclusiveAncestor(nullptr),
    198      mIsPositioned(false),
    199      mIsGenerated(false),
    200      mCalledByJS(false),
    201      mIsDynamicRange(aIsDynamicRange) {
    202  mRefCnt.SetIsOnMainThread();
    203  Init(aNode);
    204 }
    205 
    206 AbstractRange::~AbstractRange() = default;
    207 
    208 void AbstractRange::Init(nsINode* aNode) {
    209  MOZ_ASSERT(aNode, "range isn't in a document!");
    210  mOwner = aNode->OwnerDoc();
    211 }
    212 
    213 // static
    214 void AbstractRange::Shutdown() {
    215  sHasShutDown = true;
    216  if (nsTArray<RefPtr<nsRange>>* cachedRanges = nsRange::sCachedRanges) {
    217    nsRange::sCachedRanges = nullptr;
    218    cachedRanges->Clear();
    219    delete cachedRanges;
    220  }
    221  if (nsTArray<RefPtr<StaticRange>>* cachedRanges =
    222          StaticRange::sCachedRanges) {
    223    StaticRange::sCachedRanges = nullptr;
    224    cachedRanges->Clear();
    225    delete cachedRanges;
    226  }
    227  if (nsTArray<RefPtr<CrossShadowBoundaryRange>>* cachedRanges =
    228          CrossShadowBoundaryRange::sCachedRanges) {
    229    CrossShadowBoundaryRange::sCachedRanges = nullptr;
    230    cachedRanges->Clear();
    231    delete cachedRanges;
    232  }
    233 }
    234 
    235 // static
    236 template <class RangeType>
    237 bool AbstractRange::MaybeCacheToReuse(RangeType& aInstance) {
    238  static const size_t kMaxRangeCache = 64;
    239 
    240  // If the instance is not used by JS and the cache is not yet full, we
    241  // should reuse it.  Otherwise, delete it.
    242  if (sHasShutDown || aInstance.GetWrapperMaybeDead() || aInstance.GetFlags() ||
    243      (RangeType::sCachedRanges &&
    244       RangeType::sCachedRanges->Length() == kMaxRangeCache)) {
    245    return false;
    246  }
    247 
    248  aInstance.ClearForReuse();
    249 
    250  if (!RangeType::sCachedRanges) {
    251    RangeType::sCachedRanges = new nsTArray<RefPtr<RangeType>>(16);
    252  }
    253  RangeType::sCachedRanges->AppendElement(&aInstance);
    254  return true;
    255 }
    256 
    257 nsINode* AbstractRange::GetClosestCommonInclusiveAncestor(
    258    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const {
    259  if (!mIsPositioned) {
    260    return nullptr;
    261  }
    262  nsINode* startContainer = ShadowDOMSelectionHelpers::GetStartContainer(
    263      this, aAllowCrossShadowBoundary);
    264  nsINode* endContainer = ShadowDOMSelectionHelpers::GetEndContainer(
    265      this, aAllowCrossShadowBoundary);
    266 
    267  if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
    268    if (startContainer == endContainer) {
    269      return startContainer;
    270    }
    271    // Since both the start container and the end container are
    272    // guaranteed to be in the same composed document.
    273    // If one of the boundary is a document, use that document
    274    // as the common ancestor since both nodes.
    275    const bool oneBoundaryIsDocument =
    276        (startContainer && startContainer->IsDocument()) ||
    277        (endContainer && endContainer->IsDocument());
    278    if (oneBoundaryIsDocument) {
    279      MOZ_ASSERT_IF(
    280          startContainer && startContainer->IsDocument(),
    281          !endContainer || endContainer->GetComposedDoc() == startContainer);
    282      MOZ_ASSERT_IF(
    283          endContainer && endContainer->IsDocument(),
    284          !startContainer || startContainer->GetComposedDoc() == endContainer);
    285 
    286      return startContainer ? startContainer->GetComposedDoc()
    287                            : endContainer->GetComposedDoc();
    288    }
    289 
    290    const auto rescope = [](nsINode*& aContainer) {
    291      if (!aContainer) {
    292        return;
    293      }
    294      // RangeBoundary allows the container to be shadow roots; When
    295      // this happens, we should use the shadow host here.
    296      if (auto* shadowRoot = ShadowRoot::FromNode(aContainer)) {
    297        aContainer = shadowRoot->GetHost();
    298        return;
    299      }
    300    };
    301 
    302    rescope(startContainer);
    303    rescope(endContainer);
    304 
    305    return nsContentUtils::GetCommonFlattenedTreeAncestorForSelection(
    306        startContainer ? startContainer->AsContent() : nullptr,
    307        endContainer ? endContainer->AsContent() : nullptr);
    308  }
    309  return nsContentUtils::GetClosestCommonInclusiveAncestor(startContainer,
    310                                                           endContainer);
    311 }
    312 
    313 // static
    314 template <typename SPT, typename SRT, typename EPT, typename ERT,
    315          typename RangeType>
    316 nsresult AbstractRange::SetStartAndEndInternal(
    317    const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
    318    const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange,
    319    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
    320  if (NS_WARN_IF(!aStartBoundary.IsSet()) ||
    321      NS_WARN_IF(!aEndBoundary.IsSet())) {
    322    return NS_ERROR_INVALID_ARG;
    323  }
    324 
    325  nsINode* newStartRoot =
    326      RangeUtils::ComputeRootNode(aStartBoundary.GetContainer());
    327  if (!newStartRoot) {
    328    return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
    329  }
    330  if (!aStartBoundary.IsSetAndValid()) {
    331    return NS_ERROR_DOM_INDEX_SIZE_ERR;
    332  }
    333 
    334  if (aStartBoundary.GetContainer() == aEndBoundary.GetContainer()) {
    335    if (!aEndBoundary.IsSetAndValid()) {
    336      return NS_ERROR_DOM_INDEX_SIZE_ERR;
    337    }
    338    // XXX: Offsets - handle this more efficiently.
    339    // If the end offset is less than the start offset, this should be
    340    // collapsed at the end offset.
    341    if (*aStartBoundary.Offset(
    342            RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOffsets) >
    343        *aEndBoundary.Offset(
    344            RangeBoundaryBase<EPT, ERT>::OffsetFilter::kValidOffsets)) {
    345      aRange->DoSetRange(aEndBoundary, aEndBoundary, newStartRoot);
    346    } else {
    347      aRange->DoSetRange(aStartBoundary, aEndBoundary, newStartRoot);
    348    }
    349    return NS_OK;
    350  }
    351 
    352  nsINode* newEndRoot =
    353      RangeUtils::ComputeRootNode(aEndBoundary.GetContainer());
    354  if (!newEndRoot) {
    355    return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
    356  }
    357  if (!aEndBoundary.IsSetAndValid()) {
    358    return NS_ERROR_DOM_INDEX_SIZE_ERR;
    359  }
    360 
    361  // Different root
    362  if (newStartRoot != newEndRoot) {
    363    if (aRange->IsStaticRange()) {
    364      // StaticRange allows nodes in different trees, so set start and end
    365      // accordingly
    366      aRange->DoSetRange(aStartBoundary, aEndBoundary, newEndRoot);
    367    } else {
    368      MOZ_ASSERT(aRange->IsDynamicRange());
    369      // In contrast, nsRange keeps both. It has a pair of start and end
    370      // which they have been collapsed to one end, and it also may have a pair
    371      // of start and end which are the original value.
    372      aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot);
    373 
    374      // Don't create the cross shadow bounday range if the one of the roots is
    375      // an UA widget regardless whether the boundaries are allowed to cross
    376      // shadow boundary or not.
    377      if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes &&
    378          !IsRootUAWidget(newStartRoot) && !IsRootUAWidget(newEndRoot)) {
    379        aRange->AsDynamicRange()
    380            ->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
    381                aStartBoundary.AsRangeBoundaryInFlatTree(),
    382                aEndBoundary.AsRangeBoundaryInFlatTree());
    383      }
    384    }
    385    return NS_OK;
    386  }
    387 
    388  const Maybe<int32_t> pointOrder =
    389      aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes &&
    390              StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
    391          ? nsContentUtils::ComparePoints<TreeKind::Flat>(aStartBoundary,
    392                                                          aEndBoundary)
    393          : nsContentUtils::ComparePoints(aStartBoundary, aEndBoundary);
    394  if (!pointOrder) {
    395    // Safely return a value but also detected this in debug builds.
    396    MOZ_ASSERT_UNREACHABLE();
    397    return NS_ERROR_INVALID_ARG;
    398  }
    399 
    400  // If the end point is before the start point, this should be collapsed at
    401  // the end point.
    402  if (*pointOrder == 1) {
    403    aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot);
    404    return NS_OK;
    405  }
    406 
    407  // Otherwise, set the range as specified.
    408  aRange->DoSetRange(aStartBoundary, aEndBoundary, newStartRoot);
    409 
    410  if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes &&
    411      aRange->IsDynamicRange()) {
    412    auto startInFlat = aStartBoundary.AsRangeBoundaryInFlatTree();
    413    auto endInFlat = aEndBoundary.AsRangeBoundaryInFlatTree();
    414 
    415    aRange->AsDynamicRange()->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
    416        startInFlat, endInFlat);
    417  }
    418 
    419  return NS_OK;
    420 }
    421 
    422 bool AbstractRange::IsInSelection(const Selection& aSelection) const {
    423  return mSelections.Contains(&aSelection);
    424 }
    425 
    426 void AbstractRange::RegisterSelection(Selection& aSelection) {
    427  if (IsInSelection(aSelection)) {
    428    return;
    429  }
    430  bool isFirstSelection = mSelections.IsEmpty();
    431  mSelections.AppendElement(&aSelection);
    432  if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) {
    433    nsINode* commonAncestor = GetClosestCommonInclusiveAncestor(
    434        StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
    435            ? AllowRangeCrossShadowBoundary::Yes
    436            : AllowRangeCrossShadowBoundary::No);
    437    MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes");
    438    RegisterClosestCommonInclusiveAncestor(commonAncestor);
    439  }
    440 }
    441 
    442 const nsTArray<WeakPtr<Selection>>& AbstractRange::GetSelections() const {
    443  return mSelections;
    444 }
    445 
    446 void AbstractRange::UnregisterSelection(const Selection& aSelection,
    447                                        IsUnlinking aIsUnlinking) {
    448  mSelections.RemoveElement(&aSelection);
    449  if (mSelections.IsEmpty() && mRegisteredClosestCommonInclusiveAncestor) {
    450    UnregisterClosestCommonInclusiveAncestor(aIsUnlinking);
    451    MOZ_DIAGNOSTIC_ASSERT(
    452        !mRegisteredClosestCommonInclusiveAncestor,
    453        "How can we have a registered common ancestor when we "
    454        "just unregistered?");
    455    MOZ_DIAGNOSTIC_ASSERT(
    456        !isInList(),
    457        "Shouldn't be registered if we have no "
    458        "mRegisteredClosestCommonInclusiveAncestor after unregistering");
    459  }
    460 }
    461 
    462 void AbstractRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) {
    463  MOZ_ASSERT(aNode, "bad arg");
    464 
    465  MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(),
    466                        "registering range not in selection");
    467 
    468  mRegisteredClosestCommonInclusiveAncestor = aNode;
    469 
    470  MarkDescendants(*aNode);
    471 
    472  UniquePtr<LinkedList<AbstractRange>>& ranges =
    473      aNode->GetClosestCommonInclusiveAncestorRangesPtr();
    474  if (!ranges) {
    475    ranges = MakeUnique<LinkedList<AbstractRange>>();
    476  }
    477 
    478  MOZ_DIAGNOSTIC_ASSERT(!isInList());
    479  ranges->insertBack(this);
    480  aNode->SetClosestCommonInclusiveAncestorForRangeInSelection();
    481 }
    482 
    483 void AbstractRange::UnregisterClosestCommonInclusiveAncestor(
    484    IsUnlinking aIsUnlinking) {
    485  if (!mRegisteredClosestCommonInclusiveAncestor) {
    486    return;
    487  }
    488  nsCOMPtr oldClosestCommonInclusiveAncestor =
    489      mRegisteredClosestCommonInclusiveAncestor;
    490  mRegisteredClosestCommonInclusiveAncestor = nullptr;
    491  LinkedList<AbstractRange>* ranges =
    492      oldClosestCommonInclusiveAncestor
    493          ->GetExistingClosestCommonInclusiveAncestorRanges();
    494  MOZ_ASSERT(ranges);
    495 
    496 #ifdef DEBUG
    497  bool found = false;
    498  for (AbstractRange* range : *ranges) {
    499    if (range == this) {
    500      found = true;
    501      break;
    502    }
    503  }
    504  MOZ_ASSERT(found,
    505             "We should be in the list on our registered common ancestor");
    506 #endif  // DEBUG
    507 
    508  remove();
    509 
    510  // We don't want to waste time unmarking flags on nodes that are
    511  // being unlinked anyway.
    512  if (aIsUnlinking == IsUnlinking::No && ranges->isEmpty()) {
    513    oldClosestCommonInclusiveAncestor
    514        ->ClearClosestCommonInclusiveAncestorForRangeInSelection();
    515    UnmarkDescendants(*oldClosestCommonInclusiveAncestor);
    516  }
    517  oldClosestCommonInclusiveAncestor = nullptr;
    518 }
    519 
    520 void AbstractRange::UpdateCommonAncestorIfNecessary() {
    521  nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor;
    522  nsINode* newCommonAncestor =
    523      GetClosestCommonInclusiveAncestor(AllowRangeCrossShadowBoundary::Yes);
    524  if (newCommonAncestor != oldCommonAncestor) {
    525    UnregisterClosestCommonInclusiveAncestor();
    526 
    527    if (newCommonAncestor) {
    528      RegisterClosestCommonInclusiveAncestor(newCommonAncestor);
    529    } else {
    530      MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes");
    531      mSelections.Clear();
    532      MOZ_DIAGNOSTIC_ASSERT(
    533          !mRegisteredClosestCommonInclusiveAncestor,
    534          "How can we have a registered common ancestor when we "
    535          "didn't register ourselves?");
    536      MOZ_DIAGNOSTIC_ASSERT(!isInList(),
    537                            "Shouldn't be registered if we have no "
    538                            "mRegisteredClosestCommonInclusiveAncestor");
    539    }
    540  }
    541 }
    542 
    543 const RangeBoundary& AbstractRange::MayCrossShadowBoundaryStartRef() const {
    544  return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryStartRef()
    545                          : mStart;
    546 }
    547 
    548 const RangeBoundary& AbstractRange::MayCrossShadowBoundaryEndRef() const {
    549  return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryEndRef()
    550                          : mEnd;
    551 }
    552 
    553 nsIContent* AbstractRange::GetMayCrossShadowBoundaryChildAtStartOffset() const {
    554  return IsDynamicRange()
    555             ? AsDynamicRange()->GetMayCrossShadowBoundaryChildAtStartOffset()
    556             : mStart.GetChildAtOffset();
    557 }
    558 
    559 nsIContent* AbstractRange::GetMayCrossShadowBoundaryChildAtEndOffset() const {
    560  return IsDynamicRange()
    561             ? AsDynamicRange()->GetMayCrossShadowBoundaryChildAtEndOffset()
    562             : mEnd.GetChildAtOffset();
    563 }
    564 
    565 nsINode* AbstractRange::GetMayCrossShadowBoundaryStartContainer() const {
    566  return IsDynamicRange()
    567             ? AsDynamicRange()->GetMayCrossShadowBoundaryStartContainer()
    568             : mStart.GetContainer();
    569 }
    570 
    571 nsINode* AbstractRange::GetMayCrossShadowBoundaryEndContainer() const {
    572  return IsDynamicRange()
    573             ? AsDynamicRange()->GetMayCrossShadowBoundaryEndContainer()
    574             : mEnd.GetContainer();
    575 }
    576 
    577 bool AbstractRange::MayCrossShadowBoundary() const {
    578  return IsDynamicRange() ? !!AsDynamicRange()->GetCrossShadowBoundaryRange()
    579                          : false;
    580 }
    581 
    582 uint32_t AbstractRange::MayCrossShadowBoundaryStartOffset() const {
    583  return IsDynamicRange()
    584             ? AsDynamicRange()->MayCrossShadowBoundaryStartOffset()
    585             : static_cast<uint32_t>(*mStart.Offset(
    586                   RangeBoundary::OffsetFilter::kValidOrInvalidOffsets));
    587 }
    588 
    589 uint32_t AbstractRange::MayCrossShadowBoundaryEndOffset() const {
    590  return IsDynamicRange()
    591             ? AsDynamicRange()->MayCrossShadowBoundaryEndOffset()
    592             : static_cast<uint32_t>(*mEnd.Offset(
    593                   RangeBoundary::OffsetFilter::kValidOrInvalidOffsets));
    594 }
    595 
    596 nsINode* AbstractRange::GetParentObject() const { return mOwner; }
    597 
    598 JSObject* AbstractRange::WrapObject(JSContext* aCx,
    599                                    JS::Handle<JSObject*> aGivenProto) {
    600  MOZ_CRASH("Must be overridden");
    601 }
    602 
    603 bool AbstractRange::AreNormalRangeAndCrossShadowBoundaryRangeCollapsed() const {
    604  if (!Collapsed()) {
    605    return false;
    606  }
    607 
    608  // We know normal range is collapsed at this point
    609  if (IsStaticRange()) {
    610    return true;
    611  }
    612 
    613  if (const CrossShadowBoundaryRange* crossShadowBoundaryRange =
    614          AsDynamicRange()->GetCrossShadowBoundaryRange()) {
    615    return crossShadowBoundaryRange->Collapsed();
    616  }
    617 
    618  return true;
    619 }
    620 
    621 void AbstractRange::ClearForReuse() {
    622  mOwner = nullptr;
    623  mStart = RangeBoundary(mStart.GetTreeKind());
    624  mEnd = RangeBoundary(mEnd.GetTreeKind());
    625  mIsPositioned = false;
    626  mIsGenerated = false;
    627  mCalledByJS = false;
    628 }
    629 
    630 /*static*/
    631 bool AbstractRange::IsRootUAWidget(const nsINode* aRoot) {
    632  MOZ_ASSERT(aRoot);
    633  if (const ShadowRoot* shadowRoot = ShadowRoot::FromNode(aRoot)) {
    634    return shadowRoot->IsUAWidget();
    635  }
    636  return false;
    637 }
    638 
    639 already_AddRefed<StaticRange> AbstractRange::GetShrunkenRangeToVisibleLeaves()
    640    const {
    641  if (NS_WARN_IF(!IsPositioned()) || NS_WARN_IF(Collapsed()) ||
    642      NS_WARN_IF(IsStaticRange() && !AsStaticRange()->IsValid())) {
    643    return nullptr;
    644  }
    645 
    646  const RawRangeBoundary startBoundary =
    647      SelectionMovementUtils::GetFirstVisiblePointAtLeaf(*this);
    648  if (MOZ_UNLIKELY(!startBoundary.IsSet())) {
    649    return nullptr;
    650  }
    651  const RawRangeBoundary endBoundary =
    652      SelectionMovementUtils::GetLastVisiblePointAtLeaf(*this);
    653  if (MOZ_UNLIKELY(!endBoundary.IsSet())) {
    654    return nullptr;
    655  }
    656  IgnoredErrorResult error;
    657  RefPtr<StaticRange> range =
    658      StaticRange::Create(startBoundary, endBoundary, error);
    659  if (NS_WARN_IF(error.Failed())) {
    660    error.SuppressException();
    661    return nullptr;
    662  }
    663  return range.forget();
    664 }
    665 
    666 }  // namespace mozilla::dom