tor-browser

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

SelectionMovementUtils.h (10115B)


      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_SelectionMovementUtils_h
      8 #define mozilla_SelectionMovementUtils_h
      9 
     10 #include "mozilla/Attributes.h"
     11 #include "mozilla/EnumSet.h"
     12 #include "mozilla/RangeBoundary.h"
     13 #include "mozilla/Result.h"
     14 #include "mozilla/intl/BidiEmbeddingLevel.h"
     15 #include "nsIFrame.h"
     16 
     17 struct nsPrevNextBidiLevels;
     18 
     19 namespace mozilla {
     20 
     21 class PresShell;
     22 enum class PeekOffsetOption : uint16_t;
     23 
     24 namespace intl {
     25 class BidiEmbeddingLevel;
     26 }
     27 
     28 struct MOZ_STACK_CLASS FrameAndOffset {
     29  [[nodiscard]] nsIContent* GetFrameContent() const {
     30    return mFrame ? mFrame->GetContent() : nullptr;
     31  }
     32 
     33  operator nsIFrame*() const { return mFrame; }
     34 
     35  explicit operator bool() const { return !!mFrame; }
     36  [[nodiscard]] bool operator!() const { return !mFrame; }
     37 
     38  nsIFrame* operator->() const {
     39    MOZ_ASSERT(mFrame);
     40    return mFrame;
     41  }
     42 
     43  nsIFrame* mFrame = nullptr;
     44  // The offset in mFrame->GetContent().
     45  uint32_t mOffsetInFrameContent = 0;
     46 };
     47 
     48 struct MOZ_STACK_CLASS PrimaryFrameData : public FrameAndOffset {
     49  // Whether the caret should be put before or after the point. This is valid
     50  // only when mFrame is not nullptr.
     51  CaretAssociationHint mHint{0};  // Before
     52 };
     53 
     54 struct MOZ_STACK_CLASS CaretFrameData : public PrimaryFrameData {
     55  // The frame which is found only from a DOM point.  This frame becomes
     56  // different from mFrame when the point is around end of a line or
     57  // at a bidi text boundary.
     58  nsIFrame* mUnadjustedFrame = nullptr;
     59 };
     60 
     61 enum class ForceEditableRegion : bool { No, Yes };
     62 
     63 class SelectionMovementUtils final {
     64 public:
     65  using PeekOffsetOptions = EnumSet<PeekOffsetOption>;
     66 
     67  /**
     68   * @brief Creates a new `RangeBoundary` which moves `aAmount` into
     69   * `aDirection` from the input range boundary.
     70   *
     71   * @param aRangeBoundary   The input range boundary.
     72   * @param aDirection       The direction into which the new boundary should be
     73   *                         moved.
     74   * @param aHint            The `CaretAssociationHint` (is the caret before or
     75   *                         after the boundary point)
     76   * @param aCaretBidiLevel  The `BidiEmbeddingLevel`.
     77   * @param aAmount          The amount which the range boundary should be
     78   *                         moved.
     79   * @param aOptions         Additional options, see `PeekOffsetOption`.
     80   * @param aAncestorLimiter The content node that limits where Selection may be
     81   *                         expanded to.
     82   *
     83   * @return Returns a new `RangeBoundary` which is moved from `aRangeBoundary`
     84   *         by `aAmount` into `aDirection`.
     85   */
     86  template <typename ParentType, typename RefType>
     87  static Result<RangeBoundaryBase<ParentType, RefType>, nsresult>
     88  MoveRangeBoundaryToSomewhere(
     89      const RangeBoundaryBase<ParentType, RefType>& aRangeBoundary,
     90      nsDirection aDirection, CaretAssociationHint aHint,
     91      intl::BidiEmbeddingLevel aCaretBidiLevel, nsSelectionAmount aAmount,
     92      PeekOffsetOptions aOptions,
     93      const dom::Element* aAncestorLimiter = nullptr);
     94 
     95  /**
     96   * Given a node and its child offset, return the nsIFrame and the offset into
     97   * that frame.
     98   *
     99   * @param aNode input parameter for the node to look at
    100   * @param aOffset offset into above node.
    101   */
    102  static FrameAndOffset GetFrameForNodeOffset(const nsIContent* aNode,
    103                                              uint32_t aOffset,
    104                                              CaretAssociationHint aHint);
    105 
    106  /**
    107   * Return the first visible point in or at a leaf node in aRange or the first
    108   * unselectable content if aRange starts from a selectable container. E.g.,
    109   * return the start of the first visible `Text` or the position of the first
    110   * visible leaf element.  I.e., the result may be a good point to put a UI for
    111   * showing something around the start boundary.
    112   *
    113   * NOTE: This won't return any boundary point in subtrees from the tree
    114   * containing the start container of aRange due to ContentIteratorBase's
    115   * limitation. See bug 2001511.
    116   *
    117   * @param aRange Must not be collapsed because this returns a point in aRange
    118   * so that this requires the limitation of scanning forward.
    119   * @return A position in a `Text` or a position at an element.
    120   */
    121  [[nodiscard]] static RawRangeBoundary GetFirstVisiblePointAtLeaf(
    122      const dom::AbstractRange& aRange);
    123 
    124  /**
    125   * Return the last visible point in or at a leaf node in aRange or the last
    126   * unselectable content if aRange ends in a selectable container. E.g., return
    127   * the end of the last visible `Text` or the position of the last visible leaf
    128   * element. I.e., the result may be a good point to put a UI for showing
    129   * something around the end boundary.
    130   *
    131   * NOTE: This won't return any boundary point in subtrees of the tree
    132   * containing the end container of aRange due to ContentIteratorBase's
    133   * limitation. See bug 2001511.
    134   *
    135   * @param aRange Must not be collapsed because this returns a point in aRange
    136   * so that this requires the limitation of scanning forward.
    137   * @return A position in a `Text` or a position at an element.
    138   */
    139  [[nodiscard]] static RawRangeBoundary GetLastVisiblePointAtLeaf(
    140      const dom::AbstractRange& aRange);
    141 
    142  /**
    143   * GetPrevNextBidiLevels will return the frames and associated Bidi levels of
    144   * the characters logically before and after a (collapsed) selection.
    145   *
    146   * @param aNode is the node containing the selection
    147   * @param aContentOffset is the offset of the selection in the node
    148   * @param aJumpLines
    149   *   If true, look across line boundaries.
    150   *   If false, behave as if there were base-level frames at line edges.
    151   * @param aAncestorLimiter  If set, this refers only the descendants.
    152   *
    153   * @return A struct holding the before/after frame and the before/after
    154   * level.
    155   *
    156   * At the beginning and end of each line there is assumed to be a frame with
    157   * Bidi level equal to the paragraph embedding level.
    158   *
    159   * In these cases the before frame and after frame respectively will be
    160   * nullptr.
    161   */
    162  static nsPrevNextBidiLevels GetPrevNextBidiLevels(
    163      nsIContent* aNode, uint32_t aContentOffset, CaretAssociationHint aHint,
    164      bool aJumpLines, const dom::Element* aAncestorLimiter);
    165 
    166  /**
    167   * PeekOffsetForCaretMove() only peek offset for caret move from the specified
    168   * point of the normal selection.  I.e., won't change selection ranges nor
    169   * bidi information.
    170   */
    171  static Result<PeekOffsetStruct, nsresult> PeekOffsetForCaretMove(
    172      nsIContent* aContent, uint32_t aOffset, nsDirection aDirection,
    173      CaretAssociationHint aHint, intl::BidiEmbeddingLevel aCaretBidiLevel,
    174      const nsSelectionAmount aAmount, const nsPoint& aDesiredCaretPos,
    175      PeekOffsetOptions aOptions, const dom::Element* aAncestorLimiter);
    176 
    177  /**
    178   * IsIntraLineCaretMove() is a helper method for PeekOffsetForCaretMove()
    179   * and CreateRangeExtendedToSomwhereFromNormalSelection().  This returns
    180   * whether aAmount is intra line move or is crossing hard line break.
    181   * This returns error if aMount is not supported by the methods.
    182   */
    183  static Result<bool, nsresult> IsIntraLineCaretMove(
    184      nsSelectionAmount aAmount) {
    185    switch (aAmount) {
    186      case eSelectCharacter:
    187      case eSelectCluster:
    188      case eSelectWord:
    189      case eSelectWordNoSpace:
    190      case eSelectBeginLine:
    191      case eSelectEndLine:
    192        return true;
    193      case eSelectLine:
    194        return false;
    195      default:
    196        return Err(NS_ERROR_FAILURE);
    197    }
    198  }
    199 
    200  /**
    201   * Return a frame for considering caret geometry.
    202   *
    203   * @param aFrameSelection     [optional] If this is specified and selection in
    204   *                            aContent is not managed by the specified
    205   *                            instance, return nullptr.
    206   * @param aContentNode        The content node where selection is collapsed.
    207   * @param aOffset             Collapsed position in aContentNode
    208   * @param aFrameHint          Caret association hint.
    209   * @param aBidiLevel
    210   * @param aForceEditableRegion    Whether selection should be limited in
    211   *                                editable region or not.
    212   */
    213  static CaretFrameData GetCaretFrameForNodeOffset(
    214      const nsFrameSelection* aFrameSelection, nsIContent* aContentNode,
    215      uint32_t aOffset, CaretAssociationHint aFrameHint,
    216      intl::BidiEmbeddingLevel aBidiLevel,
    217      ForceEditableRegion aForceEditableRegion);
    218 
    219  static bool AdjustFrameForLineStart(nsIFrame*& aFrame,
    220                                      uint32_t& aFrameOffset);
    221 
    222  /**
    223   * Get primary frame and some other data for putting caret or extending
    224   * selection at the point.
    225   */
    226  static PrimaryFrameData GetPrimaryFrameForCaret(
    227      nsIContent* aContent, uint32_t aOffset, bool aVisual,
    228      CaretAssociationHint aHint, intl::BidiEmbeddingLevel aCaretBidiLevel);
    229 
    230 private:
    231  /**
    232   * GetFrameFromLevel will scan in a given direction
    233   * until it finds a frame with a Bidi level less than or equal to a given
    234   * level. It will return the last frame before this.
    235   *
    236   * @param aPresContext is the context to use
    237   * @param aFrameIn is the frame to start from
    238   * @param aDirection is the direction to scan
    239   * @param aBidiLevel is the level to search for
    240   */
    241  static Result<nsIFrame*, nsresult> GetFrameFromLevel(
    242      nsIFrame* aFrameIn, nsDirection aDirection,
    243      intl::BidiEmbeddingLevel aBidiLevel);
    244 
    245  // This is helper method for GetPrimaryFrameForCaret.
    246  // If aVisual is true, this returns caret frame.
    247  // If false, this returns primary frame.
    248  static PrimaryFrameData GetPrimaryOrCaretFrameForNodeOffset(
    249      nsIContent* aContent, uint32_t aOffset, bool aVisual,
    250      CaretAssociationHint aHint, intl::BidiEmbeddingLevel aCaretBidiLevel);
    251 };
    252 
    253 }  // namespace mozilla
    254 
    255 #endif  // #ifndef mozilla_SelectionMovementUtils_h