tor-browser

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

nsRange.cpp (133647B)


      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 the DOM Range object.
      9 */
     10 
     11 #include "nsRange.h"
     12 
     13 #include "RangeBoundary.h"
     14 #include "mozilla/Assertions.h"
     15 #include "mozilla/CheckedInt.h"
     16 #include "mozilla/ContentIterator.h"
     17 #include "mozilla/Likely.h"
     18 #include "mozilla/Logging.h"
     19 #include "mozilla/Maybe.h"
     20 #include "mozilla/PresShell.h"
     21 #include "mozilla/RangeUtils.h"
     22 #include "mozilla/ToString.h"
     23 #include "mozilla/dom/CharacterData.h"
     24 #include "mozilla/dom/ChildIterator.h"
     25 #include "mozilla/dom/DOMRect.h"
     26 #include "mozilla/dom/DOMStringList.h"
     27 #include "mozilla/dom/Document.h"
     28 #include "mozilla/dom/DocumentFragment.h"
     29 #include "mozilla/dom/DocumentType.h"
     30 #include "mozilla/dom/InspectorFontFace.h"
     31 #include "mozilla/dom/RangeBinding.h"
     32 #include "mozilla/dom/Selection.h"
     33 #include "mozilla/dom/Text.h"
     34 #include "mozilla/dom/TrustedTypeUtils.h"
     35 #include "mozilla/dom/TrustedTypesConstants.h"
     36 #include "nsCSSFrameConstructor.h"
     37 #include "nsComputedDOMStyle.h"
     38 #include "nsContainerFrame.h"
     39 #include "nsContentUtils.h"
     40 #include "nsDebug.h"
     41 #include "nsError.h"
     42 #include "nsFrameSelection.h"
     43 #include "nsGkAtoms.h"
     44 #include "nsIContent.h"
     45 #include "nsINodeList.h"
     46 #include "nsLayoutUtils.h"
     47 #include "nsReadableUtils.h"
     48 #include "nsString.h"
     49 #include "nsStyleStruct.h"
     50 #include "nsStyleStructInlines.h"
     51 #include "nsTextFrame.h"
     52 #include "nscore.h"
     53 
     54 #ifdef ACCESSIBILITY
     55 #  include "nsAccessibilityService.h"
     56 #endif
     57 
     58 namespace mozilla {
     59 extern LazyLogModule sSelectionAPILog;
     60 extern void LogStackForSelectionAPI();
     61 
     62 template <typename SPT, typename SRT, typename EPT, typename ERT>
     63 static void LogSelectionAPI(const dom::Selection* aSelection,
     64                            const char* aFuncName, const char* aArgName1,
     65                            const RangeBoundaryBase<SPT, SRT>& aBoundary1,
     66                            const char* aArgName2,
     67                            const RangeBoundaryBase<EPT, ERT>& aBoundary2,
     68                            const char* aArgName3, bool aBoolArg) {
     69  if (aBoundary1 == aBoundary2) {
     70    MOZ_LOG(sSelectionAPILog, LogLevel::Info,
     71            ("%p nsRange::%s(%s=%s=%s, %s=%s)", aSelection, aFuncName,
     72             aArgName1, aArgName2, ToString(aBoundary1).c_str(), aArgName3,
     73             aBoolArg ? "true" : "false"));
     74  } else {
     75    MOZ_LOG(
     76        sSelectionAPILog, LogLevel::Info,
     77        ("%p nsRange::%s(%s=%s, %s=%s, %s=%s)", aSelection, aFuncName,
     78         aArgName1, ToString(aBoundary1).c_str(), aArgName2,
     79         ToString(aBoundary2).c_str(), aArgName3, aBoolArg ? "true" : "false"));
     80  }
     81 }
     82 }  // namespace mozilla
     83 
     84 using namespace mozilla;
     85 using namespace mozilla::dom;
     86 
     87 template already_AddRefed<nsRange> nsRange::Create(
     88    const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
     89    ErrorResult& aRv, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     90 template already_AddRefed<nsRange> nsRange::Create(
     91    const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
     92    ErrorResult& aRv, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     93 template already_AddRefed<nsRange> nsRange::Create(
     94    const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
     95    ErrorResult& aRv, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
     96 template already_AddRefed<nsRange> nsRange::Create(
     97    const RawRangeBoundary& aStartBoundary,
     98    const RawRangeBoundary& aEndBoundary, ErrorResult& aRv,
     99    AllowRangeCrossShadowBoundary aAlloCrossShadowBoundary);
    100 
    101 template nsresult nsRange::SetStartAndEnd(
    102    const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
    103    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
    104 template nsresult nsRange::SetStartAndEnd(
    105    const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
    106    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
    107 template nsresult nsRange::SetStartAndEnd(
    108    const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
    109    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
    110 template nsresult nsRange::SetStartAndEnd(
    111    const RawRangeBoundary& aStartBoundary,
    112    const RawRangeBoundary& aEndBoundary,
    113    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary);
    114 
    115 template void nsRange::DoSetRange(const RangeBoundary& aStartBoundary,
    116                                  const RangeBoundary& aEndBoundary,
    117                                  nsINode* aRootNode, bool aNotInsertedYet,
    118                                  RangeBehaviour aRangeBehaviour);
    119 template void nsRange::DoSetRange(const RangeBoundary& aStartBoundary,
    120                                  const RawRangeBoundary& aEndBoundary,
    121                                  nsINode* aRootNode, bool aNotInsertedYet,
    122                                  RangeBehaviour aRangeBehaviour);
    123 template void nsRange::DoSetRange(const RawRangeBoundary& aStartBoundary,
    124                                  const RangeBoundary& aEndBoundary,
    125                                  nsINode* aRootNode, bool aNotInsertedYet,
    126                                  RangeBehaviour aRangeBehaviour);
    127 template void nsRange::DoSetRange(const RawRangeBoundary& aStartBoundary,
    128                                  const RawRangeBoundary& aEndBoundary,
    129                                  nsINode* aRootNode, bool aNotInsertedYet,
    130                                  RangeBehaviour aRangeBehaviour);
    131 
    132 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
    133    const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary);
    134 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
    135    const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary);
    136 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
    137    const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary);
    138 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
    139    const RawRangeBoundary& aStartBoundary,
    140    const RawRangeBoundary& aEndBoundary);
    141 
    142 JSObject* nsRange::WrapObject(JSContext* aCx,
    143                              JS::Handle<JSObject*> aGivenProto) {
    144  return Range_Binding::Wrap(aCx, this, aGivenProto);
    145 }
    146 
    147 DocGroup* nsRange::GetDocGroup() const {
    148  return mOwner ? mOwner->GetDocGroup() : nullptr;
    149 }
    150 
    151 /******************************************************
    152 * stack based utility class for managing monitor
    153 ******************************************************/
    154 
    155 static void InvalidateAllFrames(nsINode* aNode) {
    156  MOZ_ASSERT(aNode, "bad arg");
    157 
    158  nsIFrame* frame = nullptr;
    159  switch (aNode->NodeType()) {
    160    case nsINode::TEXT_NODE:
    161    case nsINode::ELEMENT_NODE: {
    162      nsIContent* content = static_cast<nsIContent*>(aNode);
    163      frame = content->GetPrimaryFrame();
    164      break;
    165    }
    166    case nsINode::DOCUMENT_NODE: {
    167      Document* doc = static_cast<Document*>(aNode);
    168      PresShell* presShell = doc ? doc->GetPresShell() : nullptr;
    169      frame = presShell ? presShell->GetRootFrame() : nullptr;
    170      break;
    171    }
    172  }
    173  for (nsIFrame* f = frame; f; f = f->GetNextContinuation()) {
    174    f->InvalidateFrameSubtree();
    175  }
    176 }
    177 
    178 /******************************************************
    179 * constructor/destructor
    180 ******************************************************/
    181 
    182 nsTArray<RefPtr<nsRange>>* nsRange::sCachedRanges = nullptr;
    183 
    184 nsRange::~nsRange() {
    185  NS_ASSERTION(!IsInAnySelection(), "deleting nsRange that is in use");
    186 
    187  // we want the side effects (releases and list removals)
    188  DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
    189 }
    190 
    191 nsRange::nsRange(nsINode* aNode)
    192    : AbstractRange(aNode, /* aIsDynamicRange = */ true, TreeKind::DOM),
    193      mNextStartRef(nullptr),
    194      mNextEndRef(nullptr) {
    195  // printf("Size of nsRange: %zu\n", sizeof(nsRange));
    196 
    197  static_assert(sizeof(nsRange) <= 248,
    198                "nsRange size shouldn't be increased as far as possible");
    199 }
    200 
    201 /* static */
    202 already_AddRefed<nsRange> nsRange::Create(nsINode* aNode) {
    203  MOZ_ASSERT(aNode);
    204  if (!sCachedRanges || sCachedRanges->IsEmpty()) {
    205    return do_AddRef(new nsRange(aNode));
    206  }
    207  RefPtr<nsRange> range = sCachedRanges->PopLastElement().forget();
    208  range->Init(aNode);
    209  return range.forget();
    210 }
    211 
    212 /* static */
    213 template <typename SPT, typename SRT, typename EPT, typename ERT>
    214 already_AddRefed<nsRange> nsRange::Create(
    215    const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
    216    const RangeBoundaryBase<EPT, ERT>& aEndBoundary, ErrorResult& aRv,
    217    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
    218  MOZ_ASSERT(aStartBoundary.GetTreeKind() == aEndBoundary.GetTreeKind());
    219 
    220  // If we fail to initialize the range a lot, nsRange should have a static
    221  // initializer since the allocation cost is not cheap in hot path.
    222  RefPtr<nsRange> range = nsRange::Create(aStartBoundary.GetContainer());
    223  aRv = range->SetStartAndEnd(aStartBoundary, aEndBoundary,
    224                              aAllowCrossShadowBoundary);
    225  if (NS_WARN_IF(aRv.Failed())) {
    226    return nullptr;
    227  }
    228  return range.forget();
    229 }
    230 
    231 /*
    232 * When a new boundary is given to a nsRange, compare its position with other
    233 * existing boundaries to see if we need to collapse the end points.
    234 *
    235 * aRange: The nsRange that aNewBoundary is being set to.
    236 * aNewRoot: The shadow-including root of the container of aNewBoundary
    237 * aNewBoundary: The new boundary
    238 * aIsSetStart: true if GetRangeBehaviour is called by nsRange::SetStart,
    239 * false otherwise
    240 * aAllowCrossShadowBoundary: Indicates whether the boundaries allowed to cross
    241 * shadow boundary or not
    242 */
    243 static RangeBehaviour GetRangeBehaviour(
    244    const nsRange* aRange, const nsINode* aNewRoot,
    245    const RawRangeBoundary& aNewBoundaryInDOM,
    246    const Maybe<RawRangeBoundary>& aNewBoundaryInFlat, const bool aIsSetStart,
    247    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
    248  if (!aRange->IsPositioned()) {
    249    return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges;
    250  }
    251 
    252  MOZ_ASSERT(aRange->GetRoot());
    253 
    254  if (aNewRoot != aRange->GetRoot()) {
    255    // Boundaries are in different document (or not connected), so collapse
    256    // the both the default range and the crossBoundaryRange range.
    257    if (aNewRoot->GetComposedDoc() != aRange->GetRoot()->GetComposedDoc()) {
    258      return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges;
    259    }
    260 
    261    // Always collapse both ranges if the one of the roots is an UA widget
    262    // regardless whether the boundaries are allowed to cross shadow boundary
    263    // or not.
    264    if (AbstractRange::IsRootUAWidget(aNewRoot) ||
    265        AbstractRange::IsRootUAWidget(aRange->GetRoot())) {
    266      return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges;
    267    }
    268 
    269    if (const CrossShadowBoundaryRange* crossShadowBoundaryRange =
    270            aRange->GetCrossShadowBoundaryRange()) {
    271      // Check if the existing-other-side boundary in
    272      // aRange::mCrossShadowBoundaryRange has the same root
    273      // as aNewRoot. If this is the case, it means
    274      // aRange::mCrossShadowBoundaryRange can be used to represent this
    275      // cross-boundary selection, meanwhile we collapse the default range since
    276      // this is a cross-boundary selection.
    277      const RangeBoundary& otherSideExistingBoundary =
    278          aIsSetStart ? crossShadowBoundaryRange->EndRef()
    279                      : crossShadowBoundaryRange->StartRef();
    280      const nsINode* otherSideRoot =
    281          RangeUtils::ComputeRootNode(otherSideExistingBoundary.GetContainer());
    282      if (aNewRoot == otherSideRoot) {
    283        return RangeBehaviour::CollapseDefaultRange;
    284      }
    285    }
    286 
    287    // Different root, but same document. So we only collapse the
    288    // default range if boundaries are allowed to cross shadow boundary.
    289    return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
    290               ? RangeBehaviour::CollapseDefaultRange
    291               : RangeBehaviour::
    292                     CollapseDefaultRangeAndCrossShadowBoundaryRanges;
    293  }
    294 
    295  const RangeBoundary& otherSideExistingBoundaryInDOM =
    296      aIsSetStart ? aRange->EndRef() : aRange->StartRef();
    297 
    298  auto CompareFlatTreeBoundaries = [&aNewBoundaryInFlat, aIsSetStart,
    299                                    &aRange]() {
    300    MOZ_ASSERT(aRange->GetCrossShadowBoundaryRange());
    301    MOZ_ASSERT(aNewBoundaryInFlat.isSome() &&
    302               aNewBoundaryInFlat->IsSetAndValid());
    303    const RangeBoundary& otherSideExistingCrossShadowBoundaryBoundaryInFlat =
    304        aIsSetStart ? aRange->GetCrossShadowBoundaryRange()->EndRef()
    305                    : aRange->GetCrossShadowBoundaryRange()->StartRef();
    306    const Maybe<int32_t> withCrossShadowBoundaryOrder =
    307        aIsSetStart
    308            ? nsContentUtils::ComparePoints<TreeKind::Flat>(
    309                  aNewBoundaryInFlat.ref(),
    310                  otherSideExistingCrossShadowBoundaryBoundaryInFlat.AsRaw())
    311            : nsContentUtils::ComparePoints<TreeKind::Flat>(
    312                  otherSideExistingCrossShadowBoundaryBoundaryInFlat.AsRaw(),
    313                  aNewBoundaryInFlat.ref());
    314    if (withCrossShadowBoundaryOrder && *withCrossShadowBoundaryOrder != 1) {
    315      return RangeBehaviour::CollapseDefaultRange;
    316    }
    317 
    318    // Not valid to both existing boundaries.
    319    return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges;
    320  };
    321 
    322  if (!aNewBoundaryInDOM.IsSetAndValid()) {
    323    return CompareFlatTreeBoundaries();
    324  }
    325 
    326  // Both boundaries are in the same root, now check for their position
    327  const Maybe<int32_t> order =
    328      aIsSetStart
    329          ? nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    330                aNewBoundaryInDOM, otherSideExistingBoundaryInDOM.AsRaw())
    331          : nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    332                otherSideExistingBoundaryInDOM.AsRaw(), aNewBoundaryInDOM);
    333 
    334  if (order) {
    335    if (*order != 1) {
    336      // aNewBoundary is at a valid position.
    337      //
    338      // If aIsSetStart is true, this means
    339      // aNewBoundary <= otherSideExistingBoundary which is
    340      // good because aNewBoundary intends to be the start.
    341      //
    342      // If aIsSetStart is false, this means
    343      // otherSideExistingBoundary <= aNewBoundary which is good because
    344      // aNewBoundary intends to be the end.
    345      //
    346      // So no collapse for above cases.
    347      return RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges;
    348    }
    349 
    350    if (!aRange->MayCrossShadowBoundary() ||
    351        aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::No) {
    352      return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges;
    353    }
    354 
    355    return CompareFlatTreeBoundaries();
    356  }
    357 
    358  MOZ_ASSERT_UNREACHABLE();
    359  return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges;
    360 }
    361 /******************************************************
    362 * nsISupports
    363 ******************************************************/
    364 
    365 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange)
    366 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_INTERRUPTABLE_LAST_RELEASE(
    367    nsRange, DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr),
    368    MaybeInterruptLastRelease())
    369 
    370 // QueryInterface implementation for nsRange
    371 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange)
    372  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    373 NS_INTERFACE_MAP_END_INHERITING(AbstractRange)
    374 
    375 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
    376 
    377 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsRange, AbstractRange)
    378  // `Reset()` unlinks `mStart`, `mEnd` and `mRoot`.
    379  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrossShadowBoundaryRange);
    380  tmp->Reset();
    381 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    382 
    383 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsRange, AbstractRange)
    384  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
    385  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrossShadowBoundaryRange);
    386 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    387 
    388 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsRange, AbstractRange)
    389 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    390 
    391 bool nsRange::MaybeInterruptLastRelease() {
    392  bool interrupt = AbstractRange::MaybeCacheToReuse(*this);
    393  ResetCrossShadowBoundaryRange();
    394  MOZ_ASSERT(!interrupt || IsCleared());
    395  return interrupt;
    396 }
    397 
    398 void nsRange::AdjustNextRefsOnCharacterDataSplit(
    399    const nsIContent& aContent, const CharacterDataChangeInfo& aInfo) {
    400  // If the splitted text node is immediately before a range boundary point
    401  // that refers to a child index (i.e. its parent is the boundary container)
    402  // then we need to adjust the corresponding boundary to account for the new
    403  // text node that will be inserted. However, because the new sibling hasn't
    404  // been inserted yet, that would result in an invalid boundary. Therefore,
    405  // we store the new child in mNext*Ref to make sure we adjust the boundary
    406  // in the next ContentInserted or ContentAppended call.
    407  nsINode* parentNode = aContent.GetParentNode();
    408  if (parentNode == mEnd.GetContainer()) {
    409    if (&aContent == mEnd.Ref()) {
    410      MOZ_ASSERT(aInfo.mDetails->mNextSibling);
    411      mNextEndRef = aInfo.mDetails->mNextSibling;
    412    }
    413  }
    414 
    415  if (parentNode == mStart.GetContainer()) {
    416    if (&aContent == mStart.Ref()) {
    417      MOZ_ASSERT(aInfo.mDetails->mNextSibling);
    418      mNextStartRef = aInfo.mDetails->mNextSibling;
    419    }
    420  }
    421 }
    422 
    423 nsRange::RangeBoundariesAndRoot
    424 nsRange::DetermineNewRangeBoundariesAndRootOnCharacterDataMerge(
    425    nsIContent* aContent, const CharacterDataChangeInfo& aInfo) const {
    426  RawRangeBoundary newStart;
    427  RawRangeBoundary newEnd;
    428  nsINode* newRoot = nullptr;
    429 
    430  // normalize(), aInfo.mDetails->mNextSibling is the merged text node
    431  // that will be removed
    432  nsIContent* removed = aInfo.mDetails->mNextSibling;
    433  if (removed == mStart.GetContainer()) {
    434    CheckedUint32 newStartOffset{
    435        *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)};
    436    newStartOffset += aInfo.mChangeStart;
    437 
    438    // newStartOffset.isValid() isn't checked explicitly here, because
    439    // newStartOffset.value() contains an assertion.
    440    newStart = {aContent, newStartOffset.value()};
    441    if (MOZ_UNLIKELY(removed == mRoot)) {
    442      newRoot = RangeUtils::ComputeRootNode(newStart.GetContainer());
    443    }
    444  }
    445  if (removed == mEnd.GetContainer()) {
    446    CheckedUint32 newEndOffset{
    447        *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)};
    448    newEndOffset += aInfo.mChangeStart;
    449 
    450    // newEndOffset.isValid() isn't checked explicitly here, because
    451    // newEndOffset.value() contains an assertion.
    452    newEnd = {aContent, newEndOffset.value()};
    453    if (MOZ_UNLIKELY(removed == mRoot)) {
    454      newRoot = {RangeUtils::ComputeRootNode(newEnd.GetContainer())};
    455    }
    456  }
    457  // When the removed text node's parent is one of our boundary nodes we may
    458  // need to adjust the offset to account for the removed node. However,
    459  // there will also be a ContentRemoved notification later so the only cases
    460  // we need to handle here is when the removed node is the text node after
    461  // the boundary.  (The m*Offset > 0 check is an optimization - a boundary
    462  // point before the first child is never affected by normalize().)
    463  nsINode* parentNode = aContent->GetParentNode();
    464  if (parentNode == mStart.GetContainer() &&
    465      *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) > 0 &&
    466      *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <
    467          parentNode->GetChildCount() &&
    468      removed == mStart.GetChildAtOffset()) {
    469    newStart = {aContent, aInfo.mChangeStart};
    470  }
    471  if (parentNode == mEnd.GetContainer() &&
    472      *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) > 0 &&
    473      *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <
    474          parentNode->GetChildCount() &&
    475      removed == mEnd.GetChildAtOffset()) {
    476    newEnd = {aContent, aInfo.mChangeEnd};
    477  }
    478 
    479  return {newStart, newEnd, newRoot};
    480 }
    481 
    482 /******************************************************
    483 * nsIMutationObserver implementation
    484 ******************************************************/
    485 void nsRange::CharacterDataChanged(nsIContent* aContent,
    486                                   const CharacterDataChangeInfo& aInfo) {
    487  MOZ_ASSERT(aContent);
    488  MOZ_ASSERT(mIsPositioned);
    489  MOZ_ASSERT(!mNextEndRef);
    490  MOZ_ASSERT(!mNextStartRef);
    491 
    492  nsINode* newRoot = nullptr;
    493  RawRangeBoundary newStart;
    494  RawRangeBoundary newEnd;
    495 
    496  if (aInfo.mDetails &&
    497      aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit) {
    498    AdjustNextRefsOnCharacterDataSplit(*aContent, aInfo);
    499  }
    500 
    501  // If the changed node contains our start boundary and the change starts
    502  // before the boundary we'll need to adjust the offset.
    503  if (aContent == mStart.GetContainer() &&
    504      aInfo.mChangeStart <
    505          *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)) {
    506    if (aInfo.mDetails) {
    507      // splitText(), aInfo->mDetails->mNextSibling is the new text node
    508      NS_ASSERTION(
    509          aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit,
    510          "only a split can start before the end");
    511      NS_ASSERTION(
    512          *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <=
    513              aInfo.mChangeEnd + 1,
    514          "mStart.Offset() is beyond the end of this node");
    515      const uint32_t newStartOffset =
    516          *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) -
    517          aInfo.mChangeStart;
    518      newStart = {aInfo.mDetails->mNextSibling, newStartOffset};
    519      if (MOZ_UNLIKELY(aContent == mRoot)) {
    520        newRoot = RangeUtils::ComputeRootNode(newStart.GetContainer());
    521      }
    522 
    523      bool isCommonAncestor =
    524          IsInAnySelection() && mStart.GetContainer() == mEnd.GetContainer();
    525      if (isCommonAncestor) {
    526        MOZ_DIAGNOSTIC_ASSERT(mStart.GetContainer() ==
    527                              mRegisteredClosestCommonInclusiveAncestor);
    528        UnregisterClosestCommonInclusiveAncestor();
    529        RegisterClosestCommonInclusiveAncestor(newStart.GetContainer());
    530      }
    531      if (mStart.GetContainer()
    532              ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
    533        newStart.GetContainer()
    534            ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    535      }
    536    } else {
    537      newStart = ComputeNewBoundaryWhenBoundaryInsideChangedText(
    538          aInfo, mStart.AsRaw());
    539    }
    540  }
    541 
    542  // Do the same thing for the end boundary, except for splitText of a node
    543  // with no parent then only switch to the new node if the start boundary
    544  // did so too (otherwise the range would end up with disconnected nodes).
    545  if (aContent == mEnd.GetContainer() &&
    546      aInfo.mChangeStart <
    547          *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)) {
    548    if (aInfo.mDetails &&
    549        (aContent->GetParentNode() || newStart.GetContainer())) {
    550      // splitText(), aInfo.mDetails->mNextSibling is the new text node
    551      NS_ASSERTION(
    552          aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit,
    553          "only a split can start before the end");
    554      MOZ_ASSERT(
    555          *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <=
    556              aInfo.mChangeEnd + 1,
    557          "mEnd.Offset() is beyond the end of this node");
    558 
    559      const uint32_t newEndOffset{
    560          *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) -
    561          aInfo.mChangeStart};
    562      newEnd = {aInfo.mDetails->mNextSibling, newEndOffset};
    563 
    564      bool isCommonAncestor =
    565          IsInAnySelection() && mStart.GetContainer() == mEnd.GetContainer();
    566      if (isCommonAncestor && !newStart.GetContainer()) {
    567        MOZ_DIAGNOSTIC_ASSERT(mStart.GetContainer() ==
    568                              mRegisteredClosestCommonInclusiveAncestor);
    569        // The split occurs inside the range.
    570        UnregisterClosestCommonInclusiveAncestor();
    571        RegisterClosestCommonInclusiveAncestor(
    572            mStart.GetContainer()->GetParentNode());
    573        newEnd.GetContainer()
    574            ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    575      } else if (
    576          mEnd.GetContainer()
    577              ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
    578        newEnd.GetContainer()
    579            ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    580      }
    581    } else {
    582      newEnd =
    583          ComputeNewBoundaryWhenBoundaryInsideChangedText(aInfo, mEnd.AsRaw());
    584    }
    585  }
    586 
    587  if (aInfo.mDetails &&
    588      aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eMerge) {
    589    MOZ_ASSERT(!newStart.IsSet());
    590    MOZ_ASSERT(!newEnd.IsSet());
    591 
    592    RangeBoundariesAndRoot rangeBoundariesAndRoot =
    593        DetermineNewRangeBoundariesAndRootOnCharacterDataMerge(aContent, aInfo);
    594 
    595    newStart = rangeBoundariesAndRoot.mStart;
    596    newEnd = rangeBoundariesAndRoot.mEnd;
    597    newRoot = rangeBoundariesAndRoot.mRoot;
    598  }
    599 
    600  if (newStart.IsSet() || newEnd.IsSet()) {
    601    if (!newStart.IsSet()) {
    602      newStart.CopyFrom(mStart, RangeBoundaryIsMutationObserved::Yes);
    603    }
    604    if (!newEnd.IsSet()) {
    605      newEnd.CopyFrom(mEnd, RangeBoundaryIsMutationObserved::Yes);
    606    }
    607    DoSetRange(newStart, newEnd, newRoot ? newRoot : mRoot.get(),
    608               !newEnd.GetContainer()->GetParentNode() ||
    609                   !newStart.GetContainer()->GetParentNode());
    610  } else {
    611    nsRange::AssertIfMismatchRootAndRangeBoundaries(
    612        mStart, mEnd, mRoot,
    613        (mStart.IsSet() && !mStart.GetContainer()->GetParentNode()) ||
    614            (mEnd.IsSet() && !mEnd.GetContainer()->GetParentNode()));
    615  }
    616 }
    617 
    618 void nsRange::ContentAppended(nsIContent* aFirstNewContent,
    619                              const ContentAppendInfo&) {
    620  MOZ_ASSERT(mIsPositioned);
    621 
    622  nsINode* container = aFirstNewContent->GetParentNode();
    623  MOZ_ASSERT(container);
    624  if (container->IsMaybeSelected() && IsInAnySelection()) {
    625    nsINode* child = aFirstNewContent;
    626    while (child) {
    627      if (!child
    628               ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
    629        MarkDescendants(*child);
    630        child
    631            ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    632      }
    633      child = child->GetNextSibling();
    634    }
    635  }
    636 
    637  if (mNextStartRef || mNextEndRef) {
    638    // A splitText has occurred, if any mNext*Ref was set, we need to adjust
    639    // the range boundaries.
    640    if (mNextStartRef) {
    641      mStart = {mStart.GetContainer(), mNextStartRef};
    642      MOZ_ASSERT(mNextStartRef == aFirstNewContent);
    643      mNextStartRef = nullptr;
    644    }
    645    if (mNextEndRef) {
    646      mEnd = {mEnd.GetContainer(), mNextEndRef};
    647      MOZ_ASSERT(mNextEndRef == aFirstNewContent);
    648      mNextEndRef = nullptr;
    649    }
    650    DoSetRange(mStart, mEnd, mRoot, true);
    651  } else {
    652    nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart, mEnd, mRoot);
    653  }
    654 }
    655 
    656 void nsRange::ContentInserted(nsIContent* aChild, const ContentInsertInfo&) {
    657  MOZ_ASSERT(mIsPositioned);
    658 
    659  bool updateBoundaries = false;
    660  nsINode* container = aChild->GetParentNode();
    661  MOZ_ASSERT(container);
    662  RawRangeBoundary newStart(mStart, RangeBoundaryIsMutationObserved::Yes);
    663  RawRangeBoundary newEnd(mEnd, RangeBoundaryIsMutationObserved::Yes);
    664  MOZ_ASSERT(aChild->GetParentNode() == container);
    665 
    666  // Invalidate boundary offsets if a child that may have moved them was
    667  // inserted.
    668  if (container == mStart.GetContainer()) {
    669    newStart.InvalidateOffset();
    670    updateBoundaries = true;
    671  }
    672 
    673  if (container == mEnd.GetContainer()) {
    674    newEnd.InvalidateOffset();
    675    updateBoundaries = true;
    676  }
    677 
    678  if (container->IsMaybeSelected() &&
    679      !aChild
    680           ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
    681    MarkDescendants(*aChild);
    682    aChild->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    683  }
    684 
    685  if (mNextStartRef || mNextEndRef) {
    686    if (mNextStartRef) {
    687      newStart = {mStart.GetContainer(), mNextStartRef};
    688      MOZ_ASSERT(mNextStartRef == aChild);
    689      mNextStartRef = nullptr;
    690    }
    691    if (mNextEndRef) {
    692      newEnd = {mEnd.GetContainer(), mNextEndRef};
    693      MOZ_ASSERT(mNextEndRef == aChild);
    694      mNextEndRef = nullptr;
    695    }
    696 
    697    updateBoundaries = true;
    698  }
    699 
    700  if (updateBoundaries) {
    701    DoSetRange(newStart, newEnd, mRoot);
    702  } else {
    703    nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart, mEnd, mRoot);
    704  }
    705 }
    706 
    707 void nsRange::ContentWillBeRemoved(nsIContent* aChild,
    708                                   const ContentRemoveInfo&) {
    709  MOZ_ASSERT(mIsPositioned);
    710 
    711  nsINode* container = aChild->GetParentNode();
    712  MOZ_ASSERT(container);
    713 
    714  nsINode* startContainer = mStart.GetContainer();
    715  nsINode* endContainer = mEnd.GetContainer();
    716 
    717  RawRangeBoundary newStart;
    718  RawRangeBoundary newEnd;
    719  Maybe<bool> gravitateStart;
    720  bool gravitateEnd;
    721 
    722  // Adjust position if a sibling was removed...
    723  if (container == startContainer) {
    724    // We're only interested if our boundary reference was removed, otherwise
    725    // we can just invalidate the offset.
    726    if (aChild == mStart.Ref()) {
    727      newStart = {container, aChild->GetPreviousSibling()};
    728    } else {
    729      newStart.CopyFrom(mStart, RangeBoundaryIsMutationObserved::Yes);
    730      newStart.InvalidateOffset();
    731    }
    732  } else {
    733    gravitateStart = Some(startContainer->IsInclusiveDescendantOf(aChild));
    734    if (gravitateStart.value()) {
    735      newStart = {container, aChild->GetPreviousSibling()};
    736    }
    737  }
    738 
    739  // Do same thing for end boundry.
    740  if (container == endContainer) {
    741    if (aChild == mEnd.Ref()) {
    742      newEnd = {container, aChild->GetPreviousSibling()};
    743    } else {
    744      newEnd.CopyFrom(mEnd, RangeBoundaryIsMutationObserved::Yes);
    745      newEnd.InvalidateOffset();
    746    }
    747  } else {
    748    if (startContainer == endContainer && gravitateStart.isSome()) {
    749      gravitateEnd = gravitateStart.value();
    750    } else {
    751      gravitateEnd = endContainer->IsInclusiveDescendantOf(aChild);
    752    }
    753    if (gravitateEnd) {
    754      newEnd = {container, aChild->GetPreviousSibling()};
    755    }
    756  }
    757 
    758  bool newStartIsSet = newStart.IsSet();
    759  bool newEndIsSet = newEnd.IsSet();
    760  if (newStartIsSet || newEndIsSet) {
    761    DoSetRange(
    762        newStartIsSet ? newStart : mStart.AsRaw(),
    763        newEndIsSet ? newEnd : mEnd.AsRaw(), mRoot, false,
    764        // CrossShadowBoundaryRange mutates content
    765        // removal fot itself, so no need for nsRange to do anything with it.
    766        RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges);
    767  } else {
    768    nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart, mEnd, mRoot);
    769  }
    770 
    771  MOZ_ASSERT(mStart.Ref() != aChild);
    772  MOZ_ASSERT(mEnd.Ref() != aChild);
    773 
    774  if (container->IsMaybeSelected() &&
    775      aChild
    776          ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
    777    aChild
    778        ->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
    779    UnmarkDescendants(*aChild);
    780  }
    781 }
    782 
    783 void nsRange::ParentChainChanged(nsIContent* aContent) {
    784  NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
    785  nsINode* newRoot = RangeUtils::ComputeRootNode(mStart.GetContainer());
    786  NS_ASSERTION(newRoot, "No valid boundary or root found!");
    787  if (newRoot != RangeUtils::ComputeRootNode(mEnd.GetContainer())) {
    788    // Sometimes ordering involved in cycle collection can lead to our
    789    // start parent and/or end parent being disconnected from our root
    790    // without our getting a ContentRemoved notification.
    791    // See bug 846096 for more details.
    792    NS_ASSERTION(mEnd.GetContainer()->IsInNativeAnonymousSubtree(),
    793                 "This special case should happen only with "
    794                 "native-anonymous content");
    795    // When that happens, bail out and set pointers to null; since we're
    796    // in cycle collection and unreachable it shouldn't matter.
    797    Reset();
    798    return;
    799  }
    800  // This is safe without holding a strong ref to self as long as the change
    801  // of mRoot is the last thing in DoSetRange.
    802  DoSetRange(mStart, mEnd, newRoot);
    803 }
    804 
    805 bool nsRange::IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
    806    const nsINode& aContainer) const {
    807  MOZ_ASSERT(mCrossShadowBoundaryRange &&
    808             mCrossShadowBoundaryRange->GetCommonAncestor());
    809  return aContainer.IsShadowIncludingInclusiveDescendantOf(
    810      mCrossShadowBoundaryRange->GetCommonAncestor());
    811 }
    812 
    813 bool nsRange::IsPointComparableToRange(const nsINode& aContainer,
    814                                       uint32_t aOffset,
    815                                       bool aAllowCrossShadowBoundary,
    816                                       ErrorResult& aRv) const {
    817  // our range is in a good state?
    818  if (!mIsPositioned) {
    819    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    820    return false;
    821  }
    822 
    823  const bool isContainerInRange =
    824      aContainer.IsInclusiveDescendantOf(mRoot) ||
    825      (aAllowCrossShadowBoundary && mCrossShadowBoundaryRange &&
    826       IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
    827           aContainer));
    828 
    829  if (!isContainerInRange) {
    830    // TODO(emilio): Switch to ThrowWrongDocumentError, but IsPointInRange
    831    // relies on the error code right now in order to suppress the exception.
    832    aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
    833    return false;
    834  }
    835 
    836  auto chromeOnlyAccess = mStart.GetContainer()->ChromeOnlyAccess();
    837  NS_ASSERTION(chromeOnlyAccess == mEnd.GetContainer()->ChromeOnlyAccess(),
    838               "Start and end of a range must be either both native anonymous "
    839               "content or not.");
    840  if (aContainer.ChromeOnlyAccess() != chromeOnlyAccess) {
    841    aRv.ThrowInvalidNodeTypeError(
    842        "Trying to compare restricted with unrestricted nodes");
    843    return false;
    844  }
    845 
    846  if (aContainer.NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
    847    aRv.ThrowInvalidNodeTypeError("Trying to compare with a document");
    848    return false;
    849  }
    850 
    851  if (aOffset > aContainer.Length()) {
    852    aRv.ThrowIndexSizeError("Offset is out of bounds");
    853    return false;
    854  }
    855 
    856  return true;
    857 }
    858 
    859 bool nsRange::IsPointInRange(const nsINode& aContainer, uint32_t aOffset,
    860                             ErrorResult& aRv,
    861                             bool aAllowCrossShadowBoundary) const {
    862  int16_t compareResult =
    863      ComparePoint(aContainer, aOffset, aRv, aAllowCrossShadowBoundary);
    864  // If the node isn't in the range's document, it clearly isn't in the range.
    865  if (aRv.ErrorCodeIs(NS_ERROR_DOM_WRONG_DOCUMENT_ERR)) {
    866    aRv.SuppressException();
    867    return false;
    868  }
    869 
    870  return compareResult == 0;
    871 }
    872 
    873 int16_t nsRange::ComparePoint(const nsINode& aContainer, uint32_t aOffset,
    874                              ErrorResult& aRv,
    875                              bool aAllowCrossShadowBoundary) const {
    876  if (!IsPointComparableToRange(aContainer, aOffset, aAllowCrossShadowBoundary,
    877                                aRv)) {
    878    return 0;
    879  }
    880 
    881  const auto& startRef =
    882      aAllowCrossShadowBoundary ? MayCrossShadowBoundaryStartRef() : StartRef();
    883 
    884  const RawRangeBoundary point{const_cast<nsINode*>(&aContainer), aOffset,
    885                               RangeBoundaryIsMutationObserved::Yes,
    886                               startRef.GetTreeKind()};
    887 
    888  MOZ_ASSERT(point.IsSetAndValid());
    889 
    890  if (Maybe<int32_t> order = nsContentUtils::ComparePoints(
    891          point, aAllowCrossShadowBoundary ? MayCrossShadowBoundaryStartRef()
    892                                           : StartRef());
    893      order && *order <= 0) {
    894    return int16_t(*order);
    895  }
    896  if (Maybe<int32_t> order = nsContentUtils::ComparePoints(
    897          aAllowCrossShadowBoundary ? MayCrossShadowBoundaryEndRef() : EndRef(),
    898          point);
    899      order && *order == -1) {
    900    return 1;
    901  }
    902  return 0;
    903 }
    904 
    905 bool nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) {
    906  if (!mIsPositioned) {
    907    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    908    return false;
    909  }
    910 
    911  nsINode* parent = aNode.GetParentNode();
    912  if (!parent) {
    913    // |parent| is null, so |node|'s root is |node| itself.
    914    return GetRoot() == &aNode;
    915  }
    916 
    917  const Maybe<uint32_t> nodeIndex = parent->ComputeIndexOf(&aNode);
    918  if (nodeIndex.isNothing()) {
    919    return false;
    920  }
    921 
    922  if (!IsPointComparableToRange(*parent, *nodeIndex,
    923                                false /* aAllowCrossShadowBoundary */,
    924                                IgnoreErrors())) {
    925    return false;
    926  }
    927 
    928  const Maybe<int32_t> startOrder = nsContentUtils::ComparePoints(
    929      mStart, RawRangeBoundary(parent, aNode.AsContent(), *nodeIndex + 1u));
    930  if (startOrder && (*startOrder < 0)) {
    931    const Maybe<int32_t> endOrder = nsContentUtils::ComparePoints(
    932        RawRangeBoundary(parent, aNode.GetPreviousSibling(), *nodeIndex), mEnd);
    933    return endOrder && (*endOrder < 0);
    934  }
    935 
    936  return false;
    937 }
    938 
    939 void nsRange::NotifySelectionListenersAfterRangeSet() {
    940  if (mSelections.IsEmpty()) {
    941    return;
    942  }
    943 
    944  // Our internal code should not move focus with using this instance while
    945  // it's calling Selection::NotifySelectionListeners() which may move focus
    946  // or calls selection listeners.  So, let's set mCalledByJS to false here
    947  // since non-*JS() methods don't set it to false.
    948  AutoCalledByJSRestore calledByJSRestorer(*this);
    949  mCalledByJS = false;
    950 
    951  // If this instance is not a proper range for selection, we need to remove
    952  // this from selections.
    953  const Document* const docForSelf = mStart.GetComposedDoc();
    954  const nsFrameSelection* const frameSelection =
    955      mSelections[0]->GetFrameSelection();
    956  const Document* const docForSelection =
    957      frameSelection && frameSelection->GetPresShell()
    958          ? frameSelection->GetPresShell()->GetDocument()
    959          : nullptr;
    960  if (!IsPositioned() || docForSelf != docForSelection) {
    961    // XXX Why Selection::RemoveRangeAndUnselectFramesAndNotifyListeners() does
    962    // not set whether the caller is JS or not?
    963    if (IsPartOfOneSelectionOnly()) {
    964      RefPtr<Selection> selection = mSelections[0].get();
    965      selection->RemoveRangeAndUnselectFramesAndNotifyListeners(*this,
    966                                                                IgnoreErrors());
    967    } else {
    968      nsTArray<WeakPtr<Selection>> copiedSelections = mSelections.Clone();
    969      for (const auto& weakSelection : copiedSelections) {
    970        RefPtr<Selection> selection = weakSelection.get();
    971        if (MOZ_LIKELY(selection)) {
    972          selection->RemoveRangeAndUnselectFramesAndNotifyListeners(
    973              *this, IgnoreErrors());
    974        }
    975      }
    976    }
    977    // FYI: NotifySelectionListeners() should be called by
    978    // RemoveRangeAndUnselectFramesAndNotifyListeners() if it's required.
    979    // Therefore, we need to do nothing anymore.
    980    return;
    981  }
    982 
    983  // Notify all Selections. This may modify the range,
    984  // remove it from the selection, or the selection itself may have gone after
    985  // the call. Also, new selections may be added.
    986  // To ensure that listeners are notified for all *current* selections,
    987  // create a copy of the list of selections and use that for iterating. This
    988  // way selections can be added or removed safely during iteration.
    989  // To save allocation cost, the copy is only created if there is more than
    990  // one Selection present  (which will barely ever be the case).
    991  if (IsPartOfOneSelectionOnly()) {
    992    RefPtr<Selection> selection = mSelections[0].get();
    993 #ifdef ACCESSIBILITY
    994    a11y::SelectionManager::SelectionRangeChanged(selection->GetType(), *this);
    995 #endif
    996    selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
    997  } else {
    998    nsTArray<WeakPtr<Selection>> copiedSelections = mSelections.Clone();
    999    for (const auto& weakSelection : copiedSelections) {
   1000      RefPtr<Selection> selection = weakSelection.get();
   1001      if (MOZ_LIKELY(selection)) {
   1002 #ifdef ACCESSIBILITY
   1003        a11y::SelectionManager::SelectionRangeChanged(selection->GetType(),
   1004                                                      *this);
   1005 #endif
   1006        selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
   1007      }
   1008    }
   1009  }
   1010 }
   1011 
   1012 /******************************************************
   1013 * Private helper routines
   1014 ******************************************************/
   1015 
   1016 // static
   1017 template <typename SPT, typename SRT, typename EPT, typename ERT>
   1018 void nsRange::AssertIfMismatchRootAndRangeBoundaries(
   1019    const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
   1020    const RangeBoundaryBase<EPT, ERT>& aEndBoundary, const nsINode* aRootNode,
   1021    bool aNotInsertedYet /* = false */) {
   1022 #ifdef DEBUG
   1023  if (!aRootNode) {
   1024    MOZ_ASSERT(!aStartBoundary.IsSet());
   1025    MOZ_ASSERT(!aEndBoundary.IsSet());
   1026    return;
   1027  }
   1028 
   1029  MOZ_ASSERT(aStartBoundary.IsSet());
   1030  MOZ_ASSERT(aEndBoundary.IsSet());
   1031  MOZ_ASSERT(aStartBoundary.GetTreeKind() == aEndBoundary.GetTreeKind());
   1032 
   1033  if (!aNotInsertedYet) {
   1034    // Compute temporary root for given range boundaries.  If a range in native
   1035    // anonymous subtree is being removed, tempRoot may return the fragment's
   1036    // root content, but it shouldn't be used for new root node because the node
   1037    // may be bound to the root element again.
   1038    nsINode* tempRoot =
   1039        RangeUtils::ComputeRootNode(aStartBoundary.GetContainer());
   1040    // The new range should be in the temporary root node at least.
   1041    MOZ_ASSERT(tempRoot ==
   1042               RangeUtils::ComputeRootNode(aEndBoundary.GetContainer()));
   1043    MOZ_ASSERT(
   1044        aStartBoundary.GetContainer()->IsInclusiveDescendantOf(tempRoot));
   1045    MOZ_ASSERT(aEndBoundary.GetContainer()->IsInclusiveDescendantOf(tempRoot));
   1046    // If the new range is not disconnected or not in native anonymous subtree,
   1047    // the temporary root must be same as the new root node.  Otherwise,
   1048    // aRootNode should be the parent of root of the NAC (e.g., `<input>` if the
   1049    // range is in NAC under `<input>`), but tempRoot is now root content node
   1050    // of the disconnected subtree (e.g., `<div>` element in `<input>` element).
   1051    const bool tempRootIsDisconnectedNAC =
   1052        tempRoot->IsInNativeAnonymousSubtree() && !tempRoot->GetParentNode();
   1053    MOZ_ASSERT_IF(!tempRootIsDisconnectedNAC, tempRoot == aRootNode);
   1054  }
   1055  MOZ_ASSERT(aRootNode->IsDocument() || aRootNode->IsAttr() ||
   1056             aRootNode->IsDocumentFragment() || aRootNode->IsContent());
   1057 #endif  // #ifdef DEBUG
   1058 }
   1059 
   1060 // It's important that all setting of the range start/end points
   1061 // go through this function, which will do all the right voodoo
   1062 // for content notification of range ownership.
   1063 // Calling DoSetRange with either parent argument null will collapse
   1064 // the range to have both endpoints point to the other node
   1065 template <typename SPT, typename SRT, typename EPT, typename ERT>
   1066 void nsRange::
   1067    DoSetRange(const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
   1068               const RangeBoundaryBase<EPT, ERT>& aEndBoundary,
   1069               nsINode* aRootNode,
   1070               bool aNotInsertedYet /* = false */, RangeBehaviour aRangeBehaviour /* = CollapseDefaultRangeAndCrossShadowBoundaryRanges */) {
   1071  mIsPositioned = aStartBoundary.IsSetAndValid() &&
   1072                  aEndBoundary.IsSetAndValid() && aRootNode;
   1073  MOZ_ASSERT_IF(!mIsPositioned, !aStartBoundary.IsSet());
   1074  MOZ_ASSERT_IF(!mIsPositioned, !aEndBoundary.IsSet());
   1075  MOZ_ASSERT_IF(!mIsPositioned, !aRootNode);
   1076  MOZ_ASSERT(aStartBoundary.GetTreeKind() == aEndBoundary.GetTreeKind());
   1077  MOZ_ASSERT(aStartBoundary.GetTreeKind() == TreeKind::DOM);
   1078 
   1079  nsRange::AssertIfMismatchRootAndRangeBoundaries(aStartBoundary, aEndBoundary,
   1080                                                  aRootNode, aNotInsertedYet);
   1081 
   1082  if (mRoot != aRootNode) {
   1083    if (mRoot) {
   1084      mRoot->RemoveMutationObserver(this);
   1085    }
   1086    if (aRootNode) {
   1087      aRootNode->AddMutationObserver(this);
   1088    }
   1089  }
   1090  bool checkCommonAncestor =
   1091      (mStart.GetContainer() != aStartBoundary.GetContainer() ||
   1092       mEnd.GetContainer() != aEndBoundary.GetContainer()) &&
   1093      IsInAnySelection() && !aNotInsertedYet;
   1094 
   1095  // GetClosestCommonInclusiveAncestor is unreliable while we're unlinking
   1096  // (could return null if our start/end have already been unlinked), so make
   1097  // sure to not use it here to determine our "old" current ancestor.
   1098  mStart.CopyFrom(aStartBoundary, RangeBoundaryIsMutationObserved::Yes);
   1099  mEnd.CopyFrom(aEndBoundary, RangeBoundaryIsMutationObserved::Yes);
   1100 
   1101  if (aRangeBehaviour ==
   1102      RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges) {
   1103    ResetCrossShadowBoundaryRange();
   1104  }
   1105 
   1106  if (checkCommonAncestor) {
   1107    UpdateCommonAncestorIfNecessary();
   1108  }
   1109 
   1110  // This needs to be the last thing this function does, other than notifying
   1111  // selection listeners. See comment in ParentChainChanged.
   1112  if (mRoot != aRootNode) {
   1113    mRoot = aRootNode;
   1114  }
   1115 
   1116  // Notify any selection listeners. This has to occur last because otherwise
   1117  // the world could be observed by a selection listener while the range was in
   1118  // an invalid state. So we run it off of a script runner to ensure it runs
   1119  // after the mutation observers have finished running.
   1120  if (!mSelections.IsEmpty()) {
   1121    if (MOZ_LOG_TEST(sSelectionAPILog, LogLevel::Info)) {
   1122      for (const auto& selection : mSelections) {
   1123        if (selection && selection->Type() == SelectionType::eNormal) {
   1124          LogSelectionAPI(selection, __FUNCTION__, "aStartBoundary",
   1125                          aStartBoundary, "aEndBoundary", aEndBoundary,
   1126                          "aNotInsertedYet", aNotInsertedYet);
   1127          LogStackForSelectionAPI();
   1128        }
   1129      }
   1130    }
   1131    nsContentUtils::AddScriptRunner(
   1132        NewRunnableMethod("NotifySelectionListenersAfterRangeSet", this,
   1133                          &nsRange::NotifySelectionListenersAfterRangeSet));
   1134  }
   1135 }
   1136 
   1137 void nsRange::Reset() {
   1138  DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
   1139 }
   1140 
   1141 /******************************************************
   1142 * public functionality
   1143 ******************************************************/
   1144 
   1145 void nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr) {
   1146  AutoCalledByJSRestore calledByJSRestorer(*this);
   1147  mCalledByJS = true;
   1148  SetStart(aNode, aOffset, aErr);
   1149 }
   1150 
   1151 bool nsRange::CanAccess(const nsINode& aNode) const {
   1152  if (nsContentUtils::LegacyIsCallerNativeCode()) {
   1153    return true;
   1154  }
   1155  return nsContentUtils::CanCallerAccess(&aNode);
   1156 }
   1157 
   1158 void nsRange::SetStart(
   1159    nsINode& aNode, uint32_t aOffset, ErrorResult& aRv,
   1160    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
   1161  if (!CanAccess(aNode)) {
   1162    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1163    return;
   1164  }
   1165 
   1166  AutoInvalidateSelection atEndOfBlock(this);
   1167  SetStart(RawRangeBoundary(&aNode, aOffset), aRv, aAllowCrossShadowBoundary);
   1168 }
   1169 
   1170 void nsRange::SetStart(
   1171    const RawRangeBoundary& aPoint, ErrorResult& aRv,
   1172    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
   1173  nsINode* newRoot = RangeUtils::ComputeRootNode(aPoint.GetContainer());
   1174  if (!newRoot) {
   1175    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   1176    return;
   1177  }
   1178 
   1179  // PointInFlat is necessary when the aPoint looks like
   1180  // RangeBoundary(<slot>, an arbitary offset), here aPoint
   1181  // is not a valid RangeBoundary in DOM tree (when the <slot>
   1182  // doesn't have light DOM children), however it could be
   1183  // a valid RangeBoundary in Flat tree. SetStart should
   1184  // still work for this case.
   1185 
   1186  // It also makes more sense to have CrossShadowBoundaryRange
   1187  // always use PointInFlat because this is the composed range
   1188  // that we care about, and we care it in Flat tree.
   1189  // It's error prone if we mix the usage of DOM RangeBoundary
   1190  // versus Flat RangeBoundary.
   1191  auto pointInFlat =
   1192      aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
   1193          ? Some(aPoint.AsRangeBoundaryInFlatTree())
   1194          : Nothing();
   1195 
   1196  if (!aPoint.IsSetAndValid() &&
   1197      (!pointInFlat || !pointInFlat->IsSetAndValid())) {
   1198    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   1199    return;
   1200  }
   1201 
   1202  MOZ_ASSERT_IF(pointInFlat, aPoint.IsSet());
   1203  RangeBehaviour behaviour =
   1204      GetRangeBehaviour(this, newRoot, aPoint, pointInFlat,
   1205                        true /* aIsSetStart= */, aAllowCrossShadowBoundary);
   1206 
   1207  switch (behaviour) {
   1208    case RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges:
   1209      // EndRef(..) may be same as mStart or not, depends on
   1210      // the value of mCrossShadowBoundaryRange->mEnd, We need to update
   1211      // mCrossShadowBoundaryRange and the default boundaries separately
   1212      if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
   1213        if (MayCrossShadowBoundaryEndRef() != mEnd) {
   1214          CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
   1215              pointInFlat.ref(),
   1216              MayCrossShadowBoundaryEndRef().AsRangeBoundaryInFlatTree());
   1217        }
   1218      }
   1219      if (aPoint.IsSetAndValid()) {
   1220        DoSetRange(aPoint, mEnd, mRoot, false, behaviour);
   1221      }
   1222      break;
   1223    case RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges:
   1224      if (aPoint.IsSetAndValid()) {
   1225        DoSetRange(aPoint, aPoint, newRoot, false, behaviour);
   1226      }
   1227      break;
   1228    case RangeBehaviour::CollapseDefaultRange:
   1229      MOZ_ASSERT(aAllowCrossShadowBoundary ==
   1230                 AllowRangeCrossShadowBoundary::Yes);
   1231      CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
   1232          pointInFlat.ref(),
   1233          MayCrossShadowBoundaryEndRef().AsRangeBoundaryInFlatTree());
   1234      if (aPoint.IsSetAndValid()) {
   1235        DoSetRange(aPoint, aPoint, newRoot, false, behaviour);
   1236      }
   1237      break;
   1238    default:
   1239      MOZ_ASSERT_UNREACHABLE();
   1240  }
   1241 }
   1242 
   1243 void nsRange::SetStartAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset,
   1244                                               ErrorResult& aErr) {
   1245  AutoCalledByJSRestore calledByJSRestorer(*this);
   1246  mCalledByJS = true;
   1247  SetStart(aNode, aOffset, aErr, AllowRangeCrossShadowBoundary::Yes);
   1248 }
   1249 
   1250 void nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr) {
   1251  AutoCalledByJSRestore calledByJSRestorer(*this);
   1252  mCalledByJS = true;
   1253  SetStartBefore(aNode, aErr);
   1254 }
   1255 
   1256 void nsRange::SetStartBefore(
   1257    nsINode& aNode, ErrorResult& aRv,
   1258    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
   1259  if (!CanAccess(aNode)) {
   1260    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1261    return;
   1262  }
   1263 
   1264  AutoInvalidateSelection atEndOfBlock(this);
   1265  // If the node is being removed from its parent, GetRawRangeBoundaryBefore()
   1266  // returns unset instance.  Then, SetStart() will throw
   1267  // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
   1268  SetStart(RangeUtils::GetRawRangeBoundaryBefore(&aNode), aRv,
   1269           aAllowCrossShadowBoundary);
   1270 }
   1271 
   1272 void nsRange::SetStartAfterJS(nsINode& aNode, ErrorResult& aErr) {
   1273  AutoCalledByJSRestore calledByJSRestorer(*this);
   1274  mCalledByJS = true;
   1275  SetStartAfter(aNode, aErr);
   1276 }
   1277 
   1278 void nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv) {
   1279  if (!CanAccess(aNode)) {
   1280    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1281    return;
   1282  }
   1283 
   1284  AutoInvalidateSelection atEndOfBlock(this);
   1285  // If the node is being removed from its parent, GetRawRangeBoundaryAfter()
   1286  // returns unset instance.  Then, SetStart() will throw
   1287  // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
   1288  SetStart(RangeUtils::GetRawRangeBoundaryAfter(&aNode), aRv);
   1289 }
   1290 
   1291 void nsRange::SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr) {
   1292  AutoCalledByJSRestore calledByJSRestorer(*this);
   1293  mCalledByJS = true;
   1294  SetEnd(aNode, aOffset, aErr);
   1295 }
   1296 
   1297 void nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv,
   1298                     AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
   1299  if (!CanAccess(aNode)) {
   1300    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1301    return;
   1302  }
   1303  AutoInvalidateSelection atEndOfBlock(this);
   1304  SetEnd(RawRangeBoundary(&aNode, aOffset), aRv, aAllowCrossShadowBoundary);
   1305 }
   1306 
   1307 void nsRange::SetEnd(const RawRangeBoundary& aPoint, ErrorResult& aRv,
   1308                     AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
   1309  nsINode* newRoot = RangeUtils::ComputeRootNode(aPoint.GetContainer());
   1310  if (!newRoot) {
   1311    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   1312    return;
   1313  }
   1314 
   1315  // PointInFlat is necessary when the aPoint looks like
   1316  // RangeBoundary(<slot>, an arbitary offset), here aPoint
   1317  // is not a valid RangeBoundary in DOM tree (when the <slot>
   1318  // doesn't have light DOM children), however it could be
   1319  // a valid RangeBoundary in Flat tree. SetEnd should
   1320  // still work for this case.
   1321 
   1322  // It also makes more sense to have CrossShadowBoundaryRange
   1323  // always use PointInFlat because this is the composed range
   1324  // that we care about, and we care it in Flat tree.
   1325  // It's error prone if we mix the usage of DOM RangeBoundary
   1326  // versus Flat RangeBoundary.
   1327  auto pointInFlat =
   1328      aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
   1329          ? Some(aPoint.AsRangeBoundaryInFlatTree())
   1330          : Nothing();
   1331 
   1332  if (!aPoint.IsSetAndValid() &&
   1333      (!pointInFlat || !pointInFlat->IsSetAndValid())) {
   1334    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   1335    return;
   1336  }
   1337 
   1338  MOZ_ASSERT_IF(pointInFlat, aPoint.IsSet());
   1339  RangeBehaviour policy =
   1340      GetRangeBehaviour(this, newRoot, aPoint, pointInFlat,
   1341                        false /* aIsStartStart */, aAllowCrossShadowBoundary);
   1342 
   1343  switch (policy) {
   1344    case RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges:
   1345      // StartRef(..) may be same as mStart or not, depends on
   1346      // the value of mCrossShadowBoundaryRange->mStart, so we need to update
   1347      // mCrossShadowBoundaryRange and the default boundaries separately
   1348      if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
   1349        if (MayCrossShadowBoundaryStartRef() != mStart) {
   1350          CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
   1351              MayCrossShadowBoundaryStartRef().AsRangeBoundaryInFlatTree(),
   1352              pointInFlat.ref());
   1353        }
   1354      }
   1355      if (aPoint.IsSetAndValid()) {
   1356        DoSetRange(mStart, aPoint, mRoot, false, policy);
   1357      }
   1358      break;
   1359    case RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges:
   1360      if (aPoint.IsSetAndValid()) {
   1361        DoSetRange(aPoint, aPoint, newRoot, false, policy);
   1362      }
   1363      break;
   1364    case RangeBehaviour::CollapseDefaultRange:
   1365      MOZ_ASSERT(aAllowCrossShadowBoundary ==
   1366                 AllowRangeCrossShadowBoundary::Yes);
   1367      CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
   1368          MayCrossShadowBoundaryStartRef().AsRangeBoundaryInFlatTree(),
   1369          pointInFlat.ref());
   1370      if (aPoint.IsSetAndValid()) {
   1371        DoSetRange(aPoint, aPoint, newRoot, false, policy);
   1372      }
   1373      break;
   1374    default:
   1375      MOZ_ASSERT_UNREACHABLE();
   1376  }
   1377 }
   1378 
   1379 void nsRange::SetEndAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset,
   1380                                             ErrorResult& aErr) {
   1381  AutoCalledByJSRestore calledByJSRestorer(*this);
   1382  mCalledByJS = true;
   1383  SetEnd(aNode, aOffset, aErr,
   1384         AllowRangeCrossShadowBoundary::Yes /* aAllowCrossShadowBoundary */);
   1385 }
   1386 
   1387 void nsRange::SelectNodesInContainer(nsINode* aContainer,
   1388                                     nsIContent* aStartContent,
   1389                                     nsIContent* aEndContent) {
   1390  MOZ_ASSERT(aContainer);
   1391  MOZ_ASSERT(aContainer->ComputeIndexOf(aStartContent).valueOr(0) <=
   1392             aContainer->ComputeIndexOf(aEndContent).valueOr(0));
   1393  MOZ_ASSERT(aStartContent &&
   1394             aContainer->ComputeIndexOf(aStartContent).isSome());
   1395  MOZ_ASSERT(aEndContent && aContainer->ComputeIndexOf(aEndContent).isSome());
   1396 
   1397  nsINode* newRoot = RangeUtils::ComputeRootNode(aContainer);
   1398  MOZ_ASSERT(newRoot);
   1399  if (!newRoot) {
   1400    return;
   1401  }
   1402 
   1403  RawRangeBoundary start(aContainer, aStartContent->GetPreviousSibling());
   1404  RawRangeBoundary end(aContainer, aEndContent);
   1405  DoSetRange(start, end, newRoot);
   1406 }
   1407 
   1408 void nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr) {
   1409  AutoCalledByJSRestore calledByJSRestorer(*this);
   1410  mCalledByJS = true;
   1411  SetEndBefore(aNode, aErr);
   1412 }
   1413 
   1414 void nsRange::SetEndBefore(
   1415    nsINode& aNode, ErrorResult& aRv,
   1416    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
   1417  if (!CanAccess(aNode)) {
   1418    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1419    return;
   1420  }
   1421 
   1422  AutoInvalidateSelection atEndOfBlock(this);
   1423  // If the node is being removed from its parent, GetRawRangeBoundaryBefore()
   1424  // returns unset instance.  Then, SetEnd() will throw
   1425  // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
   1426  SetEnd(RangeUtils::GetRawRangeBoundaryBefore(&aNode), aRv,
   1427         aAllowCrossShadowBoundary);
   1428 }
   1429 
   1430 void nsRange::SetEndAfterJS(nsINode& aNode, ErrorResult& aErr) {
   1431  AutoCalledByJSRestore calledByJSRestorer(*this);
   1432  mCalledByJS = true;
   1433  SetEndAfter(aNode, aErr);
   1434 }
   1435 
   1436 void nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv) {
   1437  if (!CanAccess(aNode)) {
   1438    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1439    return;
   1440  }
   1441 
   1442  AutoInvalidateSelection atEndOfBlock(this);
   1443  // If the node is being removed from its parent, GetRawRangeBoundaryAfter()
   1444  // returns unset instance.  Then, SetEnd() will throw
   1445  // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
   1446  SetEnd(RangeUtils::GetRawRangeBoundaryAfter(&aNode), aRv);
   1447 }
   1448 
   1449 void nsRange::Collapse(bool aToStart) {
   1450  if (!mIsPositioned) return;
   1451 
   1452  AutoInvalidateSelection atEndOfBlock(this);
   1453  if (aToStart) {
   1454    DoSetRange(mStart, mStart, mRoot);
   1455  } else {
   1456    DoSetRange(mEnd, mEnd, mRoot);
   1457  }
   1458 }
   1459 
   1460 void nsRange::CollapseJS(bool aToStart) {
   1461  AutoCalledByJSRestore calledByJSRestorer(*this);
   1462  mCalledByJS = true;
   1463  Collapse(aToStart);
   1464 }
   1465 
   1466 void nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr) {
   1467  AutoCalledByJSRestore calledByJSRestorer(*this);
   1468  mCalledByJS = true;
   1469  SelectNode(aNode, aErr);
   1470 }
   1471 
   1472 void nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv) {
   1473  if (!CanAccess(aNode)) {
   1474    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1475    return;
   1476  }
   1477 
   1478  nsINode* container = aNode.GetParentNode();
   1479  nsINode* newRoot = RangeUtils::ComputeRootNode(container);
   1480  if (!newRoot) {
   1481    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   1482    return;
   1483  }
   1484 
   1485  const Maybe<uint32_t> index = container->ComputeIndexOf(&aNode);
   1486  // MOZ_ASSERT(index.isSome());
   1487  // We need to compute the index here unfortunately, because, while we have
   1488  // support for XBL, |container| may be the node's binding parent without
   1489  // actually containing it.
   1490  if (MOZ_UNLIKELY(NS_WARN_IF(index.isNothing()))) {
   1491    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   1492    return;
   1493  }
   1494 
   1495  AutoInvalidateSelection atEndOfBlock(this);
   1496  DoSetRange(RawRangeBoundary{container, *index},
   1497             RawRangeBoundary{container, *index + 1u}, newRoot);
   1498 }
   1499 
   1500 void nsRange::SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr) {
   1501  AutoCalledByJSRestore calledByJSRestorer(*this);
   1502  mCalledByJS = true;
   1503  SelectNodeContents(aNode, aErr);
   1504 }
   1505 
   1506 void nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv) {
   1507  if (!CanAccess(aNode)) {
   1508    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1509    return;
   1510  }
   1511 
   1512  nsINode* newRoot = RangeUtils::ComputeRootNode(&aNode);
   1513  if (!newRoot) {
   1514    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   1515    return;
   1516  }
   1517 
   1518  AutoInvalidateSelection atEndOfBlock(this);
   1519  DoSetRange(RawRangeBoundary(&aNode, 0u),
   1520             RawRangeBoundary(&aNode, aNode.Length()), newRoot);
   1521 }
   1522 
   1523 // The Subtree Content Iterator only returns subtrees that are
   1524 // completely within a given range. It doesn't return a CharacterData
   1525 // node that contains either the start or end point of the range.,
   1526 // nor does it return element nodes when nothing in the element is selected.
   1527 // We need an iterator that will also include these start/end points
   1528 // so that our methods/algorithms aren't cluttered with special
   1529 // case code that tries to include these points while iterating.
   1530 //
   1531 // The RangeSubtreeIterator class mimics the ContentSubtreeIterator
   1532 // methods we need, so should the Content Iterator support the
   1533 // start/end points in the future, we can switchover relatively
   1534 // easy.
   1535 
   1536 class MOZ_STACK_CLASS RangeSubtreeIterator {
   1537 private:
   1538  enum RangeSubtreeIterState { eDone = 0, eUseStart, eUseIterator, eUseEnd };
   1539 
   1540  Maybe<ContentSubtreeIterator> mSubtreeIter;
   1541  RangeSubtreeIterState mIterState;
   1542 
   1543  nsCOMPtr<nsINode> mStart;
   1544  nsCOMPtr<nsINode> mEnd;
   1545 
   1546 public:
   1547  RangeSubtreeIterator() : mIterState(eDone) {}
   1548  ~RangeSubtreeIterator() = default;
   1549 
   1550  nsresult Init(nsRange* aRange, AllowRangeCrossShadowBoundary =
   1551                                     AllowRangeCrossShadowBoundary::No);
   1552  already_AddRefed<nsINode> GetCurrentNode();
   1553  void First();
   1554  void Last();
   1555  void Next();
   1556  void Prev();
   1557 
   1558  bool IsDone() { return mIterState == eDone; }
   1559 };
   1560 
   1561 nsresult RangeSubtreeIterator::Init(
   1562    nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
   1563  mIterState = eDone;
   1564  if (aRange->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) {
   1565    return NS_OK;
   1566  }
   1567 
   1568  // Grab the start point of the range and QI it to
   1569  // a CharacterData pointer. If it is CharacterData store
   1570  // a pointer to the node.
   1571 
   1572  if (!aRange->IsPositioned()) {
   1573    return NS_ERROR_FAILURE;
   1574  }
   1575 
   1576  nsINode* node = aRange->GetMayCrossShadowBoundaryStartContainer();
   1577  if (NS_WARN_IF(!node)) {
   1578    return NS_ERROR_FAILURE;
   1579  }
   1580 
   1581  if (node->IsCharacterData() ||
   1582      (node->IsElement() && node->AsElement()->GetChildCount() ==
   1583                                aRange->MayCrossShadowBoundaryStartOffset())) {
   1584    mStart = node;
   1585  }
   1586 
   1587  // Grab the end point of the range and QI it to
   1588  // a CharacterData pointer. If it is CharacterData store
   1589  // a pointer to the node.
   1590 
   1591  node = aRange->GetMayCrossShadowBoundaryEndContainer();
   1592  if (NS_WARN_IF(!node)) {
   1593    return NS_ERROR_FAILURE;
   1594  }
   1595 
   1596  if (node->IsCharacterData() ||
   1597      (node->IsElement() && aRange->MayCrossShadowBoundaryEndOffset() == 0)) {
   1598    mEnd = node;
   1599  }
   1600 
   1601  if (mStart && mStart == mEnd) {
   1602    // The range starts and stops in the same CharacterData
   1603    // node. Null out the end pointer so we only visit the
   1604    // node once!
   1605 
   1606    mEnd = nullptr;
   1607  } else {
   1608    // Now create a Content Subtree Iterator to be used
   1609    // for the subtrees between the end points!
   1610 
   1611    mSubtreeIter.emplace();
   1612 
   1613    nsresult res =
   1614        aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
   1615            ? mSubtreeIter->InitWithAllowCrossShadowBoundary(aRange)
   1616            : mSubtreeIter->Init(aRange);
   1617    if (NS_FAILED(res)) return res;
   1618 
   1619    if (mSubtreeIter->IsDone()) {
   1620      // The subtree iterator thinks there's nothing
   1621      // to iterate over, so just free it up so we
   1622      // don't accidentally call into it.
   1623 
   1624      mSubtreeIter.reset();
   1625    }
   1626  }
   1627 
   1628  // Initialize the iterator by calling First().
   1629  // Note that we are ignoring the return value on purpose!
   1630 
   1631  First();
   1632 
   1633  return NS_OK;
   1634 }
   1635 
   1636 already_AddRefed<nsINode> RangeSubtreeIterator::GetCurrentNode() {
   1637  nsCOMPtr<nsINode> node;
   1638 
   1639  if (mIterState == eUseStart && mStart) {
   1640    node = mStart;
   1641  } else if (mIterState == eUseEnd && mEnd) {
   1642    node = mEnd;
   1643  } else if (mIterState == eUseIterator && mSubtreeIter) {
   1644    node = mSubtreeIter->GetCurrentNode();
   1645  }
   1646 
   1647  return node.forget();
   1648 }
   1649 
   1650 void RangeSubtreeIterator::First() {
   1651  if (mStart)
   1652    mIterState = eUseStart;
   1653  else if (mSubtreeIter) {
   1654    mSubtreeIter->First();
   1655 
   1656    mIterState = eUseIterator;
   1657  } else if (mEnd)
   1658    mIterState = eUseEnd;
   1659  else
   1660    mIterState = eDone;
   1661 }
   1662 
   1663 void RangeSubtreeIterator::Last() {
   1664  if (mEnd)
   1665    mIterState = eUseEnd;
   1666  else if (mSubtreeIter) {
   1667    mSubtreeIter->Last();
   1668 
   1669    mIterState = eUseIterator;
   1670  } else if (mStart)
   1671    mIterState = eUseStart;
   1672  else
   1673    mIterState = eDone;
   1674 }
   1675 
   1676 void RangeSubtreeIterator::Next() {
   1677  if (mIterState == eUseStart) {
   1678    if (mSubtreeIter) {
   1679      mSubtreeIter->First();
   1680 
   1681      mIterState = eUseIterator;
   1682    } else if (mEnd)
   1683      mIterState = eUseEnd;
   1684    else
   1685      mIterState = eDone;
   1686  } else if (mIterState == eUseIterator) {
   1687    mSubtreeIter->Next();
   1688 
   1689    if (mSubtreeIter->IsDone()) {
   1690      if (mEnd)
   1691        mIterState = eUseEnd;
   1692      else
   1693        mIterState = eDone;
   1694    }
   1695  } else
   1696    mIterState = eDone;
   1697 }
   1698 
   1699 void RangeSubtreeIterator::Prev() {
   1700  if (mIterState == eUseEnd) {
   1701    if (mSubtreeIter) {
   1702      mSubtreeIter->Last();
   1703 
   1704      mIterState = eUseIterator;
   1705    } else if (mStart)
   1706      mIterState = eUseStart;
   1707    else
   1708      mIterState = eDone;
   1709  } else if (mIterState == eUseIterator) {
   1710    mSubtreeIter->Prev();
   1711 
   1712    if (mSubtreeIter->IsDone()) {
   1713      if (mStart)
   1714        mIterState = eUseStart;
   1715      else
   1716        mIterState = eDone;
   1717    }
   1718  } else
   1719    mIterState = eDone;
   1720 }
   1721 
   1722 // CollapseRangeAfterDelete() is a utility method that is used by
   1723 // DeleteContents() and ExtractContents() to collapse the range
   1724 // in the correct place, under the range's root container (the
   1725 // range end points common container) as outlined by the Range spec:
   1726 //
   1727 // http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html
   1728 // The assumption made by this method is that the delete or extract
   1729 // has been done already, and left the range in a state where there is
   1730 // no content between the 2 end points.
   1731 
   1732 static nsresult CollapseRangeAfterDelete(nsRange* aRange) {
   1733  NS_ENSURE_ARG_POINTER(aRange);
   1734 
   1735  // Check if range gravity took care of collapsing the range for us!
   1736  if (aRange->Collapsed()) {
   1737    // aRange is collapsed so there's nothing for us to do.
   1738    //
   1739    // There are 2 possible scenarios here:
   1740    //
   1741    // 1. aRange could've been collapsed prior to the delete/extract,
   1742    //    which would've resulted in nothing being removed, so aRange
   1743    //    is already where it should be.
   1744    //
   1745    // 2. Prior to the delete/extract, aRange's start and end were in
   1746    //    the same container which would mean everything between them
   1747    //    was removed, causing range gravity to collapse the range.
   1748 
   1749    return NS_OK;
   1750  }
   1751 
   1752  // aRange isn't collapsed so figure out the appropriate place to collapse!
   1753  // First get both end points and their common ancestor.
   1754 
   1755  if (!aRange->IsPositioned()) {
   1756    return NS_ERROR_NOT_INITIALIZED;
   1757  }
   1758 
   1759  nsCOMPtr<nsINode> commonAncestor =
   1760      aRange->GetClosestCommonInclusiveAncestor();
   1761 
   1762  nsCOMPtr<nsINode> startContainer = aRange->GetStartContainer();
   1763  nsCOMPtr<nsINode> endContainer = aRange->GetEndContainer();
   1764 
   1765  // Collapse to one of the end points if they are already in the
   1766  // commonAncestor. This should work ok since this method is called
   1767  // immediately after a delete or extract that leaves no content
   1768  // between the 2 end points!
   1769 
   1770  if (startContainer == commonAncestor) {
   1771    aRange->Collapse(true);
   1772    return NS_OK;
   1773  }
   1774  if (endContainer == commonAncestor) {
   1775    aRange->Collapse(false);
   1776    return NS_OK;
   1777  }
   1778 
   1779  // End points are at differing levels. We want to collapse to the
   1780  // point that is between the 2 subtrees that contain each point,
   1781  // under the common ancestor.
   1782 
   1783  nsCOMPtr<nsINode> nodeToSelect(startContainer);
   1784 
   1785  while (nodeToSelect) {
   1786    nsCOMPtr<nsINode> parent = nodeToSelect->GetParentNode();
   1787    if (parent == commonAncestor) break;  // We found the nodeToSelect!
   1788 
   1789    nodeToSelect = parent;
   1790  }
   1791 
   1792  if (!nodeToSelect) return NS_ERROR_FAILURE;  // This should never happen!
   1793 
   1794  ErrorResult error;
   1795  aRange->SelectNode(*nodeToSelect, error);
   1796  if (error.Failed()) {
   1797    return error.StealNSResult();
   1798  }
   1799 
   1800  aRange->Collapse(false);
   1801  return NS_OK;
   1802 }
   1803 
   1804 NS_IMETHODIMP
   1805 PrependChild(nsINode* aContainer, nsINode* aChild) {
   1806  nsCOMPtr<nsINode> first = aContainer->GetFirstChild();
   1807  ErrorResult rv;
   1808  aContainer->InsertBefore(*aChild, first, rv);
   1809  return rv.StealNSResult();
   1810 }
   1811 
   1812 // Helper function for CutContents, making sure that the current node wasn't
   1813 // removed by mutation events (bug 766426)
   1814 static bool ValidateCurrentNode(nsRange* aRange, RangeSubtreeIterator& aIter) {
   1815  bool before, after;
   1816  nsCOMPtr<nsINode> node = aIter.GetCurrentNode();
   1817  if (!node) {
   1818    // We don't have to worry that the node was removed if it doesn't exist,
   1819    // e.g., the iterator is done.
   1820    return true;
   1821  }
   1822 
   1823  nsresult rv = RangeUtils::CompareNodeToRange(node, aRange, &before, &after);
   1824  if (NS_WARN_IF(NS_FAILED(rv))) {
   1825    return false;
   1826  }
   1827 
   1828  if (before || after) {
   1829    if (node->IsCharacterData()) {
   1830      // If we're dealing with the start/end container which is a character
   1831      // node, pretend that the node is in the range.
   1832      if (before && node == aRange->GetStartContainer()) {
   1833        before = false;
   1834      }
   1835      if (after && node == aRange->GetEndContainer()) {
   1836        after = false;
   1837      }
   1838    }
   1839  }
   1840 
   1841  return !before && !after;
   1842 }
   1843 
   1844 /**
   1845 * Delete unnecessary data from aCharacterData which must be the container of
   1846 * aStartRef or aEndRef. Then, this returns a clone node of aCharacterData if
   1847 * aCloneCutContent is true.
   1848 */
   1849 static already_AddRefed<nsINode> CutCharacterData(
   1850    CharacterData& aCharacterData, const RangeBoundary& aStartRef,
   1851    const RangeBoundary& aEndRef, bool aCloneCutContent, ErrorResult& aRv) {
   1852  MOZ_ASSERT(&aCharacterData == aStartRef.GetContainer() ||
   1853             &aCharacterData == aEndRef.GetContainer());
   1854 
   1855  const uint32_t startOffset =
   1856      *aStartRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
   1857  const uint32_t endOffset =
   1858      *aEndRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
   1859  if (aStartRef.GetContainer() == aEndRef.GetContainer()) {
   1860    // This range is completely contained within a single text node.
   1861    // Delete or extract the data between startOffset and endOffset.
   1862    if (*aEndRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets) <=
   1863        *aStartRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets)) {
   1864      return nullptr;
   1865    }
   1866    nsCOMPtr<nsINode> clone;
   1867    if (aCloneCutContent) {
   1868      nsAutoString cutValue;
   1869      aCharacterData.SubstringData(startOffset, endOffset - startOffset,
   1870                                   cutValue, aRv);
   1871      if (NS_WARN_IF(aRv.Failed())) {
   1872        return nullptr;
   1873      }
   1874      clone = aCharacterData.CloneNode(false, aRv);
   1875      if (NS_WARN_IF(aRv.Failed())) {
   1876        return nullptr;
   1877      }
   1878      clone->SetNodeValueInternal(cutValue, aRv);
   1879      if (NS_WARN_IF(aRv.Failed())) {
   1880        return nullptr;
   1881      }
   1882    }
   1883 
   1884    aCharacterData.DeleteData(startOffset, endOffset - startOffset, aRv);
   1885    return clone.forget();
   1886  }
   1887 
   1888  if (&aCharacterData == aStartRef.GetContainer()) {
   1889    // Delete or extract everything after startOffset.
   1890    const uint32_t dataLength = aCharacterData.TextDataLength();
   1891    if (dataLength < startOffset) {
   1892      return nullptr;
   1893    }
   1894    nsCOMPtr<nsINode> clone;
   1895    if (aCloneCutContent) {
   1896      nsAutoString cutValue;
   1897      aCharacterData.SubstringData(startOffset, dataLength, cutValue, aRv);
   1898      if (NS_WARN_IF(aRv.Failed())) {
   1899        return nullptr;
   1900      }
   1901      clone = aCharacterData.CloneNode(false, aRv);
   1902      if (NS_WARN_IF(aRv.Failed())) {
   1903        return nullptr;
   1904      }
   1905      clone->SetNodeValueInternal(cutValue, aRv);
   1906      if (NS_WARN_IF(aRv.Failed())) {
   1907        return nullptr;
   1908      }
   1909    }
   1910 
   1911    aCharacterData.DeleteData(startOffset, dataLength, aRv);
   1912    if (NS_WARN_IF(aRv.Failed())) {
   1913      return nullptr;
   1914    }
   1915    return clone.forget();
   1916  }
   1917 
   1918  MOZ_ASSERT(&aCharacterData == aEndRef.GetContainer());
   1919  // Delete or extract everything before endOffset.
   1920  nsCOMPtr<nsINode> clone;
   1921  if (aCloneCutContent) {
   1922    nsAutoString cutValue;
   1923    aCharacterData.SubstringData(0, endOffset, cutValue, aRv);
   1924    if (NS_WARN_IF(aRv.Failed())) {
   1925      return nullptr;
   1926    }
   1927    clone = aCharacterData.CloneNode(false, aRv);
   1928    if (NS_WARN_IF(aRv.Failed())) {
   1929      return nullptr;
   1930    }
   1931    clone->SetNodeValueInternal(cutValue, aRv);
   1932    if (NS_WARN_IF(aRv.Failed())) {
   1933      return nullptr;
   1934    }
   1935  }
   1936 
   1937  aCharacterData.DeleteData(0, endOffset, aRv);
   1938  if (NS_WARN_IF(aRv.Failed())) {
   1939    return nullptr;
   1940  }
   1941  return clone.forget();
   1942 }
   1943 
   1944 void nsRange::CutContents(DocumentFragment** aFragment,
   1945                          ElementHandler aElementHandler, ErrorResult& aRv) {
   1946  if (aFragment && aElementHandler) {
   1947    // Theoretically no reason it can't be handled, but not plumbed in enough to
   1948    // test.
   1949    MOZ_ASSERT_UNREACHABLE("Not handling both aFragment and aElementHandler");
   1950    aRv.Throw(NS_ERROR_UNEXPECTED);
   1951    return;
   1952  }
   1953  if (aFragment) {
   1954    *aFragment = nullptr;
   1955  }
   1956 
   1957  if (!CanAccess(*GetMayCrossShadowBoundaryStartContainer()) ||
   1958      !CanAccess(*GetMayCrossShadowBoundaryEndContainer())) {
   1959    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   1960    return;
   1961  }
   1962 
   1963  nsCOMPtr<Document> doc = mStart.GetContainer()->OwnerDoc();
   1964 
   1965  nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(
   1966      aRv, StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
   1967               ? AllowRangeCrossShadowBoundary::Yes
   1968               : AllowRangeCrossShadowBoundary::No);
   1969  if (aRv.Failed()) {
   1970    return;
   1971  }
   1972 
   1973  // If aFragment isn't null, create a temporary fragment to hold our return.
   1974  RefPtr<DocumentFragment> retval;
   1975  if (aFragment) {
   1976    retval =
   1977        new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
   1978  }
   1979  nsCOMPtr<nsINode> commonCloneAncestor = retval.get();
   1980 
   1981  // Save the range end points locally to avoid interference
   1982  // of Range gravity during our edits!
   1983 
   1984  const RangeBoundary startRef = MayCrossShadowBoundaryStartRef();
   1985  const RangeBoundary endRef = MayCrossShadowBoundaryEndRef();
   1986 
   1987  // `GetCommonAncestorContainer()` above ensures the range is positioned, hence
   1988  // there have to be valid offsets. Fix them in startRef/endRef right now.
   1989  const uint32_t startOffset =
   1990      *startRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
   1991  const uint32_t endOffset =
   1992      *endRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
   1993 
   1994  if (retval) {
   1995    // For extractContents(), abort early if there's a doctype (bug 719533).
   1996    // This can happen only if the common ancestor is a document, in which case
   1997    // we just need to find its doctype child and check if that's in the range.
   1998    nsCOMPtr<Document> commonAncestorDocument =
   1999        do_QueryInterface(commonAncestor);
   2000    if (commonAncestorDocument) {
   2001      RefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype();
   2002 
   2003      // `GetCommonAncestorContainer()` above ensured the range is positioned.
   2004      // Hence, start and end are both set and valid. If available, `doctype`
   2005      // has a common ancestor with start and end, hence both have to be
   2006      // comparable to it.
   2007      if (doctype &&
   2008          *nsContentUtils::ComparePointsWithIndices(
   2009              startRef.GetContainer(), startOffset, doctype, 0) < 0 &&
   2010          *nsContentUtils::ComparePointsWithIndices(
   2011              doctype, 0, endRef.GetContainer(), endOffset) < 0) {
   2012        aRv.ThrowHierarchyRequestError("Start or end position isn't valid.");
   2013        return;
   2014      }
   2015    }
   2016  }
   2017 
   2018  // Create and initialize a subtree iterator that will give
   2019  // us all the subtrees within the range.
   2020 
   2021  RangeSubtreeIterator iter;
   2022 
   2023  aRv = iter.Init(this,
   2024                  StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
   2025                      ? AllowRangeCrossShadowBoundary::Yes
   2026                      : AllowRangeCrossShadowBoundary::No);
   2027  if (aRv.Failed()) {
   2028    return;
   2029  }
   2030 
   2031  if (iter.IsDone()) {
   2032    // There's nothing for us to delete.
   2033    aRv = CollapseRangeAfterDelete(this);
   2034    if (!aRv.Failed() && aFragment) {
   2035      retval.forget(aFragment);
   2036    }
   2037    return;
   2038  }
   2039 
   2040  iter.First();
   2041 
   2042  // With the exception of text nodes that contain one of the range
   2043  // end points, the subtree iterator should only give us back subtrees
   2044  // that are completely contained between the range's end points.
   2045 
   2046  while (!iter.IsDone()) {
   2047    nsCOMPtr<nsINode> nodeToResult;
   2048    const nsCOMPtr<nsINode> node = iter.GetCurrentNode();
   2049 
   2050    // Before we delete anything, advance the iterator to the next node that's
   2051    // not a descendant of this one.  XXX It's a bit silly to iterate through
   2052    // the descendants only to throw them out, we should use an iterator that
   2053    // skips the descendants to begin with.
   2054 
   2055    iter.Next();
   2056    nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
   2057    while (nextNode && nextNode->IsInclusiveDescendantOf(node)) {
   2058      iter.Next();
   2059      nextNode = iter.GetCurrentNode();
   2060    }
   2061 
   2062    if (node == startRef.GetContainer() || node == endRef.GetContainer()) {
   2063      // If it's CharacterData, make sure we might need to delete
   2064      // part of the data, instead of removing the whole node.
   2065      //
   2066      // XXX_kin: We need to also handle ProcessingInstruction
   2067      // XXX_kin: according to the spec.
   2068      if (auto* const charData = CharacterData::FromNode(node)) {
   2069        nsMutationGuard guard;
   2070        nodeToResult =
   2071            CutCharacterData(*charData, startRef, endRef, !!retval, aRv);
   2072        if (MOZ_UNLIKELY(aRv.Failed())) {
   2073          return;
   2074        }
   2075        // FYI: Cutting some data from a CharacterData shouldn't cause any other
   2076        // mutations of the composed tree.  Therefore, basically, we should be
   2077        // able to skip this check.  However, the accessible caret may update
   2078        // its caret and that appears as some numbers of mutations.  Therefore,
   2079        // this expensive validation needs to run if the accessible caret is now
   2080        // enabled.
   2081        if (guard.Mutated(0) && !ValidateCurrentNode(this, iter)) {
   2082          aRv.Throw(NS_ERROR_UNEXPECTED);
   2083          return;
   2084        }
   2085      } else if (auto* const element = Element::FromNode(node)) {
   2086        // If the start boundary is the end of the start container which is an
   2087        // element, RangeSubtreeIterator iterates the start container element as
   2088        // the first node.  Otherwise, and the end boundary is the start of the
   2089        // end container which is an element, RangeSubtreeIterator iterates the
   2090        // end container element as the last node.
   2091        if ((element == endRef.GetContainer() && endRef.IsStartOfContainer()) ||
   2092            (element == startRef.GetContainer() &&
   2093             startRef.IsEndOfContainer())) {
   2094          // Then, we want to return the empty cloned container since nothing is
   2095          // selected by this range in the element.  However, we don't want to
   2096          // delete the element itself.
   2097          // Note that even if the start/end container is a void element like
   2098          // <img>, <input>, etc, we anyway clone the void element so that the
   2099          // replaced content will be duplicated without deleting them from the
   2100          // DOM.  This behavior matches with Chrome 142 and Safari 26.1.
   2101          if (retval) {
   2102            nodeToResult = element->CloneNode(false, aRv);
   2103            if (aRv.Failed()) {
   2104              return;
   2105            }
   2106          }
   2107        } else {
   2108          // The current node is an element and the start or end container of
   2109          // the range and the offset is middle of the element.  However,
   2110          // RangeSubtreeIterator should not iterate this because of outside of
   2111          // the range. I.e., this should never happen.
   2112          MOZ_DIAGNOSTIC_ASSERT(
   2113              false, "The container shouldn't be iterated due to out of range");
   2114          continue;  // Just ignore the illegal case in the release channel.
   2115        }
   2116      } else {
   2117        // The current node which is the same as the start container or the end
   2118        // container of the range is not a CharacterData nor an element but the
   2119        // node is the start or end container of the range.  This should be an
   2120        // illegal case too.
   2121        MOZ_ASSERT(node == startRef.GetContainer() ||
   2122                   node == endRef.GetContainer());
   2123        MOZ_ASSERT(!node->IsCharacterData() && !node->IsElement());
   2124        NS_WARNING(
   2125            nsPrintfCString("Unexpected type of content node (%s) is iterated "
   2126                            "by RangeSubtreeIterator",
   2127                            mozilla::ToString(*node).c_str())
   2128                .get());
   2129        MOZ_DIAGNOSTIC_ASSERT(false,
   2130                              "Unexpected type of content node is iterated by "
   2131                              "RangeSubtreeIterator");
   2132        continue;  // Just ignore the illegal case in the release channel.
   2133      }
   2134    } else {
   2135      // The current node is completely in the range so that we can remove it
   2136      // from the document.
   2137      MOZ_ASSERT(node != startRef.GetContainer() &&
   2138                 node != endRef.GetContainer());
   2139      // When this is called, the current node must be completely contained
   2140      // within the range.
   2141      if (aElementHandler && node->IsElement()) {
   2142        // This is an element, and the caller specified a handler for it, so
   2143        // use it.
   2144        MOZ_ASSERT(!aFragment, "Fragment requested when ElementHandler given?");
   2145        nsMutationGuard guard;
   2146        auto* const element = node->AsElement();
   2147        aElementHandler(element);
   2148        // No need to validate - we know this node is an element, so any
   2149        // case that may cause the node to fail to validate is covered by
   2150        // the mutation guard.
   2151        if (MOZ_UNLIKELY(guard.Mutated(0))) {
   2152          aRv.Throw(NS_ERROR_UNEXPECTED);
   2153          return;
   2154        }
   2155      } else {
   2156        // Otherwise, just remove it from the tree.
   2157        nodeToResult = node;
   2158      }
   2159    }
   2160 
   2161    uint32_t parentCount = 0;
   2162    // Set the result to document fragment if we have 'retval'.
   2163    if (retval) {
   2164      nsCOMPtr<nsINode> oldCommonAncestor = commonAncestor;
   2165      if (!iter.IsDone()) {
   2166        // Setup the parameters for the next iteration of the loop.
   2167        if (!nextNode) {
   2168          aRv.Throw(NS_ERROR_UNEXPECTED);
   2169          return;
   2170        }
   2171 
   2172        // Get node's and nextNode's common parent. Do this before moving
   2173        // nodes from original DOM to result fragment.
   2174        commonAncestor =
   2175            nsContentUtils::GetClosestCommonInclusiveAncestor(node, nextNode);
   2176        if (!commonAncestor) {
   2177          aRv.Throw(NS_ERROR_UNEXPECTED);
   2178          return;
   2179        }
   2180 
   2181        nsCOMPtr<nsINode> parentCounterNode = node;
   2182        while (parentCounterNode && parentCounterNode != commonAncestor) {
   2183          ++parentCount;
   2184          parentCounterNode = parentCounterNode->GetParentNode();
   2185          if (!parentCounterNode) {
   2186            aRv.Throw(NS_ERROR_UNEXPECTED);
   2187            return;
   2188          }
   2189        }
   2190      }
   2191 
   2192      // Clone the parent hierarchy between commonAncestor and node.
   2193      nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
   2194      aRv = CloneParentsBetween(oldCommonAncestor, node,
   2195                                getter_AddRefs(closestAncestor),
   2196                                getter_AddRefs(farthestAncestor));
   2197      if (aRv.Failed()) {
   2198        return;
   2199      }
   2200 
   2201      if (farthestAncestor) {
   2202        commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
   2203        if (NS_WARN_IF(aRv.Failed())) {
   2204          return;
   2205        }
   2206      }
   2207 
   2208      nsMutationGuard guard;
   2209      const bool isCloneNode = !nodeToResult->GetParentNode();
   2210      if (closestAncestor) {
   2211        closestAncestor->AppendChild(*nodeToResult, aRv);
   2212      } else {
   2213        commonCloneAncestor->AppendChild(*nodeToResult, aRv);
   2214      }
   2215      if (NS_WARN_IF(aRv.Failed())) {
   2216        return;
   2217      }
   2218      // If the node is a clone, its removal is not happen.  Therefore, only the
   2219      // connecting mutation should be counted.
   2220      // If the node is in the document, both it's removal from the document
   2221      // and connecting to the new parent should be counted.
   2222      //
   2223      // When removing the node from document, DevTools may break on the removal
   2224      // and the user may modify the DOM.  Additionally, when the removing node
   2225      // contains subdocuments, its `unload` and `beforeunload` are fired
   2226      // synchronously.  Then, the unloading is also counted as mutations.
   2227      // Finally, if there is accessible caret, its mutations are also counted.
   2228      // Therefore, we often need to run the expensive validation here.
   2229      if (NS_WARN_IF(guard.Mutated(isCloneNode ? 1 : 2) &&
   2230                     !ValidateCurrentNode(this, iter))) {
   2231        aRv.Throw(NS_ERROR_UNEXPECTED);
   2232        return;
   2233      }
   2234    } else if (nodeToResult) {
   2235      if (const nsCOMPtr<nsINode> parent = nodeToResult->GetParentNode()) {
   2236        nsMutationGuard guard;
   2237        parent->RemoveChild(*nodeToResult, aRv);
   2238        if (MOZ_UNLIKELY(aRv.Failed())) {
   2239          return;
   2240        }
   2241        // When removing the node from document, DevTools may break on the
   2242        // removal and the user may modify the DOM.  Additionally, when the
   2243        // removing node contains subdocuments, its `unload` and `beforeunload`
   2244        // are fired synchronously.  Then, the unloading is also counted as
   2245        // mutations.  Finally, if there is accessible caret, its mutations are
   2246        // also counted.  Therefore, we often need to run the expensive
   2247        // validation here.
   2248        if (NS_WARN_IF(guard.Mutated(1) && !ValidateCurrentNode(this, iter))) {
   2249          aRv.Throw(NS_ERROR_UNEXPECTED);
   2250          return;
   2251        }
   2252      }
   2253    }
   2254 
   2255    if (!iter.IsDone() && retval) {
   2256      // Find the equivalent of commonAncestor in the cloned tree.
   2257      nsCOMPtr<nsINode> newCloneAncestor = nodeToResult;
   2258      for (uint32_t i = parentCount; i; --i) {
   2259        newCloneAncestor = newCloneAncestor->GetParentNode();
   2260        if (!newCloneAncestor) {
   2261          aRv.Throw(NS_ERROR_UNEXPECTED);
   2262          return;
   2263        }
   2264      }
   2265      commonCloneAncestor = newCloneAncestor;
   2266    }
   2267  }
   2268 
   2269  aRv = CollapseRangeAfterDelete(this);
   2270  if (!aRv.Failed() && aFragment) {
   2271    retval.forget(aFragment);
   2272  }
   2273 }
   2274 
   2275 void nsRange::DeleteContents(ErrorResult& aRv) {
   2276  CutContents(nullptr, nullptr, aRv);
   2277 }
   2278 
   2279 already_AddRefed<DocumentFragment> nsRange::ExtractContents(ErrorResult& rv) {
   2280  RefPtr<DocumentFragment> fragment;
   2281  CutContents(getter_AddRefs(fragment), nullptr, rv);
   2282  return fragment.forget();
   2283 }
   2284 
   2285 int16_t nsRange::CompareBoundaryPoints(uint16_t aHow,
   2286                                       const nsRange& aOtherRange,
   2287                                       ErrorResult& aRv) {
   2288  if (!mIsPositioned || !aOtherRange.IsPositioned()) {
   2289    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   2290    return 0;
   2291  }
   2292 
   2293  RawRangeBoundary ourBoundary, otherBoundary;
   2294  switch (aHow) {
   2295    case Range_Binding::START_TO_START:
   2296      ourBoundary = mStart.AsRaw();
   2297      otherBoundary = aOtherRange.StartRef().AsRaw();
   2298      break;
   2299    case Range_Binding::START_TO_END:
   2300      ourBoundary = mEnd.AsRaw();
   2301      otherBoundary = aOtherRange.StartRef().AsRaw();
   2302      break;
   2303    case Range_Binding::END_TO_START:
   2304      ourBoundary = mStart.AsRaw();
   2305      otherBoundary = aOtherRange.EndRef().AsRaw();
   2306      break;
   2307    case Range_Binding::END_TO_END:
   2308      ourBoundary = mEnd.AsRaw();
   2309      otherBoundary = aOtherRange.EndRef().AsRaw();
   2310      break;
   2311    default:
   2312      // We were passed an illegal value
   2313      aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   2314      return 0;
   2315  }
   2316 
   2317  if (mRoot != aOtherRange.GetRoot()) {
   2318    aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
   2319    return 0;
   2320  }
   2321 
   2322  const Maybe<int32_t> order =
   2323      nsContentUtils::ComparePoints(ourBoundary, otherBoundary);
   2324 
   2325  // `this` and `aOtherRange` share the same root and ourBoundary, otherBoundary
   2326  // correspond to some of their boundaries. Hence, ourBoundary and
   2327  // otherBoundary have to be comparable.
   2328  return *order;
   2329 }
   2330 
   2331 /* static */
   2332 nsresult nsRange::CloneParentsBetween(nsINode* aAncestor, nsINode* aNode,
   2333                                      nsINode** aClosestAncestor,
   2334                                      nsINode** aFarthestAncestor) {
   2335  NS_ENSURE_ARG_POINTER(
   2336      (aAncestor && aNode && aClosestAncestor && aFarthestAncestor));
   2337 
   2338  *aClosestAncestor = nullptr;
   2339  *aFarthestAncestor = nullptr;
   2340 
   2341  if (aAncestor == aNode) return NS_OK;
   2342 
   2343  AutoTArray<nsCOMPtr<nsINode>, 16> parentStack;
   2344 
   2345  nsCOMPtr<nsINode> parent = aNode->GetParentNode();
   2346  while (parent && parent != aAncestor) {
   2347    parentStack.AppendElement(parent);
   2348    parent = parent->GetParentNode();
   2349  }
   2350 
   2351  nsCOMPtr<nsINode> firstParent;
   2352  nsCOMPtr<nsINode> lastParent;
   2353  for (int32_t i = parentStack.Length() - 1; i >= 0; i--) {
   2354    ErrorResult rv;
   2355    nsCOMPtr<nsINode> clone = parentStack[i]->CloneNode(false, rv);
   2356 
   2357    if (rv.Failed()) {
   2358      return rv.StealNSResult();
   2359    }
   2360    if (!clone) {
   2361      return NS_ERROR_FAILURE;
   2362    }
   2363 
   2364    if (!lastParent) {
   2365      lastParent = clone;
   2366    } else {
   2367      firstParent->AppendChild(*clone, rv);
   2368      if (rv.Failed()) {
   2369        return rv.StealNSResult();
   2370      }
   2371    }
   2372 
   2373    firstParent = clone;
   2374  }
   2375 
   2376  firstParent.forget(aClosestAncestor);
   2377  lastParent.forget(aFarthestAncestor);
   2378 
   2379  return NS_OK;
   2380 }
   2381 
   2382 already_AddRefed<DocumentFragment> nsRange::CloneContents(ErrorResult& aRv) {
   2383  nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv);
   2384  MOZ_ASSERT(!aRv.Failed(), "GetCommonAncestorContainer() shouldn't fail!");
   2385 
   2386  nsCOMPtr<Document> doc = mStart.GetContainer()->OwnerDoc();
   2387  NS_ASSERTION(doc, "CloneContents needs a document to continue.");
   2388  if (!doc) {
   2389    aRv.Throw(NS_ERROR_FAILURE);
   2390    return nullptr;
   2391  }
   2392 
   2393  // Create a new document fragment in the context of this document,
   2394  // which might be null
   2395 
   2396  RefPtr<DocumentFragment> clonedFrag =
   2397      new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
   2398 
   2399  if (Collapsed()) {
   2400    return clonedFrag.forget();
   2401  }
   2402 
   2403  nsCOMPtr<nsINode> commonCloneAncestor = clonedFrag.get();
   2404 
   2405  // Create and initialize a subtree iterator that will give
   2406  // us all the subtrees within the range.
   2407 
   2408  RangeSubtreeIterator iter;
   2409 
   2410  aRv = iter.Init(this);
   2411  if (aRv.Failed()) {
   2412    return nullptr;
   2413  }
   2414 
   2415  if (iter.IsDone()) {
   2416    // There's nothing to add to the doc frag, we must be done!
   2417    return clonedFrag.forget();
   2418  }
   2419 
   2420  iter.First();
   2421 
   2422  // With the exception of text nodes that contain one of the range
   2423  // end points and elements which don't have any content selected the subtree
   2424  // iterator should only give us back subtrees that are completely contained
   2425  // between the range's end points.
   2426  //
   2427  // Unfortunately these subtrees don't contain the parent hierarchy/context
   2428  // that the Range spec requires us to return. This loop clones the
   2429  // parent hierarchy, adds a cloned version of the subtree, to it, then
   2430  // correctly places this new subtree into the doc fragment.
   2431 
   2432  while (!iter.IsDone()) {
   2433    nsCOMPtr<nsINode> node = iter.GetCurrentNode();
   2434    bool deepClone =
   2435        !node->IsElement() ||
   2436        (!(node == mEnd.GetContainer() &&
   2437           *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets) == 0) &&
   2438         !(node == mStart.GetContainer() &&
   2439           *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets) ==
   2440               node->AsElement()->GetChildCount()));
   2441 
   2442    // Clone the current subtree!
   2443 
   2444    nsCOMPtr<nsINode> clone = node->CloneNode(deepClone, aRv);
   2445    if (aRv.Failed()) {
   2446      return nullptr;
   2447    }
   2448 
   2449    // If it's CharacterData, make sure we only clone what
   2450    // is in the range.
   2451    //
   2452    // XXX_kin: We need to also handle ProcessingInstruction
   2453    // XXX_kin: according to the spec.
   2454 
   2455    if (auto charData = CharacterData::FromNode(clone)) {
   2456      if (node == mEnd.GetContainer()) {
   2457        // We only need the data before mEndOffset, so get rid of any
   2458        // data after it.
   2459 
   2460        uint32_t dataLength = charData->Length();
   2461        if (dataLength >
   2462            *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets)) {
   2463          charData->DeleteData(
   2464              *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   2465              dataLength -
   2466                  *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   2467              aRv);
   2468          if (aRv.Failed()) {
   2469            return nullptr;
   2470          }
   2471        }
   2472      }
   2473 
   2474      if (node == mStart.GetContainer()) {
   2475        // We don't need any data before mStartOffset, so just
   2476        // delete it!
   2477 
   2478        if (*mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets) > 0) {
   2479          charData->DeleteData(
   2480              0, *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   2481              aRv);
   2482          if (aRv.Failed()) {
   2483            return nullptr;
   2484          }
   2485        }
   2486      }
   2487    }
   2488 
   2489    // Clone the parent hierarchy between commonAncestor and node.
   2490 
   2491    nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
   2492 
   2493    aRv = CloneParentsBetween(commonAncestor, node,
   2494                              getter_AddRefs(closestAncestor),
   2495                              getter_AddRefs(farthestAncestor));
   2496 
   2497    if (aRv.Failed()) {
   2498      return nullptr;
   2499    }
   2500 
   2501    // Hook the parent hierarchy/context of the subtree into the clone tree.
   2502 
   2503    if (farthestAncestor) {
   2504      commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
   2505 
   2506      if (aRv.Failed()) {
   2507        return nullptr;
   2508      }
   2509    }
   2510 
   2511    // Place the cloned subtree into the cloned doc frag tree!
   2512 
   2513    nsCOMPtr<nsINode> cloneNode = clone;
   2514    if (closestAncestor) {
   2515      // Append the subtree under closestAncestor since it is the
   2516      // immediate parent of the subtree.
   2517 
   2518      closestAncestor->AppendChild(*cloneNode, aRv);
   2519    } else {
   2520      // If we get here, there is no missing parent hierarchy between
   2521      // commonAncestor and node, so just append clone to commonCloneAncestor.
   2522 
   2523      commonCloneAncestor->AppendChild(*cloneNode, aRv);
   2524    }
   2525    if (aRv.Failed()) {
   2526      return nullptr;
   2527    }
   2528 
   2529    // Get the next subtree to be processed. The idea here is to setup
   2530    // the parameters for the next iteration of the loop.
   2531 
   2532    iter.Next();
   2533 
   2534    if (iter.IsDone()) break;  // We must be done!
   2535 
   2536    nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
   2537    if (!nextNode) {
   2538      aRv.Throw(NS_ERROR_FAILURE);
   2539      return nullptr;
   2540    }
   2541 
   2542    // Get node and nextNode's common parent.
   2543    commonAncestor =
   2544        nsContentUtils::GetClosestCommonInclusiveAncestor(node, nextNode);
   2545 
   2546    if (!commonAncestor) {
   2547      aRv.Throw(NS_ERROR_FAILURE);
   2548      return nullptr;
   2549    }
   2550 
   2551    // Find the equivalent of commonAncestor in the cloned tree!
   2552 
   2553    while (node && node != commonAncestor) {
   2554      node = node->GetParentNode();
   2555      if (aRv.Failed()) {
   2556        return nullptr;
   2557      }
   2558 
   2559      if (!node) {
   2560        aRv.Throw(NS_ERROR_FAILURE);
   2561        return nullptr;
   2562      }
   2563 
   2564      cloneNode = cloneNode->GetParentNode();
   2565      if (!cloneNode) {
   2566        aRv.Throw(NS_ERROR_FAILURE);
   2567        return nullptr;
   2568      }
   2569    }
   2570 
   2571    commonCloneAncestor = cloneNode;
   2572  }
   2573 
   2574  return clonedFrag.forget();
   2575 }
   2576 
   2577 already_AddRefed<nsRange> nsRange::CloneRange() const {
   2578  RefPtr<nsRange> range = nsRange::Create(mOwner);
   2579  range->DoSetRange(mStart, mEnd, mRoot);
   2580  if (mCrossShadowBoundaryRange) {
   2581    range->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
   2582        mCrossShadowBoundaryRange->StartRef(),
   2583        mCrossShadowBoundaryRange->EndRef());
   2584  }
   2585  return range.forget();
   2586 }
   2587 
   2588 void nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv) {
   2589  if (!CanAccess(aNode)) {
   2590    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   2591    return;
   2592  }
   2593 
   2594  if (!IsPositioned()) {
   2595    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   2596    return;
   2597  }
   2598 
   2599  uint32_t tStartOffset = StartOffset();
   2600 
   2601  nsCOMPtr<nsINode> tStartContainer = GetStartContainer();
   2602 
   2603  if (!CanAccess(*tStartContainer)) {
   2604    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   2605    return;
   2606  }
   2607 
   2608  if (&aNode == tStartContainer) {
   2609    aRv.ThrowHierarchyRequestError(
   2610        "The inserted node can not be range's start node.");
   2611    return;
   2612  }
   2613 
   2614  // This is the node we'll be inserting before, and its parent
   2615  nsCOMPtr<nsINode> referenceNode;
   2616  nsCOMPtr<nsINode> referenceParentNode = tStartContainer;
   2617 
   2618  RefPtr<Text> startTextNode = tStartContainer->GetAsText();
   2619  nsCOMPtr<nsINodeList> tChildList;
   2620  if (startTextNode) {
   2621    referenceParentNode = tStartContainer->GetParentNode();
   2622    if (!referenceParentNode) {
   2623      aRv.ThrowHierarchyRequestError(
   2624          "Can not get range's start node's parent.");
   2625      return;
   2626    }
   2627 
   2628    referenceParentNode->EnsurePreInsertionValidity(aNode, tStartContainer,
   2629                                                    aRv);
   2630    if (aRv.Failed()) {
   2631      return;
   2632    }
   2633 
   2634    RefPtr<Text> secondPart = startTextNode->SplitText(tStartOffset, aRv);
   2635    if (aRv.Failed()) {
   2636      return;
   2637    }
   2638 
   2639    referenceNode = secondPart;
   2640  } else {
   2641    tChildList = tStartContainer->ChildNodes();
   2642 
   2643    // find the insertion point in the DOM and insert the Node
   2644    referenceNode = tChildList->Item(tStartOffset);
   2645 
   2646    tStartContainer->EnsurePreInsertionValidity(aNode, referenceNode, aRv);
   2647    if (aRv.Failed()) {
   2648      return;
   2649    }
   2650  }
   2651 
   2652  // We might need to update the end to include the new node (bug 433662).
   2653  // Ideally we'd only do this if needed, but it's tricky to know when it's
   2654  // needed in advance (bug 765799).
   2655  uint32_t newOffset;
   2656 
   2657  if (referenceNode) {
   2658    Maybe<uint32_t> indexInParent = referenceNode->ComputeIndexInParentNode();
   2659    if (MOZ_UNLIKELY(NS_WARN_IF(indexInParent.isNothing()))) {
   2660      aRv.Throw(NS_ERROR_FAILURE);
   2661      return;
   2662    }
   2663    newOffset = *indexInParent;
   2664  } else {
   2665    newOffset = tChildList->Length();
   2666  }
   2667 
   2668  if (aNode.NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
   2669    newOffset += aNode.GetChildCount();
   2670  } else {
   2671    newOffset++;
   2672  }
   2673 
   2674  // Now actually insert the node
   2675  nsCOMPtr<nsINode> tResultNode;
   2676  tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv);
   2677  if (aRv.Failed()) {
   2678    return;
   2679  }
   2680 
   2681  if (Collapsed()) {
   2682    aRv = SetEnd(referenceParentNode, newOffset);
   2683  }
   2684 }
   2685 
   2686 void nsRange::SurroundContents(nsINode& aNewParent, ErrorResult& aRv) {
   2687  if (!CanAccess(aNewParent)) {
   2688    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   2689    return;
   2690  }
   2691 
   2692  if (!mRoot) {
   2693    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   2694    return;
   2695  }
   2696  // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text
   2697  // node.
   2698  if (mStart.GetContainer() != mEnd.GetContainer()) {
   2699    bool startIsText = mStart.GetContainer()->IsText();
   2700    bool endIsText = mEnd.GetContainer()->IsText();
   2701    nsINode* startGrandParent = mStart.GetContainer()->GetParentNode();
   2702    nsINode* endGrandParent = mEnd.GetContainer()->GetParentNode();
   2703    if (!((startIsText && endIsText && startGrandParent &&
   2704           startGrandParent == endGrandParent) ||
   2705          (startIsText && startGrandParent &&
   2706           startGrandParent == mEnd.GetContainer()) ||
   2707          (endIsText && endGrandParent &&
   2708           endGrandParent == mStart.GetContainer()))) {
   2709      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   2710      return;
   2711    }
   2712  }
   2713 
   2714  // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted
   2715  // (Document, DocumentType, DocumentFragment)
   2716  uint16_t nodeType = aNewParent.NodeType();
   2717  if (nodeType == nsINode::DOCUMENT_NODE ||
   2718      nodeType == nsINode::DOCUMENT_TYPE_NODE ||
   2719      nodeType == nsINode::DOCUMENT_FRAGMENT_NODE) {
   2720    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   2721    return;
   2722  }
   2723 
   2724  // Extract the contents within the range.
   2725 
   2726  RefPtr<DocumentFragment> docFrag = ExtractContents(aRv);
   2727 
   2728  if (aRv.Failed()) {
   2729    return;
   2730  }
   2731 
   2732  if (!docFrag) {
   2733    aRv.Throw(NS_ERROR_FAILURE);
   2734    return;
   2735  }
   2736 
   2737  // Spec says we need to remove all of aNewParent's
   2738  // children prior to insertion.
   2739 
   2740  nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes();
   2741  if (!children) {
   2742    aRv.Throw(NS_ERROR_FAILURE);
   2743    return;
   2744  }
   2745 
   2746  uint32_t numChildren = children->Length();
   2747 
   2748  while (numChildren) {
   2749    nsCOMPtr<nsINode> child = children->Item(--numChildren);
   2750    if (!child) {
   2751      aRv.Throw(NS_ERROR_FAILURE);
   2752      return;
   2753    }
   2754 
   2755    aNewParent.RemoveChild(*child, aRv);
   2756    if (aRv.Failed()) {
   2757      return;
   2758    }
   2759  }
   2760 
   2761  // Insert aNewParent at the range's start point.
   2762 
   2763  InsertNode(aNewParent, aRv);
   2764  if (aRv.Failed()) {
   2765    return;
   2766  }
   2767 
   2768  // Append the content we extracted under aNewParent.
   2769  aNewParent.AppendChild(*docFrag, aRv);
   2770  if (aRv.Failed()) {
   2771    return;
   2772  }
   2773 
   2774  // Select aNewParent, and its contents.
   2775 
   2776  SelectNode(aNewParent, aRv);
   2777 }
   2778 
   2779 void nsRange::ToString(nsAString& aReturn, ErrorResult& aErr) {
   2780  // clear the string
   2781  aReturn.Truncate();
   2782 
   2783  // If we're unpositioned, return the empty string
   2784  if (!mIsPositioned) {
   2785    return;
   2786  }
   2787 
   2788 #ifdef DEBUG_range
   2789  printf("Range dump: -----------------------\n");
   2790 #endif /* DEBUG */
   2791 
   2792  // effeciency hack for simple case
   2793  if (mStart.GetContainer() == mEnd.GetContainer()) {
   2794    Text* textNode =
   2795        mStart.GetContainer() ? mStart.GetContainer()->GetAsText() : nullptr;
   2796 
   2797    if (textNode) {
   2798 #ifdef DEBUG_range
   2799      // If debug, dump it:
   2800      textNode->List(stdout);
   2801      printf("End Range dump: -----------------------\n");
   2802 #endif /* DEBUG */
   2803 
   2804      // grab the text
   2805      textNode->SubstringData(
   2806          *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   2807          *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets) -
   2808              *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   2809          aReturn, aErr);
   2810      return;
   2811    }
   2812  }
   2813 
   2814  /* complex case: mStart.GetContainer() != mEnd.GetContainer(), or mStartParent
   2815     not a text node revisit - there are potential optimizations here and also
   2816     tradeoffs.
   2817  */
   2818 
   2819  PostContentIterator postOrderIter;
   2820  nsresult rv = postOrderIter.Init(this);
   2821  if (NS_WARN_IF(NS_FAILED(rv))) {
   2822    aErr.Throw(rv);
   2823    return;
   2824  }
   2825 
   2826  nsString tempString;
   2827 
   2828  // loop through the content iterator, which returns nodes in the range in
   2829  // close tag order, and grab the text from any text node
   2830  for (; !postOrderIter.IsDone(); postOrderIter.Next()) {
   2831    nsINode* n = postOrderIter.GetCurrentNode();
   2832 
   2833 #ifdef DEBUG_range
   2834    // If debug, dump it:
   2835    n->List(stdout);
   2836 #endif /* DEBUG */
   2837    Text* textNode = n->GetAsText();
   2838    if (textNode)  // if it's a text node, get the text
   2839    {
   2840      if (n == mStart.GetContainer()) {  // only include text past start offset
   2841        uint32_t strLength = textNode->Length();
   2842        textNode->SubstringData(
   2843            *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   2844            strLength -
   2845                *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   2846            tempString, IgnoreErrors());
   2847        aReturn += tempString;
   2848      } else if (n ==
   2849                 mEnd.GetContainer()) {  // only include text before end offset
   2850        textNode->SubstringData(
   2851            0, *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   2852            tempString, IgnoreErrors());
   2853        aReturn += tempString;
   2854      } else {  // grab the whole kit-n-kaboodle
   2855        textNode->GetData(tempString);
   2856        aReturn += tempString;
   2857      }
   2858    }
   2859  }
   2860 
   2861 #ifdef DEBUG_range
   2862  printf("End Range dump: -----------------------\n");
   2863 #endif /* DEBUG */
   2864 }
   2865 
   2866 void nsRange::Detach() {}
   2867 
   2868 already_AddRefed<DocumentFragment> nsRange::CreateContextualFragment(
   2869    const nsAString& aFragment, ErrorResult& aRv) const {
   2870  if (!mIsPositioned) {
   2871    aRv.Throw(NS_ERROR_FAILURE);
   2872    return nullptr;
   2873  }
   2874 
   2875  return nsContentUtils::CreateContextualFragment(mStart.GetContainer(),
   2876                                                  aFragment, false, aRv);
   2877 }
   2878 
   2879 already_AddRefed<DocumentFragment> nsRange::CreateContextualFragment(
   2880    const TrustedHTMLOrString& aFragment, nsIPrincipal* aSubjectPrincipal,
   2881    ErrorResult& aRv) const {
   2882  if (!mIsPositioned) {
   2883    aRv.Throw(NS_ERROR_FAILURE);
   2884    return nullptr;
   2885  }
   2886  MOZ_ASSERT(mStart.GetContainer());
   2887 
   2888  constexpr nsLiteralString sink = u"Range createContextualFragment"_ns;
   2889  Maybe<nsAutoString> compliantStringHolder;
   2890  nsCOMPtr<nsINode> node = mStart.GetContainer();
   2891  const nsAString* compliantString =
   2892      TrustedTypeUtils::GetTrustedTypesCompliantString(
   2893          aFragment, sink, kTrustedTypesOnlySinkGroup, *node, aSubjectPrincipal,
   2894          compliantStringHolder, aRv);
   2895  if (aRv.Failed()) {
   2896    return nullptr;
   2897  }
   2898 
   2899  return nsContentUtils::CreateContextualFragment(mStart.GetContainer(),
   2900                                                  *compliantString, false, aRv);
   2901 }
   2902 
   2903 static void ExtractRectFromOffset(nsIFrame* aFrame, const int32_t aOffset,
   2904                                  nsRect* aR, bool aFlushToOriginEdge,
   2905                                  bool aClampToEdge) {
   2906  MOZ_ASSERT(aFrame);
   2907  MOZ_ASSERT(aR);
   2908 
   2909  nsPoint point;
   2910  aFrame->GetPointFromOffset(aOffset, &point);
   2911 
   2912  // Determine if aFrame has a vertical writing mode, which will change our math
   2913  // on the output rect.
   2914  bool isVertical = aFrame->GetWritingMode().IsVertical();
   2915 
   2916  if (!aClampToEdge && !aR->Contains(point)) {
   2917    // If point is outside aR, and we aren't clamping, output an empty rect
   2918    // with origin at the point.
   2919    if (isVertical) {
   2920      aR->SetHeight(0);
   2921      aR->y = point.y;
   2922    } else {
   2923      aR->SetWidth(0);
   2924      aR->x = point.x;
   2925    }
   2926    return;
   2927  }
   2928 
   2929  if (aClampToEdge) {
   2930    point = aR->ClampPoint(point);
   2931  }
   2932 
   2933  // point is within aR, and now we'll modify aR to output a rect that has point
   2934  // on one edge. But which edge?
   2935  if (aFlushToOriginEdge) {
   2936    // The output rect should be flush to the edge of aR that contains the
   2937    // origin.
   2938    if (isVertical) {
   2939      aR->SetHeight(point.y - aR->y);
   2940    } else {
   2941      aR->SetWidth(point.x - aR->x);
   2942    }
   2943  } else {
   2944    // The output rect should be flush to the edge of aR opposite the origin.
   2945    if (isVertical) {
   2946      aR->SetHeight(aR->YMost() - point.y);
   2947      aR->y = point.y;
   2948    } else {
   2949      aR->SetWidth(aR->XMost() - point.x);
   2950      aR->x = point.x;
   2951    }
   2952  }
   2953 }
   2954 
   2955 static nsTextFrame* GetTextFrameForContent(nsIContent* aContent,
   2956                                           bool aFlushLayout) {
   2957  RefPtr<Document> doc = aContent->OwnerDoc();
   2958  PresShell* presShell = doc->GetPresShell();
   2959  if (!presShell) {
   2960    return nullptr;
   2961  }
   2962 
   2963  // Try to un-suppress whitespace if needed, but only if we'll be able to flush
   2964  // to immediately see the results of the un-suppression. If we can't flush
   2965  // here, then calling EnsureFrameForTextNodeIsCreatedAfterFlush would be
   2966  // pointless anyway.
   2967  if (aFlushLayout) {
   2968    const bool frameWillBeUnsuppressed =
   2969        presShell->FrameConstructor()
   2970            ->EnsureFrameForTextNodeIsCreatedAfterFlush(
   2971                static_cast<CharacterData*>(aContent));
   2972    if (frameWillBeUnsuppressed) {
   2973      doc->FlushPendingNotifications(FlushType::Layout);
   2974    }
   2975  }
   2976 
   2977  nsIFrame* frame = aContent->GetPrimaryFrame();
   2978  if (!frame || !frame->IsTextFrame()) {
   2979    return nullptr;
   2980  }
   2981  return static_cast<nsTextFrame*>(frame);
   2982 }
   2983 
   2984 static nsresult GetPartialTextRect(RectCallback* aCallback,
   2985                                   Sequence<nsString>* aTextList,
   2986                                   nsIContent* aContent, int32_t aStartOffset,
   2987                                   int32_t aEndOffset, bool aClampToEdge,
   2988                                   bool aFlushLayout) {
   2989  nsTextFrame* textFrame = GetTextFrameForContent(aContent, aFlushLayout);
   2990  if (textFrame) {
   2991    nsIFrame* relativeTo =
   2992        nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
   2993 
   2994    for (nsTextFrame* f = textFrame->FindContinuationForOffset(aStartOffset); f;
   2995         f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
   2996      int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
   2997      if (fend <= aStartOffset) {
   2998        continue;
   2999      }
   3000      if (fstart >= aEndOffset) {
   3001        break;
   3002      }
   3003 
   3004      // Calculate the text content offsets we'll need if text is requested.
   3005      int32_t textContentStart = fstart;
   3006      int32_t textContentEnd = fend;
   3007 
   3008      // overlapping with the offset we want
   3009      f->EnsureTextRun(nsTextFrame::eInflated);
   3010      NS_ENSURE_TRUE(f->GetTextRun(nsTextFrame::eInflated),
   3011                     NS_ERROR_OUT_OF_MEMORY);
   3012      bool topLeftToBottomRight =
   3013          !f->GetTextRun(nsTextFrame::eInflated)->IsInlineReversed();
   3014      nsRect r = f->GetRectRelativeToSelf();
   3015      if (fstart < aStartOffset) {
   3016        // aStartOffset is within this frame
   3017        ExtractRectFromOffset(f, aStartOffset, &r, !topLeftToBottomRight,
   3018                              aClampToEdge);
   3019        textContentStart = aStartOffset;
   3020      }
   3021      if (fend > aEndOffset) {
   3022        // aEndOffset is in the middle of this frame
   3023        ExtractRectFromOffset(f, aEndOffset, &r, topLeftToBottomRight,
   3024                              aClampToEdge);
   3025        textContentEnd = aEndOffset;
   3026      }
   3027      r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, relativeTo);
   3028      aCallback->AddRect(r);
   3029 
   3030      // Finally capture the text, if requested.
   3031      if (aTextList) {
   3032        nsIFrame::RenderedText renderedText =
   3033            f->GetRenderedText(textContentStart, textContentEnd,
   3034                               nsIFrame::TextOffsetType::OffsetsInContentText,
   3035                               nsIFrame::TrailingWhitespace::DontTrim);
   3036 
   3037        NS_ENSURE_TRUE(aTextList->AppendElement(renderedText.mString, fallible),
   3038                       NS_ERROR_OUT_OF_MEMORY);
   3039      }
   3040    }
   3041  }
   3042  return NS_OK;
   3043 }
   3044 
   3045 static void CollectClientRectsForSubtree(
   3046    nsINode* aNode, RectCallback* aCollector, Sequence<nsString>* aTextList,
   3047    nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer,
   3048    uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout, bool aTextOnly) {
   3049  auto* content = nsIContent::FromNode(aNode);
   3050  if (!content) {
   3051    return;
   3052  }
   3053 
   3054  const bool isText = content->IsText();
   3055  if (isText) {
   3056    if (aNode == aStartContainer) {
   3057      int32_t offset = aStartContainer == aEndContainer
   3058                           ? static_cast<int32_t>(aEndOffset)
   3059                           : content->AsText()->TextDataLength();
   3060      GetPartialTextRect(aCollector, aTextList, content,
   3061                         static_cast<int32_t>(aStartOffset), offset,
   3062                         aClampToEdge, aFlushLayout);
   3063      return;
   3064    }
   3065 
   3066    if (aNode == aEndContainer) {
   3067      GetPartialTextRect(aCollector, aTextList, content, 0,
   3068                         static_cast<int32_t>(aEndOffset), aClampToEdge,
   3069                         aFlushLayout);
   3070      return;
   3071    }
   3072  }
   3073 
   3074  if (nsIFrame* frame = content->GetPrimaryFrame()) {
   3075    if (!aTextOnly || isText) {
   3076      nsLayoutUtils::GetAllInFlowRectsAndTexts(
   3077          frame, nsLayoutUtils::GetContainingBlockForClientRect(frame),
   3078          aCollector, aTextList,
   3079          nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
   3080      if (isText) {
   3081        return;
   3082      }
   3083      aTextOnly = true;
   3084      // We just get the text when calling GetAllInFlowRectsAndTexts, so we
   3085      // don't need to call it again when visiting the children.
   3086      aTextList = nullptr;
   3087    }
   3088  } else if (!content->IsElement() ||
   3089             !content->AsElement()->IsDisplayContents()) {
   3090    return;
   3091  }
   3092 
   3093  FlattenedChildIterator childIter(content);
   3094  for (nsIContent* child = childIter.GetNextChild(); child;
   3095       child = childIter.GetNextChild()) {
   3096    CollectClientRectsForSubtree(child, aCollector, aTextList, aStartContainer,
   3097                                 aStartOffset, aEndContainer, aEndOffset,
   3098                                 aClampToEdge, aFlushLayout, aTextOnly);
   3099  }
   3100 }
   3101 
   3102 /* static */
   3103 void nsRange::CollectClientRectsAndText(
   3104    RectCallback* aCollector, Sequence<nsString>* aTextList, nsRange* aRange,
   3105    nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer,
   3106    uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout) {
   3107  // Currently, this method is called with start of end offset of nsRange.
   3108  // So, they must be between 0 - INT32_MAX.
   3109  MOZ_ASSERT(RangeUtils::IsValidOffset(aStartOffset));
   3110  MOZ_ASSERT(RangeUtils::IsValidOffset(aEndOffset));
   3111 
   3112  // Hold strong pointers across the flush
   3113  nsCOMPtr<nsINode> startContainer = aStartContainer;
   3114  nsCOMPtr<nsINode> endContainer = aEndContainer;
   3115 
   3116  // Flush out layout so our frames are up to date.
   3117  if (!aStartContainer->IsInComposedDoc()) {
   3118    return;
   3119  }
   3120 
   3121  if (aFlushLayout) {
   3122    if (auto* content = nsIContent::FromNode(aStartContainer)) {
   3123      content->GetPrimaryFrame(FlushType::Layout);
   3124    } else {
   3125      aStartContainer->OwnerDoc()->FlushPendingNotifications(FlushType::Layout);
   3126    }
   3127    // Recheck whether we're still in the document
   3128    if (!aStartContainer->IsInComposedDoc()) {
   3129      return;
   3130    }
   3131  }
   3132 
   3133  RangeSubtreeIterator iter;
   3134 
   3135  nsresult rv = iter.Init(aRange);
   3136  if (NS_FAILED(rv)) return;
   3137 
   3138  if (iter.IsDone()) {
   3139    // the range is collapsed, only continue if the cursor is in a text node
   3140    if (aStartContainer->IsText()) {
   3141      nsTextFrame* textFrame =
   3142          GetTextFrameForContent(aStartContainer->AsText(), aFlushLayout);
   3143      if (textFrame) {
   3144        int32_t outOffset;
   3145        nsIFrame* outFrame;
   3146        textFrame->GetChildFrameContainingOffset(
   3147            static_cast<int32_t>(aStartOffset), false, &outOffset, &outFrame);
   3148        if (outFrame) {
   3149          nsIFrame* relativeTo =
   3150              nsLayoutUtils::GetContainingBlockForClientRect(outFrame);
   3151          nsRect r = outFrame->GetRectRelativeToSelf();
   3152          ExtractRectFromOffset(outFrame, static_cast<int32_t>(aStartOffset),
   3153                                &r, false, aClampToEdge);
   3154          r.SetWidth(0);
   3155          r = nsLayoutUtils::TransformFrameRectToAncestor(outFrame, r,
   3156                                                          relativeTo);
   3157          aCollector->AddRect(r);
   3158        }
   3159      }
   3160    }
   3161    return;
   3162  }
   3163 
   3164  do {
   3165    nsCOMPtr<nsINode> node = iter.GetCurrentNode();
   3166    iter.Next();
   3167 
   3168    CollectClientRectsForSubtree(node, aCollector, aTextList, aStartContainer,
   3169                                 aStartOffset, aEndContainer, aEndOffset,
   3170                                 aClampToEdge, aFlushLayout, false);
   3171  } while (!iter.IsDone());
   3172 }
   3173 
   3174 already_AddRefed<DOMRect> nsRange::GetBoundingClientRect(bool aClampToEdge,
   3175                                                         bool aFlushLayout) {
   3176  RefPtr<DOMRect> rect = new DOMRect(ToSupports(mOwner));
   3177  if (!mIsPositioned) {
   3178    return rect.forget();
   3179  }
   3180 
   3181  nsLayoutUtils::RectAccumulator accumulator;
   3182  CollectClientRectsAndText(
   3183      &accumulator, nullptr, this, mStart.GetContainer(),
   3184      *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   3185      mEnd.GetContainer(),
   3186      *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), aClampToEdge,
   3187      aFlushLayout);
   3188 
   3189  nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect
   3190                                               : accumulator.mResultRect;
   3191  rect->SetLayoutRect(r);
   3192  return rect.forget();
   3193 }
   3194 
   3195 already_AddRefed<DOMRectList> nsRange::GetClientRects(bool aClampToEdge,
   3196                                                      bool aFlushLayout) {
   3197  return GetClientRectsInner(AllowRangeCrossShadowBoundary::No, aClampToEdge,
   3198                             aFlushLayout);
   3199 }
   3200 
   3201 already_AddRefed<DOMRectList> nsRange::GetAllowCrossShadowBoundaryClientRects(
   3202    bool aClampToEdge, bool aFlushLayout) {
   3203  return GetClientRectsInner(AllowRangeCrossShadowBoundary::Yes, aClampToEdge,
   3204                             aFlushLayout);
   3205 }
   3206 
   3207 already_AddRefed<DOMRectList> nsRange::GetClientRectsInner(
   3208    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundaryRange,
   3209    bool aClampToEdge, bool aFlushLayout) {
   3210  if (!mIsPositioned) {
   3211    return nullptr;
   3212  }
   3213 
   3214  RefPtr<DOMRectList> rectList = new DOMRectList(ToSupports(mOwner));
   3215 
   3216  nsLayoutUtils::RectListBuilder builder(rectList);
   3217 
   3218  const auto& startRef =
   3219      aAllowCrossShadowBoundaryRange == AllowRangeCrossShadowBoundary::Yes
   3220          ? MayCrossShadowBoundaryStartRef()
   3221          : mStart;
   3222  const auto& endRef =
   3223      aAllowCrossShadowBoundaryRange == AllowRangeCrossShadowBoundary::Yes
   3224          ? MayCrossShadowBoundaryEndRef()
   3225          : mEnd;
   3226 
   3227  CollectClientRectsAndText(
   3228      &builder, nullptr, this, startRef.GetContainer(),
   3229      *startRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   3230      endRef.GetContainer(),
   3231      *endRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets), aClampToEdge,
   3232      aFlushLayout);
   3233  return rectList.forget();
   3234 }
   3235 
   3236 void nsRange::GetClientRectsAndTexts(mozilla::dom::ClientRectsAndTexts& aResult,
   3237                                     ErrorResult& aErr) {
   3238  if (!mIsPositioned) {
   3239    return;
   3240  }
   3241 
   3242  aResult.mRectList = new DOMRectList(ToSupports(mOwner));
   3243 
   3244  nsLayoutUtils::RectListBuilder builder(aResult.mRectList);
   3245 
   3246  CollectClientRectsAndText(
   3247      &builder, &aResult.mTextList, this, mStart.GetContainer(),
   3248      *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   3249      mEnd.GetContainer(),
   3250      *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), true, true);
   3251 }
   3252 
   3253 nsresult nsRange::GetUsedFontFaces(nsLayoutUtils::UsedFontFaceList& aResult,
   3254                                   uint32_t aMaxRanges,
   3255                                   bool aSkipCollapsedWhitespace) {
   3256  NS_ENSURE_TRUE(mIsPositioned, NS_ERROR_UNEXPECTED);
   3257 
   3258  nsCOMPtr<nsINode> startContainer = mStart.GetContainer();
   3259  nsCOMPtr<nsINode> endContainer = mEnd.GetContainer();
   3260 
   3261  // Flush out layout so our frames are up to date.
   3262  Document* doc = mStart.GetContainer()->OwnerDoc();
   3263  NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
   3264  doc->FlushPendingNotifications(FlushType::Frames);
   3265 
   3266  // Recheck whether we're still in the document
   3267  NS_ENSURE_TRUE(mStart.IsSetAndInComposedDoc(), NS_ERROR_UNEXPECTED);
   3268 
   3269  // A table to map gfxFontEntry objects to InspectorFontFace objects.
   3270  // This table does NOT own the InspectorFontFace objects, it only holds
   3271  // raw pointers to them. They are owned by the aResult array.
   3272  nsLayoutUtils::UsedFontFaceTable fontFaces;
   3273 
   3274  RangeSubtreeIterator iter;
   3275  nsresult rv = iter.Init(this);
   3276  NS_ENSURE_SUCCESS(rv, rv);
   3277 
   3278  while (!iter.IsDone()) {
   3279    // only collect anything if the range is not collapsed
   3280    nsCOMPtr<nsINode> node = iter.GetCurrentNode();
   3281    iter.Next();
   3282 
   3283    nsCOMPtr<nsIContent> content = do_QueryInterface(node);
   3284    if (!content) {
   3285      continue;
   3286    }
   3287    nsIFrame* frame = content->GetPrimaryFrame();
   3288    if (!frame) {
   3289      continue;
   3290    }
   3291 
   3292    if (content->IsText()) {
   3293      if (node == startContainer) {
   3294        int32_t offset =
   3295            startContainer == endContainer
   3296                ? *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets)
   3297                : content->AsText()->TextDataLength();
   3298        nsLayoutUtils::GetFontFacesForText(
   3299            frame, *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   3300            offset, true, aResult, fontFaces, aMaxRanges,
   3301            aSkipCollapsedWhitespace);
   3302        continue;
   3303      }
   3304      if (node == endContainer) {
   3305        nsLayoutUtils::GetFontFacesForText(
   3306            frame, 0, *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
   3307            true, aResult, fontFaces, aMaxRanges, aSkipCollapsedWhitespace);
   3308        continue;
   3309      }
   3310    }
   3311 
   3312    nsLayoutUtils::GetFontFacesForFrames(frame, aResult, fontFaces, aMaxRanges,
   3313                                         aSkipCollapsedWhitespace);
   3314  }
   3315 
   3316  return NS_OK;
   3317 }
   3318 
   3319 nsINode* nsRange::GetRegisteredClosestCommonInclusiveAncestor() {
   3320  MOZ_ASSERT(IsInAnySelection(),
   3321             "GetRegisteredClosestCommonInclusiveAncestor only valid for range "
   3322             "in selection");
   3323  MOZ_ASSERT(mRegisteredClosestCommonInclusiveAncestor);
   3324  return mRegisteredClosestCommonInclusiveAncestor;
   3325 }
   3326 
   3327 void nsRange::SuppressContentsForPrintSelection(ErrorResult& aRv) {
   3328  CutContents(
   3329      nullptr,
   3330      [](Element* aElement) {
   3331        // Elements need to be left as-is when we're deleting nodes for
   3332        // printing, to preserve the style matches containing tree-structural
   3333        // pseudo-classes, such as :first-child. Partial texts are still deleted
   3334        // since we don't have a good way to suppress partial texts, but that'd
   3335        // preserve e.g. ::first-letter.
   3336        aElement->AddStates(ElementState::SUPPRESS_FOR_PRINT_SELECTION);
   3337      },
   3338      aRv);
   3339 }
   3340 
   3341 /* static */
   3342 bool nsRange::AutoInvalidateSelection::sIsNested;
   3343 
   3344 nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() {
   3345  if (!mCommonAncestor) {
   3346    return;
   3347  }
   3348  sIsNested = false;
   3349  ::InvalidateAllFrames(mCommonAncestor);
   3350 
   3351  // Our range might not be in a selection anymore, because one of our selection
   3352  // listeners might have gone ahead and run script of various sorts that messed
   3353  // with selections, ranges, etc.  But if it still is, we should check whether
   3354  // we have a different common ancestor now, and if so invalidate its subtree
   3355  // so it paints the selection it's in now.
   3356  if (mRange->IsInAnySelection()) {
   3357    nsINode* commonAncestor =
   3358        mRange->GetRegisteredClosestCommonInclusiveAncestor();
   3359    // XXXbz can commonAncestor really be null here?  I wouldn't think so!  If
   3360    // it _were_, then in a debug build
   3361    // GetRegisteredClosestCommonInclusiveAncestor() would have fatally
   3362    // asserted.
   3363    if (commonAncestor && commonAncestor != mCommonAncestor) {
   3364      ::InvalidateAllFrames(commonAncestor);
   3365    }
   3366  }
   3367 }
   3368 
   3369 /* static */
   3370 already_AddRefed<nsRange> nsRange::Constructor(const GlobalObject& aGlobal,
   3371                                               ErrorResult& aRv) {
   3372  nsCOMPtr<nsPIDOMWindowInner> window =
   3373      do_QueryInterface(aGlobal.GetAsSupports());
   3374  if (!window || !window->GetDoc()) {
   3375    aRv.Throw(NS_ERROR_FAILURE);
   3376    return nullptr;
   3377  }
   3378 
   3379  return window->GetDoc()->CreateRange(aRv);
   3380 }
   3381 
   3382 static bool ExcludeIfNextToNonSelectable(nsIContent* aContent) {
   3383  return aContent->IsText() &&
   3384         aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE);
   3385 }
   3386 
   3387 void nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges) {
   3388  if (!mIsPositioned) {
   3389    MOZ_ASSERT(false);
   3390    return;
   3391  }
   3392  MOZ_ASSERT(mEnd.GetContainer());
   3393  MOZ_ASSERT(mStart.GetContainer());
   3394 
   3395  nsRange* range = this;
   3396  RefPtr<nsRange> newRange;
   3397  while (range) {
   3398    PreContentIterator preOrderIter;
   3399    nsresult rv = preOrderIter.Init(range);
   3400    if (NS_FAILED(rv)) {
   3401      return;
   3402    }
   3403 
   3404    bool added = false;
   3405    bool seenSelectable = false;
   3406    // |firstNonSelectableContent| is the first node in a consecutive sequence
   3407    // of non-IsSelectable nodes.  When we find a selectable node after such
   3408    // a sequence we'll end the last nsRange, create a new one and restart
   3409    // the outer loop.
   3410    nsIContent* firstNonSelectableContent = nullptr;
   3411    while (true) {
   3412      nsINode* node = preOrderIter.GetCurrentNode();
   3413      preOrderIter.Next();
   3414      bool selectable = true;
   3415      nsIContent* content = nsIContent::FromNodeOrNull(node);
   3416      if (content) {
   3417        if (firstNonSelectableContent &&
   3418            ExcludeIfNextToNonSelectable(content)) {
   3419          // Ignorable whitespace next to a sequence of non-selectable nodes
   3420          // counts as non-selectable (bug 1216001).
   3421          selectable = false;
   3422        }
   3423        if (selectable) {
   3424          // FIXME: Use content->IsSelectable()
   3425          nsIFrame* frame = content->GetPrimaryFrame();
   3426          for (nsIContent* p = content; !frame && (p = p->GetParent());) {
   3427            frame = p->GetPrimaryFrame();
   3428          }
   3429          if (frame) {
   3430            selectable = frame->IsSelectable();
   3431          }
   3432        }
   3433      }
   3434 
   3435      if (!selectable) {
   3436        if (!firstNonSelectableContent) {
   3437          firstNonSelectableContent = content;
   3438        }
   3439        if (preOrderIter.IsDone()) {
   3440          if (seenSelectable) {
   3441            // The tail end of the initial range is non-selectable - truncate
   3442            // the current range before the first non-selectable node.
   3443            range->SetEndBefore(*firstNonSelectableContent, IgnoreErrors());
   3444          }
   3445          return;
   3446        }
   3447        continue;
   3448      }
   3449 
   3450      if (firstNonSelectableContent) {
   3451        if (range == this && !seenSelectable) {
   3452          // This is the initial range and all its nodes until now are
   3453          // non-selectable so just trim them from the start.
   3454          IgnoredErrorResult err;
   3455          range->SetStartBefore(*node, err, AllowRangeCrossShadowBoundary::Yes);
   3456          if (err.Failed()) {
   3457            return;
   3458          }
   3459          break;  // restart the same range with a new iterator
   3460        }
   3461 
   3462        // Save the end point before truncating the range.
   3463        nsINode* endContainer = range->mEnd.GetContainer();
   3464        const uint32_t endOffset =
   3465            *range->mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
   3466 
   3467        // Truncate the current range before the first non-selectable node.
   3468        IgnoredErrorResult err;
   3469        range->SetEndBefore(*firstNonSelectableContent, err,
   3470                            AllowRangeCrossShadowBoundary::Yes);
   3471 
   3472        // Store it in the result (strong ref) - do this before creating
   3473        // a new range in |newRange| below so we don't drop the last ref
   3474        // to the range created in the previous iteration.
   3475        if (!added && !err.Failed()) {
   3476          aOutRanges->AppendElement(range);
   3477        }
   3478 
   3479        // Create a new range for the remainder.
   3480        nsINode* startContainer = node;
   3481        Maybe<uint32_t> startOffset = Some(0);
   3482        // Don't start *inside* a node with independent selection though
   3483        // (e.g. <input>).
   3484        if (content && content->HasIndependentSelection()) {
   3485          nsINode* parent = startContainer->GetParent();
   3486          if (parent) {
   3487            startOffset = parent->ComputeIndexOf(startContainer);
   3488            startContainer = parent;
   3489          }
   3490        }
   3491        newRange =
   3492            nsRange::Create(startContainer, startOffset.valueOr(UINT32_MAX),
   3493                            endContainer, endOffset, IgnoreErrors());
   3494        if (!newRange || newRange->Collapsed()) {
   3495          newRange = nullptr;
   3496        }
   3497        range = newRange;
   3498        break;  // create a new iterator for the new range, if any
   3499      }
   3500 
   3501      seenSelectable = true;
   3502      if (!added) {
   3503        added = true;
   3504        aOutRanges->AppendElement(range);
   3505      }
   3506      if (preOrderIter.IsDone()) {
   3507        return;
   3508      }
   3509    }
   3510  }
   3511 }
   3512 
   3513 struct InnerTextAccumulator {
   3514  explicit InnerTextAccumulator(mozilla::dom::DOMString& aValue)
   3515      : mString(aValue.AsAString()), mRequiredLineBreakCount(0) {}
   3516  void FlushLineBreaks() {
   3517    while (mRequiredLineBreakCount > 0) {
   3518      // Required line breaks at the start of the text are suppressed.
   3519      if (!mString.IsEmpty()) {
   3520        mString.Append('\n');
   3521      }
   3522      --mRequiredLineBreakCount;
   3523    }
   3524  }
   3525  void Append(char aCh) { Append(nsAutoString(aCh)); }
   3526  void Append(const nsAString& aString) {
   3527    if (aString.IsEmpty()) {
   3528      return;
   3529    }
   3530    FlushLineBreaks();
   3531    mString.Append(aString);
   3532  }
   3533  void AddRequiredLineBreakCount(int8_t aCount) {
   3534    mRequiredLineBreakCount = std::max(mRequiredLineBreakCount, aCount);
   3535  }
   3536 
   3537  nsAString& mString;
   3538  int8_t mRequiredLineBreakCount;
   3539 };
   3540 
   3541 static bool IsVisibleAndNotInReplacedElement(nsIFrame* aFrame) {
   3542  if (!aFrame || !aFrame->StyleVisibility()->IsVisible() ||
   3543      aFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   3544    return false;
   3545  }
   3546  if (aFrame->HidesContent()) {
   3547    return false;
   3548  }
   3549  for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
   3550    if (f->HidesContent()) {
   3551      return false;
   3552    }
   3553    if (f->IsReplaced() &&
   3554        !f->GetContent()->IsAnyOfHTMLElements(nsGkAtoms::button,
   3555                                              nsGkAtoms::select) &&
   3556        !f->GetContent()->IsSVGElement()) {
   3557      return false;
   3558    }
   3559  }
   3560  return true;
   3561 }
   3562 
   3563 static void AppendTransformedText(InnerTextAccumulator& aResult,
   3564                                  nsIContent* aContainer) {
   3565  auto textNode = static_cast<CharacterData*>(aContainer);
   3566 
   3567  nsIFrame* frame = textNode->GetPrimaryFrame();
   3568  if (!IsVisibleAndNotInReplacedElement(frame)) {
   3569    return;
   3570  }
   3571 
   3572  nsIFrame::RenderedText text =
   3573      frame->GetRenderedText(0, aContainer->GetChildCount());
   3574  aResult.Append(text.mString);
   3575 }
   3576 
   3577 /**
   3578 * States for tree traversal. AT_NODE means that we are about to enter
   3579 * the current DOM node. AFTER_NODE means that we have just finished traversing
   3580 * the children of the current DOM node and are about to apply any
   3581 * "after processing the node's children" steps before we finish visiting
   3582 * the node.
   3583 */
   3584 enum TreeTraversalState { AT_NODE, AFTER_NODE };
   3585 
   3586 static int8_t GetRequiredInnerTextLineBreakCount(nsIFrame* aFrame) {
   3587  if (aFrame->GetContent()->IsHTMLElement(nsGkAtoms::p)) {
   3588    return 2;
   3589  }
   3590  const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
   3591  if (styleDisplay->IsBlockOutside(aFrame) ||
   3592      styleDisplay->mDisplay == StyleDisplay::TableCaption) {
   3593    return 1;
   3594  }
   3595  return 0;
   3596 }
   3597 
   3598 static bool IsLastCellOfRow(nsIFrame* aFrame) {
   3599  LayoutFrameType type = aFrame->Type();
   3600  if (type != LayoutFrameType::TableCell) {
   3601    return true;
   3602  }
   3603  for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
   3604    if (c->GetNextSibling()) {
   3605      return false;
   3606    }
   3607  }
   3608  return true;
   3609 }
   3610 
   3611 static bool IsLastRowOfRowGroup(nsIFrame* aFrame) {
   3612  if (!aFrame->IsTableRowFrame()) {
   3613    return true;
   3614  }
   3615  for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
   3616    if (c->GetNextSibling()) {
   3617      return false;
   3618    }
   3619  }
   3620  return true;
   3621 }
   3622 
   3623 static bool IsLastNonemptyRowGroupOfTable(nsIFrame* aFrame) {
   3624  if (!aFrame->IsTableRowGroupFrame()) {
   3625    return true;
   3626  }
   3627  for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
   3628    for (nsIFrame* next = c->GetNextSibling(); next;
   3629         next = next->GetNextSibling()) {
   3630      if (next->PrincipalChildList().FirstChild()) {
   3631        return false;
   3632      }
   3633    }
   3634  }
   3635  return true;
   3636 }
   3637 
   3638 void nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError,
   3639                                  nsIContent* aContainer) {
   3640  InnerTextAccumulator result(aValue);
   3641 
   3642  if (aContainer->IsText()) {
   3643    AppendTransformedText(result, aContainer);
   3644    return;
   3645  }
   3646 
   3647  nsIContent* currentNode = aContainer;
   3648  TreeTraversalState currentState = AFTER_NODE;
   3649 
   3650  nsIContent* endNode = aContainer;
   3651  TreeTraversalState endState = AFTER_NODE;
   3652 
   3653  nsIContent* firstChild = aContainer->GetFirstChild();
   3654  if (firstChild) {
   3655    currentNode = firstChild;
   3656    currentState = AT_NODE;
   3657  }
   3658 
   3659  while (currentNode != endNode || currentState != endState) {
   3660    nsIFrame* f = currentNode->GetPrimaryFrame();
   3661    bool isVisibleAndNotReplaced = IsVisibleAndNotInReplacedElement(f);
   3662    if (currentState == AT_NODE) {
   3663      bool isText = currentNode->IsText();
   3664      if (isVisibleAndNotReplaced) {
   3665        result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
   3666        if (isText) {
   3667          nsIFrame::RenderedText text = f->GetRenderedText();
   3668          result.Append(text.mString);
   3669        }
   3670      }
   3671      nsIContent* child = currentNode->GetFirstChild();
   3672      if (child) {
   3673        currentNode = child;
   3674        continue;
   3675      }
   3676      currentState = AFTER_NODE;
   3677    }
   3678    if (currentNode == endNode && currentState == endState) {
   3679      break;
   3680    }
   3681    if (isVisibleAndNotReplaced) {
   3682      if (currentNode->IsHTMLElement(nsGkAtoms::br)) {
   3683        result.Append('\n');
   3684      }
   3685      switch (f->StyleDisplay()->DisplayInside()) {
   3686        case StyleDisplayInside::TableCell:
   3687          if (!IsLastCellOfRow(f)) {
   3688            result.Append('\t');
   3689          }
   3690          break;
   3691        case StyleDisplayInside::TableRow:
   3692          if (!IsLastRowOfRowGroup(f) ||
   3693              !IsLastNonemptyRowGroupOfTable(f->GetParent())) {
   3694            result.Append('\n');
   3695          }
   3696          break;
   3697        default:
   3698          break;  // Do nothing
   3699      }
   3700      result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
   3701    }
   3702    nsIContent* next = currentNode->GetNextSibling();
   3703    if (next) {
   3704      currentNode = next;
   3705      currentState = AT_NODE;
   3706    } else {
   3707      currentNode = currentNode->GetParent();
   3708    }
   3709  }
   3710 
   3711  // Do not flush trailing line breaks! Required breaks at the end of the text
   3712  // are suppressed.
   3713 }
   3714 
   3715 template <typename SPT, typename SRT, typename EPT, typename ERT>
   3716 void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
   3717    const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary,
   3718    const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary) {
   3719  if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   3720    return;
   3721  }
   3722 
   3723  MOZ_ASSERT(aStartBoundary.IsSetAndValid() && aEndBoundary.IsSetAndValid());
   3724  MOZ_ASSERT(aStartBoundary.GetTreeKind() == aEndBoundary.GetTreeKind());
   3725  MOZ_ASSERT(aStartBoundary.GetTreeKind() == TreeKind::Flat);
   3726 
   3727  nsINode* startNode = aStartBoundary.GetContainer();
   3728  nsINode* endNode = aEndBoundary.GetContainer();
   3729 
   3730  if (!startNode && !endNode) {
   3731    ResetCrossShadowBoundaryRange();
   3732    return;
   3733  }
   3734 
   3735  // Nodes at least needs to be in the same document.
   3736  if (startNode && endNode &&
   3737      startNode->GetComposedDoc() != endNode->GetComposedDoc()) {
   3738    ResetCrossShadowBoundaryRange();
   3739    return;
   3740  }
   3741 
   3742  auto CanBecomeCrossShadowBoundaryPoint = [](nsINode* aContainer) -> bool {
   3743    if (!aContainer) {
   3744      return true;
   3745    }
   3746 
   3747    // Unlike normal ranges, shadow cross ranges don't work
   3748    // when the nodes aren't in document.
   3749    if (!aContainer->IsInComposedDoc()) {
   3750      return false;
   3751    }
   3752 
   3753    // We don't allow ranges to span different NAC subtrees (because we don't
   3754    // notify when unbinding NAC roots historically). nsRange can already deal
   3755    // with the "same anonymous subtree" case.
   3756    if (aContainer->IsInNativeAnonymousSubtree()) {
   3757      return false;
   3758    }
   3759 
   3760    // AbstractRange::GetClosestCommonInclusiveAncestor only supports
   3761    // Document and Content nodes.
   3762    return aContainer->IsDocument() || aContainer->IsContent();
   3763  };
   3764 
   3765  if (!CanBecomeCrossShadowBoundaryPoint(startNode) ||
   3766      !CanBecomeCrossShadowBoundaryPoint(endNode)) {
   3767    ResetCrossShadowBoundaryRange();
   3768    return;
   3769  }
   3770 
   3771  if (!mCrossShadowBoundaryRange) {
   3772    mCrossShadowBoundaryRange =
   3773        CrossShadowBoundaryRange::Create(aStartBoundary, aEndBoundary, this);
   3774    return;
   3775  }
   3776 
   3777  mCrossShadowBoundaryRange->SetStartAndEnd(aStartBoundary, aEndBoundary);
   3778 }
   3779 
   3780 RawRangeBoundary nsRange::ComputeNewBoundaryWhenBoundaryInsideChangedText(
   3781    const CharacterDataChangeInfo& aInfo, const RawRangeBoundary& aBoundary) {
   3782  MOZ_ASSERT(aInfo.mChangeStart <
   3783             *aBoundary.Offset(
   3784                 RawRangeBoundary::OffsetFilter::kValidOrInvalidOffsets));
   3785  // If boundary is inside changed text, position it before change
   3786  // else adjust start offset for the change in length.
   3787  CheckedUint32 newOffset{0};
   3788  if (*aBoundary.Offset(
   3789          RawRangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <=
   3790      aInfo.mChangeEnd) {
   3791    newOffset = aInfo.mChangeStart;
   3792  } else {
   3793    newOffset = *aBoundary.Offset(
   3794        RawRangeBoundary::OffsetFilter::kValidOrInvalidOffsets);
   3795    newOffset -= aInfo.LengthOfRemovedText();
   3796    newOffset += aInfo.mReplaceLength;
   3797  }
   3798 
   3799  // newOffset.isValid() isn't checked explicitly here, because
   3800  // newOffset.value() contains an assertion.
   3801  return {aBoundary.GetContainer(), newOffset.value()};
   3802 }