tor-browser

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

Selection.cpp (164366B)


      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 mozilla::dom::Selection
      9 */
     10 
     11 #include "Selection.h"
     12 
     13 #include <algorithm>
     14 
     15 #include "ErrorList.h"
     16 #include "LayoutConstants.h"
     17 #include "mozilla/AccessibleCaretEventHub.h"
     18 #include "mozilla/Assertions.h"
     19 #include "mozilla/AsyncEventDispatcher.h"
     20 #include "mozilla/Attributes.h"
     21 #include "mozilla/AutoCopyListener.h"
     22 #include "mozilla/AutoRestore.h"
     23 #include "mozilla/BasePrincipal.h"
     24 #include "mozilla/CaretAssociationHint.h"
     25 #include "mozilla/ContentIterator.h"
     26 #include "mozilla/ErrorResult.h"
     27 #include "mozilla/HTMLEditor.h"
     28 #include "mozilla/IntegerRange.h"
     29 #include "mozilla/Logging.h"
     30 #include "mozilla/PresShell.h"
     31 #include "mozilla/RangeBoundary.h"
     32 #include "mozilla/RangeUtils.h"
     33 #include "mozilla/SelectionMovementUtils.h"
     34 #include "mozilla/StackWalk.h"
     35 #include "mozilla/StaticPrefs_dom.h"
     36 #include "mozilla/ToString.h"
     37 #include "mozilla/Try.h"
     38 #include "mozilla/dom/CharacterDataBuffer.h"
     39 #include "mozilla/dom/ChildIterator.h"
     40 #include "mozilla/dom/Document.h"
     41 #include "mozilla/dom/Element.h"
     42 #include "mozilla/dom/SelectionBinding.h"
     43 #include "mozilla/dom/ShadowRoot.h"
     44 #include "mozilla/dom/StaticRange.h"
     45 #include "mozilla/dom/TreeIterator.h"
     46 #include "mozilla/intl/Bidi.h"
     47 #include "mozilla/intl/BidiEmbeddingLevel.h"
     48 #include "nsBidiPresUtils.h"
     49 #include "nsCCUncollectableMarker.h"
     50 #include "nsCOMPtr.h"
     51 #include "nsCaret.h"
     52 #include "nsContentUtils.h"
     53 #include "nsCopySupport.h"
     54 #include "nsDebug.h"
     55 #include "nsDeviceContext.h"
     56 #include "nsDirection.h"
     57 #include "nsError.h"
     58 #include "nsFmtString.h"
     59 #include "nsFocusManager.h"
     60 #include "nsFrameSelection.h"
     61 #include "nsGkAtoms.h"
     62 #include "nsIContent.h"
     63 #include "nsIContentInlines.h"
     64 #include "nsIDocumentEncoder.h"
     65 #include "nsIFrameInlines.h"
     66 #include "nsINamed.h"
     67 #include "nsISelectionController.h"  //for the enums
     68 #include "nsISelectionListener.h"
     69 #include "nsITableCellLayout.h"
     70 #include "nsITimer.h"
     71 #include "nsLayoutUtils.h"
     72 #include "nsPIDOMWindow.h"
     73 #include "nsPresContext.h"
     74 #include "nsRange.h"
     75 #include "nsRefreshDriver.h"
     76 #include "nsString.h"
     77 #include "nsTArray.h"
     78 #include "nsTableCellFrame.h"
     79 #include "nsTableWrapperFrame.h"
     80 #include "nsTextFrame.h"
     81 #include "nsThreadUtils.h"
     82 
     83 #ifdef ACCESSIBILITY
     84 #  include "nsAccessibilityService.h"
     85 #endif
     86 
     87 namespace mozilla {
     88 // "Selection" logs only the calls of AddRangesForSelectableNodes and
     89 // NotifySelectionListeners in debug level.
     90 static LazyLogModule sSelectionLog("Selection");
     91 // "SelectionAPI" logs all API calls (both internal ones and exposed to script
     92 // ones) of normal selection which may change selection ranges.
     93 // 3. Info: Calls of APIs
     94 // 4. Debug: Call stacks with 7 ancestor callers of APIs
     95 // 5. Verbose: Complete call stacks of APIs.
     96 LazyLogModule sSelectionAPILog("SelectionAPI");
     97 
     98 MOZ_ALWAYS_INLINE bool NeedsToLogSelectionAPI(dom::Selection& aSelection) {
     99  return aSelection.Type() == SelectionType::eNormal &&
    100         MOZ_LOG_TEST(sSelectionAPILog, LogLevel::Info);
    101 }
    102 
    103 void LogStackForSelectionAPI() {
    104  if (!MOZ_LOG_TEST(sSelectionAPILog, LogLevel::Debug)) {
    105    return;
    106  }
    107  static nsAutoCString* sBufPtr = nullptr;
    108  MOZ_ASSERT(!sBufPtr);
    109  nsAutoCString buf;
    110  sBufPtr = &buf;
    111  auto writer = [](const char* aBuf) { sBufPtr->Append(aBuf); };
    112  const LogLevel logLevel = MOZ_LOG_TEST(sSelectionAPILog, LogLevel::Verbose)
    113                                ? LogLevel::Verbose
    114                                : LogLevel::Debug;
    115  MozWalkTheStackWithWriter(writer, CallerPC(),
    116                            logLevel == LogLevel::Verbose
    117                                ? 0u /* all */
    118                                : 8u /* 8 inclusive ancestors */);
    119  MOZ_LOG(sSelectionAPILog, logLevel, ("\n%s", buf.get()));
    120  sBufPtr = nullptr;
    121 }
    122 
    123 static void LogSelectionAPI(const dom::Selection* aSelection,
    124                            const char* aFuncName) {
    125  MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    126          ("%p Selection::%s()", aSelection, aFuncName));
    127 }
    128 
    129 static void LogSelectionAPI(const dom::Selection* aSelection,
    130                            const char* aFuncName, const char* aArgName,
    131                            const nsINode* aNode) {
    132  MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    133          ("%p Selection::%s(%s=%s)", aSelection, aFuncName, aArgName,
    134           aNode ? ToString(*aNode).c_str() : "nullptr"));
    135 }
    136 
    137 static void LogSelectionAPI(const dom::Selection* aSelection,
    138                            const char* aFuncName, const char* aArgName,
    139                            const dom::AbstractRange& aRange) {
    140  MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    141          ("%p Selection::%s(%s=%s)", aSelection, aFuncName, aArgName,
    142           ToString(aRange).c_str()));
    143 }
    144 
    145 static void LogSelectionAPI(const dom::Selection* aSelection,
    146                            const char* aFuncName, const char* aArgName1,
    147                            const nsINode* aNode, const char* aArgName2,
    148                            uint32_t aOffset) {
    149  MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    150          ("%p Selection::%s(%s=%s, %s=%u)", aSelection, aFuncName, aArgName1,
    151           aNode ? ToString(*aNode).c_str() : "nullptr", aArgName2, aOffset));
    152 }
    153 
    154 static void LogSelectionAPI(const dom::Selection* aSelection,
    155                            const char* aFuncName, const char* aArgName,
    156                            const RawRangeBoundary& aBoundary) {
    157  MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    158          ("%p Selection::%s(%s=%s)", aSelection, aFuncName, aArgName,
    159           ToString(aBoundary).c_str()));
    160 }
    161 
    162 static void LogSelectionAPI(const dom::Selection* aSelection,
    163                            const char* aFuncName, const char* aArgName1,
    164                            const nsAString& aStr1, const char* aArgName2,
    165                            const nsAString& aStr2, const char* aArgName3,
    166                            const nsAString& aStr3) {
    167  MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    168          ("%p Selection::%s(%s=%s, %s=%s, %s=%s)", aSelection, aFuncName,
    169           aArgName1, NS_ConvertUTF16toUTF8(aStr1).get(), aArgName2,
    170           NS_ConvertUTF16toUTF8(aStr2).get(), aArgName3,
    171           NS_ConvertUTF16toUTF8(aStr3).get()));
    172 }
    173 
    174 static void LogSelectionAPI(const dom::Selection* aSelection,
    175                            const char* aFuncName, const char* aNodeArgName1,
    176                            const nsINode& aNode1, const char* aOffsetArgName1,
    177                            uint32_t aOffset1, const char* aNodeArgName2,
    178                            const nsINode& aNode2, const char* aOffsetArgName2,
    179                            uint32_t aOffset2) {
    180  if (&aNode1 == &aNode2 && aOffset1 == aOffset2) {
    181    MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    182            ("%p Selection::%s(%s=%s=%s, %s=%s=%u)", aSelection, aFuncName,
    183             aNodeArgName1, aNodeArgName2, ToString(aNode1).c_str(),
    184             aOffsetArgName1, aOffsetArgName2, aOffset1));
    185  } else {
    186    MOZ_LOG(
    187        sSelectionAPILog, LogLevel::Info,
    188        ("%p Selection::%s(%s=%s, %s=%u, %s=%s, %s=%u)", aSelection, aFuncName,
    189         aNodeArgName1, ToString(aNode1).c_str(), aOffsetArgName1, aOffset1,
    190         aNodeArgName2, ToString(aNode2).c_str(), aOffsetArgName2, aOffset2));
    191  }
    192 }
    193 
    194 static void LogSelectionAPI(const dom::Selection* aSelection,
    195                            const char* aFuncName, const char* aNodeArgName1,
    196                            const nsINode& aNode1, const char* aOffsetArgName1,
    197                            uint32_t aOffset1, const char* aNodeArgName2,
    198                            const nsINode& aNode2, const char* aOffsetArgName2,
    199                            uint32_t aOffset2, const char* aDirArgName,
    200                            nsDirection aDirection, const char* aReasonArgName,
    201                            int16_t aReason) {
    202  if (&aNode1 == &aNode2 && aOffset1 == aOffset2) {
    203    MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    204            ("%p Selection::%s(%s=%s=%s, %s=%s=%u, %s=%s, %s=%d)", aSelection,
    205             aFuncName, aNodeArgName1, aNodeArgName2, ToString(aNode1).c_str(),
    206             aOffsetArgName1, aOffsetArgName2, aOffset1, aDirArgName,
    207             ToString(aDirection).c_str(), aReasonArgName, aReason));
    208  } else {
    209    MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    210            ("%p Selection::%s(%s=%s, %s=%u, %s=%s, %s=%u, %s=%s, %s=%d)",
    211             aSelection, aFuncName, aNodeArgName1, ToString(aNode1).c_str(),
    212             aOffsetArgName1, aOffset1, aNodeArgName2, ToString(aNode2).c_str(),
    213             aOffsetArgName2, aOffset2, aDirArgName,
    214             ToString(aDirection).c_str(), aReasonArgName, aReason));
    215  }
    216 }
    217 
    218 static void LogSelectionAPI(const dom::Selection* aSelection,
    219                            const char* aFuncName, const char* aArgName1,
    220                            const RawRangeBoundary& aBoundary1,
    221                            const char* aArgName2,
    222                            const RawRangeBoundary& aBoundary2) {
    223  if (aBoundary1 == aBoundary2) {
    224    MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    225            ("%p Selection::%s(%s=%s=%s)", aSelection, aFuncName, aArgName1,
    226             aArgName2, ToString(aBoundary1).c_str()));
    227  } else {
    228    MOZ_LOG(sSelectionAPILog, LogLevel::Info,
    229            ("%p Selection::%s(%s=%s, %s=%s)", aSelection, aFuncName, aArgName1,
    230             ToString(aBoundary1).c_str(), aArgName2,
    231             ToString(aBoundary2).c_str()));
    232  }
    233 }
    234 }  // namespace mozilla
    235 
    236 using namespace mozilla;
    237 using namespace mozilla::dom;
    238 
    239 // #define DEBUG_TABLE 1
    240 
    241 #ifdef PRINT_RANGE
    242 static void printRange(nsRange* aDomRange);
    243 #  define DEBUG_OUT_RANGE(x) printRange(x)
    244 #else
    245 #  define DEBUG_OUT_RANGE(x)
    246 #endif  // PRINT_RANGE
    247 
    248 static constexpr nsLiteralCString kNoDocumentTypeNodeError =
    249    "DocumentType nodes are not supported"_ns;
    250 static constexpr nsLiteralCString kNoRangeExistsError =
    251    "No selection range exists"_ns;
    252 
    253 namespace mozilla {
    254 
    255 /******************************************************************************
    256 * Utility methods defined in nsISelectionListener.idl
    257 ******************************************************************************/
    258 
    259 nsCString SelectionChangeReasonsToCString(int16_t aReasons) {
    260  nsCString reasons;
    261  if (!aReasons) {
    262    reasons.AssignLiteral("NO_REASON");
    263    return reasons;
    264  }
    265  auto EnsureSeparator = [](nsCString& aString) -> void {
    266    if (!aString.IsEmpty()) {
    267      aString.AppendLiteral(" | ");
    268    }
    269  };
    270  struct ReasonData {
    271    int16_t mReason;
    272    const char* mReasonStr;
    273 
    274    ReasonData(int16_t aReason, const char* aReasonStr)
    275        : mReason(aReason), mReasonStr(aReasonStr) {}
    276  };
    277  for (const ReasonData& reason :
    278       {ReasonData(nsISelectionListener::DRAG_REASON, "DRAG_REASON"),
    279        ReasonData(nsISelectionListener::MOUSEDOWN_REASON, "MOUSEDOWN_REASON"),
    280        ReasonData(nsISelectionListener::MOUSEUP_REASON, "MOUSEUP_REASON"),
    281        ReasonData(nsISelectionListener::KEYPRESS_REASON, "KEYPRESS_REASON"),
    282        ReasonData(nsISelectionListener::SELECTALL_REASON, "SELECTALL_REASON"),
    283        ReasonData(nsISelectionListener::COLLAPSETOSTART_REASON,
    284                   "COLLAPSETOSTART_REASON"),
    285        ReasonData(nsISelectionListener::COLLAPSETOEND_REASON,
    286                   "COLLAPSETOEND_REASON"),
    287        ReasonData(nsISelectionListener::IME_REASON, "IME_REASON"),
    288        ReasonData(nsISelectionListener::JS_REASON, "JS_REASON")}) {
    289    if (aReasons & reason.mReason) {
    290      EnsureSeparator(reasons);
    291      reasons.Append(reason.mReasonStr);
    292    }
    293  }
    294  return reasons;
    295 }
    296 
    297 }  // namespace mozilla
    298 
    299 SelectionNodeCache::SelectionNodeCache(PresShell& aOwningPresShell)
    300    : mOwningPresShell(aOwningPresShell) {
    301  MOZ_ASSERT(!mOwningPresShell.mSelectionNodeCache);
    302  mOwningPresShell.mSelectionNodeCache = this;
    303 }
    304 
    305 SelectionNodeCache::~SelectionNodeCache() {
    306  mOwningPresShell.mSelectionNodeCache = nullptr;
    307 }
    308 
    309 bool SelectionNodeCache::MaybeCollectNodesAndCheckIfFullySelectedInAnyOf(
    310    const nsINode* aNode, const nsTArray<Selection*>& aSelections) {
    311  for (const auto* sel : aSelections) {
    312    if (MaybeCollectNodesAndCheckIfFullySelected(aNode, sel)) {
    313      return true;
    314    }
    315  }
    316  return false;
    317 }
    318 
    319 const nsTHashSet<const nsINode*>& SelectionNodeCache::MaybeCollect(
    320    const Selection* aSelection) {
    321  MOZ_ASSERT(aSelection);
    322  return mSelectedNodes.LookupOrInsertWith(aSelection, [sel = RefPtr(
    323                                                            aSelection)] {
    324    nsTHashSet<const nsINode*> fullySelectedNodes;
    325    for (size_t rangeIndex = 0; rangeIndex < sel->RangeCount(); ++rangeIndex) {
    326      AbstractRange* range = sel->GetAbstractRangeAt(rangeIndex);
    327      MOZ_ASSERT(range);
    328      if (range->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) {
    329        continue;
    330      }
    331      if (range->IsStaticRange() && !range->AsStaticRange()->IsValid()) {
    332        continue;
    333      }
    334      const RangeBoundary& startRef = range->MayCrossShadowBoundaryStartRef();
    335      const RangeBoundary& endRef = range->MayCrossShadowBoundaryEndRef();
    336 
    337      const nsINode* startContainer =
    338          startRef.IsStartOfContainer() ? nullptr : startRef.GetContainer();
    339      const nsINode* endContainer =
    340          endRef.IsEndOfContainer() ? nullptr : endRef.GetContainer();
    341 
    342      auto AddNodeIfFullySelected = [&](const nsINode* aNode) {
    343        if (!aNode) {
    344          return;
    345        }
    346        // Only collect start and end container if they are fully
    347        // selected (they are null in that case).
    348        if (aNode == startContainer || aNode == endContainer) {
    349          return;
    350        }
    351        fullySelectedNodes.Insert(aNode);
    352      };
    353 
    354      if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
    355        UnsafePreContentIterator iter;
    356        nsresult rv = iter.Init(range);
    357        if (NS_FAILED(rv)) {
    358          continue;
    359        }
    360        for (; !iter.IsDone(); iter.Next()) {
    361          AddNodeIfFullySelected(iter.GetCurrentNode());
    362        }
    363      } else {
    364        ContentSubtreeIterator subtreeIter;
    365        nsresult rv = subtreeIter.InitWithAllowCrossShadowBoundary(range);
    366        if (NS_FAILED(rv)) {
    367          continue;
    368        }
    369 
    370        for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
    371          MOZ_DIAGNOSTIC_ASSERT(subtreeIter.GetCurrentNode());
    372          if (subtreeIter.GetCurrentNode()->IsContent()) {
    373            TreeIterator<FlattenedChildIterator> iter(
    374                *(subtreeIter.GetCurrentNode()->AsContent()));
    375            for (; iter.GetCurrent(); iter.GetNext()) {
    376              AddNodeIfFullySelected(iter.GetCurrent());
    377            }
    378          }
    379        }
    380      }
    381    }
    382    return fullySelectedNodes;
    383  });
    384 }
    385 
    386 // #define DEBUG_SELECTION // uncomment for printf describing every collapse and
    387 //  extend. #define DEBUG_NAVIGATION
    388 
    389 // #define DEBUG_TABLE_SELECTION 1
    390 
    391 struct CachedOffsetForFrame {
    392  CachedOffsetForFrame()
    393      : mCachedFrameOffset(0, 0)  // nsPoint ctor
    394        ,
    395        mLastCaretFrame(nullptr),
    396        mLastContentOffset(0),
    397        mCanCacheFrameOffset(false) {}
    398 
    399  nsPoint mCachedFrameOffset;  // cached frame offset
    400  nsIFrame* mLastCaretFrame;   // store the frame the caret was last drawn in.
    401  int32_t mLastContentOffset;  // store last content offset
    402  bool mCanCacheFrameOffset;   // cached frame offset is valid?
    403 };
    404 
    405 class AutoScroller final : public nsITimerCallback, public nsINamed {
    406 public:
    407  NS_DECL_ISUPPORTS
    408 
    409  explicit AutoScroller(nsFrameSelection* aFrameSelection)
    410      : mFrameSelection(aFrameSelection),
    411        mPresContext(0),
    412        mPoint(0, 0),
    413        mDelayInMs(30),
    414        mFurtherScrollingAllowed(FurtherScrollingAllowed::kYes) {
    415    MOZ_ASSERT(mFrameSelection);
    416  }
    417 
    418  MOZ_CAN_RUN_SCRIPT nsresult DoAutoScroll(nsIFrame* aFrame, nsPoint aPoint);
    419 
    420 private:
    421  // aPoint is relative to aPresContext's root frame
    422  nsresult ScheduleNextDoAutoScroll(nsPresContext* aPresContext,
    423                                    nsPoint& aPoint) {
    424    if (NS_WARN_IF(mFurtherScrollingAllowed == FurtherScrollingAllowed::kNo)) {
    425      return NS_ERROR_FAILURE;
    426    }
    427 
    428    mPoint = aPoint;
    429 
    430    // Store the presentation context. The timer will be
    431    // stopped by the selection if the prescontext is destroyed.
    432    mPresContext = aPresContext;
    433 
    434    mContent = PresShell::GetCapturingContent();
    435 
    436    if (!mTimer) {
    437      mTimer = NS_NewTimer(GetMainThreadSerialEventTarget());
    438      if (!mTimer) {
    439        return NS_ERROR_OUT_OF_MEMORY;
    440      }
    441    }
    442 
    443    return mTimer->InitWithCallback(this, mDelayInMs, nsITimer::TYPE_ONE_SHOT);
    444  }
    445 
    446 public:
    447  enum class FurtherScrollingAllowed { kYes, kNo };
    448 
    449  void Stop(const FurtherScrollingAllowed aFurtherScrollingAllowed) {
    450    MOZ_ASSERT((aFurtherScrollingAllowed == FurtherScrollingAllowed::kNo) ||
    451               (mFurtherScrollingAllowed == FurtherScrollingAllowed::kYes));
    452 
    453    if (mTimer) {
    454      mTimer->Cancel();
    455      mTimer = nullptr;
    456    }
    457 
    458    mContent = nullptr;
    459    mFurtherScrollingAllowed = aFurtherScrollingAllowed;
    460  }
    461 
    462  void SetDelay(uint32_t aDelayInMs) { mDelayInMs = aDelayInMs; }
    463 
    464  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Notify(nsITimer* timer) override {
    465    if (mPresContext) {
    466      AutoWeakFrame frame =
    467          mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
    468      if (!frame) {
    469        return NS_OK;
    470      }
    471      mContent = nullptr;
    472 
    473      nsPoint pt = mPoint - frame->GetOffsetTo(
    474                                mPresContext->PresShell()->GetRootFrame());
    475      RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
    476      frameSelection->HandleDrag(frame, pt);
    477      if (!frame.IsAlive()) {
    478        return NS_OK;
    479      }
    480 
    481      NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
    482      DoAutoScroll(frame, pt);
    483    }
    484    return NS_OK;
    485  }
    486 
    487  NS_IMETHOD GetName(nsACString& aName) override {
    488    aName.AssignLiteral("AutoScroller");
    489    return NS_OK;
    490  }
    491 
    492 protected:
    493  virtual ~AutoScroller() {
    494    if (mTimer) {
    495      mTimer->Cancel();
    496    }
    497  }
    498 
    499 private:
    500  nsFrameSelection* const mFrameSelection;
    501  nsPresContext* mPresContext;
    502  // relative to mPresContext's root frame
    503  nsPoint mPoint;
    504  nsCOMPtr<nsITimer> mTimer;
    505  nsCOMPtr<nsIContent> mContent;
    506  uint32_t mDelayInMs;
    507  FurtherScrollingAllowed mFurtherScrollingAllowed;
    508 };
    509 
    510 NS_IMPL_ISUPPORTS(AutoScroller, nsITimerCallback, nsINamed)
    511 
    512 #ifdef PRINT_RANGE
    513 void printRange(nsRange* aDomRange) {
    514  if (!aDomRange) {
    515    printf("NULL Range\n");
    516  }
    517  nsINode* startNode = aDomRange->GetStartContainer();
    518  nsINode* endNode = aDomRange->GetEndContainer();
    519  int32_t startOffset = aDomRange->StartOffset();
    520  int32_t endOffset = aDomRange->EndOffset();
    521 
    522  printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
    523         (unsigned long)aDomRange, (unsigned long)startNode, (long)startOffset,
    524         (unsigned long)endNode, (long)endOffset);
    525 }
    526 #endif /* PRINT_RANGE */
    527 
    528 void Selection::Stringify(nsAString& aResult, CallerType aCallerType,
    529                          FlushFrames aFlushFrames) {
    530  if (aFlushFrames == FlushFrames::Yes) {
    531    // We need FlushType::Frames here to make sure frames have been created for
    532    // the selected content.  Use mFrameSelection->GetPresShell() which returns
    533    // null if the Selection has been disconnected (the shell is Destroyed).
    534    RefPtr<PresShell> presShell =
    535        mFrameSelection ? mFrameSelection->GetPresShell() : nullptr;
    536    if (!presShell) {
    537      aResult.Truncate();
    538      return;
    539    }
    540    presShell->FlushPendingNotifications(FlushType::Frames);
    541  }
    542 
    543  IgnoredErrorResult rv;
    544  uint32_t flags = nsIDocumentEncoder::SkipInvisibleContent;
    545  if (StaticPrefs::dom_selection_mimic_chrome_tostring_enabled() &&
    546      Type() == SelectionType::eNormal &&
    547      aCallerType == CallerType::NonSystem) {
    548    if (mFrameSelection &&
    549        !mFrameSelection->GetIndependentSelectionRootElement()) {
    550      // NonSystem and non-independent selection
    551      flags |= nsIDocumentEncoder::MimicChromeToStringBehaviour;
    552    }
    553  }
    554 
    555  ToStringWithFormat(u"text/plain"_ns, flags, 0, aResult, rv);
    556  if (rv.Failed()) {
    557    aResult.Truncate();
    558  }
    559 }
    560 
    561 void Selection::ToStringWithFormat(const nsAString& aFormatType,
    562                                   uint32_t aFlags, int32_t aWrapCol,
    563                                   nsAString& aReturn, ErrorResult& aRv) {
    564  nsCOMPtr<nsIDocumentEncoder> encoder =
    565      do_createDocumentEncoder(NS_ConvertUTF16toUTF8(aFormatType).get());
    566  if (!encoder) {
    567    aRv.Throw(NS_ERROR_FAILURE);
    568    return;
    569  }
    570 
    571  PresShell* presShell = GetPresShell();
    572  if (!presShell) {
    573    aRv.Throw(NS_ERROR_FAILURE);
    574    return;
    575  }
    576 
    577  Document* doc = presShell->GetDocument();
    578 
    579  // Flags should always include OutputSelectionOnly if we're coming from here:
    580  aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
    581  nsAutoString readstring;
    582  readstring.Assign(aFormatType);
    583  nsresult rv = encoder->Init(doc, readstring, aFlags);
    584  if (NS_FAILED(rv)) {
    585    aRv.Throw(rv);
    586    return;
    587  }
    588 
    589  Selection* selectionToEncode = this;
    590 
    591  if (aFlags & nsIDocumentEncoder::MimicChromeToStringBehaviour) {
    592    if (const nsFrameSelection* sel =
    593            presShell->GetLastSelectionForToString()) {
    594      MOZ_ASSERT(StaticPrefs::dom_selection_mimic_chrome_tostring_enabled());
    595      selectionToEncode = &sel->NormalSelection();
    596    }
    597  }
    598 
    599  encoder->SetSelection(selectionToEncode);
    600  if (aWrapCol != 0) encoder->SetWrapColumn(aWrapCol);
    601 
    602  rv = encoder->EncodeToString(aReturn);
    603  if (NS_FAILED(rv)) {
    604    aRv.Throw(rv);
    605  }
    606 }
    607 
    608 nsresult Selection::SetInterlinePosition(InterlinePosition aInterlinePosition) {
    609  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
    610  MOZ_ASSERT(aInterlinePosition != InterlinePosition::Undefined);
    611 
    612  if (!mFrameSelection) {
    613    return NS_ERROR_NOT_INITIALIZED;  // Can't do selection
    614  }
    615 
    616  mFrameSelection->SetHint(aInterlinePosition ==
    617                                   InterlinePosition::StartOfNextLine
    618                               ? CaretAssociationHint::After
    619                               : CaretAssociationHint::Before);
    620  return NS_OK;
    621 }
    622 
    623 Selection::InterlinePosition Selection::GetInterlinePosition() const {
    624  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
    625 
    626  if (!mFrameSelection) {
    627    return InterlinePosition::Undefined;
    628  }
    629  return mFrameSelection->GetHint() == CaretAssociationHint::After
    630             ? InterlinePosition::StartOfNextLine
    631             : InterlinePosition::EndOfLine;
    632 }
    633 
    634 void Selection::SetInterlinePositionJS(bool aHintRight, ErrorResult& aRv) {
    635  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
    636 
    637  aRv = SetInterlinePosition(aHintRight ? InterlinePosition::StartOfNextLine
    638                                        : InterlinePosition::EndOfLine);
    639 }
    640 
    641 bool Selection::GetInterlinePositionJS(ErrorResult& aRv) const {
    642  const InterlinePosition interlinePosition = GetInterlinePosition();
    643  if (interlinePosition == InterlinePosition::Undefined) {
    644    aRv.Throw(NS_ERROR_NOT_INITIALIZED);  // Can't do selection
    645    return false;
    646  }
    647  return interlinePosition == InterlinePosition::StartOfNextLine;
    648 }
    649 
    650 static bool IsEditorNode(const nsINode* aNode) {
    651  if (!aNode) {
    652    return false;
    653  }
    654 
    655  if (aNode->IsEditable()) {
    656    return true;
    657  }
    658 
    659  auto* element = Element::FromNode(aNode);
    660  return element && element->State().HasState(ElementState::READWRITE);
    661 }
    662 
    663 bool Selection::IsEditorSelection() const {
    664  return IsEditorNode(GetFocusNode());
    665 }
    666 
    667 Nullable<int16_t> Selection::GetCaretBidiLevel(
    668    mozilla::ErrorResult& aRv) const {
    669  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
    670 
    671  if (!mFrameSelection) {
    672    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    673    return Nullable<int16_t>();
    674  }
    675  mozilla::intl::BidiEmbeddingLevel caretBidiLevel =
    676      static_cast<mozilla::intl::BidiEmbeddingLevel>(
    677          mFrameSelection->GetCaretBidiLevel());
    678  return (caretBidiLevel & BIDI_LEVEL_UNDEFINED)
    679             ? Nullable<int16_t>()
    680             : Nullable<int16_t>(caretBidiLevel);
    681 }
    682 
    683 void Selection::SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel,
    684                                  mozilla::ErrorResult& aRv) {
    685  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
    686 
    687  if (!mFrameSelection) {
    688    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    689    return;
    690  }
    691  if (aCaretBidiLevel.IsNull()) {
    692    mFrameSelection->UndefineCaretBidiLevel();
    693  } else {
    694    mFrameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(
    695        mozilla::intl::BidiEmbeddingLevel(aCaretBidiLevel.Value()));
    696  }
    697 }
    698 
    699 /**
    700 * Test whether the supplied range points to a single table element.
    701 * Result is one of the TableSelectionMode constants. "None" means
    702 * a table element isn't selected.
    703 */
    704 // TODO: Figure out TableSelectionMode::Column and TableSelectionMode::AllCells
    705 static nsresult GetTableSelectionMode(const nsRange& aRange,
    706                                      TableSelectionMode* aTableSelectionType) {
    707  if (!aTableSelectionType) {
    708    return NS_ERROR_NULL_POINTER;
    709  }
    710 
    711  *aTableSelectionType = TableSelectionMode::None;
    712 
    713  nsINode* startNode = aRange.GetStartContainer();
    714  if (!startNode) {
    715    return NS_ERROR_FAILURE;
    716  }
    717 
    718  nsINode* endNode = aRange.GetEndContainer();
    719  if (!endNode) {
    720    return NS_ERROR_FAILURE;
    721  }
    722 
    723  // Not a single selected node
    724  if (startNode != endNode) {
    725    return NS_OK;
    726  }
    727 
    728  nsIContent* child = aRange.GetChildAtStartOffset();
    729 
    730  // Not a single selected node
    731  if (!child || child->GetNextSibling() != aRange.GetChildAtEndOffset()) {
    732    return NS_OK;
    733  }
    734 
    735  if (!startNode->IsHTMLElement()) {
    736    // Implies a check for being an element; if we ever make this work
    737    // for non-HTML, need to keep checking for elements.
    738    return NS_OK;
    739  }
    740 
    741  if (startNode->IsHTMLElement(nsGkAtoms::tr)) {
    742    *aTableSelectionType = TableSelectionMode::Cell;
    743  } else  // check to see if we are selecting a table or row (column and all
    744          // cells not done yet)
    745  {
    746    if (child->IsHTMLElement(nsGkAtoms::table)) {
    747      *aTableSelectionType = TableSelectionMode::Table;
    748    } else if (child->IsHTMLElement(nsGkAtoms::tr)) {
    749      *aTableSelectionType = TableSelectionMode::Row;
    750    }
    751  }
    752 
    753  return NS_OK;
    754 }
    755 
    756 nsresult Selection::MaybeAddTableCellRange(nsRange& aRange,
    757                                           Maybe<size_t>* aOutIndex) {
    758  if (!aOutIndex) {
    759    return NS_ERROR_NULL_POINTER;
    760  }
    761 
    762  MOZ_ASSERT(aOutIndex->isNothing());
    763 
    764  if (!mFrameSelection) {
    765    return NS_OK;
    766  }
    767 
    768  // Get if we are adding a cell selection and the row, col of cell if we are
    769  TableSelectionMode tableMode;
    770  nsresult result = GetTableSelectionMode(aRange, &tableMode);
    771  if (NS_FAILED(result)) return result;
    772 
    773  // If not adding a cell range, we are done here
    774  if (tableMode != TableSelectionMode::Cell) {
    775    mFrameSelection->mTableSelection.mMode = tableMode;
    776    // Don't fail if range isn't a selected cell, aDidAddRange tells caller if
    777    // we didn't proceed
    778    return NS_OK;
    779  }
    780 
    781  // Set frame selection mode only if not already set to a table mode
    782  // so we don't lose the select row and column flags (not detected by
    783  // getTableCellLocation)
    784  if (mFrameSelection->mTableSelection.mMode == TableSelectionMode::None) {
    785    mFrameSelection->mTableSelection.mMode = tableMode;
    786  }
    787 
    788  return AddRangesForSelectableNodes(&aRange, aOutIndex,
    789                                     DispatchSelectstartEvent::Maybe);
    790 }
    791 
    792 Selection::Selection(SelectionType aSelectionType,
    793                     nsFrameSelection* aFrameSelection)
    794    : mFrameSelection(aFrameSelection),
    795      mCachedOffsetForFrame(nullptr),
    796      mDirection(eDirNext),
    797      mSelectionType(aSelectionType),
    798      mCustomColors(nullptr),
    799      mSelectionChangeBlockerCount(0),
    800      mUserInitiated(false),
    801      mCalledByJS(false),
    802      mNotifyAutoCopy(false) {}
    803 
    804 Selection::~Selection() { Disconnect(); }
    805 
    806 void Selection::Disconnect() {
    807  RemoveAnchorFocusRange();
    808 
    809  mStyledRanges.UnregisterSelection();
    810 
    811  if (mAutoScroller) {
    812    mAutoScroller->Stop(AutoScroller::FurtherScrollingAllowed::kNo);
    813    mAutoScroller = nullptr;
    814  }
    815 
    816  mScrollEvent.Revoke();
    817 
    818  if (mCachedOffsetForFrame) {
    819    delete mCachedOffsetForFrame;
    820    mCachedOffsetForFrame = nullptr;
    821  }
    822 }
    823 
    824 Document* Selection::GetParentObject() const {
    825  PresShell* presShell = GetPresShell();
    826  return presShell ? presShell->GetDocument() : nullptr;
    827 }
    828 
    829 DocGroup* Selection::GetDocGroup() const {
    830  PresShell* presShell = GetPresShell();
    831  if (!presShell) {
    832    return nullptr;
    833  }
    834  Document* doc = presShell->GetDocument();
    835  return doc ? doc->GetDocGroup() : nullptr;
    836 }
    837 
    838 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Selection)
    839 
    840 MOZ_CAN_RUN_SCRIPT_BOUNDARY
    841 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
    842  // Unlink the selection listeners *before* we do RemoveAllRangesInternal since
    843  // we don't want to notify the listeners during JS GC (they could be
    844  // in JS!).
    845  tmp->mNotifyAutoCopy = false;
    846  if (tmp->mAccessibleCaretEventHub) {
    847    tmp->StopNotifyingAccessibleCaretEventHub();
    848  }
    849  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionChangeEventDispatcher)
    850  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
    851  MOZ_KnownLive(tmp)->RemoveAllRangesInternal(IgnoreErrors(), IsUnlinking::Yes);
    852  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection)
    853  NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightData.mHighlight)
    854  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    855  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
    856  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
    857 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    858 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection)
    859  {
    860    uint32_t i, count = tmp->mStyledRanges.Length();
    861    for (i = 0; i < count; ++i) {
    862      NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyledRanges.mRanges[i].mRange)
    863    }
    864    count = tmp->mStyledRanges.mInvalidStaticRanges.Length();
    865    for (i = 0; i < count; ++i) {
    866      NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
    867          mStyledRanges.mInvalidStaticRanges[i].mRange);
    868    }
    869  }
    870  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorFocusRange)
    871  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameSelection)
    872  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightData.mHighlight)
    873  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionChangeEventDispatcher)
    874  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListeners)
    875 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    876 
    877 // QueryInterface implementation for Selection
    878 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Selection)
    879  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    880  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    881  NS_INTERFACE_MAP_ENTRY(nsISupports)
    882 NS_INTERFACE_MAP_END
    883 
    884 NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection)
    885 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Selection, Disconnect())
    886 
    887 const RangeBoundary& Selection::AnchorRef(
    888    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const {
    889  if (!mAnchorFocusRange) {
    890    static RangeBoundary sEmpty;
    891    return sEmpty;
    892  }
    893 
    894  if (GetDirection() == eDirNext) {
    895    return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
    896               ? mAnchorFocusRange->MayCrossShadowBoundaryStartRef()
    897               : mAnchorFocusRange->StartRef();
    898  }
    899 
    900  return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
    901             ? mAnchorFocusRange->MayCrossShadowBoundaryEndRef()
    902             : mAnchorFocusRange->EndRef();
    903 }
    904 
    905 const RangeBoundary& Selection::FocusRef(
    906    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const {
    907  if (!mAnchorFocusRange) {
    908    static RangeBoundary sEmpty;
    909    return sEmpty;
    910  }
    911 
    912  if (GetDirection() == eDirNext) {
    913    return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
    914               ? mAnchorFocusRange->MayCrossShadowBoundaryEndRef()
    915               : mAnchorFocusRange->EndRef();
    916  }
    917  return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
    918             ? mAnchorFocusRange->MayCrossShadowBoundaryStartRef()
    919             : mAnchorFocusRange->StartRef();
    920 }
    921 
    922 void Selection::SetAnchorFocusRange(size_t aIndex) {
    923  if (aIndex >= mStyledRanges.Length()) {
    924    return;
    925  }
    926  // Highlight selections may contain static ranges.
    927  MOZ_ASSERT(mSelectionType != SelectionType::eHighlight);
    928  AbstractRange* anchorFocusRange = mStyledRanges.mRanges[aIndex].mRange;
    929  mAnchorFocusRange = anchorFocusRange->AsDynamicRange();
    930 }
    931 
    932 template <TreeKind aKind, typename PT, typename RT,
    933          typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
    934                                      aKind == TreeKind::Flat>>
    935 static int32_t CompareToRangeStart(
    936    const RangeBoundaryBase<PT, RT>& aCompareBoundary,
    937    const AbstractRange& aRange, nsContentUtils::NodeIndexCache* aCache) {
    938  MOZ_ASSERT(aCompareBoundary.IsSet());
    939  MOZ_ASSERT(aRange.GetMayCrossShadowBoundaryStartContainer());
    940  // If the nodes that we're comparing are not in the same document, assume
    941  // that aCompareNode will fall at the end of the ranges.
    942  if (aCompareBoundary.GetComposedDoc() !=
    943          aRange.MayCrossShadowBoundaryStartRef().GetComposedDoc() ||
    944      !aRange.MayCrossShadowBoundaryStartRef().IsSetAndInComposedDoc()) {
    945    NS_WARNING(
    946        "`CompareToRangeStart` couldn't compare nodes, pretending some order.");
    947    return 1;
    948  }
    949  return *nsContentUtils::ComparePoints<aKind>(
    950      aCompareBoundary,
    951      ConstRawRangeBoundary{aRange.GetMayCrossShadowBoundaryStartContainer(),
    952                            aRange.MayCrossShadowBoundaryStartOffset()},
    953      aCache);
    954 }
    955 
    956 template <TreeKind aKind, typename PT, typename RT,
    957          typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
    958                                      aKind == TreeKind::Flat>>
    959 static int32_t CompareToRangeStart(
    960    const RangeBoundaryBase<PT, RT>& aCompareBoundary,
    961    const AbstractRange& aRange) {
    962  return CompareToRangeStart<aKind>(aCompareBoundary, aRange, nullptr);
    963 }
    964 
    965 template <TreeKind aKind, typename PT, typename RT,
    966          typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
    967                                      aKind == TreeKind::Flat>>
    968 static int32_t CompareToRangeEnd(
    969    const RangeBoundaryBase<PT, RT>& aCompareBoundary,
    970    const AbstractRange& aRange) {
    971  MOZ_ASSERT(aCompareBoundary.IsSet());
    972  MOZ_ASSERT(aRange.IsPositioned());
    973  // If the nodes that we're comparing are not in the same document or in the
    974  // same subtree, assume that aCompareNode will fall at the end of the ranges.
    975  if (aCompareBoundary.GetComposedDoc() !=
    976          aRange.MayCrossShadowBoundaryEndRef().GetComposedDoc() ||
    977      !aRange.MayCrossShadowBoundaryEndRef().IsSetAndInComposedDoc()) {
    978    NS_WARNING(
    979        "`CompareToRangeEnd` couldn't compare nodes, pretending some order.");
    980    return 1;
    981  }
    982 
    983  nsINode* end = aRange.GetMayCrossShadowBoundaryEndContainer();
    984  uint32_t endOffset = aRange.MayCrossShadowBoundaryEndOffset();
    985  if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
    986    return *nsContentUtils::ComparePoints<TreeKind::Flat>(
    987        aCompareBoundary, ConstRawRangeBoundary{end, endOffset});
    988  }
    989  return *nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    990      aCompareBoundary, ConstRawRangeBoundary{end, endOffset});
    991 }
    992 
    993 // static
    994 template <typename PT, typename RT>
    995 size_t Selection::StyledRanges::FindInsertionPoint(
    996    const nsTArray<StyledRange>* aElementArray,
    997    const RangeBoundaryBase<PT, RT>& aBoundary,
    998    int32_t (*aComparator)(const RangeBoundaryBase<PT, RT>&,
    999                           const AbstractRange&)) {
   1000  int32_t beginSearch = 0;
   1001  int32_t endSearch = aElementArray->Length();  // one beyond what to check
   1002 
   1003  if (endSearch) {
   1004    int32_t center = endSearch - 1;  // Check last index, then binary search
   1005    do {
   1006      const AbstractRange* range = (*aElementArray)[center].mRange;
   1007 
   1008      int32_t cmp{aComparator(aBoundary, *range)};
   1009 
   1010      if (cmp < 0) {  // point < cur
   1011        endSearch = center;
   1012      } else if (cmp > 0) {  // point > cur
   1013        beginSearch = center + 1;
   1014      } else {  // found match, done
   1015        beginSearch = center;
   1016        break;
   1017      }
   1018      center = (endSearch - beginSearch) / 2 + beginSearch;
   1019    } while (endSearch - beginSearch > 0);
   1020  }
   1021 
   1022  return AssertedCast<size_t>(beginSearch);
   1023 }
   1024 
   1025 // Selection::SubtractRange
   1026 //
   1027 //    A helper function that subtracts aSubtract from aRange, and adds
   1028 //    1 or 2 StyledRange objects representing the remaining non-overlapping
   1029 //    difference to aOutput. It is assumed that the caller has checked that
   1030 //    aRange and aSubtract do indeed overlap
   1031 
   1032 // static
   1033 nsresult Selection::StyledRanges::SubtractRange(
   1034    StyledRange& aRange, nsRange& aSubtract, nsTArray<StyledRange>* aOutput) {
   1035  AbstractRange* range = aRange.mRange;
   1036  if (NS_WARN_IF(!range->IsPositioned())) {
   1037    return NS_ERROR_UNEXPECTED;
   1038  }
   1039 
   1040  if (range->GetStartContainer()->SubtreeRoot() !=
   1041      aSubtract.GetStartContainer()->SubtreeRoot()) {
   1042    // These are ranges for different shadow trees, we can't subtract them in
   1043    // any sensible way.
   1044    aOutput->InsertElementAt(0, aRange);
   1045    return NS_OK;
   1046  }
   1047 
   1048  // First we want to compare to the range start
   1049  int32_t cmp = [&range, &aSubtract]() {
   1050    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   1051      return CompareToRangeStart<TreeKind::Flat>(range->StartRef(), aSubtract);
   1052    }
   1053    return CompareToRangeStart<TreeKind::ShadowIncludingDOM>(range->StartRef(),
   1054                                                             aSubtract);
   1055  }();
   1056 
   1057  // Also, make a comparison to the range end
   1058  int32_t cmp2 = [&range, &aSubtract]() {
   1059    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   1060      return CompareToRangeEnd<TreeKind::Flat>(range->EndRef(), aSubtract);
   1061    }
   1062    return CompareToRangeEnd<TreeKind::ShadowIncludingDOM>(range->EndRef(),
   1063                                                           aSubtract);
   1064  }();
   1065 
   1066  // If the existing range left overlaps the new range (aSubtract) then
   1067  // cmp < 0, and cmp2 < 0
   1068  // If it right overlaps the new range then cmp > 0 and cmp2 > 0
   1069  // If it fully contains the new range, then cmp < 0 and cmp2 > 0
   1070 
   1071  if (cmp2 > 0) {
   1072    // We need to add a new StyledRange to the output, running from
   1073    // the end of aSubtract to the end of range
   1074    ErrorResult error;
   1075    RefPtr<nsRange> postOverlap =
   1076        nsRange::Create(aSubtract.EndRef(), range->EndRef(), error);
   1077    if (NS_WARN_IF(error.Failed())) {
   1078      return error.StealNSResult();
   1079    }
   1080    MOZ_ASSERT(postOverlap);
   1081    if (!postOverlap->Collapsed()) {
   1082      // XXX(Bug 1631371) Check if this should use a fallible operation as it
   1083      // pretended earlier.
   1084      aOutput->InsertElementAt(0, StyledRange(postOverlap));
   1085      (*aOutput)[0].mTextRangeStyle = aRange.mTextRangeStyle;
   1086    }
   1087  }
   1088 
   1089  if (cmp < 0) {
   1090    // We need to add a new StyledRange to the output, running from
   1091    // the start of the range to the start of aSubtract
   1092    ErrorResult error;
   1093    RefPtr<nsRange> preOverlap =
   1094        nsRange::Create(range->StartRef(), aSubtract.StartRef(), error);
   1095    if (NS_WARN_IF(error.Failed())) {
   1096      return error.StealNSResult();
   1097    }
   1098    MOZ_ASSERT(preOverlap);
   1099    if (!preOverlap->Collapsed()) {
   1100      // XXX(Bug 1631371) Check if this should use a fallible operation as it
   1101      // pretended earlier.
   1102      aOutput->InsertElementAt(0, StyledRange(preOverlap));
   1103      (*aOutput)[0].mTextRangeStyle = aRange.mTextRangeStyle;
   1104    }
   1105  }
   1106 
   1107  return NS_OK;
   1108 }
   1109 
   1110 static void UserSelectRangesToAdd(nsRange* aItem,
   1111                                  nsTArray<RefPtr<nsRange>>& aRangesToAdd) {
   1112  // We cannot directly call IsEditorSelection() because we may be in an
   1113  // inconsistent state during Collapse() (we're cleared already but we haven't
   1114  // got a new focus node yet).
   1115  if (!StaticPrefs::dom_selection_exclude_non_selectable_nodes() ||
   1116      (IsEditorNode(aItem->GetStartContainer()) &&
   1117       IsEditorNode(aItem->GetEndContainer()))) {
   1118    // Don't mess with the selection ranges for editing, editor doesn't really
   1119    // deal well with multi-range selections.
   1120    aRangesToAdd.AppendElement(aItem);
   1121  } else {
   1122    aItem->ExcludeNonSelectableNodes(&aRangesToAdd);
   1123  }
   1124 }
   1125 
   1126 static nsINode* DetermineSelectstartEventTarget(
   1127    const bool aSelectionEventsOnTextControlsEnabled, const nsRange& aRange) {
   1128  nsINode* target = aRange.GetStartContainer();
   1129  if (aSelectionEventsOnTextControlsEnabled) {
   1130    // Get the first element which isn't in a native anonymous subtree
   1131    while (target && target->IsInNativeAnonymousSubtree()) {
   1132      target = target->GetParent();
   1133    }
   1134  } else {
   1135    if (target->IsInNativeAnonymousSubtree()) {
   1136      // This is a selection under a text control, so don't dispatch the
   1137      // event.
   1138      target = nullptr;
   1139    }
   1140  }
   1141  return target;
   1142 }
   1143 
   1144 /**
   1145 * @return true, iff the default action should be executed.
   1146 */
   1147 static bool MaybeDispatchSelectstartEvent(
   1148    const nsRange& aRange, const bool aSelectionEventsOnTextControlsEnabled,
   1149    Document* aDocument) {
   1150  nsCOMPtr<nsINode> selectstartEventTarget = DetermineSelectstartEventTarget(
   1151      aSelectionEventsOnTextControlsEnabled, aRange);
   1152 
   1153  bool executeDefaultAction = true;
   1154 
   1155  if (selectstartEventTarget) {
   1156    nsContentUtils::DispatchTrustedEvent(
   1157        aDocument, selectstartEventTarget, u"selectstart"_ns, CanBubble::eYes,
   1158        Cancelable::eYes, &executeDefaultAction);
   1159  }
   1160 
   1161  return executeDefaultAction;
   1162 }
   1163 
   1164 // static
   1165 bool Selection::IsUserSelectionCollapsed(
   1166    const nsRange& aRange, nsTArray<RefPtr<nsRange>>& aTempRangesToAdd) {
   1167  MOZ_ASSERT(aTempRangesToAdd.IsEmpty());
   1168 
   1169  RefPtr<nsRange> scratchRange = aRange.CloneRange();
   1170  UserSelectRangesToAdd(scratchRange, aTempRangesToAdd);
   1171  const bool userSelectionCollapsed =
   1172      (aTempRangesToAdd.Length() == 0) ||
   1173      ((aTempRangesToAdd.Length() == 1) && aTempRangesToAdd[0]->Collapsed());
   1174 
   1175  aTempRangesToAdd.ClearAndRetainStorage();
   1176 
   1177  return userSelectionCollapsed;
   1178 }
   1179 
   1180 nsresult Selection::AddRangesForUserSelectableNodes(
   1181    nsRange* aRange, Maybe<size_t>* aOutIndex,
   1182    const DispatchSelectstartEvent aDispatchSelectstartEvent) {
   1183  MOZ_ASSERT(mUserInitiated);
   1184  MOZ_ASSERT(aOutIndex);
   1185  MOZ_ASSERT(aOutIndex->isNothing());
   1186 
   1187  if (!aRange) {
   1188    return NS_ERROR_NULL_POINTER;
   1189  }
   1190 
   1191  if (!aRange->IsPositioned()) {
   1192    return NS_ERROR_UNEXPECTED;
   1193  }
   1194 
   1195  AutoTArray<RefPtr<nsRange>, 4> rangesToAdd;
   1196  if (mStyledRanges.Length()) {
   1197    aOutIndex->emplace(mStyledRanges.Length() - 1);
   1198  }
   1199 
   1200  Document* doc = GetDocument();
   1201 
   1202  if (aDispatchSelectstartEvent == DispatchSelectstartEvent::Maybe &&
   1203      mSelectionType == SelectionType::eNormal && IsCollapsed() &&
   1204      !IsBlockingSelectionChangeEvents()) {
   1205    // We consider a selection to be starting if we are currently collapsed,
   1206    // and the selection is becoming uncollapsed, and this is caused by a
   1207    // user initiated event.
   1208 
   1209    // First, we generate the ranges to add with a scratch range, which is a
   1210    // clone of the original range passed in. We do this seperately, because
   1211    // the selectstart event could have caused the world to change, and
   1212    // required ranges to be re-generated
   1213 
   1214    const bool userSelectionCollapsed =
   1215        IsUserSelectionCollapsed(*aRange, rangesToAdd);
   1216    MOZ_ASSERT(userSelectionCollapsed || nsContentUtils::IsSafeToRunScript());
   1217    if (!userSelectionCollapsed && nsContentUtils::IsSafeToRunScript()) {
   1218      // The spec currently doesn't say that we should dispatch this event
   1219      // on text controls, so for now we only support doing that under a
   1220      // pref, disabled by default.
   1221      // See https://github.com/w3c/selection-api/issues/53.
   1222      const bool executeDefaultAction = MaybeDispatchSelectstartEvent(
   1223          *aRange,
   1224          StaticPrefs::dom_select_events_textcontrols_selectstart_enabled(),
   1225          doc);
   1226 
   1227      if (!executeDefaultAction) {
   1228        return NS_OK;
   1229      }
   1230 
   1231      // As we potentially dispatched an event to the DOM, something could have
   1232      // changed under our feet. Re-generate the rangesToAdd array, and
   1233      // ensure that the range we are about to add is still valid.
   1234      if (!aRange->IsPositioned()) {
   1235        return NS_ERROR_UNEXPECTED;
   1236      }
   1237    }
   1238  }
   1239 
   1240  // Generate the ranges to add
   1241  UserSelectRangesToAdd(aRange, rangesToAdd);
   1242  size_t newAnchorFocusIndex =
   1243      GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1;
   1244  for (size_t i = 0; i < rangesToAdd.Length(); ++i) {
   1245    Maybe<size_t> index;
   1246    // `MOZ_KnownLive` needed because of broken static analysis
   1247    // (https://bugzilla.mozilla.org/show_bug.cgi?id=1622253#c1).
   1248    nsresult rv = mStyledRanges.MaybeAddRangeAndTruncateOverlaps(
   1249        MOZ_KnownLive(rangesToAdd[i]), &index);
   1250    NS_ENSURE_SUCCESS(rv, rv);
   1251    if (i == newAnchorFocusIndex) {
   1252      *aOutIndex = index;
   1253      rangesToAdd[i]->SetIsGenerated(false);
   1254    } else {
   1255      rangesToAdd[i]->SetIsGenerated(true);
   1256    }
   1257  }
   1258  return NS_OK;
   1259 }
   1260 
   1261 nsresult Selection::AddRangesForSelectableNodes(
   1262    nsRange* aRange, Maybe<size_t>* aOutIndex,
   1263    const DispatchSelectstartEvent aDispatchSelectstartEvent) {
   1264  MOZ_ASSERT(aOutIndex);
   1265  MOZ_ASSERT(aOutIndex->isNothing());
   1266 
   1267  if (!aRange) {
   1268    return NS_ERROR_NULL_POINTER;
   1269  }
   1270 
   1271  if (!aRange->IsPositioned()) {
   1272    return NS_ERROR_UNEXPECTED;
   1273  }
   1274 
   1275  MOZ_LOG(
   1276      sSelectionLog, LogLevel::Debug,
   1277      ("%s: selection=%p, type=%i, range=(%p, StartOffset=%u, EndOffset=%u)",
   1278       __FUNCTION__, this, static_cast<int>(GetType()), aRange,
   1279       aRange->StartOffset(), aRange->EndOffset()));
   1280 
   1281  if (mUserInitiated) {
   1282    return AddRangesForUserSelectableNodes(aRange, aOutIndex,
   1283                                           aDispatchSelectstartEvent);
   1284  }
   1285 
   1286  return mStyledRanges.MaybeAddRangeAndTruncateOverlaps(aRange, aOutIndex);
   1287 }
   1288 
   1289 nsresult Selection::StyledRanges::AddRangeAndIgnoreOverlaps(
   1290    AbstractRange* aRange) {
   1291  MOZ_ASSERT(aRange);
   1292  MOZ_ASSERT(aRange->IsPositioned());
   1293  MOZ_ASSERT(mSelection.mSelectionType == SelectionType::eHighlight);
   1294  if (aRange->IsStaticRange() && !aRange->AsStaticRange()->IsValid()) {
   1295    mInvalidStaticRanges.AppendElement(StyledRange(aRange));
   1296    return NS_OK;
   1297  }
   1298 
   1299  // a common case is that we have no ranges yet
   1300  if (mRanges.Length() == 0) {
   1301    mRanges.AppendElement(StyledRange(aRange));
   1302    aRange->RegisterSelection(MOZ_KnownLive(mSelection));
   1303 #ifdef ACCESSIBILITY
   1304    a11y::SelectionManager::SelectionRangeChanged(mSelection.GetType(),
   1305                                                  *aRange);
   1306 #endif
   1307    return NS_OK;
   1308  }
   1309 
   1310  Maybe<size_t> maybeStartIndex, maybeEndIndex;
   1311  nsresult rv =
   1312      GetIndicesForInterval(aRange->GetStartContainer(), aRange->StartOffset(),
   1313                            aRange->GetEndContainer(), aRange->EndOffset(),
   1314                            false, maybeStartIndex, maybeEndIndex);
   1315  NS_ENSURE_SUCCESS(rv, rv);
   1316 
   1317  size_t startIndex(0);
   1318  if (maybeEndIndex.isNothing()) {
   1319    // All ranges start after the given range. We can insert our range at
   1320    // position 0.
   1321    startIndex = 0;
   1322  } else if (maybeStartIndex.isNothing()) {
   1323    // All ranges end before the given range. We can insert our range at
   1324    // the end of the array.
   1325    startIndex = mRanges.Length();
   1326  } else {
   1327    startIndex = *maybeStartIndex;
   1328  }
   1329 
   1330  mRanges.InsertElementAt(startIndex, StyledRange(aRange));
   1331  aRange->RegisterSelection(MOZ_KnownLive(mSelection));
   1332 #ifdef ACCESSIBILITY
   1333  a11y::SelectionManager::SelectionRangeChanged(mSelection.GetType(), *aRange);
   1334 #endif
   1335  return NS_OK;
   1336 }
   1337 
   1338 nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps(
   1339    nsRange* aRange, Maybe<size_t>* aOutIndex) {
   1340  MOZ_ASSERT(aRange);
   1341  MOZ_ASSERT(aRange->IsPositioned());
   1342  MOZ_ASSERT(aOutIndex);
   1343  MOZ_ASSERT(aOutIndex->isNothing());
   1344 
   1345  // a common case is that we have no ranges yet
   1346  if (mRanges.Length() == 0) {
   1347    // XXX(Bug 1631371) Check if this should use a fallible operation as it
   1348    // pretended earlier.
   1349    mRanges.AppendElement(StyledRange(aRange));
   1350    aRange->RegisterSelection(MOZ_KnownLive(mSelection));
   1351 #ifdef ACCESSIBILITY
   1352    a11y::SelectionManager::SelectionRangeChanged(mSelection.GetType(),
   1353                                                  *aRange);
   1354 #endif
   1355 
   1356    aOutIndex->emplace(0u);
   1357    return NS_OK;
   1358  }
   1359 
   1360  Maybe<size_t> maybeStartIndex, maybeEndIndex;
   1361  nsresult rv =
   1362      GetIndicesForInterval(aRange->GetStartContainer(), aRange->StartOffset(),
   1363                            aRange->GetEndContainer(), aRange->EndOffset(),
   1364                            false, maybeStartIndex, maybeEndIndex);
   1365  NS_ENSURE_SUCCESS(rv, rv);
   1366 
   1367  size_t startIndex, endIndex;
   1368  if (maybeEndIndex.isNothing()) {
   1369    // All ranges start after the given range. We can insert our range at
   1370    // position 0, knowing there are no overlaps (handled below)
   1371    startIndex = endIndex = 0;
   1372  } else if (maybeStartIndex.isNothing()) {
   1373    // All ranges end before the given range. We can insert our range at
   1374    // the end of the array, knowing there are no overlaps (handled below)
   1375    startIndex = endIndex = mRanges.Length();
   1376  } else {
   1377    startIndex = *maybeStartIndex;
   1378    endIndex = *maybeEndIndex;
   1379  }
   1380 
   1381  // If the range is already contained in mRanges, silently
   1382  // succeed
   1383  const bool sameRange = HasEqualRangeBoundariesAt(*aRange, startIndex);
   1384  if (sameRange) {
   1385    aOutIndex->emplace(startIndex);
   1386    return NS_OK;
   1387  }
   1388 
   1389  // Beyond this point, we will expand the selection to cover aRange.
   1390  // Accessibility doesn't need to know about ranges split due to overlaps. It
   1391  // just needs a range that covers any text leaf that is impacted by the
   1392  // change.
   1393 #ifdef ACCESSIBILITY
   1394  a11y::SelectionManager::SelectionRangeChanged(mSelection.GetType(), *aRange);
   1395 #endif
   1396 
   1397  if (startIndex == endIndex) {
   1398    // The new range doesn't overlap any existing ranges
   1399    // XXX(Bug 1631371) Check if this should use a fallible operation as it
   1400    // pretended earlier.
   1401    mRanges.InsertElementAt(startIndex, StyledRange(aRange));
   1402    aRange->RegisterSelection(MOZ_KnownLive(mSelection));
   1403    aOutIndex->emplace(startIndex);
   1404    return NS_OK;
   1405  }
   1406 
   1407  // We now know that at least 1 existing range overlaps with the range that
   1408  // we are trying to add. In fact, the only ranges of interest are those at
   1409  // the two end points, startIndex and endIndex - 1 (which may point to the
   1410  // same range) as these may partially overlap the new range. Any ranges
   1411  // between these indices are fully overlapped by the new range, and so can be
   1412  // removed.
   1413  AutoTArray<StyledRange, 2> overlaps;
   1414  overlaps.AppendElement(mRanges[startIndex]);
   1415  if (endIndex - 1 != startIndex) {
   1416    overlaps.AppendElement(mRanges[endIndex - 1]);
   1417  }
   1418 
   1419  // Remove all the overlapping ranges
   1420  for (size_t i = startIndex; i < endIndex; ++i) {
   1421    mRanges[i].mRange->UnregisterSelection(mSelection);
   1422  }
   1423  mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
   1424 
   1425  AutoTArray<StyledRange, 3> temp;
   1426  for (const size_t i : Reversed(IntegerRange(overlaps.Length()))) {
   1427    nsresult rv = SubtractRange(overlaps[i], *aRange, &temp);
   1428    NS_ENSURE_SUCCESS(rv, rv);
   1429  }
   1430 
   1431  // Insert the new element into our "leftovers" array
   1432  // `aRange` is positioned, so it has to have a start container.
   1433  size_t insertionPoint = [&temp, &aRange]() {
   1434    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   1435      return FindInsertionPoint(&temp, aRange->StartRef(),
   1436                                CompareToRangeStart<TreeKind::Flat>);
   1437    };
   1438    return FindInsertionPoint(
   1439        &temp, aRange->StartRef(),
   1440        CompareToRangeStart<TreeKind::ShadowIncludingDOM>);
   1441  }();
   1442 
   1443  temp.InsertElementAt(insertionPoint, StyledRange(aRange));
   1444 
   1445  // Merge the leftovers back in to mRanges
   1446  mRanges.InsertElementsAt(startIndex, temp);
   1447 
   1448  for (uint32_t i = 0; i < temp.Length(); ++i) {
   1449    if (temp[i].mRange->IsDynamicRange()) {
   1450      MOZ_KnownLive(temp[i].mRange->AsDynamicRange())
   1451          ->RegisterSelection(MOZ_KnownLive(mSelection));
   1452      // `MOZ_KnownLive` is required because of
   1453      // https://bugzilla.mozilla.org/show_bug.cgi?id=1622253.
   1454    }
   1455  }
   1456 
   1457  aOutIndex->emplace(startIndex + insertionPoint);
   1458  return NS_OK;
   1459 }
   1460 
   1461 nsresult Selection::StyledRanges::RemoveRangeAndUnregisterSelection(
   1462    AbstractRange& aRange) {
   1463  // Find the range's index & remove it. We could use FindInsertionPoint to
   1464  // get O(log n) time, but that requires many expensive DOM comparisons.
   1465  // For even several thousand items, this is probably faster because the
   1466  // comparisons are so fast.
   1467  int32_t idx = -1;
   1468  uint32_t i;
   1469  for (i = 0; i < mRanges.Length(); i++) {
   1470    if (mRanges[i].mRange == &aRange) {
   1471      idx = (int32_t)i;
   1472      break;
   1473    }
   1474  }
   1475  if (idx < 0) return NS_ERROR_DOM_NOT_FOUND_ERR;
   1476 
   1477  mRanges.RemoveElementAt(idx);
   1478  aRange.UnregisterSelection(mSelection);
   1479 #ifdef ACCESSIBILITY
   1480  a11y::SelectionManager::SelectionRangeChanged(mSelection.GetType(), aRange);
   1481 #endif
   1482 
   1483  return NS_OK;
   1484 }
   1485 nsresult Selection::RemoveCollapsedRanges() {
   1486  if (NeedsToLogSelectionAPI(*this)) {
   1487    LogSelectionAPI(this, __FUNCTION__);
   1488    LogStackForSelectionAPI();
   1489  }
   1490 
   1491  return mStyledRanges.RemoveCollapsedRanges();
   1492 }
   1493 
   1494 nsresult Selection::StyledRanges::RemoveCollapsedRanges() {
   1495  uint32_t i = 0;
   1496  while (i < mRanges.Length()) {
   1497    const AbstractRange* range = mRanges[i].mRange;
   1498    // If nsRange::mCrossShadowBoundaryRange exists, it means
   1499    // there's a cross boundary selection, so obviously
   1500    // we shouldn't remove this range.
   1501    const bool collapsed =
   1502        range->Collapsed() && !range->MayCrossShadowBoundary();
   1503    // Cross boundary range should always be uncollapsed.
   1504    MOZ_ASSERT_IF(
   1505        range->MayCrossShadowBoundary(),
   1506        !range->AsDynamicRange()->CrossShadowBoundaryRangeCollapsed());
   1507 
   1508    if (collapsed) {
   1509      nsresult rv = RemoveRangeAndUnregisterSelection(*mRanges[i].mRange);
   1510      NS_ENSURE_SUCCESS(rv, rv);
   1511    } else {
   1512      ++i;
   1513    }
   1514  }
   1515  return NS_OK;
   1516 }
   1517 
   1518 void Selection::Clear(nsPresContext* aPresContext, IsUnlinking aIsUnlinking) {
   1519  RemoveAnchorFocusRange();
   1520 
   1521  mStyledRanges.UnregisterSelection();
   1522  for (uint32_t i = 0; i < mStyledRanges.Length(); ++i) {
   1523    SelectFrames(aPresContext, *mStyledRanges.mRanges[i].mRange, false);
   1524  }
   1525  mStyledRanges.Clear();
   1526 
   1527  // Reset direction so for more dependable table selection range handling
   1528  SetDirection(eDirNext);
   1529 
   1530  // If this was an ATTENTION selection, change it back to normal now
   1531  if (mFrameSelection && mFrameSelection->GetDisplaySelection() ==
   1532                             nsISelectionController::SELECTION_ATTENTION) {
   1533    mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
   1534  }
   1535 }
   1536 
   1537 bool Selection::StyledRanges::HasEqualRangeBoundariesAt(
   1538    const AbstractRange& aRange, size_t aRangeIndex) const {
   1539  if (aRangeIndex < mRanges.Length()) {
   1540    const AbstractRange* range = mRanges[aRangeIndex].mRange;
   1541    return range->HasEqualBoundaries(aRange);
   1542  }
   1543  return false;
   1544 }
   1545 
   1546 void Selection::GetRangesForInterval(nsINode& aBeginNode, uint32_t aBeginOffset,
   1547                                     nsINode& aEndNode, uint32_t aEndOffset,
   1548                                     bool aAllowAdjacent,
   1549                                     nsTArray<RefPtr<nsRange>>& aReturn,
   1550                                     mozilla::ErrorResult& aRv) {
   1551  AutoTArray<nsRange*, 2> results;
   1552  nsresult rv =
   1553      GetDynamicRangesForIntervalArray(&aBeginNode, aBeginOffset, &aEndNode,
   1554                                       aEndOffset, aAllowAdjacent, &results);
   1555  if (NS_FAILED(rv)) {
   1556    aRv.Throw(rv);
   1557    return;
   1558  }
   1559 
   1560  aReturn.SetLength(results.Length());
   1561  for (size_t i = 0; i < results.Length(); ++i) {
   1562    aReturn[i] = results[i];  // AddRefs
   1563  }
   1564 }
   1565 
   1566 nsresult Selection::GetAbstractRangesForIntervalArray(
   1567    nsINode* aBeginNode, uint32_t aBeginOffset, nsINode* aEndNode,
   1568    uint32_t aEndOffset, bool aAllowAdjacent,
   1569    nsTArray<AbstractRange*>* aRanges) {
   1570  if (NS_WARN_IF(!aBeginNode)) {
   1571    return NS_ERROR_UNEXPECTED;
   1572  }
   1573 
   1574  if (NS_WARN_IF(!aEndNode)) {
   1575    return NS_ERROR_UNEXPECTED;
   1576  }
   1577 
   1578  aRanges->Clear();
   1579  Maybe<size_t> maybeStartIndex, maybeEndIndex;
   1580  nsresult res = mStyledRanges.GetIndicesForInterval(
   1581      aBeginNode, aBeginOffset, aEndNode, aEndOffset, aAllowAdjacent,
   1582      maybeStartIndex, maybeEndIndex);
   1583  NS_ENSURE_SUCCESS(res, res);
   1584 
   1585  if (maybeStartIndex.isNothing() || maybeEndIndex.isNothing()) {
   1586    return NS_OK;
   1587  }
   1588 
   1589  for (const size_t i : IntegerRange(*maybeStartIndex, *maybeEndIndex)) {
   1590    // XXX(Bug 1631371) Check if this should use a fallible operation as it
   1591    // pretended earlier.
   1592    aRanges->AppendElement(mStyledRanges.mRanges[i].mRange);
   1593  }
   1594 
   1595  return NS_OK;
   1596 }
   1597 
   1598 nsresult Selection::GetDynamicRangesForIntervalArray(
   1599    nsINode* aBeginNode, uint32_t aBeginOffset, nsINode* aEndNode,
   1600    uint32_t aEndOffset, bool aAllowAdjacent, nsTArray<nsRange*>* aRanges) {
   1601  MOZ_ASSERT(mSelectionType != SelectionType::eHighlight);
   1602  AutoTArray<AbstractRange*, 2> abstractRanges;
   1603  nsresult rv = GetAbstractRangesForIntervalArray(
   1604      aBeginNode, aBeginOffset, aEndNode, aEndOffset, aAllowAdjacent,
   1605      &abstractRanges);
   1606  NS_ENSURE_SUCCESS(rv, rv);
   1607  aRanges->Clear();
   1608  aRanges->SetCapacity(abstractRanges.Length());
   1609  for (auto* abstractRange : abstractRanges) {
   1610    aRanges->AppendElement(abstractRange->AsDynamicRange());
   1611  }
   1612  return NS_OK;
   1613 }
   1614 
   1615 void Selection::StyledRanges::ReorderRangesIfNecessary() {
   1616  const Document* doc = mSelection.GetDocument();
   1617  if (!doc) {
   1618    return;
   1619  }
   1620  if (mRanges.Length() < 2 && mInvalidStaticRanges.IsEmpty()) {
   1621    // There is nothing to be reordered.
   1622    return;
   1623  }
   1624  const int32_t currentDocumentGeneration = doc->GetGeneration();
   1625  const bool domMutationHasHappened =
   1626      currentDocumentGeneration != mDocumentGeneration;
   1627  if (domMutationHasHappened) {
   1628    // After a DOM mutation, invalid static ranges might have become valid and
   1629    // valid static ranges might have become invalid.
   1630    StyledRangeArray invalidStaticRanges;
   1631    for (StyledRangeArray::const_iterator iter = mRanges.begin();
   1632         iter != mRanges.end();) {
   1633      const AbstractRange* range = iter->mRange;
   1634      if (range->IsStaticRange() && !range->AsStaticRange()->IsValid()) {
   1635        invalidStaticRanges.AppendElement(*iter);
   1636        iter = mRanges.RemoveElementAt(iter);
   1637      } else {
   1638        ++iter;
   1639      }
   1640    }
   1641    for (StyledRangeArray::const_iterator iter = mInvalidStaticRanges.begin();
   1642         iter != mInvalidStaticRanges.end();) {
   1643      MOZ_ASSERT(iter->mRange->IsStaticRange());
   1644      if (iter->mRange->AsStaticRange()->IsValid()) {
   1645        mRanges.AppendElement(*iter);
   1646        if (!iter->mRange->IsInSelection(mSelection)) {
   1647          iter->mRange->RegisterSelection(mSelection);
   1648        }
   1649        iter = mInvalidStaticRanges.RemoveElementAt(iter);
   1650      } else {
   1651        ++iter;
   1652      }
   1653    }
   1654    mInvalidStaticRanges.AppendElements(std::move(invalidStaticRanges));
   1655  }
   1656  if (domMutationHasHappened || mRangesMightHaveChanged) {
   1657    // This is hot code. Proceed with caution.
   1658    // This path uses a cache that keep the last 100 node/index combinations
   1659    // in a stack-allocated array to save up on expensive calls to
   1660    // nsINode::ComputeIndexOf() (which happen in
   1661    // nsContentUtils::ComparePoints()).
   1662    // The second expensive call here is the sort() below, which should be
   1663    // avoided if possible. Sorting can be avoided if the ranges are still in
   1664    // order. Checking the order is cheap compared to sorting (also, it fills up
   1665    // the cache, which is reused by the sort call).
   1666    nsContentUtils::NodeIndexCache cache;
   1667    bool rangeOrderHasChanged = false;
   1668    RawRangeBoundary previousStartRef;
   1669    for (const StyledRange& range : mRanges) {
   1670      if (!previousStartRef.IsSet()) {
   1671        previousStartRef = range.mRange->StartRef().AsRaw();
   1672        continue;
   1673      }
   1674      // Calling ComparePoints here saves one call of
   1675      // AbstractRange::StartOffset() per iteration (which is surprisingly
   1676      // expensive).
   1677      const Maybe<int32_t> compareResult =
   1678          StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
   1679              ? nsContentUtils::ComparePoints<TreeKind::Flat>(
   1680                    range.mRange->StartRef(), previousStartRef, &cache)
   1681              : nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
   1682                    range.mRange->StartRef(), previousStartRef, &cache);
   1683      // If the nodes are in different subtrees, the Maybe is empty.
   1684      // Since CompareToRangeStart pretends ranges to be ordered, this aligns
   1685      // to that behavior.
   1686      if (compareResult.valueOr(1) != 1) {
   1687        rangeOrderHasChanged = true;
   1688        break;
   1689      }
   1690      previousStartRef = range.mRange->StartRef().AsRaw();
   1691    }
   1692    if (rangeOrderHasChanged) {
   1693      std::function<int32_t(const StyledRange&, const StyledRange&)> compare;
   1694      if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   1695        compare = [&cache](const StyledRange& a, const StyledRange& b) {
   1696          return CompareToRangeStart<TreeKind::Flat>(a.mRange->StartRef(),
   1697                                                     *b.mRange, &cache);
   1698        };
   1699      } else {
   1700        compare = [&cache](const StyledRange& a, const StyledRange& b) {
   1701          return CompareToRangeStart<TreeKind::ShadowIncludingDOM>(
   1702              a.mRange->StartRef(), *b.mRange, &cache);
   1703        };
   1704      }
   1705      mRanges.Sort(compare);
   1706    }
   1707    mDocumentGeneration = currentDocumentGeneration;
   1708    mRangesMightHaveChanged = false;
   1709  }
   1710 }
   1711 
   1712 nsresult Selection::StyledRanges::GetIndicesForInterval(
   1713    const nsINode* aBeginNode, uint32_t aBeginOffset, const nsINode* aEndNode,
   1714    uint32_t aEndOffset, bool aAllowAdjacent, Maybe<size_t>& aStartIndex,
   1715    Maybe<size_t>& aEndIndex) {
   1716  MOZ_ASSERT(aStartIndex.isNothing());
   1717  MOZ_ASSERT(aEndIndex.isNothing());
   1718 
   1719  if (NS_WARN_IF(!aBeginNode)) {
   1720    return NS_ERROR_INVALID_POINTER;
   1721  }
   1722 
   1723  if (NS_WARN_IF(!aEndNode)) {
   1724    return NS_ERROR_INVALID_POINTER;
   1725  }
   1726 
   1727  ReorderRangesIfNecessary();
   1728 
   1729  if (mRanges.Length() == 0) {
   1730    return NS_OK;
   1731  }
   1732 
   1733  const bool intervalIsCollapsed =
   1734      aBeginNode == aEndNode && aBeginOffset == aEndOffset;
   1735 
   1736  // Ranges that end before the given interval and begin after the given
   1737  // interval can be discarded
   1738  size_t endsBeforeIndex = [this, &aEndNode, &aEndOffset]() {
   1739    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   1740      return FindInsertionPoint(
   1741          &mRanges,
   1742          ConstRawRangeBoundary(aEndNode, aEndOffset,
   1743                                RangeBoundaryIsMutationObserved::No),
   1744          &CompareToRangeStart<TreeKind::Flat>);
   1745    }
   1746    return FindInsertionPoint(
   1747        &mRanges,
   1748        ConstRawRangeBoundary(aEndNode, aEndOffset,
   1749                              RangeBoundaryIsMutationObserved::No),
   1750        &CompareToRangeStart<TreeKind::ShadowIncludingDOM>);
   1751  }();
   1752 
   1753  if (endsBeforeIndex == 0) {
   1754    const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange;
   1755 
   1756    // If the interval is strictly before the range at index 0, we can optimize
   1757    // by returning now - all ranges start after the given interval
   1758    if (!endRange->StartRef().Equals(aEndNode, aEndOffset)) {
   1759      return NS_OK;
   1760    }
   1761 
   1762    // We now know that the start point of mRanges[0].mRange
   1763    // equals the end of the interval. Thus, when aAllowadjacent is true, the
   1764    // caller is always interested in this range. However, when excluding
   1765    // adjacencies, we must remember to include the range when both it and the
   1766    // given interval are collapsed to the same point
   1767    if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
   1768      return NS_OK;
   1769  }
   1770  aEndIndex.emplace(endsBeforeIndex);
   1771 
   1772  size_t beginsAfterIndex = [this, &aBeginNode, &aBeginOffset]() {
   1773    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   1774      return FindInsertionPoint(
   1775          &mRanges,
   1776          ConstRawRangeBoundary(aBeginNode, aBeginOffset,
   1777                                RangeBoundaryIsMutationObserved::No),
   1778          &CompareToRangeEnd<TreeKind::Flat>);
   1779    }
   1780    return FindInsertionPoint(
   1781        &mRanges,
   1782        ConstRawRangeBoundary(aBeginNode, aBeginOffset,
   1783                              RangeBoundaryIsMutationObserved::No),
   1784        &CompareToRangeEnd<TreeKind::ShadowIncludingDOM>);
   1785  }();
   1786 
   1787  if (beginsAfterIndex == mRanges.Length()) {
   1788    return NS_OK;  // optimization: all ranges are strictly before us
   1789  }
   1790 
   1791  if (aAllowAdjacent) {
   1792    // At this point, one of the following holds:
   1793    //   endsBeforeIndex == mRanges.Length(),
   1794    //   endsBeforeIndex points to a range whose start point does not equal the
   1795    //     given interval's start point
   1796    //   endsBeforeIndex points to a range whose start point equals the given
   1797    //     interval's start point
   1798    // In the final case, there can be two such ranges, a collapsed range, and
   1799    // an adjacent range (they will appear in mRanges in that
   1800    // order). For this final case, we need to increment endsBeforeIndex, until
   1801    // one of the first two possibilities hold
   1802    while (endsBeforeIndex < mRanges.Length()) {
   1803      const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange;
   1804      if (!endRange->StartRef().Equals(aEndNode, aEndOffset)) {
   1805        break;
   1806      }
   1807      endsBeforeIndex++;
   1808    }
   1809 
   1810    // Likewise, one of the following holds:
   1811    //   beginsAfterIndex == 0,
   1812    //   beginsAfterIndex points to a range whose end point does not equal
   1813    //     the given interval's end point
   1814    //   beginsOnOrAfter points to a range whose end point equals the given
   1815    //     interval's end point
   1816    // In the final case, there can be two such ranges, an adjacent range, and
   1817    // a collapsed range (they will appear in mRanges in that
   1818    // order). For this final case, we only need to take action if both those
   1819    // ranges exist, and we are pointing to the collapsed range - we need to
   1820    // point to the adjacent range
   1821    const AbstractRange* beginRange = mRanges[beginsAfterIndex].mRange;
   1822    if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
   1823        beginRange->EndRef().Equals(aBeginNode, aBeginOffset)) {
   1824      beginRange = mRanges[beginsAfterIndex - 1].mRange;
   1825      if (beginRange->EndRef().Equals(aBeginNode, aBeginOffset)) {
   1826        beginsAfterIndex--;
   1827      }
   1828    }
   1829  } else {
   1830    // See above for the possibilities at this point. The only case where we
   1831    // need to take action is when the range at beginsAfterIndex ends on
   1832    // the given interval's start point, but that range isn't collapsed (a
   1833    // collapsed range should be included in the returned results).
   1834    const AbstractRange* beginRange = mRanges[beginsAfterIndex].mRange;
   1835    if (beginRange->MayCrossShadowBoundaryEndRef().Equals(aBeginNode,
   1836                                                          aBeginOffset) &&
   1837        !beginRange->Collapsed()) {
   1838      beginsAfterIndex++;
   1839    }
   1840 
   1841    // Again, see above for the meaning of endsBeforeIndex at this point.
   1842    // In particular, endsBeforeIndex may point to a collaped range which
   1843    // represents the point at the end of the interval - this range should be
   1844    // included
   1845    if (endsBeforeIndex < mRanges.Length()) {
   1846      const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange;
   1847      if (endRange->MayCrossShadowBoundaryStartRef().Equals(aEndNode,
   1848                                                            aEndOffset) &&
   1849          endRange->Collapsed()) {
   1850        endsBeforeIndex++;
   1851      }
   1852    }
   1853  }
   1854 
   1855  NS_ASSERTION(beginsAfterIndex <= endsBeforeIndex, "Is mRanges not ordered?");
   1856  NS_ENSURE_STATE(beginsAfterIndex <= endsBeforeIndex);
   1857 
   1858  aStartIndex.emplace(beginsAfterIndex);
   1859  aEndIndex = Some(endsBeforeIndex);
   1860  return NS_OK;
   1861 }
   1862 
   1863 nsIFrame* Selection::GetPrimaryFrameForAnchorNode() const {
   1864  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
   1865 
   1866  nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
   1867  if (content && mFrameSelection) {
   1868    return SelectionMovementUtils::GetFrameForNodeOffset(
   1869        content, AnchorOffset(), mFrameSelection->GetHint());
   1870  }
   1871  return nullptr;
   1872 }
   1873 
   1874 PrimaryFrameData Selection::GetPrimaryFrameForCaretAtFocusNode(
   1875    bool aVisual) const {
   1876  nsIContent* content = nsIContent::FromNodeOrNull(GetFocusNode());
   1877  if (!content || !mFrameSelection || !mFrameSelection->GetPresShell()) {
   1878    return {};
   1879  }
   1880 
   1881  MOZ_ASSERT(mFrameSelection->GetPresShell()->GetDocument() ==
   1882             content->GetComposedDoc());
   1883 
   1884  CaretAssociationHint hint = mFrameSelection->GetHint();
   1885  intl::BidiEmbeddingLevel caretBidiLevel =
   1886      mFrameSelection->GetCaretBidiLevel();
   1887  return SelectionMovementUtils::GetPrimaryFrameForCaret(
   1888      content, FocusOffset(), aVisual, hint, caretBidiLevel);
   1889 }
   1890 
   1891 void Selection::SelectFramesOf(nsIContent* aContent, bool aSelected) const {
   1892  nsIFrame* frame = aContent->GetPrimaryFrame();
   1893  if (!frame) {
   1894    return;
   1895  }
   1896  // The frame could be an SVG text frame, in which case we don't treat it
   1897  // as a text frame.
   1898  if (frame->IsTextFrame()) {
   1899    nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
   1900    textFrame->SelectionStateChanged(
   1901        0, textFrame->CharacterDataBuffer().GetLength(), aSelected,
   1902        mSelectionType);
   1903  } else {
   1904    frame->SelectionStateChanged();
   1905  }
   1906 }
   1907 
   1908 nsresult Selection::SelectFramesOfInclusiveDescendantsOfContent(
   1909    PostContentIterator& aPostOrderIter, nsIContent* aContent,
   1910    bool aSelected) const {
   1911  // If aContent doesn't have children, we should avoid to use the content
   1912  // iterator for performance reason.
   1913  if (!aContent->HasChildren()) {
   1914    SelectFramesOf(aContent, aSelected);
   1915    return NS_OK;
   1916  }
   1917 
   1918  if (NS_WARN_IF(NS_FAILED(aPostOrderIter.Init(aContent)))) {
   1919    return NS_ERROR_FAILURE;
   1920  }
   1921 
   1922  for (; !aPostOrderIter.IsDone(); aPostOrderIter.Next()) {
   1923    nsINode* node = aPostOrderIter.GetCurrentNode();
   1924    MOZ_ASSERT(node);
   1925    nsIContent* innercontent = node->IsContent() ? node->AsContent() : nullptr;
   1926    SelectFramesOf(innercontent, aSelected);
   1927  }
   1928 
   1929  return NS_OK;
   1930 }
   1931 
   1932 void Selection::SelectFramesInAllRanges(nsPresContext* aPresContext) {
   1933  // this method is currently only called in a user-initiated context.
   1934  // therefore it is safe to assume that we are not in a Highlight selection
   1935  // and we only have to deal with nsRanges (no StaticRanges).
   1936  MOZ_ASSERT(mSelectionType != SelectionType::eHighlight);
   1937  for (size_t i = 0; i < mStyledRanges.Length(); ++i) {
   1938    nsRange* range = mStyledRanges.mRanges[i].mRange->AsDynamicRange();
   1939    MOZ_ASSERT(range->IsInAnySelection());
   1940    SelectFrames(aPresContext, *range, range->IsInAnySelection());
   1941  }
   1942 }
   1943 
   1944 /**
   1945 * The idea of this helper method is to select or deselect "top to bottom",
   1946 * traversing through the frames
   1947 */
   1948 nsresult Selection::SelectFrames(nsPresContext* aPresContext,
   1949                                 AbstractRange& aRange, bool aSelect) const {
   1950  if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
   1951    // nothing to do
   1952    return NS_OK;
   1953  }
   1954 
   1955  MOZ_DIAGNOSTIC_ASSERT_IF(!aRange.IsPositioned(),
   1956                           !aRange.MayCrossShadowBoundary());
   1957 
   1958  MOZ_DIAGNOSTIC_ASSERT(aRange.IsPositioned());
   1959 
   1960  const Document* const document = GetDocument();
   1961  if (MOZ_UNLIKELY(!document ||
   1962                   aRange.GetComposedDocOfContainers() != document)) {
   1963    return NS_OK;  // Do nothing if the range is now in different document.
   1964  }
   1965 
   1966  if (aRange.IsStaticRange() && !aRange.AsStaticRange()->IsValid()) {
   1967    // TODO jjaschke: Actions necessary to unselect invalid static ranges?
   1968    return NS_OK;
   1969  }
   1970 
   1971  if (mFrameSelection->IsInTableSelectionMode()) {
   1972    const nsIContent* const commonAncestorContent =
   1973        nsIContent::FromNodeOrNull(aRange.GetClosestCommonInclusiveAncestor(
   1974            StaticPrefs::dom_select_events_textcontrols_selectstart_enabled()
   1975                ? AllowRangeCrossShadowBoundary::Yes
   1976                : AllowRangeCrossShadowBoundary::No));
   1977    nsIFrame* const frame = commonAncestorContent
   1978                                ? commonAncestorContent->GetPrimaryFrame()
   1979                                : aPresContext->PresShell()->GetRootFrame();
   1980    if (frame) {
   1981      if (frame->IsTextFrame()) {
   1982        MOZ_ASSERT(commonAncestorContent ==
   1983                   aRange.GetMayCrossShadowBoundaryStartContainer());
   1984        MOZ_ASSERT(commonAncestorContent ==
   1985                   aRange.GetMayCrossShadowBoundaryEndContainer());
   1986        static_cast<nsTextFrame*>(frame)->SelectionStateChanged(
   1987            aRange.MayCrossShadowBoundaryStartOffset(),
   1988            aRange.MayCrossShadowBoundaryEndOffset(), aSelect, mSelectionType);
   1989      } else {
   1990        frame->SelectionStateChanged();
   1991      }
   1992    }
   1993 
   1994    return NS_OK;
   1995  }
   1996 
   1997  // Loop through the content iterator for each content node; for each text
   1998  // node, call SetSelected on it:
   1999  nsIContent* const startContent = nsIContent::FromNodeOrNull(
   2000      aRange.GetMayCrossShadowBoundaryStartContainer());
   2001  if (MOZ_UNLIKELY(!startContent)) {
   2002    // Don't warn, bug 1055722
   2003    // XXX The range can start from a document node and such range can be
   2004    //     added to Selection with JS.  Therefore, even in such cases,
   2005    //     shouldn't we handle selection in the range?
   2006    return NS_ERROR_UNEXPECTED;
   2007  }
   2008  MOZ_DIAGNOSTIC_ASSERT(startContent->IsInComposedDoc());
   2009 
   2010  // We must call first one explicitly
   2011  nsINode* const endNode = aRange.GetMayCrossShadowBoundaryEndContainer();
   2012  if (NS_WARN_IF(!endNode)) {
   2013    // We null-checked start node above, therefore, end node should also be
   2014    // non-null here.
   2015    return NS_ERROR_UNEXPECTED;
   2016  }
   2017  const bool isFirstContentTextNode = startContent->IsText();
   2018  if (isFirstContentTextNode) {
   2019    if (nsIFrame* const frame = startContent->GetPrimaryFrame()) {
   2020      // The frame could be an SVG text frame, in which case we don't treat it
   2021      // as a text frame.
   2022      if (frame->IsTextFrame()) {
   2023        const uint32_t startOffset = aRange.MayCrossShadowBoundaryStartOffset();
   2024        const uint32_t endOffset =
   2025            endNode == startContent ? aRange.MayCrossShadowBoundaryEndOffset()
   2026                                    : startContent->Length();
   2027        static_cast<nsTextFrame*>(frame)->SelectionStateChanged(
   2028            startOffset, endOffset, aSelect, mSelectionType);
   2029      } else {
   2030        frame->SelectionStateChanged();
   2031      }
   2032    }
   2033  }
   2034 
   2035  // If the range is in a node and the node is a leaf node, we don't need to
   2036  // walk the subtree.
   2037  if ((aRange.Collapsed() && !aRange.MayCrossShadowBoundary()) ||
   2038      (startContent == endNode && !startContent->HasChildren())) {
   2039    if (!isFirstContentTextNode) {
   2040      SelectFramesOf(startContent, aSelect);
   2041    }
   2042    return NS_OK;
   2043  }
   2044 
   2045  ContentSubtreeIterator subtreeIter;
   2046  nsresult rv = subtreeIter.InitWithAllowCrossShadowBoundary(&aRange);
   2047  if (NS_FAILED(rv)) {
   2048    return rv;
   2049  }
   2050  if (isFirstContentTextNode && !subtreeIter.IsDone() &&
   2051      subtreeIter.GetCurrentNode() == startContent) {
   2052    subtreeIter.Next();  // first content has already been handled.
   2053  }
   2054  PostContentIterator postOrderIter;
   2055  for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
   2056    MOZ_DIAGNOSTIC_ASSERT(subtreeIter.GetCurrentNode());
   2057    if (nsIContent* const content =
   2058            nsIContent::FromNodeOrNull(subtreeIter.GetCurrentNode())) {
   2059      if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   2060        SelectFramesOfFlattenedTreeOfContent(content, aSelect);
   2061      } else {
   2062        SelectFramesOfInclusiveDescendantsOfContent(postOrderIter, content,
   2063                                                    aSelect);
   2064      }
   2065    }
   2066  }
   2067 
   2068  // We must now do the last one if it is not the same as the first
   2069  if (endNode == startContent || !endNode->IsText()) {
   2070    return NS_OK;
   2071  }
   2072 
   2073  if (nsIFrame* const frame = endNode->AsText()->GetPrimaryFrame()) {
   2074    // The frame could be an SVG text frame, in which case we'll ignore it.
   2075    if (frame->IsTextFrame()) {
   2076      static_cast<nsTextFrame*>(frame)->SelectionStateChanged(
   2077          0, aRange.MayCrossShadowBoundaryEndOffset(), aSelect, mSelectionType);
   2078    }
   2079  }
   2080  return NS_OK;
   2081 }
   2082 
   2083 void Selection::SelectFramesOfFlattenedTreeOfContent(nsIContent* aContent,
   2084                                                     bool aSelected) const {
   2085  MOZ_ASSERT(aContent);
   2086  MOZ_ASSERT(StaticPrefs::dom_shadowdom_selection_across_boundary_enabled());
   2087  TreeIterator<FlattenedChildIterator> iter(*aContent);
   2088  for (; iter.GetCurrent(); iter.GetNext()) {
   2089    SelectFramesOf(iter.GetCurrent(), aSelected);
   2090  }
   2091 }
   2092 
   2093 // Selection::LookUpSelection
   2094 //
   2095 //    This function is called when a node wants to know where the selection is
   2096 //    over itself.
   2097 //
   2098 //    Usually, this is called when we already know there is a selection over
   2099 //    the node in question, and we only need to find the boundaries of it on
   2100 //    that node. This is when slowCheck is false--a strict test is not needed.
   2101 //    Other times, the caller has no idea, and wants us to test everything,
   2102 //    so we are supposed to determine whether there is a selection over the
   2103 //    node at all.
   2104 //
   2105 //    A previous version of this code used this flag to do less work when
   2106 //    inclusion was already known (slowCheck=false). However, our tree
   2107 //    structure allows us to quickly determine ranges overlapping the node,
   2108 //    so we just ignore the slowCheck flag and do the full test every time.
   2109 //
   2110 //    PERFORMANCE: a common case is that we are doing a fast check with exactly
   2111 //    one range in the selection. In this case, this function is slower than
   2112 //    brute force because of the overhead of checking the tree. We can optimize
   2113 //    this case to make it faster by doing the same thing the previous version
   2114 //    of this function did in the case of 1 range. This would also mean that
   2115 //    the aSlowCheck flag would have meaning again.
   2116 
   2117 UniquePtr<SelectionDetails> Selection::LookUpSelection(
   2118    nsIContent* aContent, uint32_t aContentOffset, uint32_t aContentLength,
   2119    UniquePtr<SelectionDetails> aDetailsHead, SelectionType aSelectionType) {
   2120  if (!aContent) {
   2121    return aDetailsHead;
   2122  }
   2123 
   2124  // it is common to have no ranges, to optimize that
   2125  if (mStyledRanges.Length() == 0) {
   2126    return aDetailsHead;
   2127  }
   2128 
   2129  nsTArray<AbstractRange*> overlappingRanges;
   2130  SelectionNodeCache* cache =
   2131      GetPresShell() ? GetPresShell()->GetSelectionNodeCache() : nullptr;
   2132  if (cache && RangeCount() == 1) {
   2133    const bool isFullySelected =
   2134        cache->MaybeCollectNodesAndCheckIfFullySelected(aContent, this);
   2135    if (isFullySelected) {
   2136      auto newHead = MakeUnique<SelectionDetails>();
   2137 
   2138      newHead->mNext = std::move(aDetailsHead);
   2139      newHead->mStart = AssertedCast<int32_t>(0);
   2140      newHead->mEnd = AssertedCast<int32_t>(aContentLength);
   2141      newHead->mSelectionType = aSelectionType;
   2142      newHead->mHighlightData = mHighlightData;
   2143      StyledRange* rd = mStyledRanges.FindRangeData(GetAbstractRangeAt(0));
   2144      if (rd) {
   2145        newHead->mTextRangeStyle = rd->mTextRangeStyle;
   2146      }
   2147      auto detailsHead = std::move(newHead);
   2148 
   2149      return detailsHead;
   2150    }
   2151  }
   2152 
   2153  nsresult rv = GetAbstractRangesForIntervalArray(
   2154      aContent, aContentOffset, aContent, aContentOffset + aContentLength,
   2155      false, &overlappingRanges);
   2156  if (NS_FAILED(rv)) {
   2157    return aDetailsHead;
   2158  }
   2159 
   2160  if (overlappingRanges.Length() == 0) {
   2161    return aDetailsHead;
   2162  }
   2163 
   2164  UniquePtr<SelectionDetails> detailsHead = std::move(aDetailsHead);
   2165 
   2166  for (size_t i = 0; i < overlappingRanges.Length(); i++) {
   2167    AbstractRange* range = overlappingRanges[i];
   2168    if (range->IsStaticRange() && !range->AsStaticRange()->IsValid()) {
   2169      continue;
   2170    }
   2171 
   2172    nsINode* startNode = range->GetMayCrossShadowBoundaryStartContainer();
   2173    nsINode* endNode = range->GetMayCrossShadowBoundaryEndContainer();
   2174    uint32_t startOffset = range->MayCrossShadowBoundaryStartOffset();
   2175    uint32_t endOffset = range->MayCrossShadowBoundaryEndOffset();
   2176 
   2177    Maybe<uint32_t> start, end;
   2178    if (startNode == aContent && endNode == aContent) {
   2179      if (startOffset < (aContentOffset + aContentLength) &&
   2180          endOffset > aContentOffset) {
   2181        // this range is totally inside the requested content range
   2182        start.emplace(
   2183            startOffset >= aContentOffset ? startOffset - aContentOffset : 0u);
   2184        end.emplace(std::min(aContentLength, endOffset - aContentOffset));
   2185      }
   2186      // otherwise, range is inside the requested node, but does not intersect
   2187      // the requested content range, so ignore it
   2188    } else if (startNode == aContent) {
   2189      if (startOffset < (aContentOffset + aContentLength)) {
   2190        // the beginning of the range is inside the requested node, but the
   2191        // end is outside, select everything from there to the end
   2192        start.emplace(
   2193            startOffset >= aContentOffset ? startOffset - aContentOffset : 0u);
   2194        end.emplace(aContentLength);
   2195      }
   2196    } else if (endNode == aContent) {
   2197      if (endOffset > aContentOffset) {
   2198        // the end of the range is inside the requested node, but the beginning
   2199        // is outside, select everything from the beginning to there
   2200        start.emplace(0u);
   2201        end.emplace(std::min(aContentLength, endOffset - aContentOffset));
   2202      }
   2203    } else {
   2204      // this range does not begin or end in the requested node, but since
   2205      // GetRangesForInterval returned this range, we know it overlaps.
   2206      // Therefore, this node is enclosed in the range, and we select all
   2207      // of it.
   2208      start.emplace(0u);
   2209      end.emplace(aContentLength);
   2210    }
   2211    if (start.isNothing()) {
   2212      continue;  // the ranges do not overlap the input range
   2213    }
   2214 
   2215    auto newHead = MakeUnique<SelectionDetails>();
   2216 
   2217    newHead->mNext = std::move(detailsHead);
   2218    newHead->mStart = AssertedCast<int32_t>(*start);
   2219    newHead->mEnd = AssertedCast<int32_t>(*end);
   2220    newHead->mSelectionType = aSelectionType;
   2221    newHead->mHighlightData = mHighlightData;
   2222    StyledRange* rd = mStyledRanges.FindRangeData(range);
   2223    if (rd) {
   2224      newHead->mTextRangeStyle = rd->mTextRangeStyle;
   2225    }
   2226    detailsHead = std::move(newHead);
   2227  }
   2228  return detailsHead;
   2229 }
   2230 
   2231 NS_IMETHODIMP
   2232 Selection::Repaint(nsPresContext* aPresContext) {
   2233  int32_t arrCount = (int32_t)mStyledRanges.Length();
   2234 
   2235  if (arrCount < 1) return NS_OK;
   2236 
   2237  int32_t i;
   2238 
   2239  for (i = 0; i < arrCount; i++) {
   2240    MOZ_ASSERT(mStyledRanges.mRanges[i].mRange);
   2241    nsresult rv =
   2242        SelectFrames(aPresContext, *mStyledRanges.mRanges[i].mRange, true);
   2243 
   2244    if (NS_FAILED(rv)) {
   2245      return rv;
   2246    }
   2247  }
   2248 
   2249  return NS_OK;
   2250 }
   2251 
   2252 void Selection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset) {
   2253  if (!mCachedOffsetForFrame) {
   2254    mCachedOffsetForFrame = new CachedOffsetForFrame;
   2255  }
   2256 
   2257  mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
   2258 
   2259  // clean up cached frame when turn off cache
   2260  // fix bug 207936
   2261  if (!aCanCacheFrameOffset) {
   2262    mCachedOffsetForFrame->mLastCaretFrame = nullptr;
   2263  }
   2264 }
   2265 
   2266 nsresult Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
   2267                                         nsPoint& aPoint) {
   2268  if (!mCachedOffsetForFrame) {
   2269    mCachedOffsetForFrame = new CachedOffsetForFrame;
   2270  }
   2271 
   2272  nsresult rv = NS_OK;
   2273  if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
   2274      mCachedOffsetForFrame->mLastCaretFrame &&
   2275      (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
   2276      (inOffset == mCachedOffsetForFrame->mLastContentOffset)) {
   2277    // get cached frame offset
   2278    aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
   2279  } else {
   2280    // Recalculate frame offset and cache it. Don't cache a frame offset if
   2281    // GetPointFromOffset fails, though.
   2282    rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
   2283    if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
   2284      mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
   2285      mCachedOffsetForFrame->mLastCaretFrame = aFrame;
   2286      mCachedOffsetForFrame->mLastContentOffset = inOffset;
   2287    }
   2288  }
   2289 
   2290  return rv;
   2291 }
   2292 
   2293 Element* Selection::GetAncestorLimiter() const {
   2294  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
   2295 
   2296  if (mFrameSelection) {
   2297    return mFrameSelection->GetAncestorLimiter();
   2298  }
   2299  return nullptr;
   2300 }
   2301 
   2302 void Selection::SetAncestorLimiter(Element* aLimiter) {
   2303  if (NeedsToLogSelectionAPI(*this)) {
   2304    LogSelectionAPI(this, __FUNCTION__, "aLimiter", aLimiter);
   2305    LogStackForSelectionAPI();
   2306  }
   2307 
   2308  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
   2309 
   2310  if (mFrameSelection) {
   2311    RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   2312    frameSelection->SetAncestorLimiter(aLimiter);
   2313  }
   2314 }
   2315 
   2316 void Selection::StyledRanges::UnregisterSelection(IsUnlinking aIsUnlinking) {
   2317  uint32_t count = mRanges.Length();
   2318  for (uint32_t i = 0; i < count; ++i) {
   2319    mRanges[i].mRange->UnregisterSelection(mSelection, aIsUnlinking);
   2320  }
   2321 }
   2322 
   2323 void Selection::StyledRanges::Clear() {
   2324 #ifdef ACCESSIBILITY
   2325  for (auto& range : mRanges) {
   2326    if (!a11y::SelectionManager::SelectionRangeChanged(mSelection.GetType(),
   2327                                                       *range.mRange)) {
   2328      break;
   2329    }
   2330  }
   2331 #endif
   2332  mRanges.Clear();
   2333  mInvalidStaticRanges.Clear();
   2334 }
   2335 
   2336 StyledRange* Selection::StyledRanges::FindRangeData(AbstractRange* aRange) {
   2337  NS_ENSURE_TRUE(aRange, nullptr);
   2338  for (uint32_t i = 0; i < mRanges.Length(); i++) {
   2339    if (mRanges[i].mRange == aRange) {
   2340      return &mRanges[i];
   2341    }
   2342  }
   2343  return nullptr;
   2344 }
   2345 
   2346 Selection::StyledRanges::StyledRangeArray::size_type
   2347 Selection::StyledRanges::Length() const {
   2348  return mRanges.Length();
   2349 }
   2350 
   2351 nsresult Selection::SetTextRangeStyle(nsRange* aRange,
   2352                                      const TextRangeStyle& aTextRangeStyle) {
   2353  NS_ENSURE_ARG_POINTER(aRange);
   2354  StyledRange* rd = mStyledRanges.FindRangeData(aRange);
   2355  if (rd) {
   2356    rd->mTextRangeStyle = aTextRangeStyle;
   2357  }
   2358  return NS_OK;
   2359 }
   2360 
   2361 nsresult Selection::StartAutoScrollTimer(nsIFrame* aFrame,
   2362                                         const nsPoint& aPoint,
   2363                                         uint32_t aDelayInMs) {
   2364  MOZ_ASSERT(aFrame, "Need a frame");
   2365  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
   2366 
   2367  if (!mFrameSelection) {
   2368    return NS_OK;  // nothing to do
   2369  }
   2370 
   2371  if (!mAutoScroller) {
   2372    mAutoScroller = new AutoScroller(mFrameSelection);
   2373  }
   2374 
   2375  mAutoScroller->SetDelay(aDelayInMs);
   2376 
   2377  RefPtr<AutoScroller> autoScroller{mAutoScroller};
   2378  return autoScroller->DoAutoScroll(aFrame, aPoint);
   2379 }
   2380 
   2381 nsresult Selection::StopAutoScrollTimer() {
   2382  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
   2383 
   2384  if (mAutoScroller) {
   2385    mAutoScroller->Stop(AutoScroller::FurtherScrollingAllowed::kYes);
   2386  }
   2387 
   2388  return NS_OK;
   2389 }
   2390 
   2391 nsresult AutoScroller::DoAutoScroll(nsIFrame* aFrame, nsPoint aPoint) {
   2392  MOZ_ASSERT(aFrame, "Need a frame");
   2393 
   2394  Stop(FurtherScrollingAllowed::kYes);
   2395 
   2396  nsPresContext* presContext = aFrame->PresContext();
   2397  RefPtr<PresShell> presShell = presContext->PresShell();
   2398  nsRootPresContext* rootPC = presContext->GetRootPresContext();
   2399  if (!rootPC) {
   2400    return NS_OK;
   2401  }
   2402  nsIFrame* rootmostFrame = rootPC->PresShell()->GetRootFrame();
   2403  AutoWeakFrame weakRootFrame(rootmostFrame);
   2404  AutoWeakFrame weakFrame(aFrame);
   2405  // Get the point relative to the root most frame because the scroll we are
   2406  // about to do will change the coordinates of aFrame.
   2407  nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
   2408 
   2409  bool done = false;
   2410  bool didScroll;
   2411  while (true) {
   2412    didScroll = presShell->ScrollFrameIntoView(
   2413        aFrame, Some(nsRect(aPoint, nsSize())), ScrollAxis(), ScrollAxis(),
   2414        ScrollFlags::None);
   2415    if (!weakFrame || !weakRootFrame) {
   2416      return NS_OK;
   2417    }
   2418    if (!didScroll && !done) {
   2419      // If aPoint is at the very edge of the root, then try to scroll anyway,
   2420      // once.
   2421      nsRect rootRect = rootmostFrame->GetRect();
   2422      nscoord onePx = AppUnitsPerCSSPixel();
   2423      nscoord scrollAmount = 10 * onePx;
   2424      if (std::abs(rootRect.x - globalPoint.x) <= onePx) {
   2425        aPoint.x -= scrollAmount;
   2426      } else if (std::abs(rootRect.XMost() - globalPoint.x) <= onePx) {
   2427        aPoint.x += scrollAmount;
   2428      } else if (std::abs(rootRect.y - globalPoint.y) <= onePx) {
   2429        aPoint.y -= scrollAmount;
   2430      } else if (std::abs(rootRect.YMost() - globalPoint.y) <= onePx) {
   2431        aPoint.y += scrollAmount;
   2432      } else {
   2433        break;
   2434      }
   2435      done = true;
   2436      continue;
   2437    }
   2438    break;
   2439  }
   2440 
   2441  // Start the AutoScroll timer if necessary.
   2442  // `ScrollFrameRectIntoView` above may have run script and this may have
   2443  // forbidden to continue scrolling.
   2444  if (didScroll && mFurtherScrollingAllowed == FurtherScrollingAllowed::kYes) {
   2445    nsPoint presContextPoint =
   2446        globalPoint -
   2447        presShell->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
   2448    ScheduleNextDoAutoScroll(presContext, presContextPoint);
   2449  }
   2450 
   2451  return NS_OK;
   2452 }
   2453 
   2454 void Selection::RemoveAllRanges(ErrorResult& aRv) {
   2455  if (NeedsToLogSelectionAPI(*this)) {
   2456    LogSelectionAPI(this, __FUNCTION__);
   2457    LogStackForSelectionAPI();
   2458  }
   2459 
   2460  RemoveAllRangesInternal(aRv);
   2461 }
   2462 
   2463 already_AddRefed<StaticRange> Selection::GetComposedRange(
   2464    const AbstractRange* aRange,
   2465    const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) const {
   2466  // If aIsEndNode is true, this method does the Step 5.1 and 5.2
   2467  // in https://www.w3.org/TR/selection-api/#dom-selection-getcomposedranges,
   2468  // otherwise it does the Step 3.1 and 3.2.
   2469  auto reScope = [&aShadowRoots](nsINode*& aNode, uint32_t& aOffset,
   2470                                 bool aIsEndNode) {
   2471    MOZ_ASSERT(aNode);
   2472    while (aNode) {
   2473      const ShadowRoot* shadowRootOfNode = aNode->GetContainingShadow();
   2474      if (!shadowRootOfNode) {
   2475        return;
   2476      }
   2477 
   2478      for (const OwningNonNull<ShadowRoot>& shadowRoot : aShadowRoots) {
   2479        if (shadowRoot->IsShadowIncludingInclusiveDescendantOf(
   2480                shadowRootOfNode)) {
   2481          return;
   2482        }
   2483      }
   2484 
   2485      const nsIContent* host = aNode->GetContainingShadowHost();
   2486      const Maybe<uint32_t> maybeIndex = host->ComputeIndexInParentContent();
   2487      MOZ_ASSERT(maybeIndex.isSome(), "not parent or anonymous child?");
   2488      if (MOZ_UNLIKELY(maybeIndex.isNothing())) {
   2489        // Unlikely to happen, but still set aNode to nullptr to avoid
   2490        // leaking information about the shadow tree.
   2491        aNode = nullptr;
   2492        return;
   2493      }
   2494      aOffset = maybeIndex.value();
   2495      if (aIsEndNode) {
   2496        aOffset += 1;
   2497      }
   2498      aNode = host->GetParentNode();
   2499    }
   2500  };
   2501 
   2502  nsINode* startNode = aRange->GetMayCrossShadowBoundaryStartContainer();
   2503  uint32_t startOffset = aRange->MayCrossShadowBoundaryStartOffset();
   2504  nsINode* endNode = aRange->GetMayCrossShadowBoundaryEndContainer();
   2505  uint32_t endOffset = aRange->MayCrossShadowBoundaryEndOffset();
   2506 
   2507  reScope(startNode, startOffset, false /* aIsEndNode */);
   2508  reScope(endNode, endOffset, true /* aIsEndNode */);
   2509 
   2510  RefPtr<StaticRange> composedRange = StaticRange::Create(
   2511      startNode, startOffset, endNode, endOffset, IgnoreErrors());
   2512  return composedRange.forget();
   2513 }
   2514 
   2515 void Selection::GetComposedRanges(
   2516    const ShadowRootOrGetComposedRangesOptions&
   2517        aShadowRootOrGetComposedRangesOptions,
   2518    const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots,
   2519    nsTArray<RefPtr<StaticRange>>& aComposedRanges) {
   2520  aComposedRanges.SetCapacity(mStyledRanges.mRanges.Length());
   2521 
   2522  auto GetComposedRangesForAllRanges =
   2523      [this, &aComposedRanges](
   2524          const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) {
   2525        for (const auto& range : this->mStyledRanges.mRanges) {
   2526          aComposedRanges.AppendElement(
   2527              GetComposedRange(range.mRange, aShadowRoots));
   2528        }
   2529      };
   2530 
   2531  if (aShadowRootOrGetComposedRangesOptions.IsGetComposedRangesOptions()) {
   2532    // { shadowRoots: ... }
   2533    auto& options =
   2534        aShadowRootOrGetComposedRangesOptions.GetAsGetComposedRangesOptions();
   2535    return GetComposedRangesForAllRanges(options.mShadowRoots);
   2536  }
   2537 
   2538  Sequence<OwningNonNull<ShadowRoot>> shadowRoots(aShadowRoots);
   2539 
   2540  if (aShadowRootOrGetComposedRangesOptions.IsShadowRoot()) {
   2541    // Single shadow root provide
   2542    //
   2543    // The order in shadowRoots doesn't matter, as we just loop
   2544    // through them eventually.
   2545    if (!shadowRoots.AppendElement(
   2546            aShadowRootOrGetComposedRangesOptions.GetAsShadowRoot(),
   2547            fallible)) {
   2548      // OOM
   2549      return;
   2550    }
   2551  }
   2552 
   2553  // single + variadic
   2554  return GetComposedRangesForAllRanges(shadowRoots);
   2555 }
   2556 
   2557 void Selection::RemoveAllRangesInternal(ErrorResult& aRv,
   2558                                        IsUnlinking aIsUnlinking) {
   2559  if (!mFrameSelection) {
   2560    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   2561    return;
   2562  }
   2563 
   2564  RefPtr<nsPresContext> presContext = GetPresContext();
   2565  Clear(presContext, aIsUnlinking);
   2566 
   2567  // Turn off signal for table selection
   2568  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   2569  frameSelection->ClearTableCellSelection();
   2570 
   2571  RefPtr<Selection> kungFuDeathGrip{this};
   2572  // Be aware, this instance may be destroyed after this call.
   2573  NotifySelectionListeners();
   2574 }
   2575 
   2576 void Selection::AddRangeJS(nsRange& aRange, ErrorResult& aRv) {
   2577  if (NeedsToLogSelectionAPI(*this)) {
   2578    LogSelectionAPI(this, __FUNCTION__, "aRange", aRange);
   2579    LogStackForSelectionAPI();
   2580  }
   2581 
   2582  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   2583  mCalledByJS = true;
   2584  RefPtr<Document> document(GetDocument());
   2585  AddRangeAndSelectFramesAndNotifyListenersInternal(aRange, document, aRv);
   2586  if (StaticPrefs::dom_selection_mimic_chrome_tostring_enabled() &&
   2587      !aRv.Failed()) {
   2588    if (auto* presShell = GetPresShell()) {
   2589      presShell->UpdateLastSelectionForToString(mFrameSelection);
   2590    }
   2591  }
   2592 }
   2593 
   2594 void Selection::AddRangeAndSelectFramesAndNotifyListeners(nsRange& aRange,
   2595                                                          ErrorResult& aRv) {
   2596  if (NeedsToLogSelectionAPI(*this)) {
   2597    LogSelectionAPI(this, __FUNCTION__, "aRange", aRange);
   2598    LogStackForSelectionAPI();
   2599  }
   2600 
   2601  RefPtr<Document> document(GetDocument());
   2602  return AddRangeAndSelectFramesAndNotifyListenersInternal(aRange, document,
   2603                                                           aRv);
   2604 }
   2605 
   2606 void Selection::AddRangeAndSelectFramesAndNotifyListenersInternal(
   2607    nsRange& aRange, Document* aDocument, ErrorResult& aRv) {
   2608  RefPtr<nsRange> range = &aRange;
   2609  if (aRange.IsInAnySelection()) {
   2610    if (aRange.IsInSelection(*this)) {
   2611      // If we already have the range, we don't need to handle this except
   2612      // setting the interline position.
   2613      if (mSelectionType == SelectionType::eNormal) {
   2614        SetInterlinePosition(InterlinePosition::StartOfNextLine);
   2615      }
   2616      return;
   2617    }
   2618    if (mSelectionType != SelectionType::eNormal &&
   2619        mSelectionType != SelectionType::eHighlight) {
   2620      range = aRange.CloneRange();
   2621    }
   2622  }
   2623 
   2624  nsINode* rangeRoot = range->GetRoot();
   2625  if (aDocument != rangeRoot &&
   2626      (!rangeRoot || aDocument != rangeRoot->GetComposedDoc())) {
   2627    // http://w3c.github.io/selection-api/#dom-selection-addrange
   2628    // "...  if the root of the range's boundary points are the document
   2629    // associated with context object. Otherwise, this method must do nothing."
   2630    return;
   2631  }
   2632 
   2633  // MaybeAddTableCellRange might flush frame and `NotifySelectionListeners`
   2634  // below might destruct `this`.
   2635  RefPtr<Selection> kungFuDeathGrip(this);
   2636 
   2637  // This inserts a table cell range in proper document order
   2638  // and returns NS_OK if range doesn't contain just one table cell
   2639  Maybe<size_t> maybeRangeIndex;
   2640  nsresult result = MaybeAddTableCellRange(*range, &maybeRangeIndex);
   2641  if (NS_FAILED(result)) {
   2642    aRv.Throw(result);
   2643    return;
   2644  }
   2645 
   2646  if (maybeRangeIndex.isNothing()) {
   2647    result = AddRangesForSelectableNodes(range, &maybeRangeIndex,
   2648                                         DispatchSelectstartEvent::Maybe);
   2649    if (NS_FAILED(result)) {
   2650      aRv.Throw(result);
   2651      return;
   2652    }
   2653    if (maybeRangeIndex.isNothing()) {
   2654      return;
   2655    }
   2656  }
   2657 
   2658  MOZ_ASSERT(*maybeRangeIndex < mStyledRanges.Length());
   2659 
   2660  SetAnchorFocusRange(*maybeRangeIndex);
   2661 
   2662  // Make sure the caret appears on the next line, if at a newline
   2663  if (mSelectionType == SelectionType::eNormal) {
   2664    SetInterlinePosition(InterlinePosition::StartOfNextLine);
   2665  }
   2666 
   2667  if (!mFrameSelection) {
   2668    return;  // nothing to do
   2669  }
   2670 
   2671  RefPtr<nsPresContext> presContext = GetPresContext();
   2672  SelectFrames(presContext, *range, true);
   2673 
   2674  // Be aware, this instance may be destroyed after this call.
   2675  NotifySelectionListeners();
   2676  // Range order is guaranteed after adding a range.
   2677  // Therefore, this flag can be reset to avoid
   2678  // another unnecessary and costly reordering.
   2679  mStyledRanges.mRangesMightHaveChanged = false;
   2680 }
   2681 
   2682 void Selection::AddHighlightRangeAndSelectFramesAndNotifyListeners(
   2683    AbstractRange& aRange) {
   2684  MOZ_ASSERT(mSelectionType == SelectionType::eHighlight);
   2685  nsresult rv = mStyledRanges.AddRangeAndIgnoreOverlaps(&aRange);
   2686  if (NS_FAILED(rv)) {
   2687    return;
   2688  }
   2689 
   2690  if (!mFrameSelection) {
   2691    return;  // nothing to do
   2692  }
   2693 
   2694  RefPtr<nsPresContext> presContext = GetPresContext();
   2695  SelectFrames(presContext, aRange, true);
   2696 
   2697  // Be aware, this instance may be destroyed after this call.
   2698  RefPtr<Selection> kungFuDeathGrip(this);
   2699  NotifySelectionListeners();
   2700  // Range order is guaranteed after adding a range.
   2701  // Therefore, this flag can be reset to avoid
   2702  // another unnecessary and costly reordering.
   2703  mStyledRanges.mRangesMightHaveChanged = false;
   2704 }
   2705 
   2706 // Selection::RemoveRangeAndUnselectFramesAndNotifyListeners
   2707 //
   2708 //    Removes the given range from the selection. The tricky part is updating
   2709 //    the flags on the frames that indicate whether they have a selection or
   2710 //    not. There could be several selection ranges on the frame, and clearing
   2711 //    the bit would cause the selection to not be drawn, even when there is
   2712 //    another range on the frame (bug 346185).
   2713 //
   2714 //    We therefore find any ranges that intersect the same nodes as the range
   2715 //    being removed, and cause them to set the selected bits back on their
   2716 //    selected frames after we've cleared the bit from ours.
   2717 
   2718 void Selection::RemoveRangeAndUnselectFramesAndNotifyListeners(
   2719    AbstractRange& aRange, ErrorResult& aRv) {
   2720  if (NeedsToLogSelectionAPI(*this)) {
   2721    LogSelectionAPI(this, __FUNCTION__, "aRange", aRange);
   2722    LogStackForSelectionAPI();
   2723  }
   2724 
   2725  nsresult rv = mStyledRanges.RemoveRangeAndUnregisterSelection(aRange);
   2726  if (NS_FAILED(rv)) {
   2727    aRv.Throw(rv);
   2728    return;
   2729  }
   2730 
   2731  nsINode* beginNode = aRange.GetStartContainer();
   2732  nsINode* endNode = aRange.GetEndContainer();
   2733 
   2734  if (!beginNode || !endNode) {
   2735    // Detached range; nothing else to do here.
   2736    return;
   2737  }
   2738 
   2739  // find out the length of the end node, so we can select all of it
   2740  uint32_t beginOffset, endOffset;
   2741  if (endNode->IsText()) {
   2742    // Get the length of the text. We can't just use the offset because
   2743    // another range could be touching this text node but not intersect our
   2744    // range.
   2745    beginOffset = 0;
   2746    endOffset = endNode->AsText()->TextLength();
   2747  } else {
   2748    // For non-text nodes, the given offsets should be sufficient.
   2749    beginOffset = aRange.StartOffset();
   2750    endOffset = aRange.EndOffset();
   2751  }
   2752 
   2753  // clear the selected bit from the removed range's frames
   2754  RefPtr<nsPresContext> presContext = GetPresContext();
   2755  SelectFrames(presContext, aRange, false);
   2756 
   2757  // add back the selected bit for each range touching our nodes
   2758  nsTArray<AbstractRange*> affectedRanges;
   2759  rv = GetAbstractRangesForIntervalArray(beginNode, beginOffset, endNode,
   2760                                         endOffset, true, &affectedRanges);
   2761  if (NS_FAILED(rv)) {
   2762    aRv.Throw(rv);
   2763    return;
   2764  }
   2765  for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
   2766    MOZ_ASSERT(affectedRanges[i]);
   2767    SelectFrames(presContext, *affectedRanges[i], true);
   2768  }
   2769 
   2770  if (&aRange == mAnchorFocusRange) {
   2771    const size_t rangeCount = mStyledRanges.Length();
   2772    if (rangeCount) {
   2773      SetAnchorFocusRange(rangeCount - 1);
   2774    } else {
   2775      RemoveAnchorFocusRange();
   2776    }
   2777 
   2778    // When the selection is user-created it makes sense to scroll the range
   2779    // into view. The spell-check selection, however, is created and destroyed
   2780    // in the background. We don't want to scroll in this case or the view
   2781    // might appear to be moving randomly (bug 337871).
   2782    if (mSelectionType != SelectionType::eSpellCheck && rangeCount) {
   2783      ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
   2784    }
   2785  }
   2786 
   2787  if (!mFrameSelection) return;  // nothing to do
   2788 
   2789  RefPtr<Selection> kungFuDeathGrip{this};
   2790  // Be aware, this instance may be destroyed after this call.
   2791  NotifySelectionListeners();
   2792 }
   2793 
   2794 /*
   2795 * Collapse sets the whole selection to be one point.
   2796 */
   2797 void Selection::CollapseJS(nsINode* aContainer, uint32_t aOffset,
   2798                           ErrorResult& aRv) {
   2799  if (NeedsToLogSelectionAPI(*this)) {
   2800    LogSelectionAPI(this, __FUNCTION__, "aContainer", aContainer, "aOffset",
   2801                    aOffset);
   2802    LogStackForSelectionAPI();
   2803  }
   2804 
   2805  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   2806  mCalledByJS = true;
   2807  if (!aContainer) {
   2808    RemoveAllRangesInternal(aRv);
   2809    return;
   2810  }
   2811  CollapseInternal(InLimiter::eNo, RawRangeBoundary(aContainer, aOffset), aRv);
   2812 }
   2813 
   2814 void Selection::CollapseInLimiter(const RawRangeBoundary& aPoint,
   2815                                  ErrorResult& aRv) {
   2816  if (NeedsToLogSelectionAPI(*this)) {
   2817    LogSelectionAPI(this, __FUNCTION__, "aPoint", aPoint);
   2818    LogStackForSelectionAPI();
   2819  }
   2820 
   2821  CollapseInternal(InLimiter::eYes, aPoint, aRv);
   2822 }
   2823 
   2824 void Selection::CollapseInternal(InLimiter aInLimiter,
   2825                                 const RawRangeBoundary& aPoint,
   2826                                 ErrorResult& aRv) {
   2827  if (!mFrameSelection) {
   2828    aRv.Throw(NS_ERROR_NOT_INITIALIZED);  // Can't do selection
   2829    return;
   2830  }
   2831 
   2832  if (!aPoint.IsSet()) {
   2833    aRv.Throw(NS_ERROR_INVALID_ARG);
   2834    return;
   2835  }
   2836 
   2837  if (aPoint.GetContainer()->NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
   2838    aRv.ThrowInvalidNodeTypeError(kNoDocumentTypeNodeError);
   2839    return;
   2840  }
   2841 
   2842  // RawRangeBoundary::IsSetAndValid() checks if the point actually refers
   2843  // a child of the container when IsSet() is true.  If its offset hasn't been
   2844  // computed yet, this just checks it with its mRef.  So, we can avoid
   2845  // computing offset here.
   2846  if (!aPoint.IsSetAndValid()) {
   2847    aRv.ThrowIndexSizeError("The offset is out of range.");
   2848    return;
   2849  }
   2850 
   2851  if (!HasSameRootOrSameComposedDoc(*aPoint.GetContainer())) {
   2852    // Return with no error
   2853    return;
   2854  }
   2855 
   2856  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   2857  frameSelection->InvalidateDesiredCaretPos();
   2858  if (aInLimiter == InLimiter::eYes &&
   2859      !frameSelection->NodeIsInLimiters(aPoint.GetContainer())) {
   2860    aRv.Throw(NS_ERROR_FAILURE);
   2861    return;
   2862  }
   2863  nsresult result;
   2864 
   2865  RefPtr<nsPresContext> presContext = GetPresContext();
   2866  if (!presContext ||
   2867      presContext->Document() != aPoint.GetContainer()->OwnerDoc()) {
   2868    aRv.Throw(NS_ERROR_FAILURE);
   2869    return;
   2870  }
   2871 
   2872  // Delete all of the current ranges
   2873  Clear(presContext);
   2874 
   2875  // Turn off signal for table selection
   2876  frameSelection->ClearTableCellSelection();
   2877 
   2878  // Hack to display the caret on the right line (bug 1237236).
   2879  frameSelection->SetHint(ComputeCaretAssociationHint(
   2880      frameSelection->GetHint(), frameSelection->GetCaretBidiLevel(), aPoint));
   2881 
   2882  RefPtr<nsRange> range = nsRange::Create(aPoint.GetContainer());
   2883  result = range->CollapseTo(aPoint);
   2884  if (NS_FAILED(result)) {
   2885    aRv.Throw(result);
   2886    return;
   2887  }
   2888 
   2889 #ifdef DEBUG_SELECTION
   2890  nsCOMPtr<nsIContent> content = do_QueryInterface(aPoint.GetContainer());
   2891  nsCOMPtr<Document> doc = do_QueryInterface(aPoint.GetContainer());
   2892  printf("Sel. Collapse to %p %s %d\n", container.get(),
   2893         content ? nsAtomCString(content->NodeInfo()->NameAtom()).get()
   2894                 : (doc ? "DOCUMENT" : "???"),
   2895         aPoint.Offset());
   2896 #endif
   2897 
   2898  Maybe<size_t> maybeRangeIndex;
   2899  result = AddRangesForSelectableNodes(range, &maybeRangeIndex,
   2900                                       DispatchSelectstartEvent::Maybe);
   2901  if (NS_FAILED(result)) {
   2902    aRv.Throw(result);
   2903    return;
   2904  }
   2905  SetAnchorFocusRange(0);
   2906  SelectFrames(presContext, *range, true);
   2907 
   2908  RefPtr<Selection> kungFuDeathGrip{this};
   2909  // Be aware, this instance may be destroyed after this call.
   2910  NotifySelectionListeners();
   2911 }
   2912 
   2913 /*
   2914 * Sets the whole selection to be one point
   2915 * at the start of the current selection
   2916 */
   2917 void Selection::CollapseToStartJS(ErrorResult& aRv) {
   2918  if (NeedsToLogSelectionAPI(*this)) {
   2919    LogSelectionAPI(this, __FUNCTION__);
   2920    LogStackForSelectionAPI();
   2921  }
   2922 
   2923  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   2924  mCalledByJS = true;
   2925  CollapseToStart(aRv);
   2926 }
   2927 
   2928 void Selection::CollapseToStart(ErrorResult& aRv) {
   2929  if (!mCalledByJS && NeedsToLogSelectionAPI(*this)) {
   2930    LogSelectionAPI(this, __FUNCTION__);
   2931    LogStackForSelectionAPI();
   2932  }
   2933 
   2934  if (RangeCount() == 0) {
   2935    aRv.ThrowInvalidStateError(kNoRangeExistsError);
   2936    return;
   2937  }
   2938 
   2939  // Get the first range
   2940  const AbstractRange* firstRange = mStyledRanges.mRanges[0].mRange;
   2941  if (!firstRange) {
   2942    aRv.Throw(NS_ERROR_FAILURE);
   2943    return;
   2944  }
   2945 
   2946  if (mFrameSelection) {
   2947    mFrameSelection->AddChangeReasons(
   2948        nsISelectionListener::COLLAPSETOSTART_REASON);
   2949  }
   2950  nsINode* container = firstRange->GetStartContainer();
   2951  if (!container) {
   2952    aRv.Throw(NS_ERROR_FAILURE);
   2953    return;
   2954  }
   2955  CollapseInternal(InLimiter::eNo,
   2956                   RawRangeBoundary(container, firstRange->StartOffset()), aRv);
   2957 }
   2958 
   2959 /*
   2960 * Sets the whole selection to be one point
   2961 * at the end of the current selection
   2962 */
   2963 void Selection::CollapseToEndJS(ErrorResult& aRv) {
   2964  if (NeedsToLogSelectionAPI(*this)) {
   2965    LogSelectionAPI(this, __FUNCTION__);
   2966    LogStackForSelectionAPI();
   2967  }
   2968 
   2969  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   2970  mCalledByJS = true;
   2971  CollapseToEnd(aRv);
   2972 }
   2973 
   2974 void Selection::CollapseToEnd(ErrorResult& aRv) {
   2975  if (!mCalledByJS && NeedsToLogSelectionAPI(*this)) {
   2976    LogSelectionAPI(this, __FUNCTION__);
   2977    LogStackForSelectionAPI();
   2978  }
   2979 
   2980  uint32_t cnt = RangeCount();
   2981  if (cnt == 0) {
   2982    aRv.ThrowInvalidStateError(kNoRangeExistsError);
   2983    return;
   2984  }
   2985 
   2986  // Get the last range
   2987  const AbstractRange* lastRange = mStyledRanges.mRanges[cnt - 1].mRange;
   2988  if (!lastRange) {
   2989    aRv.Throw(NS_ERROR_FAILURE);
   2990    return;
   2991  }
   2992 
   2993  if (mFrameSelection) {
   2994    mFrameSelection->AddChangeReasons(
   2995        nsISelectionListener::COLLAPSETOEND_REASON);
   2996  }
   2997  nsINode* container = lastRange->GetEndContainer();
   2998  if (!container) {
   2999    aRv.Throw(NS_ERROR_FAILURE);
   3000    return;
   3001  }
   3002  CollapseInternal(InLimiter::eNo,
   3003                   RawRangeBoundary(container, lastRange->EndOffset()), aRv);
   3004 }
   3005 
   3006 void Selection::GetType(nsAString& aOutType) const {
   3007  if (!RangeCount()) {
   3008    aOutType.AssignLiteral("None");
   3009  } else if (IsCollapsed()) {
   3010    aOutType.AssignLiteral("Caret");
   3011  } else {
   3012    aOutType.AssignLiteral("Range");
   3013  }
   3014 }
   3015 
   3016 nsRange* Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv) {
   3017  nsRange* range = GetRangeAt(aIndex);
   3018  if (!range) {
   3019    aRv.ThrowIndexSizeError(nsPrintfCString("%u is out of range", aIndex));
   3020    return nullptr;
   3021  }
   3022 
   3023  return range;
   3024 }
   3025 
   3026 AbstractRange* Selection::GetAbstractRangeAt(uint32_t aIndex) const {
   3027  StyledRange empty(nullptr);
   3028  return mStyledRanges.mRanges.SafeElementAt(aIndex, empty).mRange;
   3029 }
   3030 
   3031 // https://www.w3.org/TR/selection-api/#dom-selection-direction
   3032 void Selection::GetDirection(nsAString& aDirection) const {
   3033  if (mStyledRanges.mRanges.IsEmpty() ||
   3034      (mFrameSelection && (mFrameSelection->IsDoubleClickSelection() ||
   3035                           mFrameSelection->IsTripleClickSelection()))) {
   3036    // Empty range and double/triple clicks result a directionless selection.
   3037    aDirection.AssignLiteral("none");
   3038  } else if (mDirection == nsDirection::eDirNext) {
   3039    // This is the default direction. It could be that the direction
   3040    // is really "forward", or the direction is "none" if the selection
   3041    // is collapsed.
   3042    if (AreNormalAndCrossShadowBoundaryRangesCollapsed()) {
   3043      aDirection.AssignLiteral("none");
   3044      return;
   3045    }
   3046    aDirection.AssignLiteral("forward");
   3047  } else {
   3048    MOZ_ASSERT(!AreNormalAndCrossShadowBoundaryRangesCollapsed());
   3049    aDirection.AssignLiteral("backward");
   3050  }
   3051 }
   3052 
   3053 nsRange* Selection::GetRangeAt(uint32_t aIndex) const {
   3054  // This method per IDL spec returns a dynamic range.
   3055  // Therefore, it must be ensured that it is only called
   3056  // for a selection which contains dynamic ranges exclusively.
   3057  // Highlight Selections are allowed to contain StaticRanges,
   3058  // therefore this method must not be called.
   3059  MOZ_ASSERT(mSelectionType != SelectionType::eHighlight);
   3060  AbstractRange* abstractRange = GetAbstractRangeAt(aIndex);
   3061  if (!abstractRange) {
   3062    return nullptr;
   3063  }
   3064  return abstractRange->AsDynamicRange();
   3065 }
   3066 
   3067 nsresult Selection::SetAnchorFocusToRange(nsRange* aRange) {
   3068  NS_ENSURE_STATE(mAnchorFocusRange);
   3069 
   3070  const DispatchSelectstartEvent dispatchSelectstartEvent =
   3071      IsCollapsed() ? DispatchSelectstartEvent::Maybe
   3072                    : DispatchSelectstartEvent::No;
   3073 
   3074  nsresult rv =
   3075      mStyledRanges.RemoveRangeAndUnregisterSelection(*mAnchorFocusRange);
   3076  if (NS_FAILED(rv)) {
   3077    return rv;
   3078  }
   3079 
   3080  Maybe<size_t> maybeOutIndex;
   3081  rv = AddRangesForSelectableNodes(aRange, &maybeOutIndex,
   3082                                   dispatchSelectstartEvent);
   3083  if (NS_FAILED(rv)) {
   3084    return rv;
   3085  }
   3086  if (maybeOutIndex.isSome()) {
   3087    SetAnchorFocusRange(*maybeOutIndex);
   3088  } else {
   3089    RemoveAnchorFocusRange();
   3090  }
   3091 
   3092  return NS_OK;
   3093 }
   3094 
   3095 void Selection::ReplaceAnchorFocusRange(nsRange* aRange) {
   3096  NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
   3097  RefPtr<nsPresContext> presContext = GetPresContext();
   3098  if (presContext) {
   3099    SelectFrames(presContext, *mAnchorFocusRange, false);
   3100    SetAnchorFocusToRange(aRange);
   3101    SelectFrames(presContext, *mAnchorFocusRange, true);
   3102  }
   3103 }
   3104 
   3105 void Selection::AdjustAnchorFocusForMultiRange(nsDirection aDirection) {
   3106  if (aDirection == mDirection) {
   3107    return;
   3108  }
   3109  SetDirection(aDirection);
   3110 
   3111  if (RangeCount() <= 1) {
   3112    return;
   3113  }
   3114 
   3115  nsRange* firstRange = GetRangeAt(0);
   3116  nsRange* lastRange = GetRangeAt(RangeCount() - 1);
   3117 
   3118  if (mDirection == eDirPrevious) {
   3119    firstRange->SetIsGenerated(false);
   3120    lastRange->SetIsGenerated(true);
   3121    SetAnchorFocusRange(0);
   3122  } else {  // aDir == eDirNext
   3123    firstRange->SetIsGenerated(true);
   3124    lastRange->SetIsGenerated(false);
   3125    SetAnchorFocusRange(RangeCount() - 1);
   3126  }
   3127 }
   3128 
   3129 /*
   3130 * Extend extends the selection away from the anchor.
   3131 * We don't need to know the direction, because we always change the focus.
   3132 */
   3133 void Selection::ExtendJS(nsINode& aContainer, uint32_t aOffset,
   3134                         ErrorResult& aRv) {
   3135  if (NeedsToLogSelectionAPI(*this)) {
   3136    LogSelectionAPI(this, __FUNCTION__, "aContainer", &aContainer, "aOffset",
   3137                    aOffset);
   3138    LogStackForSelectionAPI();
   3139  }
   3140 
   3141  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   3142  mCalledByJS = true;
   3143  Extend(aContainer, aOffset, aRv);
   3144 }
   3145 
   3146 nsresult Selection::Extend(nsINode* aContainer, uint32_t aOffset) {
   3147  if (NeedsToLogSelectionAPI(*this)) {
   3148    LogSelectionAPI(this, __FUNCTION__, "aContainer", aContainer, "aOffset",
   3149                    aOffset);
   3150    LogStackForSelectionAPI();
   3151  }
   3152 
   3153  if (!aContainer) {
   3154    return NS_ERROR_INVALID_ARG;
   3155  }
   3156 
   3157  ErrorResult result;
   3158  Extend(*aContainer, aOffset, result);
   3159  return result.StealNSResult();
   3160 }
   3161 
   3162 void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
   3163                       ErrorResult& aRv) {
   3164  /*
   3165    Notes which might come in handy for extend:
   3166 
   3167    We can tell the direction of the selection by asking for the anchors
   3168    selection if the begin is less than the end then we know the selection is to
   3169    the "right", else it is a backwards selection. Notation: a = anchor, 1 = old
   3170    cursor, 2 = new cursor.
   3171 
   3172      if (a <= 1 && 1 <=2)    a,1,2  or (a1,2)
   3173      if (a < 2 && 1 > 2)     a,2,1
   3174      if (1 < a && a <2)      1,a,2
   3175      if (a > 2 && 2 >1)      1,2,a
   3176      if (2 < a && a <1)      2,a,1
   3177      if (a > 1 && 1 >2)      2,1,a
   3178    then execute
   3179    a  1  2 select from 1 to 2
   3180    a  2  1 deselect from 2 to 1
   3181    1  a  2 deselect from 1 to a select from a to 2
   3182    1  2  a deselect from 1 to 2
   3183    2  1  a = continue selection from 2 to 1
   3184  */
   3185 
   3186  // First, find the range containing the old focus point:
   3187  if (!mAnchorFocusRange) {
   3188    aRv.ThrowInvalidStateError(kNoRangeExistsError);
   3189    return;
   3190  }
   3191 
   3192  if (!mFrameSelection) {
   3193    aRv.Throw(NS_ERROR_NOT_INITIALIZED);  // Can't do selection
   3194    return;
   3195  }
   3196 
   3197  if (!HasSameRootOrSameComposedDoc(aContainer)) {
   3198    // Return with no error
   3199    return;
   3200  }
   3201 
   3202  if (!mFrameSelection->NodeIsInLimiters(&aContainer)) {
   3203    aRv.Throw(NS_ERROR_FAILURE);
   3204    return;
   3205  }
   3206 
   3207  if (aContainer.GetFrameSelection() != mFrameSelection) {
   3208    NS_ASSERTION(
   3209        false,
   3210        nsFmtCString(
   3211            FMT_STRING("mFrameSelection is {} which is expected as "
   3212                       "aContainer.GetFrameSelection() ({})"),
   3213            mozilla::ToString(mFrameSelection).c_str(),
   3214            mozilla::ToString(RefPtr{aContainer.GetFrameSelection()}).c_str())
   3215            .get());
   3216    aRv.Throw(NS_ERROR_FAILURE);
   3217    return;
   3218  }
   3219 
   3220  RefPtr<nsPresContext> presContext = GetPresContext();
   3221  if (!presContext || presContext->Document() != aContainer.OwnerDoc()) {
   3222    aRv.Throw(NS_ERROR_FAILURE);
   3223    return;
   3224  }
   3225 
   3226 #ifdef DEBUG_SELECTION
   3227  nsDirection oldDirection = GetDirection();
   3228 #endif
   3229  nsINode* anchorNode = GetMayCrossShadowBoundaryAnchorNode();
   3230  nsINode* focusNode = GetMayCrossShadowBoundaryFocusNode();
   3231  const uint32_t anchorOffset = MayCrossShadowBoundaryAnchorOffset();
   3232  const uint32_t focusOffset = MayCrossShadowBoundaryFocusOffset();
   3233 
   3234  RefPtr<nsRange> range = mAnchorFocusRange->CloneRange();
   3235 
   3236  nsINode* startNode = range->GetMayCrossShadowBoundaryStartContainer();
   3237  nsINode* endNode = range->GetMayCrossShadowBoundaryEndContainer();
   3238  const uint32_t startOffset = range->MayCrossShadowBoundaryStartOffset();
   3239  const uint32_t endOffset = range->MayCrossShadowBoundaryEndOffset();
   3240 
   3241  bool shouldClearRange = false;
   3242 
   3243  auto ComparePoints = [](const nsINode* aNode1, const uint32_t aOffset1,
   3244                          const nsINode* aNode2, const uint32_t aOffset2) {
   3245    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   3246      return nsContentUtils::ComparePointsWithIndices<TreeKind::Flat>(
   3247          aNode1, aOffset1, aNode2, aOffset2);
   3248    }
   3249    return nsContentUtils::ComparePointsWithIndices<
   3250        TreeKind::ShadowIncludingDOM>(aNode1, aOffset1, aNode2, aOffset2);
   3251  };
   3252  const Maybe<int32_t> anchorOldFocusOrder =
   3253      ComparePoints(anchorNode, anchorOffset, focusNode, focusOffset);
   3254  shouldClearRange |= !anchorOldFocusOrder;
   3255  const Maybe<int32_t> oldFocusNewFocusOrder =
   3256      ComparePoints(focusNode, focusOffset, &aContainer, aOffset);
   3257  shouldClearRange |= !oldFocusNewFocusOrder;
   3258  const Maybe<int32_t> anchorNewFocusOrder =
   3259      ComparePoints(anchorNode, anchorOffset, &aContainer, aOffset);
   3260  shouldClearRange |= !anchorNewFocusOrder;
   3261 
   3262  // If the points are disconnected, the range will be collapsed below,
   3263  // resulting in a range that selects nothing.
   3264  nsresult res;
   3265  if (shouldClearRange) {
   3266    // Repaint the current range with the selection removed.
   3267    SelectFrames(presContext, *range, false);
   3268 
   3269    res = range->CollapseTo(&aContainer, aOffset);
   3270    if (NS_FAILED(res)) {
   3271      aRv.Throw(res);
   3272      return;
   3273    }
   3274 
   3275    res = SetAnchorFocusToRange(range);
   3276    if (NS_FAILED(res)) {
   3277      aRv.Throw(res);
   3278      return;
   3279    }
   3280  } else {
   3281    RefPtr<nsRange> difRange = nsRange::Create(&aContainer);
   3282    if ((*anchorOldFocusOrder == 0 && *anchorNewFocusOrder < 0) ||
   3283        (*anchorOldFocusOrder <= 0 &&
   3284         *oldFocusNewFocusOrder < 0)) {  // a1,2  a,1,2
   3285      // select from 1 to 2 unless they are collapsed
   3286      range->SetEnd(aContainer, aOffset, aRv,
   3287                    AllowRangeCrossShadowBoundary::Yes);
   3288      if (aRv.Failed()) {
   3289        return;
   3290      }
   3291      SetDirection(eDirNext);
   3292      res = difRange->SetStartAndEnd(
   3293          focusNode, focusOffset,
   3294          range->GetMayCrossShadowBoundaryEndContainer(),
   3295          range->MayCrossShadowBoundaryEndOffset(),
   3296          AllowRangeCrossShadowBoundary::Yes);
   3297      if (NS_FAILED(res)) {
   3298        aRv.Throw(res);
   3299        return;
   3300      }
   3301      SelectFrames(presContext, *difRange, true);
   3302      res = SetAnchorFocusToRange(range);
   3303      if (NS_FAILED(res)) {
   3304        aRv.Throw(res);
   3305        return;
   3306      }
   3307    } else if (*anchorOldFocusOrder == 0 &&
   3308               *anchorNewFocusOrder > 0) {  // 2, a1
   3309      // select from 2 to 1a
   3310      SetDirection(eDirPrevious);
   3311      range->SetStart(aContainer, aOffset, aRv,
   3312                      AllowRangeCrossShadowBoundary::Yes);
   3313      if (aRv.Failed()) {
   3314        return;
   3315      }
   3316      SelectFrames(presContext, *range, true);
   3317      res = SetAnchorFocusToRange(range);
   3318      if (NS_FAILED(res)) {
   3319        aRv.Throw(res);
   3320        return;
   3321      }
   3322    } else if (*anchorNewFocusOrder <= 0 &&
   3323               *oldFocusNewFocusOrder >= 0) {  // a,2,1 or a2,1 or a,21 or a21
   3324      // deselect from 2 to 1
   3325      res =
   3326          difRange->SetStartAndEnd(&aContainer, aOffset, focusNode, focusOffset,
   3327                                   AllowRangeCrossShadowBoundary::Yes);
   3328      if (NS_FAILED(res)) {
   3329        aRv.Throw(res);
   3330        return;
   3331      }
   3332 
   3333      range->SetEnd(aContainer, aOffset, aRv,
   3334                    AllowRangeCrossShadowBoundary::Yes);
   3335      if (aRv.Failed()) {
   3336        return;
   3337      }
   3338      res = SetAnchorFocusToRange(range);
   3339      if (NS_FAILED(res)) {
   3340        aRv.Throw(res);
   3341        return;
   3342      }
   3343      SelectFrames(presContext, *difRange, false);  // deselect now
   3344      difRange->SetEnd(range->GetMayCrossShadowBoundaryEndContainer(),
   3345                       range->MayCrossShadowBoundaryEndOffset(),
   3346                       AllowRangeCrossShadowBoundary::Yes);
   3347      SelectFrames(presContext, *difRange, true);  // must reselect last node
   3348                                                   // maybe more
   3349    } else if (*anchorOldFocusOrder >= 0 &&
   3350               *anchorNewFocusOrder <= 0) {  // 1,a,2 or 1a,2 or 1,a2 or 1a2
   3351      if (GetDirection() == eDirPrevious) {
   3352        res = range->SetStart(endNode, endOffset,
   3353                              AllowRangeCrossShadowBoundary::Yes);
   3354        if (NS_FAILED(res)) {
   3355          aRv.Throw(res);
   3356          return;
   3357        }
   3358      }
   3359      SetDirection(eDirNext);
   3360      range->SetEnd(aContainer, aOffset, aRv,
   3361                    AllowRangeCrossShadowBoundary::Yes);
   3362      if (aRv.Failed()) {
   3363        return;
   3364      }
   3365      if (focusNode != anchorNode ||
   3366          focusOffset != anchorOffset) {  // if collapsed diff dont do anything
   3367        res = difRange->SetStart(focusNode, focusOffset,
   3368                                 AllowRangeCrossShadowBoundary::Yes);
   3369        nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset,
   3370                                        AllowRangeCrossShadowBoundary::Yes);
   3371        if (NS_FAILED(tmp)) {
   3372          res = tmp;
   3373        }
   3374        if (NS_FAILED(res)) {
   3375          aRv.Throw(res);
   3376          return;
   3377        }
   3378        res = SetAnchorFocusToRange(range);
   3379        if (NS_FAILED(res)) {
   3380          aRv.Throw(res);
   3381          return;
   3382        }
   3383        // deselect from 1 to a
   3384        SelectFrames(presContext, *difRange, false);
   3385      } else {
   3386        res = SetAnchorFocusToRange(range);
   3387        if (NS_FAILED(res)) {
   3388          aRv.Throw(res);
   3389          return;
   3390        }
   3391      }
   3392      // select from a to 2
   3393      SelectFrames(presContext, *range, true);
   3394    } else if (*oldFocusNewFocusOrder <= 0 &&
   3395               *anchorNewFocusOrder >= 0) {  // 1,2,a or 12,a or 1,2a or 12a
   3396      // deselect from 1 to 2
   3397      res =
   3398          difRange->SetStartAndEnd(focusNode, focusOffset, &aContainer, aOffset,
   3399                                   AllowRangeCrossShadowBoundary::Yes);
   3400      if (NS_FAILED(res)) {
   3401        aRv.Throw(res);
   3402        return;
   3403      }
   3404 
   3405      SetDirection(eDirPrevious);
   3406      range->SetStart(aContainer, aOffset, aRv,
   3407                      AllowRangeCrossShadowBoundary::Yes);
   3408      if (aRv.Failed()) {
   3409        return;
   3410      }
   3411 
   3412      res = SetAnchorFocusToRange(range);
   3413      if (NS_FAILED(res)) {
   3414        aRv.Throw(res);
   3415        return;
   3416      }
   3417      SelectFrames(presContext, *difRange, false);
   3418      difRange->SetStart(range->GetMayCrossShadowBoundaryStartContainer(),
   3419                         range->MayCrossShadowBoundaryStartOffset(),
   3420                         AllowRangeCrossShadowBoundary::Yes);
   3421      SelectFrames(presContext, *difRange, true);  // must reselect last node
   3422    } else if (*anchorNewFocusOrder >= 0 &&
   3423               *anchorOldFocusOrder <= 0) {  // 2,a,1 or 2a,1 or 2,a1 or 2a1
   3424      if (GetDirection() == eDirNext) {
   3425        range->SetEnd(startNode, startOffset,
   3426                      AllowRangeCrossShadowBoundary::Yes);
   3427      }
   3428      SetDirection(eDirPrevious);
   3429      range->SetStart(aContainer, aOffset, aRv,
   3430                      AllowRangeCrossShadowBoundary::Yes);
   3431      if (aRv.Failed()) {
   3432        return;
   3433      }
   3434      // deselect from a to 1
   3435      if (focusNode != anchorNode ||
   3436          focusOffset != anchorOffset) {  // if collapsed diff dont do anything
   3437        res = difRange->SetStartAndEnd(anchorNode, anchorOffset, focusNode,
   3438                                       focusOffset,
   3439                                       AllowRangeCrossShadowBoundary::Yes);
   3440        nsresult tmp = SetAnchorFocusToRange(range);
   3441        if (NS_FAILED(tmp)) {
   3442          res = tmp;
   3443        }
   3444        if (NS_FAILED(res)) {
   3445          aRv.Throw(res);
   3446          return;
   3447        }
   3448        SelectFrames(presContext, *difRange, false);
   3449      } else {
   3450        res = SetAnchorFocusToRange(range);
   3451        if (NS_FAILED(res)) {
   3452          aRv.Throw(res);
   3453          return;
   3454        }
   3455      }
   3456      // select from 2 to a
   3457      SelectFrames(presContext, *range, true);
   3458    } else if (*oldFocusNewFocusOrder >= 0 &&
   3459               *anchorOldFocusOrder >= 0) {  // 2,1,a or 21,a or 2,1a or 21a
   3460      // select from 2 to 1
   3461      range->SetStart(aContainer, aOffset, aRv,
   3462                      AllowRangeCrossShadowBoundary::Yes);
   3463      if (aRv.Failed()) {
   3464        return;
   3465      }
   3466      SetDirection(eDirPrevious);
   3467      res = difRange->SetStartAndEnd(
   3468          range->GetStartContainer(), range->StartOffset(), focusNode,
   3469          focusOffset, AllowRangeCrossShadowBoundary::Yes);
   3470      if (NS_FAILED(res)) {
   3471        aRv.Throw(res);
   3472        return;
   3473      }
   3474 
   3475      SelectFrames(presContext, *difRange, true);
   3476      res = SetAnchorFocusToRange(range);
   3477      if (NS_FAILED(res)) {
   3478        aRv.Throw(res);
   3479        return;
   3480      }
   3481    }
   3482  }
   3483 
   3484  if (mStyledRanges.Length() > 1) {
   3485    SelectFramesInAllRanges(presContext);
   3486  }
   3487 
   3488  DEBUG_OUT_RANGE(range);
   3489 #ifdef DEBUG_SELECTION
   3490  if (GetDirection() != oldDirection) {
   3491    printf("    direction changed to %s\n",
   3492           GetDirection() == eDirNext ? "eDirNext" : "eDirPrevious");
   3493  }
   3494  nsCOMPtr<nsIContent> content = do_QueryInterface(&aContainer);
   3495  printf("Sel. Extend to %p %s %d\n", content.get(),
   3496         nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset);
   3497 #endif
   3498 
   3499  RefPtr<Selection> kungFuDeathGrip{this};
   3500  // Be aware, this instance may be destroyed after this call.
   3501  NotifySelectionListeners();
   3502 }
   3503 
   3504 void Selection::SelectAllChildrenJS(nsINode& aNode, ErrorResult& aRv) {
   3505  if (NeedsToLogSelectionAPI(*this)) {
   3506    LogSelectionAPI(this, __FUNCTION__, "aNode", &aNode);
   3507    LogStackForSelectionAPI();
   3508  }
   3509 
   3510  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   3511  mCalledByJS = true;
   3512  SelectAllChildren(aNode, aRv);
   3513  if (StaticPrefs::dom_selection_mimic_chrome_tostring_enabled() &&
   3514      !aRv.Failed()) {
   3515    if (auto* presShell = GetPresShell()) {
   3516      presShell->UpdateLastSelectionForToString(mFrameSelection);
   3517    }
   3518  }
   3519 }
   3520 
   3521 void Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv) {
   3522  if (!mCalledByJS && NeedsToLogSelectionAPI(*this)) {
   3523    LogSelectionAPI(this, __FUNCTION__, "aNode", &aNode);
   3524    LogStackForSelectionAPI();
   3525  }
   3526 
   3527  if (aNode.NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
   3528    aRv.ThrowInvalidNodeTypeError(kNoDocumentTypeNodeError);
   3529    return;
   3530  }
   3531 
   3532  if (!HasSameRootOrSameComposedDoc(aNode)) {
   3533    // Return with no error
   3534    return;
   3535  }
   3536 
   3537  if (mFrameSelection) {
   3538    mFrameSelection->AddChangeReasons(nsISelectionListener::SELECTALL_REASON);
   3539  }
   3540 
   3541  // Chrome moves focus when aNode is outside of active editing host.
   3542  // So, we don't need to respect the limiter with this method.
   3543  SetStartAndEndInternal(InLimiter::eNo, RawRangeBoundary(&aNode, 0u),
   3544                         RawRangeBoundary(&aNode, aNode.GetChildCount()),
   3545                         eDirNext, aRv);
   3546 }
   3547 
   3548 bool Selection::ContainsNode(nsINode& aNode, bool aAllowPartial,
   3549                             ErrorResult& aRv) {
   3550  nsresult rv;
   3551  if (mStyledRanges.Length() == 0) {
   3552    return false;
   3553  }
   3554 
   3555  // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
   3556  uint32_t nodeLength;
   3557  auto* nodeAsCharData = CharacterData::FromNode(aNode);
   3558  if (nodeAsCharData) {
   3559    nodeLength = nodeAsCharData->TextLength();
   3560  } else {
   3561    nodeLength = aNode.GetChildCount();
   3562  }
   3563 
   3564  nsTArray<AbstractRange*> overlappingRanges;
   3565  rv = GetAbstractRangesForIntervalArray(&aNode, 0, &aNode, nodeLength, false,
   3566                                         &overlappingRanges);
   3567  if (NS_FAILED(rv)) {
   3568    aRv.Throw(rv);
   3569    return false;
   3570  }
   3571  if (overlappingRanges.Length() == 0) return false;  // no ranges overlap
   3572 
   3573  // if the caller said partial intersections are OK, we're done
   3574  if (aAllowPartial) {
   3575    return true;
   3576  }
   3577 
   3578  // text nodes always count as inside
   3579  if (nodeAsCharData) {
   3580    return true;
   3581  }
   3582 
   3583  // The caller wants to know if the node is entirely within the given range,
   3584  // so we have to check all intersecting ranges.
   3585  for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
   3586    bool nodeStartsBeforeRange, nodeEndsAfterRange;
   3587    if (NS_SUCCEEDED(RangeUtils::CompareNodeToRange(
   3588            &aNode, overlappingRanges[i], &nodeStartsBeforeRange,
   3589            &nodeEndsAfterRange))) {
   3590      if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
   3591        return true;
   3592      }
   3593    }
   3594  }
   3595  return false;
   3596 }
   3597 
   3598 class PointInRectChecker : public mozilla::RectCallback {
   3599 public:
   3600  explicit PointInRectChecker(const nsPoint& aPoint)
   3601      : mPoint(aPoint), mMatchFound(false) {}
   3602 
   3603  void AddRect(const nsRect& aRect) override {
   3604    mMatchFound = mMatchFound || aRect.Contains(mPoint);
   3605  }
   3606 
   3607  bool MatchFound() { return mMatchFound; }
   3608 
   3609 private:
   3610  nsPoint mPoint;
   3611  bool mMatchFound;
   3612 };
   3613 
   3614 bool Selection::ContainsPoint(const nsPoint& aPoint) {
   3615  if (IsCollapsed()) {
   3616    return false;
   3617  }
   3618  PointInRectChecker checker(aPoint);
   3619  const uint32_t rangeCount = RangeCount();
   3620  for (const uint32_t i : IntegerRange(rangeCount)) {
   3621    MOZ_ASSERT(RangeCount() == rangeCount);
   3622    nsRange* range = GetRangeAt(i);
   3623    MOZ_ASSERT(range);
   3624    nsRange::CollectClientRectsAndText(
   3625        &checker, nullptr, range, range->GetStartContainer(),
   3626        range->StartOffset(), range->GetEndContainer(), range->EndOffset(),
   3627        true, false);
   3628    if (checker.MatchFound()) {
   3629      return true;
   3630    }
   3631  }
   3632  return false;
   3633 }
   3634 
   3635 void Selection::MaybeNotifyAccessibleCaretEventHub(PresShell* aPresShell) {
   3636  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
   3637 
   3638  if (!mAccessibleCaretEventHub && aPresShell) {
   3639    mAccessibleCaretEventHub = aPresShell->GetAccessibleCaretEventHub();
   3640  }
   3641 }
   3642 
   3643 void Selection::StopNotifyingAccessibleCaretEventHub() {
   3644  MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
   3645 
   3646  mAccessibleCaretEventHub = nullptr;
   3647 }
   3648 
   3649 nsPresContext* Selection::GetPresContext() const {
   3650  PresShell* presShell = GetPresShell();
   3651  return presShell ? presShell->GetPresContext() : nullptr;
   3652 }
   3653 
   3654 PresShell* Selection::GetPresShell() const {
   3655  if (!mFrameSelection) {
   3656    return nullptr;  // nothing to do
   3657  }
   3658  return mFrameSelection->GetPresShell();
   3659 }
   3660 
   3661 Document* Selection::GetDocument() const {
   3662  PresShell* presShell = GetPresShell();
   3663  return presShell ? presShell->GetDocument() : nullptr;
   3664 }
   3665 
   3666 nsIFrame* Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion,
   3667                                                nsRect* aRect) {
   3668  if (!mFrameSelection) return nullptr;  // nothing to do
   3669 
   3670  NS_ENSURE_TRUE(aRect, nullptr);
   3671 
   3672  aRect->SetRect(0, 0, 0, 0);
   3673 
   3674  switch (aRegion) {
   3675    case nsISelectionController::SELECTION_ANCHOR_REGION:
   3676    case nsISelectionController::SELECTION_FOCUS_REGION:
   3677      return GetSelectionEndPointGeometry(aRegion, aRect);
   3678    case nsISelectionController::SELECTION_WHOLE_SELECTION:
   3679      break;
   3680    default:
   3681      return nullptr;
   3682  }
   3683 
   3684  NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
   3685               "should only be SELECTION_WHOLE_SELECTION here");
   3686 
   3687  nsRect anchorRect;
   3688  nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
   3689      nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
   3690  if (!anchorFrame) return nullptr;
   3691 
   3692  nsRect focusRect;
   3693  nsIFrame* focusFrame = GetSelectionEndPointGeometry(
   3694      nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
   3695  if (!focusFrame) return nullptr;
   3696 
   3697  NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
   3698               "points of selection in different documents?");
   3699  // make focusRect relative to anchorFrame
   3700  focusRect += focusFrame->GetOffsetTo(anchorFrame);
   3701 
   3702  *aRect = anchorRect.UnionEdges(focusRect);
   3703  return anchorFrame;
   3704 }
   3705 
   3706 nsIFrame* Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion,
   3707                                                  nsRect* aRect) {
   3708  if (!mFrameSelection) return nullptr;  // nothing to do
   3709 
   3710  NS_ENSURE_TRUE(aRect, nullptr);
   3711 
   3712  aRect->SetRect(0, 0, 0, 0);
   3713 
   3714  nsINode* node = nullptr;
   3715  uint32_t nodeOffset = 0;
   3716 
   3717  switch (aRegion) {
   3718    case nsISelectionController::SELECTION_ANCHOR_REGION:
   3719      node = GetAnchorNode();
   3720      nodeOffset = AnchorOffset();
   3721      break;
   3722    case nsISelectionController::SELECTION_FOCUS_REGION:
   3723      node = GetFocusNode();
   3724      nodeOffset = FocusOffset();
   3725      break;
   3726    default:
   3727      return nullptr;
   3728  }
   3729 
   3730  if (!node) return nullptr;
   3731 
   3732  nsCOMPtr<nsIContent> content = do_QueryInterface(node);
   3733  NS_ENSURE_TRUE(content.get(), nullptr);
   3734  FrameAndOffset frameAndOffset = SelectionMovementUtils::GetFrameForNodeOffset(
   3735      content, nodeOffset, mFrameSelection->GetHint());
   3736  if (!frameAndOffset) {
   3737    return nullptr;
   3738  }
   3739 
   3740  SelectionMovementUtils::AdjustFrameForLineStart(
   3741      frameAndOffset.mFrame, frameAndOffset.mOffsetInFrameContent);
   3742 
   3743  // Figure out what node type we have, then get the
   3744  // appropriate rect for its nodeOffset.
   3745  bool isText = node->IsText();
   3746 
   3747  nsPoint pt(0, 0);
   3748  if (isText) {
   3749    nsIFrame* childFrame = nullptr;
   3750    int32_t frameOffset = 0;
   3751    nsresult rv = frameAndOffset->GetChildFrameContainingOffset(
   3752        // FIXME: nodeOffset is offset in content (same as node) but
   3753        // frameAndOffset.mFrame may be a frame for its descendant.  Therefore,
   3754        // frameAndOffset.mOffsetInFrameContent should be used here.
   3755        nodeOffset, mFrameSelection->GetHint() == CaretAssociationHint::After,
   3756        &frameOffset, &childFrame);
   3757    if (NS_FAILED(rv)) return nullptr;
   3758    if (!childFrame) return nullptr;
   3759 
   3760    frameAndOffset.mFrame = childFrame;
   3761 
   3762    // Get the coordinates of the offset into the text frame.
   3763    rv = GetCachedFrameOffset(
   3764        frameAndOffset.mFrame,
   3765        static_cast<int32_t>(frameAndOffset.mOffsetInFrameContent), pt);
   3766    if (NS_FAILED(rv)) return nullptr;
   3767  }
   3768 
   3769  // Return the rect relative to the frame, with zero inline-size.  The
   3770  // inline-position is either 'pt' (if we're a text node) or otherwise just
   3771  // the physical "end" edge of the frame (which we express as the frame's own
   3772  // width or height, since the returned position is relative to the frame).
   3773  // The block position and size are set so as to fill the frame in that axis.
   3774  // (i.e. block-position of 0, and block-size matching the frame's own block
   3775  // size).
   3776  const WritingMode wm = frameAndOffset->GetWritingMode();
   3777  // Helper to determine the inline-axis position for the aRect outparam.
   3778  auto GetInlinePosition = [&]() {
   3779    if (isText) {
   3780      return wm.IsVertical() ? pt.y : pt.x;
   3781    }
   3782    // Return the frame's physical end edge of its inline axis, relative to the
   3783    // frame.  That's just its height or width.
   3784    // TODO(dholbert): This seems to work, but perhaps we really want the
   3785    // inline-end edge (rather than physical end of inline axis)? (i.e. if we
   3786    // have direction:rtl, maybe this code would want to return 0 instead of
   3787    // height/width?)
   3788    return frameAndOffset->ISize(wm);
   3789  };
   3790 
   3791  // Set the inline position and block-size. Leave inline size and block
   3792  // position set to 0, as discussed above.
   3793  if (wm.IsVertical()) {
   3794    aRect->y = GetInlinePosition();
   3795    aRect->SetWidth(frameAndOffset->BSize(wm));
   3796  } else {
   3797    aRect->x = GetInlinePosition();
   3798    aRect->SetHeight(frameAndOffset->BSize(wm));
   3799  }
   3800 
   3801  return frameAndOffset;
   3802 }
   3803 
   3804 NS_IMETHODIMP
   3805 Selection::ScrollSelectionIntoViewEvent::Run() {
   3806  if (!mSelection) {
   3807    // event revoked
   3808    return NS_OK;
   3809  }
   3810 
   3811  const RefPtr<Selection> selection{mSelection};
   3812  selection->mScrollEvent.Forget();
   3813  selection->ScrollIntoView(mRegion, mVerticalScroll, mHorizontalScroll, mFlags,
   3814                            SelectionScrollMode::SyncFlush);
   3815  return NS_OK;
   3816 }
   3817 
   3818 nsresult Selection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion,
   3819                                                     ScrollFlags aFlags,
   3820                                                     ScrollAxis aVertical,
   3821                                                     ScrollAxis aHorizontal) {
   3822  // If we've already posted an event, revoke it and place a new one at the
   3823  // end of the queue to make sure that any new pending reflow events are
   3824  // processed before we scroll. This will insure that we scroll to the
   3825  // correct place on screen.
   3826  mScrollEvent.Revoke();
   3827  nsPresContext* presContext = GetPresContext();
   3828  NS_ENSURE_STATE(presContext);
   3829  nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
   3830  NS_ENSURE_STATE(refreshDriver);
   3831 
   3832  mScrollEvent = new ScrollSelectionIntoViewEvent(this, aRegion, aVertical,
   3833                                                  aHorizontal, aFlags);
   3834  refreshDriver->AddEarlyRunner(mScrollEvent.get());
   3835  return NS_OK;
   3836 }
   3837 
   3838 nsresult Selection::ScrollIntoView(SelectionRegion aRegion,
   3839                                   ScrollAxis aVertical, ScrollAxis aHorizontal,
   3840                                   ScrollFlags aScrollFlags,
   3841                                   SelectionScrollMode aMode) {
   3842  if (!mFrameSelection) {
   3843    return NS_ERROR_NOT_INITIALIZED;
   3844  }
   3845 
   3846  RefPtr<PresShell> presShell = mFrameSelection->GetPresShell();
   3847  if (!presShell || !presShell->GetDocument()) {
   3848    return NS_OK;
   3849  }
   3850 
   3851  if (mFrameSelection->IsBatching()) {
   3852    return NS_OK;
   3853  }
   3854 
   3855  if (aMode == SelectionScrollMode::Async) {
   3856    return PostScrollSelectionIntoViewEvent(aRegion, aScrollFlags, aVertical,
   3857                                            aHorizontal);
   3858  }
   3859 
   3860  MOZ_ASSERT(aMode == SelectionScrollMode::SyncFlush ||
   3861             aMode == SelectionScrollMode::SyncNoFlush);
   3862 
   3863  // From this point on, the presShell may get destroyed by the calls below, so
   3864  // hold on to it using a strong reference to ensure the safety of the
   3865  // accesses to frame pointers in the callees.
   3866  RefPtr<PresShell> kungFuDeathGrip(presShell);
   3867 
   3868  // Now that text frame character offsets are always valid (though not
   3869  // necessarily correct), the worst that will happen if we don't flush here
   3870  // is that some callers might scroll to the wrong place.  Those should
   3871  // either manually flush if they're in a safe position for it or use the
   3872  // async version of this method.
   3873  if (aMode == SelectionScrollMode::SyncFlush) {
   3874    presShell->GetDocument()->FlushPendingNotifications(FlushType::Layout);
   3875 
   3876    // Reget the presshell, since it might have been Destroy'ed.
   3877    presShell = mFrameSelection ? mFrameSelection->GetPresShell() : nullptr;
   3878    if (!presShell) {
   3879      return NS_OK;
   3880    }
   3881  }
   3882 
   3883  nsRect rect;
   3884  nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
   3885  if (!frame) {
   3886    return NS_ERROR_FAILURE;
   3887  }
   3888 
   3889  presShell->ScrollFrameIntoView(frame, Some(rect), aVertical, aHorizontal,
   3890                                 aScrollFlags);
   3891  return NS_OK;
   3892 }
   3893 
   3894 void Selection::AddSelectionListener(nsISelectionListener* aNewListener) {
   3895  MOZ_ASSERT(aNewListener);
   3896  mSelectionListeners.AppendElement(aNewListener);  // AddRefs
   3897 }
   3898 
   3899 void Selection::RemoveSelectionListener(
   3900    nsISelectionListener* aListenerToRemove) {
   3901  mSelectionListeners.RemoveElement(aListenerToRemove);  // Releases
   3902 }
   3903 
   3904 Element* Selection::StyledRanges::GetCommonEditingHost() const {
   3905  Element* editingHost = nullptr;
   3906  for (const StyledRange& rangeData : mRanges) {
   3907    const AbstractRange* range = rangeData.mRange;
   3908    MOZ_ASSERT(range);
   3909    nsINode* commonAncestorNode = range->GetClosestCommonInclusiveAncestor();
   3910    if (!commonAncestorNode || !commonAncestorNode->IsContent()) {
   3911      return nullptr;
   3912    }
   3913    nsIContent* commonAncestor = commonAncestorNode->AsContent();
   3914    Element* foundEditingHost = commonAncestor->GetEditingHost();
   3915    // Even when common ancestor is a non-editable element in a contenteditable
   3916    // element, we don't need to move focus to the contenteditable element
   3917    // because Chromium doesn't set focus to it.
   3918    if (!foundEditingHost) {
   3919      return nullptr;
   3920    }
   3921    if (!editingHost) {
   3922      editingHost = foundEditingHost;
   3923      continue;
   3924    }
   3925    if (editingHost == foundEditingHost) {
   3926      continue;
   3927    }
   3928    if (foundEditingHost->IsInclusiveDescendantOf(editingHost)) {
   3929      continue;
   3930    }
   3931    if (editingHost->IsInclusiveDescendantOf(foundEditingHost)) {
   3932      editingHost = foundEditingHost;
   3933      continue;
   3934    }
   3935    // editingHost and foundEditingHost are not a descendant of the other.
   3936    // So, there is no common editing host.
   3937    return nullptr;
   3938  }
   3939  return editingHost;
   3940 }
   3941 
   3942 void Selection::StyledRanges::MaybeFocusCommonEditingHost(
   3943    PresShell* aPresShell) const {
   3944  if (!aPresShell) {
   3945    return;
   3946  }
   3947 
   3948  nsPresContext* presContext = aPresShell->GetPresContext();
   3949  if (!presContext) {
   3950    return;
   3951  }
   3952 
   3953  Document* document = aPresShell->GetDocument();
   3954  if (!document) {
   3955    return;
   3956  }
   3957 
   3958  nsPIDOMWindowOuter* window = document->GetWindow();
   3959  // If the document is in design mode or doesn't have contenteditable
   3960  // element, we don't need to move focus.
   3961  if (window && !document->IsInDesignMode() &&
   3962      nsContentUtils::GetHTMLEditor(presContext)) {
   3963    RefPtr<Element> newEditingHost = GetCommonEditingHost();
   3964    RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
   3965    nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
   3966    nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
   3967        window, nsFocusManager::eOnlyCurrentWindow,
   3968        getter_AddRefs(focusedWindow));
   3969    nsCOMPtr<Element> focusedElement = do_QueryInterface(focusedContent);
   3970    // When all selected ranges are in an editing host, it should take focus.
   3971    // But otherwise, we shouldn't move focus since Chromium doesn't move
   3972    // focus but only selection range is updated.
   3973    if (newEditingHost && newEditingHost != focusedElement) {
   3974      MOZ_ASSERT(!newEditingHost->IsInNativeAnonymousSubtree());
   3975      // Note that don't steal focus from focused window if the window doesn't
   3976      // have focus.  Additionally, although when an element gets focus, we
   3977      // usually scroll to the element, but in this case, we shouldn't do it
   3978      // because Chrome does not do so.
   3979      fm->SetFocus(newEditingHost, nsIFocusManager::FLAG_NOSWITCHFRAME |
   3980                                       nsIFocusManager::FLAG_NOSCROLL);
   3981    }
   3982  }
   3983 }
   3984 
   3985 void Selection::NotifySelectionListeners(bool aCalledByJS) {
   3986  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   3987  mCalledByJS = aCalledByJS;
   3988  NotifySelectionListeners();
   3989 }
   3990 
   3991 void Selection::NotifySelectionListeners() {
   3992  if (!mFrameSelection) {
   3993    return;  // nothing to do
   3994  }
   3995 
   3996  MOZ_LOG(sSelectionLog, LogLevel::Debug,
   3997          ("%s: selection=%p", __FUNCTION__, this));
   3998 
   3999  mStyledRanges.mRangesMightHaveChanged = true;
   4000 
   4001  // This flag will be set to Double or Triple if a selection by double click or
   4002  // triple click is detected. As soon as the selection is modified, it needs to
   4003  // be reset to NotApplicable.
   4004  mFrameSelection->SetClickSelectionType(ClickSelectionType::NotApplicable);
   4005 
   4006  // If we're batching changes, record our batching flag and bail out, we'll be
   4007  // called once the batch ends.
   4008  if (mFrameSelection->IsBatching()) {
   4009    mChangesDuringBatching = true;
   4010    return;
   4011  }
   4012  // If being called at end of batching, `mFrameSelection->IsBatching()` will
   4013  // return false. In this case, this method will only be called if
   4014  // `mChangesDuringBatching` was true.
   4015  // (see `nsFrameSelection::EndBatchChanges()`).
   4016  // Since arriving here means that batching ended, the flag needs to be reset.
   4017  mChangesDuringBatching = false;
   4018 
   4019  // Our internal code should not move focus with using this class while
   4020  // this moves focus nor from selection listeners.
   4021  AutoRestore<bool> calledByJSRestorer(mCalledByJS);
   4022  mCalledByJS = false;
   4023 
   4024  // When normal selection is changed by Selection API, we need to move focus
   4025  // if common ancestor of all ranges are in an editing host.  Note that we
   4026  // don't need to move focus *to* the other focusable node because other
   4027  // browsers don't do it either.
   4028  if (mSelectionType == SelectionType::eNormal &&
   4029      calledByJSRestorer.SavedValue()) {
   4030    RefPtr<PresShell> presShell = GetPresShell();
   4031    mStyledRanges.MaybeFocusCommonEditingHost(presShell);
   4032  }
   4033 
   4034  nsCOMPtr<Document> doc;
   4035  if (PresShell* presShell = GetPresShell()) {
   4036    doc = presShell->GetDocument();
   4037    presShell->ScheduleContentRelevancyUpdate(ContentRelevancyReason::Selected);
   4038  }
   4039 
   4040  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   4041 
   4042  // We've notified all selection listeners even when some of them are removed
   4043  // (and may be destroyed) during notifying one of them.  Therefore, we should
   4044  // copy all listeners to the local variable first.
   4045  const CopyableAutoTArray<nsCOMPtr<nsISelectionListener>, 5>
   4046      selectionListeners = mSelectionListeners;
   4047 
   4048  int32_t amount = static_cast<int32_t>(frameSelection->GetCaretMoveAmount());
   4049  int16_t reason = frameSelection->PopChangeReasons();
   4050  if (calledByJSRestorer.SavedValue()) {
   4051    reason |= nsISelectionListener::JS_REASON;
   4052  }
   4053  if (mSelectionType == SelectionType::eNormal) {
   4054    if (mNotifyAutoCopy) {
   4055      AutoCopyListener::OnSelectionChange(doc, *this, reason);
   4056    }
   4057 
   4058    if (mAccessibleCaretEventHub) {
   4059      RefPtr<AccessibleCaretEventHub> hub(mAccessibleCaretEventHub);
   4060      hub->OnSelectionChange(doc, this, reason);
   4061    }
   4062 
   4063    if (mSelectionChangeEventDispatcher) {
   4064      RefPtr<SelectionChangeEventDispatcher> dispatcher(
   4065          mSelectionChangeEventDispatcher);
   4066      dispatcher->OnSelectionChange(doc, this, reason);
   4067    }
   4068  }
   4069  for (const auto& listener : selectionListeners) {
   4070    // MOZ_KnownLive because 'selectionListeners' is guaranteed to
   4071    // keep it alive.
   4072    //
   4073    // This can go away once
   4074    // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
   4075    MOZ_KnownLive(listener)->NotifySelectionChanged(doc, this, reason, amount);
   4076  }
   4077 }
   4078 
   4079 void Selection::StartBatchChanges(const char* aDetails) {
   4080  if (mFrameSelection) {
   4081    RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   4082    frameSelection->StartBatchChanges(aDetails);
   4083  }
   4084 }
   4085 
   4086 void Selection::EndBatchChanges(const char* aDetails, int16_t aReasons) {
   4087  if (mFrameSelection) {
   4088    RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   4089    frameSelection->EndBatchChanges(aDetails, aReasons);
   4090  }
   4091 }
   4092 
   4093 void Selection::AddSelectionChangeBlocker() { mSelectionChangeBlockerCount++; }
   4094 
   4095 void Selection::RemoveSelectionChangeBlocker() {
   4096  MOZ_ASSERT(mSelectionChangeBlockerCount > 0,
   4097             "mSelectionChangeBlockerCount has an invalid value - "
   4098             "maybe you have a mismatched RemoveSelectionChangeBlocker?");
   4099  mSelectionChangeBlockerCount--;
   4100 }
   4101 
   4102 bool Selection::IsBlockingSelectionChangeEvents() const {
   4103  return mSelectionChangeBlockerCount > 0;
   4104 }
   4105 
   4106 void Selection::DeleteFromDocument(ErrorResult& aRv) {
   4107  if (NeedsToLogSelectionAPI(*this)) {
   4108    LogSelectionAPI(this, __FUNCTION__);
   4109    LogStackForSelectionAPI();
   4110  }
   4111 
   4112  if (mSelectionType != SelectionType::eNormal) {
   4113    return;  // Nothing to do.
   4114  }
   4115 
   4116  // If we're already collapsed, then we do nothing (bug 719503).
   4117  if (IsCollapsed()) {
   4118    return;
   4119  }
   4120 
   4121  // nsRange::DeleteContents() may run script, let's store all ranges first.
   4122  AutoTArray<RefPtr<nsRange>, 1> ranges;
   4123  MOZ_ASSERT(RangeCount() == mStyledRanges.mRanges.Length());
   4124  ranges.SetCapacity(RangeCount());
   4125  for (uint32_t index : IntegerRange(RangeCount())) {
   4126    ranges.AppendElement(mStyledRanges.mRanges[index].mRange->AsDynamicRange());
   4127  }
   4128  for (const auto& range : ranges) {
   4129    MOZ_KnownLive(range)->DeleteContents(aRv);
   4130    if (aRv.Failed()) {
   4131      return;
   4132    }
   4133  }
   4134 
   4135  // Collapse to the new location.
   4136  // If we deleted one character, then we move back one element.
   4137  // FIXME  We don't know how to do this past frame boundaries yet.
   4138  if (AnchorOffset() > 0) {
   4139    RefPtr<nsINode> anchor = GetAnchorNode();
   4140    CollapseInLimiter(anchor, AnchorOffset());
   4141  }
   4142 #ifdef DEBUG
   4143  else {
   4144    printf("Don't know how to set selection back past frame boundary\n");
   4145  }
   4146 #endif
   4147 }
   4148 
   4149 void Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
   4150                       const nsAString& aGranularity) {
   4151  if (NeedsToLogSelectionAPI(*this)) {
   4152    LogSelectionAPI(this, __FUNCTION__, "aAlter", aAlter, "aDirection",
   4153                    aDirection, "aGranularity", aGranularity);
   4154    LogStackForSelectionAPI();
   4155  }
   4156 
   4157  if (!mFrameSelection) {
   4158    return;
   4159  }
   4160 
   4161  if (!GetAnchorFocusRange() || !GetFocusNode()) {
   4162    return;
   4163  }
   4164 
   4165  if (!aAlter.LowerCaseEqualsLiteral("move") &&
   4166      !aAlter.LowerCaseEqualsLiteral("extend")) {
   4167    return;
   4168  }
   4169 
   4170  if (!aDirection.LowerCaseEqualsLiteral("forward") &&
   4171      !aDirection.LowerCaseEqualsLiteral("backward") &&
   4172      !aDirection.LowerCaseEqualsLiteral("left") &&
   4173      !aDirection.LowerCaseEqualsLiteral("right")) {
   4174    return;
   4175  }
   4176 
   4177  // Make sure the layout is up to date as we access bidi information below.
   4178  if (RefPtr<Document> doc = GetDocument()) {
   4179    doc->FlushPendingNotifications(FlushType::Layout);
   4180  }
   4181 
   4182  // Line moves are always visual.
   4183  bool visual = aDirection.LowerCaseEqualsLiteral("left") ||
   4184                aDirection.LowerCaseEqualsLiteral("right") ||
   4185                aGranularity.LowerCaseEqualsLiteral("line");
   4186 
   4187  bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
   4188                 aDirection.LowerCaseEqualsLiteral("right");
   4189 
   4190  bool extend = aAlter.LowerCaseEqualsLiteral("extend");
   4191 
   4192  nsSelectionAmount amount;
   4193  if (aGranularity.LowerCaseEqualsLiteral("character")) {
   4194    amount = eSelectCluster;
   4195  } else if (aGranularity.LowerCaseEqualsLiteral("word")) {
   4196    amount = eSelectWordNoSpace;
   4197  } else if (aGranularity.LowerCaseEqualsLiteral("line")) {
   4198    amount = eSelectLine;
   4199  } else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
   4200    amount = forward ? eSelectEndLine : eSelectBeginLine;
   4201  } else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
   4202             aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
   4203             aGranularity.LowerCaseEqualsLiteral("paragraph") ||
   4204             aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
   4205             aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
   4206    Document* document = GetParentObject();
   4207    if (document) {
   4208      AutoTArray<nsString, 1> params;
   4209      params.AppendElement(aGranularity);
   4210      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
   4211                                      document, nsContentUtils::eDOM_PROPERTIES,
   4212                                      "SelectionModifyGranualirtyUnsupported",
   4213                                      params);
   4214    }
   4215    return;
   4216  } else {
   4217    return;
   4218  }
   4219 
   4220  // If the anchor doesn't equal the focus and we try to move without first
   4221  // collapsing the selection, MoveCaret will collapse the selection and quit.
   4222  // To avoid this, we need to collapse the selection first.
   4223  nsresult rv = NS_OK;
   4224  if (!extend) {
   4225    RefPtr<nsINode> focusNode = GetFocusNode();
   4226    // We should have checked earlier that there was a focus node.
   4227    if (!focusNode) {
   4228      return;
   4229    }
   4230    uint32_t focusOffset = FocusOffset();
   4231    CollapseInLimiter(focusNode, focusOffset);
   4232  }
   4233 
   4234  // If the paragraph direction of the focused frame is right-to-left,
   4235  // we may have to swap the direction of movement.
   4236  const PrimaryFrameData frameForFocus =
   4237      GetPrimaryFrameForCaretAtFocusNode(visual);
   4238  if (frameForFocus) {
   4239    if (visual) {
   4240      // FYI: This was done during a call of GetPrimaryFrameForCaretAtFocusNode.
   4241      // Therefore, this may not be intended by the original author.
   4242      mFrameSelection->SetHint(frameForFocus.mHint);
   4243    }
   4244    mozilla::intl::BidiDirection paraDir =
   4245        nsBidiPresUtils::ParagraphDirection(frameForFocus.mFrame);
   4246 
   4247    if (paraDir == mozilla::intl::BidiDirection::RTL && visual) {
   4248      if (amount == eSelectBeginLine) {
   4249        amount = eSelectEndLine;
   4250        forward = !forward;
   4251      } else if (amount == eSelectEndLine) {
   4252        amount = eSelectBeginLine;
   4253        forward = !forward;
   4254      }
   4255    }
   4256  }
   4257 
   4258  // MoveCaret will return an error if it can't move in the specified
   4259  // direction, but we just ignore this error unless it's a line move, in which
   4260  // case we call nsISelectionController::CompleteMove to move the cursor to
   4261  // the beginning/end of the line.
   4262  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   4263  rv = frameSelection->MoveCaret(
   4264      forward ? eDirNext : eDirPrevious,
   4265      nsFrameSelection::ExtendSelection(extend), amount,
   4266      visual ? nsFrameSelection::eVisual : nsFrameSelection::eLogical);
   4267 
   4268  if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
   4269    RefPtr<PresShell> presShell = frameSelection->GetPresShell();
   4270    if (!presShell) {
   4271      return;
   4272    }
   4273    presShell->CompleteMove(forward, extend);
   4274  }
   4275 }
   4276 
   4277 void Selection::SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset,
   4278                                   nsINode& aFocusNode, uint32_t aFocusOffset,
   4279                                   ErrorResult& aRv) {
   4280  if (NeedsToLogSelectionAPI(*this)) {
   4281    LogSelectionAPI(this, __FUNCTION__, "aAnchorNode", aAnchorNode,
   4282                    "aAnchorOffset", aAnchorOffset, "aFocusNode", aFocusNode,
   4283                    "aFocusOffset", aFocusOffset);
   4284    LogStackForSelectionAPI();
   4285  }
   4286 
   4287  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   4288  mCalledByJS = true;
   4289  SetBaseAndExtent(aAnchorNode, aAnchorOffset, aFocusNode, aFocusOffset, aRv);
   4290  if (StaticPrefs::dom_selection_mimic_chrome_tostring_enabled() &&
   4291      !aRv.Failed()) {
   4292    if (auto* presShell = GetPresShell()) {
   4293      presShell->UpdateLastSelectionForToString(mFrameSelection);
   4294    }
   4295  }
   4296 }
   4297 
   4298 void Selection::SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
   4299                                 nsINode& aFocusNode, uint32_t aFocusOffset,
   4300                                 ErrorResult& aRv) {
   4301  if (aAnchorOffset > aAnchorNode.Length()) {
   4302    aRv.ThrowIndexSizeError(nsPrintfCString(
   4303        "The anchor offset value %u is out of range", aAnchorOffset));
   4304    return;
   4305  }
   4306  if (aFocusOffset > aFocusNode.Length()) {
   4307    aRv.ThrowIndexSizeError(nsPrintfCString(
   4308        "The focus offset value %u is out of range", aFocusOffset));
   4309    return;
   4310  }
   4311 
   4312  SetBaseAndExtent(RawRangeBoundary{&aAnchorNode, aAnchorOffset},
   4313                   RawRangeBoundary{&aFocusNode, aFocusOffset}, aRv);
   4314 }
   4315 
   4316 void Selection::SetBaseAndExtent(const RawRangeBoundary& aAnchorRef,
   4317                                 const RawRangeBoundary& aFocusRef,
   4318                                 ErrorResult& aRv) {
   4319  if (!mCalledByJS && NeedsToLogSelectionAPI(*this)) {
   4320    LogSelectionAPI(this, __FUNCTION__, "aAnchorRef", aAnchorRef, "aFocusRef",
   4321                    aFocusRef);
   4322    LogStackForSelectionAPI();
   4323  }
   4324 
   4325  SetBaseAndExtentInternal(InLimiter::eNo, aAnchorRef, aFocusRef, aRv);
   4326 }
   4327 
   4328 void Selection::SetBaseAndExtentInLimiter(const RawRangeBoundary& aAnchorRef,
   4329                                          const RawRangeBoundary& aFocusRef,
   4330                                          ErrorResult& aRv) {
   4331  if (NeedsToLogSelectionAPI(*this)) {
   4332    LogSelectionAPI(this, __FUNCTION__, "aAnchorRef", aAnchorRef, "aFocusRef",
   4333                    aFocusRef);
   4334    LogStackForSelectionAPI();
   4335  }
   4336 
   4337  SetBaseAndExtentInternal(InLimiter::eYes, aAnchorRef, aFocusRef, aRv);
   4338 }
   4339 
   4340 void Selection::SetBaseAndExtentInternal(InLimiter aInLimiter,
   4341                                         const RawRangeBoundary& aAnchorRef,
   4342                                         const RawRangeBoundary& aFocusRef,
   4343                                         ErrorResult& aRv) {
   4344  if (!mFrameSelection) {
   4345    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   4346    return;
   4347  }
   4348 
   4349  if (NS_WARN_IF(!aAnchorRef.IsSet()) || NS_WARN_IF(!aFocusRef.IsSet())) {
   4350    aRv.Throw(NS_ERROR_INVALID_ARG);
   4351    return;
   4352  }
   4353 
   4354  if (!HasSameRootOrSameComposedDoc(*aAnchorRef.GetContainer()) ||
   4355      !HasSameRootOrSameComposedDoc(*aFocusRef.GetContainer())) {
   4356    // Return with no error
   4357    return;
   4358  }
   4359 
   4360  // Prevent "selectionchange" event temporarily because it should be fired
   4361  // after we set the direction.
   4362  // XXX If they are disconnected, shouldn't we return error before allocating
   4363  //     new nsRange instance?
   4364  SelectionBatcher batch(this, __FUNCTION__);
   4365  const Maybe<int32_t> order =
   4366      StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
   4367              !IsEditorSelection()
   4368          ? nsContentUtils::ComparePoints<TreeKind::Flat>(aAnchorRef, aFocusRef)
   4369          : nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
   4370                aAnchorRef, aFocusRef);
   4371  if (order && (*order <= 0)) {
   4372    SetStartAndEndInternal(aInLimiter, aAnchorRef, aFocusRef, eDirNext, aRv);
   4373    return;
   4374  }
   4375 
   4376  // If there's no `order`, the range will be collapsed, unless another error is
   4377  // detected before.
   4378  SetStartAndEndInternal(aInLimiter, aFocusRef, aAnchorRef, eDirPrevious, aRv);
   4379 }
   4380 
   4381 void Selection::SetStartAndEndInLimiter(const RawRangeBoundary& aStartRef,
   4382                                        const RawRangeBoundary& aEndRef,
   4383                                        ErrorResult& aRv) {
   4384  if (NeedsToLogSelectionAPI(*this)) {
   4385    LogSelectionAPI(this, __FUNCTION__, "aStartRef", aStartRef, "aEndRef",
   4386                    aEndRef);
   4387    LogStackForSelectionAPI();
   4388  }
   4389 
   4390  SetStartAndEndInternal(InLimiter::eYes, aStartRef, aEndRef, eDirNext, aRv);
   4391 }
   4392 
   4393 Result<Ok, nsresult> Selection::SetStartAndEndInLimiter(
   4394    nsINode& aStartContainer, uint32_t aStartOffset, nsINode& aEndContainer,
   4395    uint32_t aEndOffset, nsDirection aDirection, int16_t aReason) {
   4396  MOZ_ASSERT(aDirection == eDirPrevious || aDirection == eDirNext);
   4397  if (NeedsToLogSelectionAPI(*this)) {
   4398    LogSelectionAPI(this, __FUNCTION__, "aStartContainer", aStartContainer,
   4399                    "aStartOffset", aStartOffset, "aEndContainer",
   4400                    aEndContainer, "aEndOffset", aEndOffset, "nsDirection",
   4401                    aDirection, "aReason", aReason);
   4402    LogStackForSelectionAPI();
   4403  }
   4404 
   4405  if (mFrameSelection) {
   4406    mFrameSelection->AddChangeReasons(aReason);
   4407  }
   4408 
   4409  ErrorResult error;
   4410  SetStartAndEndInternal(
   4411      InLimiter::eYes, RawRangeBoundary(&aStartContainer, aStartOffset),
   4412      RawRangeBoundary(&aEndContainer, aEndOffset), aDirection, error);
   4413  MOZ_TRY(error.StealNSResult());
   4414  return Ok();
   4415 }
   4416 
   4417 void Selection::SetStartAndEnd(const RawRangeBoundary& aStartRef,
   4418                               const RawRangeBoundary& aEndRef,
   4419                               ErrorResult& aRv) {
   4420  if (NeedsToLogSelectionAPI(*this)) {
   4421    LogSelectionAPI(this, __FUNCTION__, "aStartRef", aStartRef, "aEndRef",
   4422                    aEndRef);
   4423    LogStackForSelectionAPI();
   4424  }
   4425 
   4426  SetStartAndEndInternal(InLimiter::eNo, aStartRef, aEndRef, eDirNext, aRv);
   4427 }
   4428 
   4429 void Selection::SetStartAndEndInternal(InLimiter aInLimiter,
   4430                                       const RawRangeBoundary& aStartRef,
   4431                                       const RawRangeBoundary& aEndRef,
   4432                                       nsDirection aDirection,
   4433                                       ErrorResult& aRv) {
   4434  if (NS_WARN_IF(!aStartRef.IsSet()) || NS_WARN_IF(!aEndRef.IsSet())) {
   4435    aRv.Throw(NS_ERROR_INVALID_ARG);
   4436    return;
   4437  }
   4438 
   4439  // Don't fire "selectionchange" event until everything done.
   4440  SelectionBatcher batch(this, __FUNCTION__);
   4441 
   4442  if (aInLimiter == InLimiter::eYes) {
   4443    if (!mFrameSelection ||
   4444        !mFrameSelection->NodeIsInLimiters(aStartRef.GetContainer())) {
   4445      aRv.Throw(NS_ERROR_FAILURE);
   4446      return;
   4447    }
   4448    if (aStartRef.GetContainer() != aEndRef.GetContainer() &&
   4449        !mFrameSelection->NodeIsInLimiters(aEndRef.GetContainer())) {
   4450      aRv.Throw(NS_ERROR_FAILURE);
   4451      return;
   4452    }
   4453  }
   4454 
   4455  RefPtr<nsRange> newRange = nsRange::Create(
   4456      aStartRef, aEndRef, aRv,
   4457      StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
   4458              aInLimiter == InLimiter::eNo
   4459          ? AllowRangeCrossShadowBoundary::Yes
   4460          : AllowRangeCrossShadowBoundary::No);
   4461  if (aRv.Failed()) {
   4462    return;
   4463  }
   4464 
   4465  RemoveAllRangesInternal(aRv);
   4466  if (aRv.Failed()) {
   4467    return;
   4468  }
   4469 
   4470  RefPtr<Document> document(GetDocument());
   4471  AddRangeAndSelectFramesAndNotifyListenersInternal(*newRange, document, aRv);
   4472  if (aRv.Failed()) {
   4473    return;
   4474  }
   4475 
   4476  // Adding a range may set 2 or more ranges if there are non-selectable
   4477  // contents only when this change is caused by a user operation.  Therefore,
   4478  // we need to select frames with the result in such case.
   4479  if (mUserInitiated) {
   4480    RefPtr<nsPresContext> presContext = GetPresContext();
   4481    if (mStyledRanges.Length() > 1 && presContext) {
   4482      SelectFramesInAllRanges(presContext);
   4483    }
   4484  }
   4485 
   4486  SetDirection(aDirection);
   4487 }
   4488 
   4489 /** SelectionLanguageChange modifies the cursor Bidi level after a change in
   4490 * keyboard direction
   4491 *  @param aLangRTL is true if the new language is right-to-left or false if the
   4492 * new language is left-to-right
   4493 */
   4494 nsresult Selection::SelectionLanguageChange(bool aLangRTL) {
   4495  if (!mFrameSelection) {
   4496    return NS_ERROR_NOT_INITIALIZED;
   4497  }
   4498 
   4499  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   4500 
   4501  // if the direction of the language hasn't changed, nothing to do
   4502  mozilla::intl::BidiEmbeddingLevel kbdBidiLevel =
   4503      aLangRTL ? mozilla::intl::BidiEmbeddingLevel::RTL()
   4504               : mozilla::intl::BidiEmbeddingLevel::LTR();
   4505  if (kbdBidiLevel == frameSelection->mKbdBidiLevel) {
   4506    return NS_OK;
   4507  }
   4508 
   4509  frameSelection->mKbdBidiLevel = kbdBidiLevel;
   4510 
   4511  PrimaryFrameData focusFrameData = GetPrimaryFrameForCaretAtFocusNode(false);
   4512  if (!focusFrameData.mFrame) {
   4513    return NS_ERROR_FAILURE;
   4514  }
   4515 
   4516  auto [frameStart, frameEnd] = focusFrameData.mFrame->GetOffsets();
   4517  RefPtr<nsPresContext> context = GetPresContext();
   4518  mozilla::intl::BidiEmbeddingLevel levelBefore, levelAfter;
   4519  if (!context) {
   4520    return NS_ERROR_FAILURE;
   4521  }
   4522 
   4523  mozilla::intl::BidiEmbeddingLevel level =
   4524      focusFrameData.mFrame->GetEmbeddingLevel();
   4525  int32_t focusOffset = static_cast<int32_t>(FocusOffset());
   4526  if ((focusOffset != frameStart) && (focusOffset != frameEnd))
   4527    // the cursor is not at a frame boundary, so the level of both the
   4528    // characters (logically) before and after the cursor is equal to the frame
   4529    // level
   4530    levelBefore = levelAfter = level;
   4531  else {
   4532    // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find
   4533    // the level of the characters before and after the cursor
   4534    nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
   4535    nsPrevNextBidiLevels levels =
   4536        frameSelection->GetPrevNextBidiLevels(focusContent, focusOffset, false);
   4537 
   4538    levelBefore = levels.mLevelBefore;
   4539    levelAfter = levels.mLevelAfter;
   4540  }
   4541 
   4542  if (levelBefore.IsSameDirection(levelAfter)) {
   4543    // if cursor is between two characters with the same orientation, changing
   4544    // the keyboard language must toggle the cursor level between the level of
   4545    // the character with the lowest level (if the new language corresponds to
   4546    // the orientation of that character) and this level plus 1 (if the new
   4547    // language corresponds to the opposite orientation)
   4548    if ((level != levelBefore) && (level != levelAfter)) {
   4549      level = std::min(levelBefore, levelAfter);
   4550    }
   4551    if (level.IsSameDirection(kbdBidiLevel)) {
   4552      frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(level);
   4553    } else {
   4554      frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(
   4555          mozilla::intl::BidiEmbeddingLevel(level + 1));
   4556    }
   4557  } else {
   4558    // if cursor is between characters with opposite orientations, changing the
   4559    // keyboard language must change the cursor level to that of the adjacent
   4560    // character with the orientation corresponding to the new language.
   4561    if (levelBefore.IsSameDirection(kbdBidiLevel)) {
   4562      frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(levelBefore);
   4563    } else {
   4564      frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(levelAfter);
   4565    }
   4566  }
   4567 
   4568  // The caret might have moved, so invalidate the desired position
   4569  // for future usages of up-arrow or down-arrow
   4570  frameSelection->InvalidateDesiredCaretPos();
   4571 
   4572  return NS_OK;
   4573 }
   4574 
   4575 void Selection::SetColors(const nsAString& aForegroundColor,
   4576                          const nsAString& aBackgroundColor,
   4577                          const nsAString& aAltForegroundColor,
   4578                          const nsAString& aAltBackgroundColor,
   4579                          ErrorResult& aRv) {
   4580  if (mSelectionType != SelectionType::eFind) {
   4581    aRv.Throw(NS_ERROR_FAILURE);
   4582    return;
   4583  }
   4584 
   4585  mCustomColors.reset(new SelectionCustomColors);
   4586 
   4587  constexpr auto currentColorStr = u"currentColor"_ns;
   4588  constexpr auto transparentStr = u"transparent"_ns;
   4589 
   4590  if (!aForegroundColor.Equals(currentColorStr)) {
   4591    nscolor foregroundColor;
   4592    nsAttrValue aForegroundColorValue;
   4593    aForegroundColorValue.ParseColor(aForegroundColor);
   4594    if (!aForegroundColorValue.GetColorValue(foregroundColor)) {
   4595      aRv.Throw(NS_ERROR_INVALID_ARG);
   4596      return;
   4597    }
   4598    mCustomColors->mForegroundColor = Some(foregroundColor);
   4599  } else {
   4600    mCustomColors->mForegroundColor = Nothing();
   4601  }
   4602 
   4603  if (!aBackgroundColor.Equals(transparentStr)) {
   4604    nscolor backgroundColor;
   4605    nsAttrValue aBackgroundColorValue;
   4606    aBackgroundColorValue.ParseColor(aBackgroundColor);
   4607    if (!aBackgroundColorValue.GetColorValue(backgroundColor)) {
   4608      aRv.Throw(NS_ERROR_INVALID_ARG);
   4609      return;
   4610    }
   4611    mCustomColors->mBackgroundColor = Some(backgroundColor);
   4612  } else {
   4613    mCustomColors->mBackgroundColor = Nothing();
   4614  }
   4615 
   4616  if (!aAltForegroundColor.Equals(currentColorStr)) {
   4617    nscolor altForegroundColor;
   4618    nsAttrValue aAltForegroundColorValue;
   4619    aAltForegroundColorValue.ParseColor(aAltForegroundColor);
   4620    if (!aAltForegroundColorValue.GetColorValue(altForegroundColor)) {
   4621      aRv.Throw(NS_ERROR_INVALID_ARG);
   4622      return;
   4623    }
   4624    mCustomColors->mAltForegroundColor = Some(altForegroundColor);
   4625  } else {
   4626    mCustomColors->mAltForegroundColor = Nothing();
   4627  }
   4628 
   4629  if (!aAltBackgroundColor.Equals(transparentStr)) {
   4630    nscolor altBackgroundColor;
   4631    nsAttrValue aAltBackgroundColorValue;
   4632    aAltBackgroundColorValue.ParseColor(aAltBackgroundColor);
   4633    if (!aAltBackgroundColorValue.GetColorValue(altBackgroundColor)) {
   4634      aRv.Throw(NS_ERROR_INVALID_ARG);
   4635      return;
   4636    }
   4637    mCustomColors->mAltBackgroundColor = Some(altBackgroundColor);
   4638  } else {
   4639    mCustomColors->mAltBackgroundColor = Nothing();
   4640  }
   4641 }
   4642 
   4643 void Selection::ResetColors() { mCustomColors = nullptr; }
   4644 
   4645 void Selection::SetHighlightSelectionData(
   4646    dom::HighlightSelectionData aHighlightSelectionData) {
   4647  MOZ_ASSERT(mSelectionType == SelectionType::eHighlight);
   4648  mHighlightData = std::move(aHighlightSelectionData);
   4649 }
   4650 
   4651 JSObject* Selection::WrapObject(JSContext* aCx,
   4652                                JS::Handle<JSObject*> aGivenProto) {
   4653  return mozilla::dom::Selection_Binding::Wrap(aCx, this, aGivenProto);
   4654 }
   4655 
   4656 // AutoHideSelectionChanges
   4657 AutoHideSelectionChanges::AutoHideSelectionChanges(
   4658    const nsFrameSelection* aFrame)
   4659    : AutoHideSelectionChanges(aFrame ? &aFrame->NormalSelection() : nullptr) {}
   4660 
   4661 bool Selection::HasSameRootOrSameComposedDoc(const nsINode& aNode) {
   4662  nsINode* root = aNode.SubtreeRoot();
   4663  Document* doc = GetDocument();
   4664  return doc == root || (root && doc == root->GetComposedDoc());
   4665 }