tor-browser

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

Selection.h (50306B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_Selection_h__
      8 #define mozilla_Selection_h__
      9 
     10 #include "mozilla/AutoRestore.h"
     11 #include "mozilla/EventForwards.h"
     12 #include "mozilla/PresShellForwards.h"
     13 #include "mozilla/RangeBoundary.h"
     14 #include "mozilla/SelectionChangeEventDispatcher.h"
     15 #include "mozilla/UniquePtr.h"
     16 #include "mozilla/WeakPtr.h"
     17 #include "mozilla/dom/Highlight.h"
     18 #include "mozilla/dom/StyledRange.h"
     19 #include "mozilla/intl/BidiEmbeddingLevel.h"
     20 #include "nsDirection.h"
     21 #include "nsISelectionController.h"
     22 #include "nsISelectionListener.h"
     23 #include "nsRange.h"
     24 #include "nsTArrayForwardDeclare.h"
     25 #include "nsThreadUtils.h"
     26 #include "nsWeakReference.h"
     27 #include "nsWrapperCache.h"
     28 
     29 struct CachedOffsetForFrame;
     30 class AutoScroller;
     31 class nsIFrame;
     32 class nsFrameSelection;
     33 class nsPIDOMWindowOuter;
     34 struct SelectionDetails;
     35 struct SelectionCustomColors;
     36 class nsCopySupport;
     37 class nsHTMLCopyEncoder;
     38 class nsPresContext;
     39 struct nsPoint;
     40 struct nsRect;
     41 
     42 namespace mozilla {
     43 class AccessibleCaretEventHub;
     44 class ErrorResult;
     45 class HTMLEditor;
     46 class PostContentIterator;
     47 enum class CaretAssociationHint;
     48 enum class TableSelectionMode : uint32_t;
     49 struct AutoPrepareFocusRange;
     50 struct PrimaryFrameData;
     51 namespace dom {
     52 class DocGroup;
     53 class ShadowRootOrGetComposedRangesOptions;
     54 }  // namespace dom
     55 }  // namespace mozilla
     56 
     57 namespace mozilla {
     58 
     59 enum class SelectionScrollMode : uint8_t {
     60  // Don't scroll synchronously. We'll flush when the scroll event fires so we
     61  // make sure to scroll to the right place.
     62  Async,
     63  // Scroll synchronously, without flushing layout.
     64  SyncNoFlush,
     65  // Scroll synchronously, flushing layout. You MUST hold a strong ref on
     66  // 'this' for the duration of this call.  This might destroy arbitrary
     67  // layout objects.
     68  SyncFlush,
     69 };
     70 
     71 namespace dom {
     72 
     73 /**
     74 * This cache allows to store all selected nodes during a reflow operation.
     75 *
     76 * All fully selected nodes are stored in a hash set per-selection instance.
     77 * This allows fast paths in `nsINode::IsSelected()` and
     78 * `Selection::LookupSelection()`. For partially selected nodes, the old
     79 * mechanisms are used. This is okay, because for partially selected nodes
     80 * no expensive node traversal is necessary.
     81 *
     82 * This cache is designed to be used in a context where no script is allowed
     83 * to run. It assumes that the selection itself, or any range therein, does not
     84 * change during its lifetime.
     85 *
     86 * By design, this class can only be instantiated in the `PresShell`.
     87 */
     88 class MOZ_RAII SelectionNodeCache final {
     89 public:
     90  ~SelectionNodeCache();
     91  /**
     92   * Returns true if `aNode` is fully selected by any of the given selections.
     93   *
     94   * This method will collect all fully selected nodes of `aSelections` and
     95   * store them internally (therefore this method isn't const).
     96   */
     97  bool MaybeCollectNodesAndCheckIfFullySelectedInAnyOf(
     98      const nsINode* aNode, const nsTArray<Selection*>& aSelections);
     99 
    100  /**
    101   * Returns true if `aNode` is fully selected by any range in `aSelection`.
    102   *
    103   * This method collects all fully selected nodes from `aSelection` and store
    104   * them internally.
    105   */
    106  bool MaybeCollectNodesAndCheckIfFullySelected(const nsINode* aNode,
    107                                                const Selection* aSelection) {
    108    return MaybeCollect(aSelection).Contains(aNode);
    109  }
    110 
    111 private:
    112  /**
    113   * This class is supposed to be only created by the PresShell.
    114   */
    115  friend PresShell;
    116  explicit SelectionNodeCache(PresShell& aOwningPresShell);
    117  /**
    118   * Iterates all ranges in `aSelection` and collects its fully selected nodes
    119   * into a hash set, which is also returned.
    120   *
    121   * If `aSelection` is already cached, the hash set is returned directly.
    122   */
    123  const nsTHashSet<const nsINode*>& MaybeCollect(const Selection* aSelection);
    124 
    125  nsTHashMap<const Selection*, nsTHashSet<const nsINode*>> mSelectedNodes;
    126 
    127  PresShell& mOwningPresShell;
    128 };
    129 
    130 // Note, the ownership of mozilla::dom::Selection depends on which way the
    131 // object is created. When nsFrameSelection has created Selection,
    132 // addreffing/releasing the Selection object is aggregated to nsFrameSelection.
    133 // Otherwise normal addref/release is used.  This ensures that nsFrameSelection
    134 // is never deleted before its Selections.
    135 class Selection final : public nsSupportsWeakReference,
    136                        public nsWrapperCache,
    137                        public SupportsWeakPtr {
    138  using AllowRangeCrossShadowBoundary =
    139      mozilla::dom::AllowRangeCrossShadowBoundary;
    140  using IsUnlinking = AbstractRange::IsUnlinking;
    141 
    142 protected:
    143  virtual ~Selection();
    144 
    145 public:
    146  /**
    147   * @param aFrameSelection can be nullptr.
    148   */
    149  explicit Selection(SelectionType aSelectionType,
    150                     nsFrameSelection* aFrameSelection);
    151 
    152  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    153  NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Selection)
    154 
    155  /**
    156   * Match this up with EndbatchChanges. will stop ui updates while multiple
    157   * selection methods are called
    158   *
    159   * @param aDetails string to explian why this is called.  This won't be
    160   * stored nor exposed to selection listeners etc.  Just for logging.
    161   */
    162  void StartBatchChanges(const char* aDetails);
    163 
    164  /**
    165   * Match this up with StartBatchChanges
    166   *
    167   * @param aDetails string to explian why this is called.  This won't be
    168   * stored nor exposed to selection listeners etc.  Just for logging.
    169   * @param aReasons potentially multiple of the reasons defined in
    170   * nsISelectionListener.idl
    171   */
    172  MOZ_CAN_RUN_SCRIPT void EndBatchChanges(
    173      const char* aDetails, int16_t aReason = nsISelectionListener::NO_REASON);
    174 
    175  /**
    176   * NotifyAutoCopy() starts to notify AutoCopyListener of selection changes.
    177   */
    178  void NotifyAutoCopy() {
    179    MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
    180 
    181    mNotifyAutoCopy = true;
    182  }
    183 
    184  /**
    185   * MaybeNotifyAccessibleCaretEventHub() starts to notify
    186   * AccessibleCaretEventHub of selection change if aPresShell has it.
    187   */
    188  void MaybeNotifyAccessibleCaretEventHub(PresShell* aPresShell);
    189 
    190  /**
    191   * StopNotifyingAccessibleCaretEventHub() stops notifying
    192   * AccessibleCaretEventHub of selection change.
    193   */
    194  void StopNotifyingAccessibleCaretEventHub();
    195 
    196  /**
    197   * EnableSelectionChangeEvent() starts to notify
    198   * SelectionChangeEventDispatcher of selection change to dispatch a
    199   * selectionchange event at every selection change.
    200   */
    201  void EnableSelectionChangeEvent() {
    202    if (!mSelectionChangeEventDispatcher) {
    203      mSelectionChangeEventDispatcher = new SelectionChangeEventDispatcher();
    204    }
    205  }
    206 
    207  // Required for WebIDL bindings, see
    208  // https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings#Adding_WebIDL_bindings_to_a_class.
    209  Document* GetParentObject() const;
    210 
    211  DocGroup* GetDocGroup() const;
    212 
    213  // utility methods for scrolling the selection into view
    214  nsPresContext* GetPresContext() const;
    215  PresShell* GetPresShell() const;
    216  nsFrameSelection* GetFrameSelection() const { return mFrameSelection; }
    217  // Returns a rect containing the selection region, and frame that that
    218  // position is relative to. For SELECTION_ANCHOR_REGION or
    219  // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
    220  // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus
    221  // region rects.
    222  nsIFrame* GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect);
    223  // Returns the position of the region (SELECTION_ANCHOR_REGION or
    224  // SELECTION_FOCUS_REGION only), and frame that that position is relative to.
    225  // The 'position' is a zero-width rectangle.
    226  nsIFrame* GetSelectionEndPointGeometry(SelectionRegion aRegion,
    227                                         nsRect* aRect);
    228 
    229  nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion,
    230                                            ScrollFlags aFlags,
    231                                            ScrollAxis aVertical,
    232                                            ScrollAxis aHorizontal);
    233 
    234  MOZ_CAN_RUN_SCRIPT nsresult ScrollIntoView(
    235      SelectionRegion, ScrollAxis aVertical = ScrollAxis(),
    236      ScrollAxis aHorizontal = ScrollAxis(), ScrollFlags = ScrollFlags::None,
    237      SelectionScrollMode = SelectionScrollMode::Async);
    238 
    239 private:
    240  static bool IsUserSelectionCollapsed(
    241      const nsRange& aRange, nsTArray<RefPtr<nsRange>>& aTempRangesToAdd);
    242  /**
    243   * https://w3c.github.io/selection-api/#selectstart-event.
    244   */
    245  enum class DispatchSelectstartEvent {
    246    No,
    247    Maybe,
    248  };
    249 
    250  /**
    251   * See `AddRangesForSelectableNodes`.
    252   */
    253  [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AddRangesForUserSelectableNodes(
    254      nsRange* aRange, Maybe<size_t>* aOutIndex,
    255      const DispatchSelectstartEvent aDispatchSelectstartEvent);
    256 
    257  /**
    258   * Adds aRange to this Selection.  If mUserInitiated is true,
    259   * then aRange is first scanned for -moz-user-select:none nodes and split up
    260   * into multiple ranges to exclude those before adding the resulting ranges
    261   * to this Selection.
    262   *
    263   * @param aOutIndex points to the range last added, if at least one was added.
    264   *                  If aRange is already contained, it points to the range
    265   *                  containing it. Nothing() if mStyledRanges.mRanges was
    266   *                  empty and no range was added.
    267   */
    268  [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AddRangesForSelectableNodes(
    269      nsRange* aRange, Maybe<size_t>* aOutIndex,
    270      DispatchSelectstartEvent aDispatchSelectstartEvent);
    271 
    272  already_AddRefed<StaticRange> GetComposedRange(
    273      const AbstractRange* aRange,
    274      const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) const;
    275 
    276 public:
    277  nsresult RemoveCollapsedRanges();
    278  void Clear(nsPresContext* aPresContext,
    279             IsUnlinking aIsUnlinking = IsUnlinking::No);
    280  MOZ_CAN_RUN_SCRIPT nsresult CollapseInLimiter(nsINode* aContainer,
    281                                                uint32_t aOffset) {
    282    if (!aContainer) {
    283      return NS_ERROR_INVALID_ARG;
    284    }
    285    return CollapseInLimiter(RawRangeBoundary(aContainer, aOffset));
    286  }
    287  MOZ_CAN_RUN_SCRIPT nsresult
    288  CollapseInLimiter(const RawRangeBoundary& aPoint) {
    289    ErrorResult result;
    290    CollapseInLimiter(aPoint, result);
    291    return result.StealNSResult();
    292  }
    293  MOZ_CAN_RUN_SCRIPT void CollapseInLimiter(const RawRangeBoundary& aPoint,
    294                                            ErrorResult& aRv);
    295 
    296  MOZ_CAN_RUN_SCRIPT nsresult Extend(nsINode* aContainer, uint32_t aOffset);
    297 
    298  /**
    299   * See mStyledRanges.mRanges.
    300   */
    301  nsRange* GetRangeAt(uint32_t aIndex) const;
    302  nsRange* GetFirstRange() const { return GetRangeAt(0); }
    303  nsRange* GetLastRange() const {
    304    return RangeCount() ? GetRangeAt(RangeCount() - 1u) : nullptr;
    305  }
    306 
    307  /**
    308   * @brief Get the |AbstractRange| at |aIndex|.
    309   *
    310   * This method is safe to be called for every selection type.
    311   * However, |StaticRange|s only occur for |SelectionType::eHighlight|.
    312   * If the SelectionType may be eHighlight, this method must be called instead
    313   * of |GetRangeAt()|.
    314   *
    315   * Returns null if |aIndex| is out of bounds.
    316   */
    317  AbstractRange* GetAbstractRangeAt(uint32_t aIndex) const;
    318  // Get the anchor-to-focus range if we don't care which end is
    319  // anchor and which end is focus.
    320  const nsRange* GetAnchorFocusRange() const { return mAnchorFocusRange; }
    321 
    322  void GetDirection(nsAString& aDirection) const;
    323 
    324  nsDirection GetDirection() const { return mDirection; }
    325 
    326  void SetDirection(nsDirection aDir) { mDirection = aDir; }
    327  MOZ_CAN_RUN_SCRIPT nsresult SetAnchorFocusToRange(nsRange* aRange);
    328 
    329  MOZ_CAN_RUN_SCRIPT void ReplaceAnchorFocusRange(nsRange* aRange);
    330 
    331  void AdjustAnchorFocusForMultiRange(nsDirection aDirection);
    332 
    333  nsIFrame* GetPrimaryFrameForAnchorNode() const;
    334 
    335  /**
    336   * Get primary frame and some other data for putting caret or extending
    337   * selection at the focus point.
    338   */
    339  PrimaryFrameData GetPrimaryFrameForCaretAtFocusNode(bool aVisual) const;
    340 
    341  UniquePtr<SelectionDetails> LookUpSelection(
    342      nsIContent* aContent, uint32_t aContentOffset, uint32_t aContentLength,
    343      UniquePtr<SelectionDetails> aDetailsHead, SelectionType aSelectionType);
    344 
    345  NS_IMETHOD Repaint(nsPresContext* aPresContext);
    346 
    347  MOZ_CAN_RUN_SCRIPT
    348  nsresult StartAutoScrollTimer(nsIFrame* aFrame, const nsPoint& aPoint,
    349                                uint32_t aDelayInMs);
    350 
    351  nsresult StopAutoScrollTimer();
    352 
    353  JSObject* WrapObject(JSContext* aCx,
    354                       JS::Handle<JSObject*> aGivenProto) override;
    355 
    356  // WebIDL methods
    357  nsINode* GetAnchorNode(CallerType aCallerType = CallerType::System) const {
    358    const RangeBoundary& anchor = AnchorRef();
    359    nsINode* anchorNode = anchor.IsSet() ? anchor.GetContainer() : nullptr;
    360    if (!anchorNode || aCallerType == CallerType::System ||
    361        !anchorNode->ChromeOnlyAccess()) {
    362      return anchorNode;
    363    }
    364    // anchor is nsIContent as ChromeOnlyAccess is nsIContent-only
    365    return anchorNode->AsContent()->FindFirstNonChromeOnlyAccessContent();
    366  }
    367  uint32_t AnchorOffset(CallerType aCallerType = CallerType::System) const {
    368    const RangeBoundary& anchor = AnchorRef();
    369    if (aCallerType != CallerType::System && anchor.IsSet() &&
    370        anchor.GetContainer()->ChromeOnlyAccess()) {
    371      return 0;
    372    }
    373    const Maybe<uint32_t> offset =
    374        anchor.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
    375    return offset ? *offset : 0;
    376  }
    377  nsINode* GetFocusNode(CallerType aCallerType = CallerType::System) const {
    378    const RangeBoundary& focus = FocusRef();
    379    nsINode* focusNode = focus.IsSet() ? focus.GetContainer() : nullptr;
    380    if (!focusNode || aCallerType == CallerType::System ||
    381        !focusNode->ChromeOnlyAccess()) {
    382      return focusNode;
    383    }
    384    // focus is nsIContent as ChromeOnlyAccess is nsIContent-only
    385    return focusNode->AsContent()->FindFirstNonChromeOnlyAccessContent();
    386  }
    387  uint32_t FocusOffset(CallerType aCallerType = CallerType::System) const {
    388    const RangeBoundary& focus = FocusRef();
    389    if (aCallerType != CallerType::System && focus.IsSet() &&
    390        focus.GetContainer()->ChromeOnlyAccess()) {
    391      return 0;
    392    }
    393    const Maybe<uint32_t> offset =
    394        focus.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
    395    return offset ? *offset : 0;
    396  }
    397 
    398  nsINode* GetMayCrossShadowBoundaryAnchorNode() const {
    399    const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes);
    400    return anchor.IsSet() ? anchor.GetContainer() : nullptr;
    401  }
    402 
    403  uint32_t MayCrossShadowBoundaryAnchorOffset() const {
    404    const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes);
    405    const Maybe<uint32_t> offset =
    406        anchor.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
    407    return offset ? *offset : 0;
    408  }
    409 
    410  nsINode* GetMayCrossShadowBoundaryFocusNode() const {
    411    const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes);
    412    return focus.IsSet() ? focus.GetContainer() : nullptr;
    413  }
    414 
    415  uint32_t MayCrossShadowBoundaryFocusOffset() const {
    416    const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes);
    417    const Maybe<uint32_t> offset =
    418        focus.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
    419    return offset ? *offset : 0;
    420  }
    421 
    422  nsIContent* GetChildAtAnchorOffset() {
    423    const RangeBoundary& anchor = AnchorRef();
    424    return anchor.IsSet() ? anchor.GetChildAtOffset() : nullptr;
    425  }
    426  nsIContent* GetChildAtFocusOffset() {
    427    const RangeBoundary& focus = FocusRef();
    428    return focus.IsSet() ? focus.GetChildAtOffset() : nullptr;
    429  }
    430 
    431  const RangeBoundary& AnchorRef(
    432      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    433          AllowRangeCrossShadowBoundary::No) const;
    434  const RangeBoundary& FocusRef(
    435      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    436          AllowRangeCrossShadowBoundary::No) const;
    437 
    438  /*
    439   * IsCollapsed -- is the whole selection just one point, or unset?
    440   */
    441  bool IsCollapsed() const {
    442    size_t cnt = mStyledRanges.Length();
    443    if (cnt == 0) {
    444      return true;
    445    }
    446 
    447    if (cnt != 1) {
    448      return false;
    449    }
    450 
    451    return mStyledRanges.mRanges[0].mRange->Collapsed();
    452  }
    453 
    454  // Returns whether both normal range and cross-shadow-boundary
    455  // range are collapsed.
    456  //
    457  // If StaticPrefs::dom_shadowdom_selection_across_boundary_enabled is
    458  // disabled, this method always returns result as nsRange::IsCollapsed.
    459  bool AreNormalAndCrossShadowBoundaryRangesCollapsed() const {
    460    if (!IsCollapsed()) {
    461      return false;
    462    }
    463 
    464    size_t cnt = mStyledRanges.Length();
    465    if (cnt == 0) {
    466      return true;
    467    }
    468 
    469    AbstractRange* range = mStyledRanges.mRanges[0].mRange;
    470    if (range->MayCrossShadowBoundary()) {
    471      return range->AsDynamicRange()->CrossShadowBoundaryRangeCollapsed();
    472    }
    473 
    474    return true;
    475  }
    476 
    477  // *JS() methods are mapped to Selection.*().
    478  // They may move focus only when the range represents normal selection.
    479  // These methods shouldn't be used by non-JS callers.
    480  MOZ_CAN_RUN_SCRIPT void CollapseJS(nsINode* aContainer, uint32_t aOffset,
    481                                     mozilla::ErrorResult& aRv);
    482  MOZ_CAN_RUN_SCRIPT void CollapseToStartJS(mozilla::ErrorResult& aRv);
    483  MOZ_CAN_RUN_SCRIPT void CollapseToEndJS(mozilla::ErrorResult& aRv);
    484 
    485  MOZ_CAN_RUN_SCRIPT void ExtendJS(nsINode& aContainer, uint32_t aOffset,
    486                                   mozilla::ErrorResult& aRv);
    487 
    488  MOZ_CAN_RUN_SCRIPT void SelectAllChildrenJS(nsINode& aNode,
    489                                              mozilla::ErrorResult& aRv);
    490 
    491  /**
    492   * Deletes this selection from document the nodes belong to.
    493   * Only if this has `SelectionType::eNormal`.
    494   */
    495  MOZ_CAN_RUN_SCRIPT void DeleteFromDocument(mozilla::ErrorResult& aRv);
    496 
    497  uint32_t RangeCount() const { return mStyledRanges.Length(); }
    498 
    499  void GetType(nsAString& aOutType) const;
    500 
    501  nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv);
    502  MOZ_CAN_RUN_SCRIPT void AddRangeJS(nsRange& aRange,
    503                                     mozilla::ErrorResult& aRv);
    504 
    505  /**
    506   * Callers need to keep `aRange` alive.
    507   */
    508  MOZ_CAN_RUN_SCRIPT void RemoveRangeAndUnselectFramesAndNotifyListeners(
    509      AbstractRange& aRange, mozilla::ErrorResult& aRv);
    510 
    511  MOZ_CAN_RUN_SCRIPT void RemoveAllRanges(mozilla::ErrorResult& aRv);
    512 
    513  // https://www.w3.org/TR/selection-api/#ref-for-dom-selection-getcomposedranges-1
    514  void GetComposedRanges(
    515      const ShadowRootOrGetComposedRangesOptions&
    516          aShadowRootOrGetComposedRangesOptions,
    517      const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots,
    518      nsTArray<RefPtr<StaticRange>>& aComposedRanges);
    519 
    520  /**
    521   * Whether Stringify should flush layout or not.
    522   */
    523  enum class FlushFrames { No, Yes };
    524  MOZ_CAN_RUN_SCRIPT
    525  void Stringify(nsAString& aResult,
    526                 CallerType aCallerType = CallerType::System,
    527                 FlushFrames = FlushFrames::Yes);
    528 
    529  /**
    530   * Indicates whether the node is part of the selection. If partlyContained
    531   * is true, the function returns true when some part of the node
    532   * is part of the selection. If partlyContained is false, the
    533   * function only returns true when the entire node is part of the selection.
    534   */
    535  bool ContainsNode(nsINode& aNode, bool aPartlyContained,
    536                    mozilla::ErrorResult& aRv);
    537 
    538  /**
    539   * Check to see if the given point is contained within the selection area. In
    540   * particular, this iterates through all the rects that make up the selection,
    541   * not just the bounding box, and checks to see if the given point is
    542   * contained in any one of them.
    543   * @param aPoint The point to check, relative to the root frame.
    544   */
    545  bool ContainsPoint(const nsPoint& aPoint);
    546 
    547  /**
    548   * Modifies the selection.  Note that the parameters are case-insensitive.
    549   *
    550   * @param alter can be one of { "move", "extend" }
    551   *   - "move" collapses the selection to the end of the selection and
    552   *      applies the movement direction/granularity to the collapsed
    553   *      selection.
    554   *   - "extend" leaves the start of the selection unchanged, and applies
    555   *      movement direction/granularity to the end of the selection.
    556   * @param direction can be one of { "forward", "backward", "left", "right" }
    557   * @param granularity can be one of { "character", "word",
    558   *                                    "line", "lineboundary" }
    559   */
    560  MOZ_CAN_RUN_SCRIPT void Modify(const nsAString& aAlter,
    561                                 const nsAString& aDirection,
    562                                 const nsAString& aGranularity);
    563 
    564  MOZ_CAN_RUN_SCRIPT
    565  void SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset,
    566                          nsINode& aFocusNode, uint32_t aFocusOffset,
    567                          mozilla::ErrorResult& aRv);
    568 
    569  bool GetInterlinePositionJS(mozilla::ErrorResult& aRv) const;
    570  void SetInterlinePositionJS(bool aHintRight, mozilla::ErrorResult& aRv);
    571 
    572  enum class InterlinePosition : uint8_t {
    573    // Caret should be put at end of line (i.e., before the line break)
    574    EndOfLine,
    575    // Caret should be put at start of next line (i.e., after the line break)
    576    StartOfNextLine,
    577    // Undefined means only what is not EndOfLine nor StartOfNextLine.
    578    // `SetInterlinePosition` should never be called with this value, and
    579    // if `GetInterlinePosition` returns this, it means that the instance has
    580    // not been initialized or cleared by the cycle collector or something.
    581    // If a method needs to consider whether to call `SetInterlinePosition` or
    582    // not call, this value can be used for the latter.
    583    Undefined,
    584  };
    585  InterlinePosition GetInterlinePosition() const;
    586  nsresult SetInterlinePosition(InterlinePosition aInterlinePosition);
    587 
    588  Nullable<int16_t> GetCaretBidiLevel(mozilla::ErrorResult& aRv) const;
    589  void SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel,
    590                         mozilla::ErrorResult& aRv);
    591 
    592  void ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags,
    593                          int32_t aWrapColumn, nsAString& aReturn,
    594                          mozilla::ErrorResult& aRv);
    595  void AddSelectionListener(nsISelectionListener* aListener);
    596  void RemoveSelectionListener(nsISelectionListener* aListener);
    597 
    598  RawSelectionType RawType() const {
    599    return ToRawSelectionType(mSelectionType);
    600  }
    601  SelectionType Type() const { return mSelectionType; }
    602 
    603  /**
    604   * @brief Sets highlight selection properties.
    605   *
    606   * This includes the highlight name as well as its priority and type.
    607   */
    608  void SetHighlightSelectionData(
    609      dom::HighlightSelectionData aHighlightSelectionData);
    610 
    611  const dom::HighlightSelectionData& HighlightSelectionData() const {
    612    return mHighlightData;
    613  }
    614 
    615  /**
    616   * See documentation of `GetRangesForInterval` in Selection.webidl.
    617   *
    618   * @param aReturn references, not copies, of the internal ranges.
    619   */
    620  void GetRangesForInterval(nsINode& aBeginNode, uint32_t aBeginOffset,
    621                            nsINode& aEndNode, uint32_t aEndOffset,
    622                            bool aAllowAdjacent,
    623                            nsTArray<RefPtr<nsRange>>& aReturn,
    624                            ErrorResult& aRv);
    625 
    626  void SetColors(const nsAString& aForeColor, const nsAString& aBackColor,
    627                 const nsAString& aAltForeColor, const nsAString& aAltBackColor,
    628                 ErrorResult& aRv);
    629 
    630  void ResetColors();
    631 
    632  /**
    633   * Non-JS callers should use the following
    634   * collapse/collapseToStart/extend/etc methods, instead of the *JS
    635   * versions that bindings call.
    636   */
    637 
    638  /**
    639   * Collapses the selection to a single point, at the specified offset
    640   * in the given node. When the selection is collapsed, and the content
    641   * is focused and editable, the caret will blink there.
    642   * @param aContainer The given node where the selection will be set
    643   * @param aOffset     Where in given dom node to place the selection (the
    644   *                    offset into the given node)
    645   */
    646  MOZ_CAN_RUN_SCRIPT void CollapseInLimiter(nsINode& aContainer,
    647                                            uint32_t aOffset,
    648                                            ErrorResult& aRv) {
    649    CollapseInLimiter(RawRangeBoundary(&aContainer, aOffset), aRv);
    650  }
    651 
    652 private:
    653  enum class InLimiter {
    654    // If eYes, the method may reset selection limiter and move focus if the
    655    // given range is out of the limiter.
    656    eYes,
    657    // If eNo, the method won't reset selection limiter.  So, if given range
    658    // is out of bounds, the method may return error.
    659    eNo,
    660  };
    661  MOZ_CAN_RUN_SCRIPT
    662  void CollapseInternal(InLimiter aInLimiter, const RawRangeBoundary& aPoint,
    663                        ErrorResult& aRv);
    664 
    665 public:
    666  /**
    667   * Collapses the whole selection to a single point at the start
    668   * of the current selection (irrespective of direction).  If content
    669   * is focused and editable, the caret will blink there.
    670   */
    671  MOZ_CAN_RUN_SCRIPT void CollapseToStart(mozilla::ErrorResult& aRv);
    672 
    673  /**
    674   * Collapses the whole selection to a single point at the end
    675   * of the current selection (irrespective of direction).  If content
    676   * is focused and editable, the caret will blink there.
    677   */
    678  MOZ_CAN_RUN_SCRIPT void CollapseToEnd(mozilla::ErrorResult& aRv);
    679 
    680  /**
    681   * Extends the selection by moving the selection end to the specified node and
    682   * offset, preserving the selection begin position. The new selection end
    683   * result will always be from the anchorNode to the new focusNode, regardless
    684   * of direction.
    685   *
    686   * @param aContainer The node where the selection will be extended to
    687   * @param aOffset    Where in aContainer to place the offset of the new
    688   *                   selection end.
    689   */
    690  MOZ_CAN_RUN_SCRIPT void Extend(nsINode& aContainer, uint32_t aOffset,
    691                                 ErrorResult& aRv);
    692 
    693  MOZ_CAN_RUN_SCRIPT void AddRangeAndSelectFramesAndNotifyListeners(
    694      nsRange& aRange, mozilla::ErrorResult& aRv);
    695 
    696  MOZ_CAN_RUN_SCRIPT void AddHighlightRangeAndSelectFramesAndNotifyListeners(
    697      AbstractRange& aRange);
    698 
    699  /**
    700   * Adds all children of the specified node to the selection.
    701   * @param aNode the parent of the children to be added to the selection.
    702   */
    703  MOZ_CAN_RUN_SCRIPT void SelectAllChildren(nsINode& aNode,
    704                                            mozilla::ErrorResult& aRv);
    705 
    706  /**
    707   * SetStartAndEnd() removes all ranges and sets new range as given range.
    708   * Different from SetBaseAndExtent(), this won't compare the DOM points of
    709   * aStartRef and aEndRef for performance nor set direction to eDirPrevious.
    710   * Note that this may reset the limiter and move focus.  If you don't want
    711   * that, use SetStartAndEndInLimiter() instead.
    712   */
    713  MOZ_CAN_RUN_SCRIPT
    714  void SetStartAndEnd(const RawRangeBoundary& aStartRef,
    715                      const RawRangeBoundary& aEndRef, ErrorResult& aRv);
    716  MOZ_CAN_RUN_SCRIPT
    717  void SetStartAndEnd(nsINode& aStartContainer, uint32_t aStartOffset,
    718                      nsINode& aEndContainer, uint32_t aEndOffset,
    719                      ErrorResult& aRv) {
    720    SetStartAndEnd(RawRangeBoundary(&aStartContainer, aStartOffset),
    721                   RawRangeBoundary(&aEndContainer, aEndOffset), aRv);
    722  }
    723 
    724  /**
    725   * SetStartAndEndInLimiter() is similar to SetStartAndEnd(), but this respects
    726   * the selection limiter.  If all or part of given range is not in the
    727   * limiter, this returns error.
    728   */
    729  MOZ_CAN_RUN_SCRIPT
    730  void SetStartAndEndInLimiter(const RawRangeBoundary& aStartRef,
    731                               const RawRangeBoundary& aEndRef,
    732                               ErrorResult& aRv);
    733  MOZ_CAN_RUN_SCRIPT
    734  void SetStartAndEndInLimiter(nsINode& aStartContainer, uint32_t aStartOffset,
    735                               nsINode& aEndContainer, uint32_t aEndOffset,
    736                               ErrorResult& aRv) {
    737    SetStartAndEndInLimiter(RawRangeBoundary(&aStartContainer, aStartOffset),
    738                            RawRangeBoundary(&aEndContainer, aEndOffset), aRv);
    739  }
    740  MOZ_CAN_RUN_SCRIPT
    741  Result<Ok, nsresult> SetStartAndEndInLimiter(
    742      nsINode& aStartContainer, uint32_t aStartOffset, nsINode& aEndContainer,
    743      uint32_t aEndOffset, nsDirection aDirection, int16_t aReason);
    744 
    745  /**
    746   * SetBaseAndExtent() is alternative of the JS API for internal use.
    747   * Different from SetStartAndEnd(), this sets anchor and focus points as
    748   * specified, then if anchor point is after focus node, this sets the
    749   * direction to eDirPrevious.
    750   * Note that this may reset the limiter and move focus.  If you don't want
    751   * that, use SetBaseAndExtentInLimier() instead.
    752   */
    753  MOZ_CAN_RUN_SCRIPT
    754  void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
    755                        nsINode& aFocusNode, uint32_t aFocusOffset,
    756                        ErrorResult& aRv);
    757  MOZ_CAN_RUN_SCRIPT
    758  void SetBaseAndExtent(const RawRangeBoundary& aAnchorRef,
    759                        const RawRangeBoundary& aFocusRef, ErrorResult& aRv);
    760 
    761  /**
    762   * SetBaseAndExtentInLimiter() is similar to SetBaseAndExtent(), but this
    763   * respects the selection limiter.  If all or part of given range is not in
    764   * the limiter, this returns error.
    765   */
    766  MOZ_CAN_RUN_SCRIPT
    767  void SetBaseAndExtentInLimiter(nsINode& aAnchorNode, uint32_t aAnchorOffset,
    768                                 nsINode& aFocusNode, uint32_t aFocusOffset,
    769                                 ErrorResult& aRv) {
    770    SetBaseAndExtentInLimiter(RawRangeBoundary(&aAnchorNode, aAnchorOffset),
    771                              RawRangeBoundary(&aFocusNode, aFocusOffset), aRv);
    772  }
    773  MOZ_CAN_RUN_SCRIPT
    774  void SetBaseAndExtentInLimiter(const RawRangeBoundary& aAnchorRef,
    775                                 const RawRangeBoundary& aFocusRef,
    776                                 ErrorResult& aRv);
    777 
    778  void AddSelectionChangeBlocker();
    779  void RemoveSelectionChangeBlocker();
    780  bool IsBlockingSelectionChangeEvents() const;
    781 
    782  // Whether this selection is focused in an editable element.
    783  bool IsEditorSelection() const;
    784 
    785  /**
    786   * Set the painting style for the range. The range must be a range in
    787   * the selection. The textRangeStyle will be used by text frame
    788   * when it is painting the selection.
    789   */
    790  nsresult SetTextRangeStyle(nsRange* aRange,
    791                             const TextRangeStyle& aTextRangeStyle);
    792 
    793  // Methods to manipulate our mFrameSelection's ancestor limiter.
    794  [[nodiscard]] Element* GetAncestorLimiter() const;
    795  MOZ_CAN_RUN_SCRIPT void SetAncestorLimiter(Element* aLimiter);
    796 
    797  /*
    798   * Frame Offset cache can be used just during calling
    799   * nsEditor::EndPlaceHolderTransaction. EndPlaceHolderTransaction will give
    800   * rise to reflow/refreshing view/scroll, and call times of
    801   * nsTextFrame::GetPointFromOffset whose return value is to be cached. see
    802   * bugs 35296 and 199412
    803   */
    804  void SetCanCacheFrameOffset(bool aCanCacheFrameOffset);
    805 
    806  // Selection::GetAbstractRangesForIntervalArray
    807  //
    808  //    Fills a nsTArray with the ranges overlapping the range specified by
    809  //    the given endpoints. Ranges in the selection exactly adjacent to the
    810  //    input range are not returned unless aAllowAdjacent is set.
    811  //
    812  //    For example, if the following ranges were in the selection
    813  //    (assume everything is within the same node)
    814  //
    815  //    Start Offset: 0 2 7 9
    816  //      End Offset: 2 5 9 10
    817  //
    818  //    and passed aBeginOffset of 2 and aEndOffset of 9, then with
    819  //    aAllowAdjacent set, all the ranges should be returned. If
    820  //    aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
    821  //    should be returned
    822  //
    823  //    Now that overlapping ranges are disallowed, there can be a maximum of
    824  //    2 adjacent ranges
    825  nsresult GetAbstractRangesForIntervalArray(nsINode* aBeginNode,
    826                                             uint32_t aBeginOffset,
    827                                             nsINode* aEndNode,
    828                                             uint32_t aEndOffset,
    829                                             bool aAllowAdjacent,
    830                                             nsTArray<AbstractRange*>* aRanges);
    831 
    832  /**
    833   * Converts the results of |GetAbstractRangesForIntervalArray()| to |nsRange|.
    834   *
    835   * |StaticRange|s can only occur in Selections of type |eHighlight|.
    836   * Therefore, this method must not be called for this selection type
    837   * as not every |AbstractRange| can be cast to |nsRange|.
    838   */
    839  nsresult GetDynamicRangesForIntervalArray(
    840      nsINode* aBeginNode, uint32_t aBeginOffset, nsINode* aEndNode,
    841      uint32_t aEndOffset, bool aAllowAdjacent, nsTArray<nsRange*>* aRanges);
    842 
    843  /**
    844   * Modifies the cursor Bidi level after a change in keyboard direction
    845   * @param langRTL is true if the new language is right-to-left or
    846   *                false if the new language is left-to-right.
    847   */
    848  nsresult SelectionLanguageChange(bool aLangRTL);
    849 
    850 private:
    851  bool HasSameRootOrSameComposedDoc(const nsINode& aNode);
    852 
    853  // XXX Please don't add additional uses of this method, it's only for
    854  // XXX supporting broken code (bug 1245883) in the following classes:
    855  friend class ::nsCopySupport;
    856  friend class ::nsHTMLCopyEncoder;
    857  MOZ_CAN_RUN_SCRIPT
    858  void AddRangeAndSelectFramesAndNotifyListenersInternal(nsRange& aRange,
    859                                                         Document* aDocument,
    860                                                         ErrorResult&);
    861 
    862  // Get the cached value for nsTextFrame::GetPointFromOffset.
    863  nsresult GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
    864                                nsPoint& aPoint);
    865 
    866  MOZ_CAN_RUN_SCRIPT
    867  void SetStartAndEndInternal(InLimiter aInLimiter,
    868                              const RawRangeBoundary& aStartRef,
    869                              const RawRangeBoundary& aEndRef,
    870                              nsDirection aDirection, ErrorResult& aRv);
    871  MOZ_CAN_RUN_SCRIPT
    872  void SetBaseAndExtentInternal(InLimiter aInLimiter,
    873                                const RawRangeBoundary& aAnchorRef,
    874                                const RawRangeBoundary& aFocusRef,
    875                                ErrorResult& aRv);
    876 
    877 public:
    878  SelectionType GetType() const { return mSelectionType; }
    879 
    880  SelectionCustomColors* GetCustomColors() const { return mCustomColors.get(); }
    881 
    882  MOZ_CAN_RUN_SCRIPT void NotifySelectionListeners(bool aCalledByJS);
    883  MOZ_CAN_RUN_SCRIPT void NotifySelectionListeners();
    884 
    885  bool ChangesDuringBatching() const { return mChangesDuringBatching; }
    886 
    887  friend struct AutoUserInitiated;
    888  struct MOZ_RAII AutoUserInitiated {
    889    explicit AutoUserInitiated(Selection& aSelectionRef)
    890        : AutoUserInitiated(&aSelectionRef) {}
    891    explicit AutoUserInitiated(Selection* aSelection)
    892        : mSavedValue(aSelection->mUserInitiated) {
    893      aSelection->mUserInitiated = true;
    894    }
    895    AutoRestore<bool> mSavedValue;
    896  };
    897 
    898 private:
    899  friend struct mozilla::AutoPrepareFocusRange;
    900  class ScrollSelectionIntoViewEvent;
    901  friend class ScrollSelectionIntoViewEvent;
    902 
    903  class ScrollSelectionIntoViewEvent : public Runnable {
    904   public:
    905    MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_DECL_NSIRUNNABLE
    906 
    907    ScrollSelectionIntoViewEvent(Selection* aSelection, SelectionRegion aRegion,
    908                                 ScrollAxis aVertical, ScrollAxis aHorizontal,
    909                                 ScrollFlags aFlags)
    910        : Runnable("dom::Selection::ScrollSelectionIntoViewEvent"),
    911          mSelection(aSelection),
    912          mRegion(aRegion),
    913          mVerticalScroll(aVertical),
    914          mHorizontalScroll(aHorizontal),
    915          mFlags(aFlags) {
    916      NS_ASSERTION(aSelection, "null parameter");
    917    }
    918    void Revoke() { mSelection = nullptr; }
    919 
    920   private:
    921    Selection* mSelection;
    922    SelectionRegion mRegion;
    923    ScrollAxis mVerticalScroll;
    924    ScrollAxis mHorizontalScroll;
    925    ScrollFlags mFlags;
    926  };
    927 
    928  /**
    929   * Set mAnchorFocusRange to mStyledRanges.mRanges[aIndex] if aIndex is a valid
    930   * index.
    931   */
    932  void SetAnchorFocusRange(size_t aIndex);
    933  void RemoveAnchorFocusRange() { mAnchorFocusRange = nullptr; }
    934  void SelectFramesOf(nsIContent* aContent, bool aSelected) const;
    935 
    936  /**
    937   * https://dom.spec.whatwg.org/#concept-tree-inclusive-descendant.
    938   */
    939  nsresult SelectFramesOfInclusiveDescendantsOfContent(
    940      PostContentIterator& aPostOrderIter, nsIContent* aContent,
    941      bool aSelected) const;
    942 
    943  void SelectFramesOfFlattenedTreeOfContent(nsIContent* aContent,
    944                                            bool aSelected) const;
    945 
    946  nsresult SelectFrames(nsPresContext* aPresContext, AbstractRange& aRange,
    947                        bool aSelect) const;
    948 
    949  /**
    950   * SelectFramesInAllRanges() calls SelectFrames() for all current
    951   * ranges.
    952   */
    953  void SelectFramesInAllRanges(nsPresContext* aPresContext);
    954 
    955  /**
    956   * @param aOutIndex   If some, points to the index of the range in
    957   * mStyledRanges.mRanges so that it's always in [0, mStyledRanges.Length()].
    958   * Otherwise, if nothing, this didn't add the range to mStyledRanges.
    959   */
    960  MOZ_CAN_RUN_SCRIPT nsresult MaybeAddTableCellRange(nsRange& aRange,
    961                                                     Maybe<size_t>* aOutIndex);
    962 
    963  Document* GetDocument() const;
    964 
    965  MOZ_CAN_RUN_SCRIPT void RemoveAllRangesInternal(
    966      mozilla::ErrorResult& aRv, IsUnlinking aIsUnlinking = IsUnlinking::No);
    967 
    968  void Disconnect();
    969 
    970  struct StyledRanges {
    971    explicit StyledRanges(Selection& aSelection) : mSelection(aSelection) {}
    972    void Clear();
    973 
    974    StyledRange* FindRangeData(AbstractRange* aRange);
    975 
    976    using StyledRangeArray = AutoTArray<StyledRange, 1>;
    977 
    978    StyledRangeArray::size_type Length() const;
    979 
    980    nsresult RemoveCollapsedRanges();
    981 
    982    nsresult RemoveRangeAndUnregisterSelection(AbstractRange& aRange);
    983 
    984    /**
    985     * Binary searches the given sorted array of ranges for the insertion point
    986     * for the given aBoundary. The given comparator is used, and the index
    987     * where the point should appear in the array is returned.
    988 
    989     * If there is an item in the array equal to aBoundary, we will return the
    990     index of this item.
    991     *
    992     * @return the index where the point should appear in the array. In
    993     *         [0, `aElementArray->Length()`].
    994     */
    995    template <typename PT, typename RT>
    996    static size_t FindInsertionPoint(
    997        const nsTArray<StyledRange>* aElementArray,
    998        const RangeBoundaryBase<PT, RT>& aBoundary,
    999        int32_t (*aComparator)(const RangeBoundaryBase<PT, RT>&,
   1000                               const AbstractRange&));
   1001 
   1002    /**
   1003     * Works on the same principle as GetRangesForIntervalArray, however
   1004     * instead this returns the indices into mRanges between which
   1005     * the overlapping ranges lie.
   1006     *
   1007     * @param aStartIndex If some, aEndIndex will also be some and the value of
   1008     *                    aStartIndex will be less or equal than aEndIndex.  If
   1009     *                    nothing, aEndIndex will also be nothing and it means
   1010     *                    that there is no range which in the range.
   1011     * @param aEndIndex   If some, the value is less than mRanges.Length().
   1012     */
   1013    nsresult GetIndicesForInterval(const nsINode* aBeginNode,
   1014                                   uint32_t aBeginOffset,
   1015                                   const nsINode* aEndNode, uint32_t aEndOffset,
   1016                                   bool aAllowAdjacent,
   1017                                   Maybe<size_t>& aStartIndex,
   1018                                   Maybe<size_t>& aEndIndex);
   1019 
   1020    bool HasEqualRangeBoundariesAt(const AbstractRange& aRange,
   1021                                   size_t aRangeIndex) const;
   1022 
   1023    /**
   1024     * Preserves the sorting and disjunctiveness of mRanges.
   1025     *
   1026     * @param aOutIndex If some, will point to the index of the added range, or
   1027     *                  if aRange is already contained, to the one containing
   1028     *                  it. Hence it'll always be in [0, mRanges.Length()).
   1029     *                  This is nothing only when the method returns an error.
   1030     */
   1031    MOZ_CAN_RUN_SCRIPT nsresult
   1032    MaybeAddRangeAndTruncateOverlaps(nsRange* aRange, Maybe<size_t>* aOutIndex);
   1033 
   1034    /**
   1035     * Adds the range even if there are overlaps.
   1036     */
   1037    MOZ_CAN_RUN_SCRIPT nsresult
   1038    AddRangeAndIgnoreOverlaps(AbstractRange* aRange);
   1039 
   1040    /**
   1041     * GetCommonEditingHost() returns common editing host of all
   1042     * ranges if there is. If at least one of the ranges is in non-editable
   1043     * element, returns nullptr.  See following examples for the detail:
   1044     *
   1045     *  <div id="a" contenteditable>
   1046     *    an[cestor
   1047     *    <div id="b" contenteditable="false">
   1048     *      non-editable
   1049     *      <div id="c" contenteditable>
   1050     *        desc]endant
   1051     *  in this case, this returns div#a because div#c is also in div#a.
   1052     *
   1053     *  <div id="a" contenteditable>
   1054     *    an[ce]stor
   1055     *    <div id="b" contenteditable="false">
   1056     *      non-editable
   1057     *      <div id="c" contenteditable>
   1058     *        de[sc]endant
   1059     *  in this case, this returns div#a because second range is also in div#a
   1060     *  and common ancestor of the range (i.e., div#c) is editable.
   1061     *
   1062     *  <div id="a" contenteditable>
   1063     *    an[ce]stor
   1064     *    <div id="b" contenteditable="false">
   1065     *      [non]-editable
   1066     *      <div id="c" contenteditable>
   1067     *        de[sc]endant
   1068     *  in this case, this returns nullptr because the second range is in
   1069     *  non-editable area.
   1070     */
   1071    Element* GetCommonEditingHost() const;
   1072 
   1073    MOZ_CAN_RUN_SCRIPT void MaybeFocusCommonEditingHost(
   1074        PresShell* aPresShell) const;
   1075 
   1076    static nsresult SubtractRange(StyledRange& aRange, nsRange& aSubtract,
   1077                                  nsTArray<StyledRange>* aOutput);
   1078 
   1079    void UnregisterSelection(IsUnlinking aIsUnlinking = IsUnlinking::No);
   1080 
   1081    // `mRanges` always needs to be sorted by the Range's start point.
   1082    // Especially when dealing with `StaticRange`s this is not guaranteed
   1083    // automatically. Therefore this method should be called before paint to
   1084    // ensure that any potential DOM mutations are incorporated in `mRanges`
   1085    // order. This method will also move invalid `StaticRange`s into
   1086    // `mInvalidStaticRanges` (and previously-invalid-now-valid-again
   1087    // `StaticRange`s back into `mRanges`).
   1088    void ReorderRangesIfNecessary();
   1089 
   1090    // These are the ranges inside this selection. They are kept sorted in order
   1091    // of DOM start position.
   1092    //
   1093    // This data structure is sorted by the range beginnings. As the ranges are
   1094    // disjoint, it is also implicitly sorted by the range endings. This allows
   1095    // us to perform binary searches when searching for existence of a range,
   1096    // giving us O(log n) search time.
   1097    //
   1098    // Inserting a new range requires finding the overlapping interval,
   1099    // requiring two binary searches plus up to an additional 6 DOM comparisons.
   1100    // If this proves to be a performance concern, then an interval tree may be
   1101    // a possible solution, allowing the calculation of the overlap interval in
   1102    // O(log n) time, though this would require rebalancing and other overhead.
   1103    StyledRangeArray mRanges;
   1104 
   1105    // With introduction of the custom highlight API, Selection must be able to
   1106    // hold `StaticRange`s as well. If they become invalid (eg. end is before
   1107    // start), they must be excluded from painting, but still kept.
   1108    // mRanges needs to contain valid ranges sorted correctly only. Therefore,
   1109    // invalid static ranges are being stored in this array, which is being kept
   1110    // up to date in `ReorderRangesIfNecessary()`.
   1111    StyledRangeArray mInvalidStaticRanges;
   1112 
   1113    Selection& mSelection;
   1114 
   1115    // The Document's generation for which `mRanges` have been ordered.
   1116    int32_t mDocumentGeneration{0};
   1117    // This flag indicates that ranges may have changed. It is set to true in
   1118    // `Selection::NotifySelectionListeners().`
   1119    bool mRangesMightHaveChanged{false};
   1120  };
   1121 
   1122  StyledRanges mStyledRanges{*this};
   1123 
   1124  RefPtr<nsRange> mAnchorFocusRange;
   1125  RefPtr<nsFrameSelection> mFrameSelection;
   1126  RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub;
   1127  RefPtr<SelectionChangeEventDispatcher> mSelectionChangeEventDispatcher;
   1128  RefPtr<AutoScroller> mAutoScroller;
   1129  nsTArray<nsCOMPtr<nsISelectionListener>> mSelectionListeners;
   1130  nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
   1131  CachedOffsetForFrame* mCachedOffsetForFrame;
   1132  nsDirection mDirection;
   1133  const SelectionType mSelectionType;
   1134  dom::HighlightSelectionData mHighlightData;
   1135  UniquePtr<SelectionCustomColors> mCustomColors;
   1136 
   1137  // Non-zero if we don't want any changes we make to the selection to be
   1138  // visible to content. If non-zero, content won't be notified about changes.
   1139  uint32_t mSelectionChangeBlockerCount;
   1140 
   1141  /**
   1142   * True if the current selection operation was initiated by user action.
   1143   * It determines whether we exclude -moz-user-select:none nodes or not,
   1144   * as well as whether selectstart events will be fired.
   1145   */
   1146  bool mUserInitiated;
   1147 
   1148  /**
   1149   * When the selection change is caused by a call of Selection API,
   1150   * mCalledByJS is true.  Otherwise, false.
   1151   */
   1152  bool mCalledByJS;
   1153 
   1154  /**
   1155   * true if AutoCopyListner::OnSelectionChange() should be called.
   1156   */
   1157  bool mNotifyAutoCopy;
   1158 
   1159  /**
   1160   * Indicates that this selection has changed during a batch change and
   1161   * `NotifySelectionListener()` should be called after batching ends.
   1162   *
   1163   * See `nsFrameSelection::StartBatchChanges()` and `::EndBatchChanges()`.
   1164   *
   1165   * This flag is set and reset in `NotifySelectionListener()`.
   1166   */
   1167  bool mChangesDuringBatching = false;
   1168 };
   1169 
   1170 // Stack-class to turn on/off selection batching.
   1171 class MOZ_STACK_CLASS SelectionBatcher final {
   1172 private:
   1173  const RefPtr<Selection> mSelection;
   1174  const int16_t mReasons;
   1175  const char* const mRequesterFuncName;
   1176 
   1177 public:
   1178  /**
   1179   * @param aRequesterFuncName function name which wants the selection batch.
   1180   * This won't be stored nor exposed to selection listeners etc, used only for
   1181   * logging.  This MUST be living when the destructor runs.
   1182   */
   1183  MOZ_CAN_RUN_SCRIPT explicit SelectionBatcher(
   1184      Selection& aSelectionRef, const char* aRequesterFuncName,
   1185      int16_t aReasons = nsISelectionListener::NO_REASON)
   1186      : SelectionBatcher(&aSelectionRef, aRequesterFuncName, aReasons) {}
   1187  MOZ_CAN_RUN_SCRIPT explicit SelectionBatcher(
   1188      Selection* aSelection, const char* aRequesterFuncName,
   1189      int16_t aReasons = nsISelectionListener::NO_REASON)
   1190      : mSelection(aSelection),
   1191        mReasons(aReasons),
   1192        mRequesterFuncName(aRequesterFuncName) {
   1193    if (mSelection) {
   1194      mSelection->StartBatchChanges(mRequesterFuncName);
   1195    }
   1196  }
   1197 
   1198  MOZ_CAN_RUN_SCRIPT ~SelectionBatcher() {
   1199    if (mSelection) {
   1200      MOZ_KnownLive(mSelection)->EndBatchChanges(mRequesterFuncName, mReasons);
   1201    }
   1202  }
   1203 };
   1204 
   1205 class MOZ_RAII AutoHideSelectionChanges final {
   1206 public:
   1207  explicit AutoHideSelectionChanges(const nsFrameSelection* aFrame);
   1208 
   1209  explicit AutoHideSelectionChanges(Selection& aSelectionRef)
   1210      : AutoHideSelectionChanges(&aSelectionRef) {}
   1211 
   1212  ~AutoHideSelectionChanges() {
   1213    if (mSelection) {
   1214      mSelection->RemoveSelectionChangeBlocker();
   1215    }
   1216  }
   1217 
   1218 private:
   1219  explicit AutoHideSelectionChanges(Selection* aSelection)
   1220      : mSelection(aSelection) {
   1221    if (mSelection) {
   1222      mSelection->AddSelectionChangeBlocker();
   1223    }
   1224  }
   1225 
   1226  RefPtr<Selection> mSelection;
   1227 };
   1228 
   1229 }  // namespace dom
   1230 
   1231 constexpr bool IsValidRawSelectionType(RawSelectionType aRawSelectionType) {
   1232  return aRawSelectionType >= nsISelectionController::SELECTION_NONE &&
   1233         aRawSelectionType <= nsISelectionController::SELECTION_TARGET_TEXT;
   1234 }
   1235 
   1236 constexpr SelectionType ToSelectionType(RawSelectionType aRawSelectionType) {
   1237  if (!IsValidRawSelectionType(aRawSelectionType)) {
   1238    return SelectionType::eInvalid;
   1239  }
   1240  return static_cast<SelectionType>(aRawSelectionType);
   1241 }
   1242 
   1243 constexpr RawSelectionType ToRawSelectionType(SelectionType aSelectionType) {
   1244  MOZ_ASSERT(aSelectionType != SelectionType::eInvalid);
   1245  return static_cast<RawSelectionType>(aSelectionType);
   1246 }
   1247 
   1248 constexpr RawSelectionType ToRawSelectionType(TextRangeType aTextRangeType) {
   1249  return ToRawSelectionType(ToSelectionType(aTextRangeType));
   1250 }
   1251 
   1252 constexpr SelectionTypeMask ToSelectionTypeMask(SelectionType aSelectionType) {
   1253  MOZ_ASSERT(aSelectionType != SelectionType::eInvalid);
   1254  return aSelectionType == SelectionType::eNone
   1255             ? 0
   1256             : static_cast<SelectionTypeMask>(
   1257                   1 << (static_cast<uint8_t>(aSelectionType) - 1));
   1258 }
   1259 
   1260 inline std::ostream& operator<<(
   1261    std::ostream& aStream, const dom::Selection::InterlinePosition& aPosition) {
   1262  using InterlinePosition = dom::Selection::InterlinePosition;
   1263  switch (aPosition) {
   1264    case InterlinePosition::EndOfLine:
   1265      return aStream << "InterlinePosition::EndOfLine";
   1266    case InterlinePosition::StartOfNextLine:
   1267      return aStream << "InterlinePosition::StartOfNextLine";
   1268    case InterlinePosition::Undefined:
   1269      return aStream << "InterlinePosition::Undefined";
   1270    default:
   1271      MOZ_ASSERT_UNREACHABLE("Illegal value");
   1272      return aStream << "<Illegal value>";
   1273  }
   1274 }
   1275 
   1276 }  // namespace mozilla
   1277 
   1278 inline nsresult nsISelectionController::ScrollSelectionIntoView(
   1279    mozilla::SelectionType aType, SelectionRegion aRegion,
   1280    const mozilla::ScrollAxis& aVertical = mozilla::ScrollAxis(),
   1281    const mozilla::ScrollAxis& aHorizontal = mozilla::ScrollAxis(),
   1282    mozilla::ScrollFlags aScrollFlags = mozilla::ScrollFlags::None,
   1283    mozilla::SelectionScrollMode aMode = mozilla::SelectionScrollMode::Async) {
   1284  RefPtr selection = GetSelection(mozilla::RawSelectionType(aType));
   1285  if (!selection) {
   1286    return NS_ERROR_FAILURE;
   1287  }
   1288  return selection->ScrollIntoView(aRegion, aVertical, aHorizontal,
   1289                                   aScrollFlags, aMode);
   1290 }
   1291 
   1292 inline nsresult nsISelectionController::ScrollSelectionIntoView(
   1293    mozilla::SelectionType aType, SelectionRegion aRegion,
   1294    mozilla::SelectionScrollMode aMode) {
   1295  return ScrollSelectionIntoView(aType, aRegion, mozilla::ScrollAxis(),
   1296                                 mozilla::ScrollAxis(),
   1297                                 mozilla::ScrollFlags::None, aMode);
   1298 }
   1299 
   1300 #endif  // mozilla_Selection_h__