tor-browser

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

nsRange.h (26757B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * Implementation of the DOM Range object.
      9 */
     10 
     11 #ifndef nsRange_h___
     12 #define nsRange_h___
     13 
     14 #include "mozilla/Attributes.h"
     15 #include "mozilla/ErrorResult.h"
     16 #include "mozilla/RangeBoundary.h"
     17 #include "mozilla/RefPtr.h"
     18 #include "mozilla/dom/AbstractRange.h"
     19 #include "mozilla/dom/CrossShadowBoundaryRange.h"
     20 #include "mozilla/dom/StaticRange.h"
     21 #include "nsCOMPtr.h"
     22 #include "nsStubMutationObserver.h"
     23 #include "nsWrapperCache.h"
     24 #include "prmon.h"
     25 
     26 class nsIPrincipal;
     27 
     28 namespace mozilla {
     29 class RectCallback;
     30 namespace dom {
     31 struct ClientRectsAndTexts;
     32 class DocGroup;
     33 class DocumentFragment;
     34 class DOMRect;
     35 class DOMRectList;
     36 class InspectorFontFace;
     37 class Selection;
     38 class TrustedHTMLOrString;
     39 
     40 enum class RangeBehaviour : uint8_t {
     41  // Keep both ranges
     42  KeepDefaultRangeAndCrossShadowBoundaryRanges,
     43  // Collapse the default range
     44  CollapseDefaultRange,
     45  // Collapse both the default range and the cross-shadow-boundary range
     46  CollapseDefaultRangeAndCrossShadowBoundaryRanges
     47 
     48 };
     49 }  // namespace dom
     50 }  // namespace mozilla
     51 
     52 class nsRange final : public mozilla::dom::AbstractRange,
     53                      public nsStubMutationObserver {
     54  using ErrorResult = mozilla::ErrorResult;
     55  using AbstractRange = mozilla::dom::AbstractRange;
     56  using DocGroup = mozilla::dom::DocGroup;
     57  using DOMRect = mozilla::dom::DOMRect;
     58  using DOMRectList = mozilla::dom::DOMRectList;
     59  using RangeBoundary = mozilla::RangeBoundary;
     60  using RawRangeBoundary = mozilla::RawRangeBoundary;
     61  using AllowRangeCrossShadowBoundary =
     62      mozilla::dom::AllowRangeCrossShadowBoundary;
     63 
     64  virtual ~nsRange();
     65  explicit nsRange(nsINode* aNode);
     66 
     67 public:
     68  /**
     69   * The following Create() returns `nsRange` instance which is initialized
     70   * only with aNode.  The result is never positioned.
     71   */
     72  static already_AddRefed<nsRange> Create(nsINode* aNode);
     73 
     74  /**
     75   * The following Create() may return `nsRange` instance which is initialized
     76   * with given range or points.  If it fails initializing new range with the
     77   * arguments, returns `nullptr`.  `ErrorResult` is set to an error only
     78   * when this returns `nullptr`.  The error code indicates the reason why
     79   * it couldn't initialize the instance.
     80   */
     81  static already_AddRefed<nsRange> Create(const AbstractRange* aAbstractRange,
     82                                          ErrorResult& aRv) {
     83    return nsRange::Create(aAbstractRange->StartRef(), aAbstractRange->EndRef(),
     84                           aRv);
     85  }
     86  static already_AddRefed<nsRange> Create(
     87      nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer,
     88      uint32_t aEndOffset, ErrorResult& aRv,
     89      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
     90          AllowRangeCrossShadowBoundary::No) {
     91    return nsRange::Create(RawRangeBoundary(aStartContainer, aStartOffset),
     92                           RawRangeBoundary(aEndContainer, aEndOffset), aRv,
     93                           aAllowCrossShadowBoundary);
     94  }
     95  template <typename SPT, typename SRT, typename EPT, typename ERT>
     96  static already_AddRefed<nsRange> Create(
     97      const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary,
     98      const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary,
     99      ErrorResult& aRv,
    100      AllowRangeCrossShadowBoundary = AllowRangeCrossShadowBoundary::No);
    101 
    102  NS_DECL_ISUPPORTS_INHERITED
    103  NS_IMETHODIMP_(void) DeleteCycleCollectable(void) override;
    104  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsRange, AbstractRange)
    105 
    106  nsrefcnt GetRefCount() const { return mRefCnt; }
    107 
    108  nsINode* GetRoot() const { return mRoot; }
    109 
    110  /**
    111   * Return true if this range was generated.
    112   * @see SetIsGenerated
    113   */
    114  bool IsGenerated() const { return mIsGenerated; }
    115 
    116  /**
    117   * Mark this range as being generated or not.
    118   * Currently it is used for marking ranges that are created when splitting up
    119   * a range to exclude a -moz-user-select:none region.
    120   * @see Selection::AddRangesForSelectableNodes
    121   * @see ExcludeNonSelectableNodes
    122   */
    123  void SetIsGenerated(bool aIsGenerated) { mIsGenerated = aIsGenerated; }
    124 
    125  void Reset();
    126 
    127  /**
    128   * SetStart() and SetEnd() sets start point or end point separately.
    129   * However, this is expensive especially when it's a range of Selection.
    130   * When you set both start and end of a range, you should use
    131   * SetStartAndEnd() instead.
    132   */
    133  nsresult SetStart(nsINode* aContainer, uint32_t aOffset,
    134                    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    135                        AllowRangeCrossShadowBoundary::No) {
    136    ErrorResult error;
    137    SetStart(RawRangeBoundary(aContainer, aOffset), error,
    138             aAllowCrossShadowBoundary);
    139    return error.StealNSResult();
    140  }
    141  nsresult SetEnd(nsINode* aContainer, uint32_t aOffset,
    142                  AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    143                      AllowRangeCrossShadowBoundary::No) {
    144    ErrorResult error;
    145    SetEnd(RawRangeBoundary(aContainer, aOffset), error,
    146           aAllowCrossShadowBoundary);
    147    return error.StealNSResult();
    148  }
    149 
    150  already_AddRefed<nsRange> CloneRange() const;
    151 
    152  /**
    153   * SetStartAndEnd() works similar to call both SetStart() and SetEnd().
    154   * Different from calls them separately, this does nothing if either
    155   * the start point or the end point is invalid point.
    156   * If the specified start point is after the end point, the range will be
    157   * collapsed at the end point.  Similarly, if they are in different root,
    158   * the range will be collapsed at the end point.
    159   */
    160  nsresult SetStartAndEnd(
    161      nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer,
    162      uint32_t aEndOffset,
    163      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    164          AllowRangeCrossShadowBoundary::No) {
    165    return SetStartAndEnd(RawRangeBoundary(aStartContainer, aStartOffset),
    166                          RawRangeBoundary(aEndContainer, aEndOffset),
    167                          aAllowCrossShadowBoundary);
    168  }
    169  template <typename SPT, typename SRT, typename EPT, typename ERT>
    170  nsresult SetStartAndEnd(
    171      const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary,
    172      const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary,
    173      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    174          AllowRangeCrossShadowBoundary::No) {
    175    return AbstractRange::SetStartAndEndInternal(
    176        aStartBoundary, aEndBoundary, this, aAllowCrossShadowBoundary);
    177  }
    178 
    179  /**
    180   * Adds all nodes between |aStartContent| and |aEndContent| to the range.
    181   * The start offset will be set before |aStartContent|,
    182   * while the end offset will be set immediately after |aEndContent|.
    183   *
    184   * Caller must guarantee both nodes are non null and
    185   * children of |aContainer| and that |aEndContent| is after |aStartContent|.
    186   */
    187  void SelectNodesInContainer(nsINode* aContainer, nsIContent* aStartContent,
    188                              nsIContent* aEndContent);
    189 
    190  /**
    191   * CollapseTo() works similar to call both SetStart() and SetEnd() with
    192   * same node and offset.  This just calls SetStartAndParent() to set
    193   * collapsed range at aContainer and aOffset.
    194   */
    195  nsresult CollapseTo(nsINode* aContainer, uint32_t aOffset) {
    196    return CollapseTo(RawRangeBoundary(aContainer, aOffset));
    197  }
    198  nsresult CollapseTo(const RawRangeBoundary& aPoint) {
    199    return SetStartAndEnd(aPoint, aPoint);
    200  }
    201 
    202  // aMaxRanges is the maximum number of text ranges to record for each face
    203  // (pass 0 to just get the list of faces, without recording exact ranges
    204  // where each face was used).
    205  nsresult GetUsedFontFaces(
    206      nsTArray<mozilla::UniquePtr<mozilla::dom::InspectorFontFace>>& aResult,
    207      uint32_t aMaxRanges, bool aSkipCollapsedWhitespace);
    208 
    209  // nsIMutationObserver methods
    210  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
    211  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
    212  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
    213  NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
    214  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
    215 
    216  // WebIDL
    217  static already_AddRefed<nsRange> Constructor(
    218      const mozilla::dom::GlobalObject& global, mozilla::ErrorResult& aRv);
    219 
    220  already_AddRefed<mozilla::dom::DocumentFragment> CreateContextualFragment(
    221      const nsAString& aString, ErrorResult& aError) const;
    222  MOZ_CAN_RUN_SCRIPT already_AddRefed<mozilla::dom::DocumentFragment>
    223  CreateContextualFragment(const mozilla::dom::TrustedHTMLOrString&,
    224                           nsIPrincipal* aSubjectPrincipal,
    225                           ErrorResult& aError) const;
    226  already_AddRefed<mozilla::dom::DocumentFragment> CloneContents(
    227      ErrorResult& aErr);
    228  int16_t CompareBoundaryPoints(uint16_t aHow, const nsRange& aOtherRange,
    229                                ErrorResult& aRv);
    230  int16_t ComparePoint(const nsINode& aContainer, uint32_t aOffset,
    231                       ErrorResult& aRv,
    232                       bool aAllowCrossShadowBoundary = false) const;
    233  void DeleteContents(ErrorResult& aRv);
    234  already_AddRefed<mozilla::dom::DocumentFragment> ExtractContents(
    235      ErrorResult& aErr);
    236  nsINode* GetCommonAncestorContainer(
    237      ErrorResult& aRv,
    238      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    239          AllowRangeCrossShadowBoundary::No) const {
    240    if (!mIsPositioned) {
    241      aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    242      return nullptr;
    243    }
    244    return GetClosestCommonInclusiveAncestor(aAllowCrossShadowBoundary);
    245  }
    246  void InsertNode(nsINode& aNode, ErrorResult& aErr);
    247  bool IntersectsNode(nsINode& aNode, ErrorResult& aRv);
    248  bool IsPointInRange(const nsINode& aContainer, uint32_t aOffset,
    249                      ErrorResult& aRv,
    250                      bool aAllowCrossShadowBoundary = false) const;
    251  void ToString(nsAString& aReturn, ErrorResult& aErr);
    252  void Detach();
    253 
    254  // *JS() methods are mapped to Range.*() of DOM.
    255  // They may move focus only when the range represents normal selection.
    256  // These methods shouldn't be used from internal.
    257  void CollapseJS(bool aToStart);
    258  void SelectNodeJS(nsINode& aNode, ErrorResult& aErr);
    259  void SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr);
    260  void SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
    261  void SetEndAfterJS(nsINode& aNode, ErrorResult& aErr);
    262  void SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr);
    263  void SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
    264  void SetStartAfterJS(nsINode& aNode, ErrorResult& aErr);
    265  void SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr);
    266 
    267  void SetStartAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset,
    268                                        ErrorResult& aErr);
    269  void SetEndAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset,
    270                                      ErrorResult& aErr);
    271 
    272  void SurroundContents(nsINode& aNode, ErrorResult& aErr);
    273  already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true,
    274                                                  bool aFlushLayout = true);
    275  already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true,
    276                                               bool aFlushLayout = true);
    277  // ChromeOnly
    278  already_AddRefed<DOMRectList> GetAllowCrossShadowBoundaryClientRects(
    279      bool aClampToEdge = true, bool aFlushLayout = true);
    280 
    281  void GetClientRectsAndTexts(mozilla::dom::ClientRectsAndTexts& aResult,
    282                              ErrorResult& aErr);
    283 
    284  // Following methods should be used for internal use instead of *JS().
    285  void SelectNode(nsINode& aNode, ErrorResult& aErr);
    286  void SelectNodeContents(nsINode& aNode, ErrorResult& aErr);
    287  void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr,
    288              AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    289                  AllowRangeCrossShadowBoundary::No);
    290  void SetEnd(const RawRangeBoundary& aPoint, ErrorResult& aErr,
    291              AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    292                  AllowRangeCrossShadowBoundary::No);
    293  void SetEndAfter(nsINode& aNode, ErrorResult& aErr);
    294  void SetEndBefore(nsINode& aNode, ErrorResult& aErr,
    295                    AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    296                        AllowRangeCrossShadowBoundary::No);
    297  void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr,
    298                AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    299                    AllowRangeCrossShadowBoundary::No);
    300  void SetStart(const RawRangeBoundary& aPoint, ErrorResult& aErr,
    301                AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    302                    AllowRangeCrossShadowBoundary::No);
    303  void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
    304  void SetStartBefore(nsINode& aNode, ErrorResult& aErr,
    305                      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    306                          AllowRangeCrossShadowBoundary::No);
    307  void Collapse(bool aToStart);
    308 
    309  static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
    310                                  mozilla::ErrorResult& aError,
    311                                  nsIContent* aContainer);
    312 
    313  virtual JSObject* WrapObject(JSContext* cx,
    314                               JS::Handle<JSObject*> aGivenProto) final;
    315  DocGroup* GetDocGroup() const;
    316 
    317  // Given a CharacterDataChangeInfo and an RangeBoundary of where the
    318  // character changes occurred at, compute the new boundary.
    319  static RawRangeBoundary ComputeNewBoundaryWhenBoundaryInsideChangedText(
    320      const CharacterDataChangeInfo& aInfo, const RawRangeBoundary& aBoundary);
    321 
    322 private:
    323  // no copy's or assigns
    324  nsRange(const nsRange&);
    325  nsRange& operator=(const nsRange&);
    326 
    327  template <typename SPT, typename SRT, typename EPT, typename ERT>
    328  static void AssertIfMismatchRootAndRangeBoundaries(
    329      const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary,
    330      const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary,
    331      const nsINode* aRootNode, bool aNotInsertedYet = false);
    332 
    333  using ElementHandler = void (*)(mozilla::dom::Element*);
    334  /**
    335   * Cut or delete the range's contents.
    336   *
    337   * @param aFragment DocumentFragment containing the nodes.
    338   *                  May be null to indicate the caller doesn't want a
    339   *                  fragment.
    340   * @param aElementHandler If this handler is provided, any element node
    341   *                        subtree root fully contained in this range is
    342   *                        passed to it, instead of being deleted. Any
    343   *                        mutation that trips nsMutationGuard is disallowed.
    344   *                        Currently incompatible with non-null aFragment.
    345   * @param aRv The error if any.
    346   */
    347  void CutContents(mozilla::dom::DocumentFragment** aFragment,
    348                   ElementHandler aElementHandler, ErrorResult& aRv);
    349 
    350  static nsresult CloneParentsBetween(nsINode* aAncestor, nsINode* aNode,
    351                                      nsINode** aClosestAncestor,
    352                                      nsINode** aFarthestAncestor);
    353 
    354  /**
    355   * Returns whether a node is safe to be accessed by the current caller.
    356   */
    357  bool CanAccess(const nsINode&) const;
    358 
    359  void AdjustNextRefsOnCharacterDataSplit(const nsIContent& aContent,
    360                                          const CharacterDataChangeInfo& aInfo);
    361 
    362  struct RangeBoundariesAndRoot {
    363    RawRangeBoundary mStart;
    364    RawRangeBoundary mEnd;
    365    nsINode* mRoot = nullptr;
    366  };
    367 
    368  /**
    369   * @param aContent Must be non-nullptr.
    370   */
    371  RangeBoundariesAndRoot DetermineNewRangeBoundariesAndRootOnCharacterDataMerge(
    372      nsIContent* aContent, const CharacterDataChangeInfo& aInfo) const;
    373 
    374  // @return true iff the range is positioned, aContainer belongs to the same
    375  //         document as the range, aContainer is a DOCUMENT_TYPE_NODE and
    376  //         aOffset doesn't exceed aContainer's length.
    377  bool IsPointComparableToRange(const nsINode& aContainer, uint32_t aOffset,
    378                                bool aAllowCrossShadowBoundary,
    379                                ErrorResult& aErrorResult) const;
    380 
    381  // @return true iff aContainer is a shadow including inclusive descendant of
    382  // the common ancestor of the mCrossBoundaryRange.
    383  bool IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
    384      const nsINode& aContainer) const;
    385 
    386  /**
    387   * @brief Returns true if the range is part of exactly one |Selection|.
    388   */
    389  bool IsPartOfOneSelectionOnly() const { return mSelections.Length() == 1; };
    390 
    391  already_AddRefed<DOMRectList> GetClientRectsInner(
    392      AllowRangeCrossShadowBoundary = AllowRangeCrossShadowBoundary::No,
    393      bool aClampToEdge = true, bool aFlushLayout = true);
    394 
    395 public:
    396  /**
    397   * This helper function gets rects and correlated text for the given range.
    398   * @param aTextList optional where nullptr = don't retrieve text
    399   */
    400  static void CollectClientRectsAndText(
    401      mozilla::RectCallback* aCollector,
    402      mozilla::dom::Sequence<nsString>* aTextList, nsRange* aRange,
    403      nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer,
    404      uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout);
    405 
    406  /**
    407   * Scan this range for -moz-user-select:none nodes and split it up into
    408   * multiple ranges to exclude those nodes.  The resulting ranges are put
    409   * in aOutRanges.  If no -moz-user-select:none node is found in the range
    410   * then |this| is unmodified and is the only range in aOutRanges.
    411   * Otherwise, |this| will be modified so that it ends before the first
    412   * -moz-user-select:none node and additional ranges may also be created.
    413   * If all nodes in the range are -moz-user-select:none then aOutRanges
    414   * will be empty.
    415   * @param aOutRanges the resulting set of ranges
    416   */
    417  void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges);
    418 
    419  /**
    420   * Notify the selection listeners after a range has been modified.
    421   */
    422  MOZ_CAN_RUN_SCRIPT void NotifySelectionListenersAfterRangeSet();
    423 
    424  /**
    425   * For a range for which IsInSelection() is true, return the closest common
    426   * inclusive ancestor
    427   * (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor)
    428   * for the range, which we had to compute when the common ancestor changed or
    429   * IsInSelection became true, so we could register with it. That is, it's a
    430   * faster version of GetClosestCommonInclusiveAncestor that only works for
    431   * ranges in a Selection. The method will assert and the behavior is undefined
    432   * if called on a range where IsInSelection() is false.
    433   */
    434  nsINode* GetRegisteredClosestCommonInclusiveAncestor();
    435 
    436  template <typename SPT, typename SRT, typename EPT, typename ERT>
    437  void CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
    438      const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary,
    439      const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary);
    440 
    441  void ResetCrossShadowBoundaryRange() { mCrossShadowBoundaryRange = nullptr; }
    442 
    443  bool CrossShadowBoundaryRangeCollapsed() const {
    444    MOZ_ASSERT(mCrossShadowBoundaryRange);
    445 
    446    return !mCrossShadowBoundaryRange->IsPositioned() ||
    447           (mCrossShadowBoundaryRange->GetStartContainer() ==
    448                mCrossShadowBoundaryRange->GetEndContainer() &&
    449            mCrossShadowBoundaryRange->StartOffset() ==
    450                mCrossShadowBoundaryRange->EndOffset());
    451  }
    452 
    453  /*
    454   * The methods marked with MayCrossShadowBoundary[..] additionally check for
    455   * the existence of mCrossShadowBoundaryRange, which indicates a range that
    456   * crosses a shadow DOM boundary (i.e. mStart and mEnd are in different
    457   * trees). If the caller can guarantee that this does not happen, there are
    458   * additional variants of these methods named without MayCrossShadowBoundary,
    459   * which provide a slightly faster implementation.
    460   * */
    461 
    462  nsIContent* GetMayCrossShadowBoundaryChildAtStartOffset() const {
    463    return mCrossShadowBoundaryRange
    464               ? mCrossShadowBoundaryRange->GetChildAtStartOffset()
    465               : mStart.GetChildAtOffset();
    466  }
    467 
    468  nsIContent* GetMayCrossShadowBoundaryChildAtEndOffset() const {
    469    return mCrossShadowBoundaryRange
    470               ? mCrossShadowBoundaryRange->GetChildAtEndOffset()
    471               : mEnd.GetChildAtOffset();
    472  }
    473 
    474  mozilla::dom::CrossShadowBoundaryRange* GetCrossShadowBoundaryRange() const {
    475    return mCrossShadowBoundaryRange;
    476  }
    477 
    478  nsINode* GetMayCrossShadowBoundaryStartContainer() const {
    479    return mCrossShadowBoundaryRange
    480               ? mCrossShadowBoundaryRange->GetStartContainer()
    481               : mStart.GetContainer();
    482  }
    483 
    484  nsINode* GetMayCrossShadowBoundaryEndContainer() const {
    485    return mCrossShadowBoundaryRange
    486               ? mCrossShadowBoundaryRange->GetEndContainer()
    487               : mEnd.GetContainer();
    488  }
    489 
    490  uint32_t MayCrossShadowBoundaryStartOffset() const {
    491    return mCrossShadowBoundaryRange ? mCrossShadowBoundaryRange->StartOffset()
    492                                     : StartOffset();
    493  }
    494 
    495  uint32_t MayCrossShadowBoundaryEndOffset() const {
    496    return mCrossShadowBoundaryRange ? mCrossShadowBoundaryRange->EndOffset()
    497                                     : EndOffset();
    498  }
    499 
    500  const RangeBoundary& MayCrossShadowBoundaryStartRef() const {
    501    return mCrossShadowBoundaryRange ? mCrossShadowBoundaryRange->StartRef()
    502                                     : StartRef();
    503  }
    504 
    505  const RangeBoundary& MayCrossShadowBoundaryEndRef() const {
    506    return mCrossShadowBoundaryRange ? mCrossShadowBoundaryRange->EndRef()
    507                                     : EndRef();
    508  }
    509 
    510  /**
    511   * Suppress rendering of selected nodes for print selection, assuming that
    512   * this range represents a part of nodes that are outside of the selection.
    513   * For all non-Element nodes, this behaves identically to DeleteContents().
    514   * Elements that are wholly contained by this range is instead marked with
    515   * state that matches :-moz-suppress-for-print-selection.
    516   * This is required to preserve matched style rules using tree-structural
    517   * pseudo-classes, as well as any feature that depends on the location of
    518   * the style node in the DOM tree (e.g. @scope with implicit scope).
    519   *
    520   * @param aRv The error, if any.
    521   */
    522  void SuppressContentsForPrintSelection(ErrorResult& aRv);
    523 
    524 protected:
    525  /**
    526   * DoSetRange() is called when `AbstractRange::SetStartAndEndInternal()` sets
    527   * mStart and mEnd, or some other internal methods modify `mStart` and/or
    528   * `mEnd`.  Therefore, this shouldn't be a virtual method.
    529   *
    530   * @param aStartBoundary      Computed start point.  This must equals or be
    531   *                            before aEndBoundary in the DOM tree order.
    532   * @param aEndBoundary        Computed end point.
    533   * @param aRootNode           The root node.
    534   * @param aNotInsertedYet     true if this is called by CharacterDataChanged()
    535   *                            to disable assertion and suppress re-registering
    536   *                            a range common ancestor node since the new text
    537   *                            node of a splitText hasn't been inserted yet.
    538   *                            CharacterDataChanged() does the re-registering
    539   *                            when needed.  Otherwise, false.
    540   */
    541  template <typename SPT, typename SRT, typename EPT, typename ERT>
    542  MOZ_CAN_RUN_SCRIPT_BOUNDARY void DoSetRange(
    543      const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary,
    544      const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary,
    545      nsINode* aRootNode, bool aNotInsertedYet = false,
    546      mozilla::dom::RangeBehaviour aRangeBehaviour = mozilla::dom::
    547          RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges);
    548 
    549  // Assume that this is guaranteed that this is held by the caller when
    550  // this is used.  (Note that we cannot use AutoRestore for mCalledByJS
    551  // due to a bit field.)
    552  class MOZ_RAII AutoCalledByJSRestore final {
    553   private:
    554    nsRange& mRange;
    555    bool mOldValue;
    556 
    557   public:
    558    explicit AutoCalledByJSRestore(nsRange& aRange)
    559        : mRange(aRange), mOldValue(aRange.mCalledByJS) {}
    560    ~AutoCalledByJSRestore() { mRange.mCalledByJS = mOldValue; }
    561    bool SavedValue() const { return mOldValue; }
    562  };
    563 
    564  struct MOZ_STACK_CLASS AutoInvalidateSelection {
    565    explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) {
    566      if (!mRange->IsInAnySelection() || sIsNested) {
    567        return;
    568      }
    569      sIsNested = true;
    570      mCommonAncestor = mRange->GetRegisteredClosestCommonInclusiveAncestor();
    571    }
    572    ~AutoInvalidateSelection();
    573    nsRange* mRange;
    574    RefPtr<nsINode> mCommonAncestor;
    575    static bool sIsNested;
    576  };
    577 
    578  bool MaybeInterruptLastRelease();
    579 
    580 #ifdef DEBUG
    581  bool IsCleared() const {
    582    return !mRoot && !mRegisteredClosestCommonInclusiveAncestor &&
    583           mSelections.IsEmpty() && !mNextStartRef && !mNextEndRef;
    584  }
    585 #endif  // #ifdef DEBUG
    586 
    587  nsCOMPtr<nsINode> mRoot;
    588 
    589  // These raw pointers are used to remember a child that is about
    590  // to be inserted between a CharacterData call and a subsequent
    591  // ContentInserted or ContentAppended call. It is safe to store
    592  // these refs because the caller is guaranteed to trigger both
    593  // notifications while holding a strong reference to the new child.
    594  nsIContent* MOZ_NON_OWNING_REF mNextStartRef;
    595  nsIContent* MOZ_NON_OWNING_REF mNextEndRef;
    596 
    597  static nsTArray<RefPtr<nsRange>>* sCachedRanges;
    598 
    599  // Used to keep track of the real start and end for a
    600  // selection where the start and the end are in different trees.
    601  // It's NULL when the nodes are in the same tree.
    602  //
    603  // mCrossShadowBoundaryRange doesn't deal with DOM mutations, because
    604  // it's still an open question about how it should be handled.
    605  // Spec: https://github.com/w3c/selection-api/issues/168.
    606  // As a result, it'll be set to NULL if that happens.
    607  //
    608  // Theoretically, mCrossShadowBoundaryRange isn't really needed because
    609  // we should be able to always store the real start and end, and
    610  // just return one point when a collapse is needed.
    611  // Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1886028 is going
    612  // to be used to improve mCrossShadowBoundaryRange.
    613  RefPtr<mozilla::dom::CrossShadowBoundaryRange> mCrossShadowBoundaryRange;
    614 
    615  friend class mozilla::dom::AbstractRange;
    616 };
    617 namespace mozilla::dom {
    618 inline nsRange* AbstractRange::AsDynamicRange() {
    619  MOZ_ASSERT(IsDynamicRange());
    620  return static_cast<nsRange*>(this);
    621 }
    622 inline const nsRange* AbstractRange::AsDynamicRange() const {
    623  MOZ_ASSERT(IsDynamicRange());
    624  return static_cast<const nsRange*>(this);
    625 }
    626 }  // namespace mozilla::dom
    627 #endif /* nsRange_h___ */