File: SelectionMovementUtils.h

package info (click to toggle)
firefox 148.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,719,544 kB
  • sloc: cpp: 7,618,291; javascript: 6,701,749; ansic: 3,781,787; python: 1,418,389; xml: 638,647; asm: 438,962; java: 186,285; sh: 62,894; makefile: 19,011; objc: 13,092; perl: 12,763; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (255 lines) | stat: -rw-r--r-- 10,115 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_SelectionMovementUtils_h
#define mozilla_SelectionMovementUtils_h

#include "mozilla/Attributes.h"
#include "mozilla/EnumSet.h"
#include "mozilla/RangeBoundary.h"
#include "mozilla/Result.h"
#include "mozilla/intl/BidiEmbeddingLevel.h"
#include "nsIFrame.h"

struct nsPrevNextBidiLevels;

namespace mozilla {

class PresShell;
enum class PeekOffsetOption : uint16_t;

namespace intl {
class BidiEmbeddingLevel;
}

struct MOZ_STACK_CLASS FrameAndOffset {
  [[nodiscard]] nsIContent* GetFrameContent() const {
    return mFrame ? mFrame->GetContent() : nullptr;
  }

  operator nsIFrame*() const { return mFrame; }

  explicit operator bool() const { return !!mFrame; }
  [[nodiscard]] bool operator!() const { return !mFrame; }

  nsIFrame* operator->() const {
    MOZ_ASSERT(mFrame);
    return mFrame;
  }

  nsIFrame* mFrame = nullptr;
  // The offset in mFrame->GetContent().
  uint32_t mOffsetInFrameContent = 0;
};

struct MOZ_STACK_CLASS PrimaryFrameData : public FrameAndOffset {
  // Whether the caret should be put before or after the point. This is valid
  // only when mFrame is not nullptr.
  CaretAssociationHint mHint{0};  // Before
};

struct MOZ_STACK_CLASS CaretFrameData : public PrimaryFrameData {
  // The frame which is found only from a DOM point.  This frame becomes
  // different from mFrame when the point is around end of a line or
  // at a bidi text boundary.
  nsIFrame* mUnadjustedFrame = nullptr;
};

enum class ForceEditableRegion : bool { No, Yes };

class SelectionMovementUtils final {
 public:
  using PeekOffsetOptions = EnumSet<PeekOffsetOption>;

  /**
   * @brief Creates a new `RangeBoundary` which moves `aAmount` into
   * `aDirection` from the input range boundary.
   *
   * @param aRangeBoundary   The input range boundary.
   * @param aDirection       The direction into which the new boundary should be
   *                         moved.
   * @param aHint            The `CaretAssociationHint` (is the caret before or
   *                         after the boundary point)
   * @param aCaretBidiLevel  The `BidiEmbeddingLevel`.
   * @param aAmount          The amount which the range boundary should be
   *                         moved.
   * @param aOptions         Additional options, see `PeekOffsetOption`.
   * @param aAncestorLimiter The content node that limits where Selection may be
   *                         expanded to.
   *
   * @return Returns a new `RangeBoundary` which is moved from `aRangeBoundary`
   *         by `aAmount` into `aDirection`.
   */
  template <typename ParentType, typename RefType>
  static Result<RangeBoundaryBase<ParentType, RefType>, nsresult>
  MoveRangeBoundaryToSomewhere(
      const RangeBoundaryBase<ParentType, RefType>& aRangeBoundary,
      nsDirection aDirection, CaretAssociationHint aHint,
      intl::BidiEmbeddingLevel aCaretBidiLevel, nsSelectionAmount aAmount,
      PeekOffsetOptions aOptions,
      const dom::Element* aAncestorLimiter = nullptr);

  /**
   * Given a node and its child offset, return the nsIFrame and the offset into
   * that frame.
   *
   * @param aNode input parameter for the node to look at
   * @param aOffset offset into above node.
   */
  static FrameAndOffset GetFrameForNodeOffset(const nsIContent* aNode,
                                              uint32_t aOffset,
                                              CaretAssociationHint aHint);

  /**
   * Return the first visible point in or at a leaf node in aRange or the first
   * unselectable content if aRange starts from a selectable container. E.g.,
   * return the start of the first visible `Text` or the position of the first
   * visible leaf element.  I.e., the result may be a good point to put a UI for
   * showing something around the start boundary.
   *
   * NOTE: This won't return any boundary point in subtrees from the tree
   * containing the start container of aRange due to ContentIteratorBase's
   * limitation. See bug 2001511.
   *
   * @param aRange Must not be collapsed because this returns a point in aRange
   * so that this requires the limitation of scanning forward.
   * @return A position in a `Text` or a position at an element.
   */
  [[nodiscard]] static RawRangeBoundary GetFirstVisiblePointAtLeaf(
      const dom::AbstractRange& aRange);

