tor-browser

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

AbstractRange.h (9621B)


      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 #ifndef mozilla_dom_AbstractRange_h
      8 #define mozilla_dom_AbstractRange_h
      9 
     10 #include <cstdint>
     11 #include <ostream>
     12 
     13 #include "ErrorList.h"
     14 #include "js/RootingAPI.h"
     15 #include "mozilla/RangeBoundary.h"
     16 #include "mozilla/RefPtr.h"
     17 #include "mozilla/WeakPtr.h"
     18 #include "nsCycleCollectionParticipant.h"
     19 #include "nsISupports.h"
     20 #include "nsWrapperCache.h"
     21 
     22 class JSObject;
     23 class nsIContent;
     24 class nsINode;
     25 class nsRange;
     26 struct JSContext;
     27 
     28 namespace mozilla::dom {
     29 class Document;
     30 class Selection;
     31 class StaticRange;
     32 class HTMLSlotElement;
     33 
     34 enum class AllowRangeCrossShadowBoundary : bool { No, Yes };
     35 
     36 class AbstractRange : public nsISupports,
     37                      public nsWrapperCache,
     38                      // For linking together selection-associated ranges.
     39                      public mozilla::LinkedListElement<AbstractRange> {
     40  using AllowRangeCrossShadowBoundary =
     41      mozilla::dom::AllowRangeCrossShadowBoundary;
     42 
     43 protected:
     44  explicit AbstractRange(nsINode* aNode, bool aIsDynamicRange,
     45                         TreeKind aBoundaryTreeKind);
     46  virtual ~AbstractRange();
     47 
     48 public:
     49  enum class IsUnlinking : bool { No, Yes };
     50 
     51  AbstractRange() = delete;
     52  explicit AbstractRange(const AbstractRange& aOther) = delete;
     53 
     54  /**
     55   * Called when the process is shutting down.
     56   */
     57  static void Shutdown();
     58 
     59  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     60  NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(AbstractRange)
     61 
     62  /**
     63   * All of the MayCrossShadowBoundary* methods are used to get the boundary
     64   * endpoints that cross shadow boundaries. They would return
     65   * the same value as the non-MayCrossShadowBoundary* methods if the range
     66   * boundaries don't cross shadow boundaries.
     67   */
     68  const RangeBoundary& StartRef() const { return mStart; }
     69  const RangeBoundary& MayCrossShadowBoundaryStartRef() const;
     70 
     71  const RangeBoundary& EndRef() const { return mEnd; }
     72  const RangeBoundary& MayCrossShadowBoundaryEndRef() const;
     73 
     74  nsIContent* GetChildAtStartOffset() const {
     75    return mStart.GetChildAtOffset();
     76  }
     77  nsIContent* GetMayCrossShadowBoundaryChildAtStartOffset() const;
     78 
     79  nsIContent* GetChildAtEndOffset() const { return mEnd.GetChildAtOffset(); }
     80  nsIContent* GetMayCrossShadowBoundaryChildAtEndOffset() const;
     81 
     82  bool IsPositioned() const { return mIsPositioned; }
     83  /**
     84   * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
     85   */
     86  nsINode* GetClosestCommonInclusiveAncestor(
     87      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
     88          AllowRangeCrossShadowBoundary::No) const;
     89 
     90  // WebIDL
     91 
     92  // If Range is created from JS, it's initialized with Document.createRange()
     93  // and it collaps the range to start of the Document.  Therefore, the
     94  // following WebIDL methods are called only when `mIsPositioned` is true.
     95  // So, it does not make sense to take `ErrorResult` as their parameter
     96  // since its destruction cost may appear in profile.  If you create range
     97  // object from C++ and needs to check whether it's positioned, should call
     98  // `IsPositioned()` directly.
     99 
    100  nsINode* GetStartContainer() const { return mStart.GetContainer(); }
    101  nsINode* GetMayCrossShadowBoundaryStartContainer() const;
    102 
    103  nsINode* GetEndContainer() const { return mEnd.GetContainer(); }
    104  nsINode* GetMayCrossShadowBoundaryEndContainer() const;
    105 
    106  /**
    107   * Return GetStartContainer() and GetEndContainer() if this is positioned.
    108   */
    109  [[nodiscard]] bool IsPositionedAndSameContainer() const {
    110    return MOZ_LIKELY(mIsPositioned) &&
    111           mStart.GetContainer() == mEnd.GetContainer();
    112  }
    113  /**
    114   * Return GetMayCrossShadowBoundaryStartContainer() and
    115   * GetMayCrossShadowBoundaryEndContainer() if this is positioned.
    116   */
    117  [[nodiscard]] bool IsPositionedAndSameContainerMayCrossShadowBoundary()
    118      const {
    119    return MOZ_LIKELY(mIsPositioned) &&
    120           GetMayCrossShadowBoundaryStartContainer() ==
    121               GetMayCrossShadowBoundaryEndContainer();
    122  }
    123 
    124  bool MayCrossShadowBoundary() const;
    125 
    126  Document* GetComposedDocOfContainers() const {
    127    return mStart.GetComposedDoc();
    128  }
    129 
    130  // FYI: Returns 0 if it's not positioned.
    131  uint32_t StartOffset() const {
    132    return static_cast<uint32_t>(
    133        *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets));
    134  }
    135  uint32_t MayCrossShadowBoundaryStartOffset() const;
    136 
    137  // FYI: Returns 0 if it's not positioned.
    138  uint32_t EndOffset() const {
    139    return static_cast<uint32_t>(
    140        *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets));
    141  }
    142  uint32_t MayCrossShadowBoundaryEndOffset() const;
    143 
    144  bool Collapsed() const {
    145    return !mIsPositioned || (mStart.GetContainer() == mEnd.GetContainer() &&
    146                              StartOffset() == EndOffset());
    147  }
    148 
    149  bool AreNormalRangeAndCrossShadowBoundaryRangeCollapsed() const;
    150 
    151  nsINode* GetParentObject() const;
    152  virtual JSObject* WrapObject(JSContext* aCx,
    153                               JS::Handle<JSObject*> aGivenProto) override;
    154 
    155  bool HasEqualBoundaries(const AbstractRange& aOther) const {
    156    return (mStart == aOther.mStart) && (mEnd == aOther.mEnd);
    157  }
    158  bool IsDynamicRange() const { return mIsDynamicRange; }
    159  bool IsStaticRange() const { return !mIsDynamicRange; }
    160  inline nsRange* AsDynamicRange();
    161  inline const nsRange* AsDynamicRange() const;
    162  inline StaticRange* AsStaticRange();
    163  inline const StaticRange* AsStaticRange() const;
    164 
    165  /**
    166   * Return true if this range is part of a Selection object
    167   * and isn't detached.
    168   */
    169  bool IsInAnySelection() const { return !mSelections.IsEmpty(); }
    170 
    171  void RegisterSelection(mozilla::dom::Selection& aSelection);
    172 
    173  void UnregisterSelection(const mozilla::dom::Selection& aSelection,
    174                           IsUnlinking aIsUnlinking = IsUnlinking::No);
    175 
    176  /**
    177   * Returns a list of all Selections the range is associated with.
    178   */
    179  const nsTArray<WeakPtr<Selection>>& GetSelections() const;
    180 
    181  /**
    182   * Return true if this range is in |aSelection|.
    183   */
    184  bool IsInSelection(const mozilla::dom::Selection& aSelection) const;
    185 
    186  /**
    187   * Return true if aRoot is a UA shadow root.
    188   */
    189  static bool IsRootUAWidget(const nsINode* aRoot);
    190 
    191  /**
    192   * Return a shrunken range computed by
    193   * SelectionMoveUtils::GetFirstVisiblePointAtLeaf() and
    194   * SelectionMoveUtils::GetLastVisiblePointAtLeaf().
    195   */
    196  already_AddRefed<StaticRange> GetShrunkenRangeToVisibleLeaves() const;
    197 
    198 protected:
    199  template <typename SPT, typename SRT, typename EPT, typename ERT,
    200            typename RangeType>
    201  static nsresult SetStartAndEndInternal(
    202      const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
    203      const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange,
    204      AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
    205          AllowRangeCrossShadowBoundary::No);
    206 
    207  template <class RangeType>
    208  static bool MaybeCacheToReuse(RangeType& aInstance);
    209 
    210  void Init(nsINode* aNode);
    211 
    212  friend std::ostream& operator<<(std::ostream& aStream,
    213                                  const AbstractRange& aRange) {
    214    if (aRange.Collapsed()) {
    215      aStream << "{ mStart=mEnd=" << aRange.mStart;
    216    } else {
    217      aStream << "{ mStart=" << aRange.mStart << ", mEnd=" << aRange.mEnd;
    218    }
    219    return aStream << ", mIsGenerated="
    220                   << (aRange.mIsGenerated ? "true" : "false")
    221                   << ", mCalledByJS="
    222                   << (aRange.mIsPositioned ? "true" : "false")
    223                   << ", mIsDynamicRange="
    224                   << (aRange.mIsDynamicRange ? "true" : "false") << " }";
    225  }
    226 
    227  /**
    228   * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
    229   */
    230  void RegisterClosestCommonInclusiveAncestor(nsINode* aNode);
    231  /**
    232   * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
    233   */
    234  void UnregisterClosestCommonInclusiveAncestor(
    235      IsUnlinking aIsUnlinking = IsUnlinking::No);
    236 
    237  void UpdateCommonAncestorIfNecessary();
    238 
    239  static void MarkDescendants(nsINode& aNode);
    240  static void UnmarkDescendants(nsINode& aNode);
    241 
    242  static void UpdateDescendantsInFlattenedTree(nsINode& aNode,
    243                                               bool aMarkDescendants);
    244  friend void mozilla::SlotAssignedNodeAdded(dom::HTMLSlotElement* aSlot,
    245                                             nsIContent& aAssignedNode);
    246  friend void mozilla::SlotAssignedNodeRemoved(dom::HTMLSlotElement* aSlot,
    247                                               nsIContent& aUnassignedNode);
    248 
    249 private:
    250  void ClearForReuse();
    251 
    252 protected:
    253  RefPtr<Document> mOwner;
    254  RangeBoundary mStart;
    255  RangeBoundary mEnd;
    256 
    257  // A Range can be part of multiple |Selection|s. This is a very rare use case.
    258  AutoTArray<WeakPtr<Selection>, 1> mSelections;
    259  // mRegisteredClosestCommonInclusiveAncestor is only non-null when the range
    260  // IsInAnySelection().
    261  nsCOMPtr<nsINode> mRegisteredClosestCommonInclusiveAncestor;
    262 
    263  // `true` if `mStart` and `mEnd` are set for StaticRange or set and valid
    264  // for nsRange.
    265  bool mIsPositioned;
    266 
    267  // Used by nsRange, but this should have this for minimizing the size.
    268  bool mIsGenerated;
    269  // Used by nsRange, but this should have this for minimizing the size.
    270  bool mCalledByJS;
    271 
    272  // true if this is an `nsRange` object.
    273  const bool mIsDynamicRange;
    274 
    275  static bool sHasShutDown;
    276 };
    277 
    278 }  // namespace mozilla::dom
    279 
    280 #endif  // #ifndef mozilla_dom_AbstractRange_h