  /**
   * Return the last visible point in or at a leaf node in aRange or the last
   * unselectable content if aRange ends in a selectable container. E.g., return
   * the end of the last visible `Text` or the position of the last visible leaf
   * element. I.e., the result may be a good point to put a UI for showing
   * something around the end boundary.
   *
   * NOTE: This won't return any boundary point in subtrees of the tree
   * containing the end container of aRange due to ContentIteratorBase's
   * limitation. See bug 2001511.
   *
   * @param aRange Must not be collapsed because this returns a point in aRange
   * so that this requires the limitation of scanning forward.
   * @return A position in a `Text` or a position at an element.
   */
  [[nodiscard]] static RawRangeBoundary GetLastVisiblePointAtLeaf(
      const dom::AbstractRange& aRange);

  /**
   * GetPrevNextBidiLevels will return the frames and associated Bidi levels of
   * the characters logically before and after a (collapsed) selection.
   *
   * @param aNode is the node containing the selection
   * @param aContentOffset is the offset of the selection in the node
   * @param aJumpLines
   *   If true, look across line boundaries.
   *   If false, behave as if there were base-level frames at line edges.
   * @param aAncestorLimiter  If set, this refers only the descendants.
   *
   * @return A struct holding the before/after frame and the before/after
   * level.
   *
   * At the beginning and end of each line there is assumed to be a frame with
   * Bidi level equal to the paragraph embedding level.
   *
   * In these cases the before frame and after frame respectively will be
   * nullptr.
   */
  static nsPrevNextBidiLevels GetPrevNextBidiLevels(
      nsIContent* aNode, uint32_t aContentOffset, CaretAssociationHint aHint,
      bool aJumpLines, const dom::Element* aAncestorLimiter);

  /**
   * PeekOffsetForCaretMove() only peek offset for caret move from the specified
   * point of the normal selection.  I.e., won't change selection ranges nor
   * bidi information.
   */
  static Result<PeekOffsetStruct, nsresult> PeekOffsetForCaretMove(
      nsIContent* aContent, uint32_t aOffset, nsDirection aDirection,
      CaretAssociationHint aHint, intl::BidiEmbeddingLevel aCaretBidiLevel,
      const nsSelectionAmount aAmount, const nsPoint& aDesiredCaretPos,
      PeekOffsetOptions aOptions, const dom::Element* aAncestorLimiter);

  /**
   * IsIntraLineCaretMove() is a helper method for PeekOffsetForCaretMove()
   * and CreateRangeExtendedToSomwhereFromNormalSelection().  This returns
   * whether aAmount is intra line move or is crossing hard line break.
   * This returns error if aMount is not supported by the methods.
   */
  static Result<bool, nsresult> IsIntraLineCaretMove(
      nsSelectionAmount aAmount) {
    switch (aAmount) {
      case eSelectCharacter:
      case eSelectCluster:
      case eSelectWord:
      case eSelectWordNoSpace:
      case eSelectBeginLine:
      case eSelectEndLine:
        return true;
      case eSelectLine:
        return false;
      default:
        return Err(NS_ERROR_FAILURE);
    }
  }

  /**
   * Return a frame for considering caret geometry.
   *
   * @param aFrameSelection     [optional] If this is specified and selection in
   *                            aContent is not managed by the specified
   *                            instance, return nullptr.
   * @param aContentNode        The content node where selection is collapsed.
   * @param aOffset             Collapsed position in aContentNode
   * @param aFrameHint          Caret association hint.
   * @param aBidiLevel
   * @param aForceEditableRegion    Whether selection should be limited in
   *                                editable region or not.
   */
  static CaretFrameData GetCaretFrameForNodeOffset(
      const nsFrameSelection* aFrameSelection, nsIContent* aContentNode,
      uint32_t aOffset, CaretAssociationHint aFrameHint,
      intl::BidiEmbeddingLevel aBidiLevel,
      ForceEditableRegion aForceEditableRegion);

  static bool AdjustFrameForLineStart(nsIFrame*& aFrame,
                                      uint32_t& aFrameOffset);

  /**
   * Get primary frame and some other data for putting caret or extending
   * selection at the point.
   */
  static PrimaryFrameData GetPrimaryFrameForCaret(
      nsIContent* aContent, uint32_t aOffset, bool aVisual,
      CaretAssociationHint aHint, intl::BidiEmbeddingLevel aCaretBidiLevel);

 private:
  /**
   * GetFrameFromLevel will scan in a given direction
   * until it finds a frame with a Bidi level less than or equal to a given
   * level. It will return the last frame before this.
   *
   * @param aPresContext is the context to use
   * @param aFrameIn is the frame to start from
   * @param aDirection is the direction to scan
   * @param aBidiLevel is the level to search for
   */
  static Result<nsIFrame*, nsresult> GetFrameFromLevel(
      nsIFrame* aFrameIn, nsDirection aDirection,
      intl::BidiEmbeddingLevel aBidiLevel);

  // This is helper method for GetPrimaryFrameForCaret.
  // If aVisual is true, this returns caret frame.
  // If false, this returns primary frame.
  static PrimaryFrameData GetPrimaryOrCaretFrameForNodeOffset(
      nsIContent* aContent, uint32_t aOffset, bool aVisual,
      CaretAssociationHint aHint, intl::BidiEmbeddingLevel aCaretBidiLevel);
};

}  // namespace mozilla

#endif  // #ifndef mozilla_SelectionMovementUtils_h