Bug 1913305 - Add test. r=mtigley
[gecko.git] / accessible / base / TextLeafRange.h
blob7f5d609945b65bd7b031dfd44af4e6a891ae5579
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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/. */
7 #ifndef mozilla_a11y_TextLeafRange_h__
8 #define mozilla_a11y_TextLeafRange_h__
10 #include <stdint.h>
12 #include "AccAttributes.h"
13 #include "nsDirection.h"
14 #include "nsIAccessibleText.h"
16 namespace mozilla {
17 namespace dom {
18 class AbstractRange;
19 class Document;
20 } // namespace dom
22 namespace a11y {
23 class Accessible;
24 class LocalAccessible;
26 /**
27 * Represents a point within accessible text.
28 * This is stored as a leaf Accessible and an offset into that Accessible.
29 * For an empty Accessible, the offset will always be 0.
30 * This will eventually replace TextPoint. Unlike TextPoint, this does not
31 * use HyperTextAccessible offsets.
33 class TextLeafPoint final {
34 public:
35 TextLeafPoint(Accessible* aAcc, int32_t aOffset);
37 /**
38 * Constructs an invalid TextPoint (mAcc is null).
39 * A TextLeafPoint in this state will evaluate to false.
40 * mAcc can be set later. Alternatively, this can be used to indicate an error
41 * (e.g. if a requested point couldn't be found).
43 TextLeafPoint() : mAcc(nullptr), mOffset(0) {}
45 /**
46 * Construct a TextLeafPoint representing the caret.
48 static TextLeafPoint GetCaret(Accessible* aAcc);
50 Accessible* mAcc;
51 int32_t mOffset;
53 /**
54 * True if this point is the insertion point at the end of a line. This is the
55 * point where the caret is positioned when pressing the end key, for example.
56 * On the very last line, mOffset will be equal to the length of the text.
57 * However, where text wraps across lines, this line end insertion point
58 * doesn't have its own offset, so mOffset will be the offset for the first
59 * character on the next line. This is where this flag becomes important.
60 * Otherwise, for example, commanding a screen reader to read the current line
61 * would read the next line instead of the current line in this case.
63 bool mIsEndOfLineInsertionPoint = false;
65 bool operator==(const TextLeafPoint& aPoint) const {
66 return mAcc == aPoint.mAcc && mOffset == aPoint.mOffset;
69 bool operator!=(const TextLeafPoint& aPoint) const {
70 return !(*this == aPoint);
73 bool operator<(const TextLeafPoint& aPoint) const;
75 bool operator<=(const TextLeafPoint& aPoint) const;
77 /**
78 * A valid TextLeafPoint evaluates to true. An invalid TextLeafPoint
79 * evaluates to false.
81 explicit operator bool() const { return !!mAcc; }
83 enum class BoundaryFlags : uint32_t {
84 eDefaultBoundaryFlags = 0,
85 // Return point unchanged if it is at the given boundary type.
86 eIncludeOrigin = 1 << 0,
87 // If current point is in editable, return point within samme editable.
88 eStopInEditable = 1 << 1,
89 // Skip over list items in searches and don't consider them line or
90 // paragraph starts.
91 eIgnoreListItemMarker = 1 << 2,
94 /**
95 * Find a boundary (word start, line start, etc.) in a specific direction.
96 * If no boundary is found, the start/end of the document is returned
97 * (depending on the direction).
99 TextLeafPoint FindBoundary(
100 AccessibleTextBoundary aBoundaryType, nsDirection aDirection,
101 BoundaryFlags aFlags = BoundaryFlags::eDefaultBoundaryFlags) const;
104 * These two functions find a line start boundary within the same
105 * LocalAccessible as this. That is, they do not cross Accessibles. If no
106 * boundary is found, an invalid TextLeafPoint is returned.
107 * These are used by FindBoundary. Most callers will want FindBoundary
108 * instead.
110 TextLeafPoint FindPrevLineStartSameLocalAcc(bool aIncludeOrigin) const;
111 TextLeafPoint FindNextLineStartSameLocalAcc(bool aIncludeOrigin) const;
114 * These two functions find a word start boundary within the same
115 * Accessible as this. That is, they do not cross Accessibles. If no
116 * boundary is found, an invalid TextLeafPoint is returned.
117 * These are used by FindBoundary. Most callers will want FindBoundary
118 * instead.
120 TextLeafPoint FindPrevWordStartSameAcc(bool aIncludeOrigin) const;
121 TextLeafPoint FindNextWordStartSameAcc(bool aIncludeOrigin) const;
124 * Get the text attributes at this point.
125 * If aIncludeDefaults is true, default attributes on the HyperTextAccessible
126 * will be included.
128 already_AddRefed<AccAttributes> GetTextAttributes(
129 bool aIncludeDefaults = true) const;
132 * Get Get the text attributes at this point in a LocalAccessible.
133 * This is used by GetTextAttributes. Most callers will want GetTextAttributes
134 * instead.
136 already_AddRefed<AccAttributes> GetTextAttributesLocalAcc(
137 bool aIncludeDefaults = true) const;
140 * Get all the attributes that apply to offset ranges in a given text leaf
141 * LocalAccessible. This should only be used when pushing the cache. Most
142 * callers will want FindTextAttrsStart instead.
144 static nsTArray<TextOffsetAttribute> GetTextOffsetAttributes(
145 LocalAccessible* aAcc);
148 * Queue a cache update for text offset attributes in a given DOM range.
150 static void UpdateCachedTextOffsetAttributes(
151 dom::Document* aDocument, const dom::AbstractRange& aRange);
154 * Find the start of a run of text attributes in a specific direction.
155 * A text attributes run is a span of text where the attributes are the same.
156 * If no boundary is found, the start/end of the container is returned
157 * (depending on the direction).
158 * If aIncludeorigin is true and this is at a boundary, this will be
159 * returned unchanged.
161 TextLeafPoint FindTextAttrsStart(nsDirection aDirection,
162 bool aIncludeOrigin = false) const;
165 * Returns a rect (in dev pixels) describing position and size of
166 * the character at mOffset in mAcc. This rect is screen-relative.
167 * This function only works on remote accessibles, and assumes caching
168 * is enabled.
170 LayoutDeviceIntRect CharBounds();
173 * Returns true if the given point (in screen coords) is contained
174 * in the char bounds of the current TextLeafPoint. Returns false otherwise.
175 * If the current point is an empty container, we use the acc's bounds instead
176 * of char bounds. Because this depends on CharBounds, this function only
177 * works on remote accessibles, and assumes caching is enabled.
179 bool ContainsPoint(int32_t aX, int32_t aY);
181 bool IsLineFeedChar() const { return GetChar() == '\n'; }
183 bool IsSpace() const;
185 bool IsParagraphStart(bool aIgnoreListItemMarker = false) const {
186 return mOffset == 0 &&
187 FindParagraphSameAcc(eDirPrevious, true, aIgnoreListItemMarker);
191 * Translate given TextLeafPoint into a DOM point.
193 MOZ_CAN_RUN_SCRIPT std::pair<nsIContent*, int32_t> ToDOMPoint(
194 bool aIncludeGenerated = true) const;
196 private:
198 * If this is the insertion point at the end of a line, return an adjusted
199 * point such that word and line boundaries can be calculated correctly.
201 TextLeafPoint AdjustEndOfLine() const;
203 bool IsEmptyLastLine() const;
205 bool IsDocEdge(nsDirection aDirection) const;
207 bool IsLeafAfterListItemMarker() const;
209 char16_t GetChar() const;
211 TextLeafPoint FindLineStartSameRemoteAcc(nsDirection aDirection,
212 bool aIncludeOrigin) const;
215 * Helper which just calls the appropriate function based on whether mAcc
216 *is local or remote.
218 TextLeafPoint FindLineStartSameAcc(nsDirection aDirection,
219 bool aIncludeOrigin,
220 bool aIgnoreListItemMarker = false) const;
222 TextLeafPoint FindLineEnd(nsDirection aDirection, BoundaryFlags aFlags) const;
223 TextLeafPoint FindWordEnd(nsDirection aDirection, BoundaryFlags aFlags) const;
225 TextLeafPoint FindParagraphSameAcc(nsDirection aDirection,
226 bool aIncludeOrigin,
227 bool aIgnoreListItemMarker = false) const;
229 TextLeafPoint FindClusterSameAcc(nsDirection aDirection,
230 bool aIncludeOrigin) const;
232 void AddTextOffsetAttributes(AccAttributes* aAttrs) const;
235 * Find a text offset attribute boundary in the same Accessible. This function
236 * searches for either start or end points, since either means a change in
237 * text attributes. This only considers attributes such as spelling errors
238 * which are mapped to DOM selections. Most callers will want
239 * FindTextAttrsStart instead.
241 TextLeafPoint FindTextOffsetAttributeSameAcc(nsDirection aDirection,
242 bool aIncludeOrigin) const;
244 // Return the point immediately succeeding or preceding this leaf depending
245 // on given direction.
246 TextLeafPoint NeighborLeafPoint(nsDirection aDirection, bool aIsEditable,
247 bool aIgnoreListItemMarker) const;
250 * This function assumes mAcc is a LocalAccessible.
251 * It iterates the continuations of mAcc's primary frame until it locates
252 * the continuation containing mOffset (a rendered offset). It then uses
253 * GetScreenRectInAppUnits to compute screen coords for the frame, resizing
254 * such that the resulting rect contains only one character.
256 LayoutDeviceIntRect ComputeBoundsFromFrame() const;
259 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TextLeafPoint::BoundaryFlags)
262 * Represents a range of accessible text.
263 * This will eventually replace TextRange.
265 class TextLeafRange final {
266 public:
267 TextLeafRange(const TextLeafPoint& aStart, const TextLeafPoint& aEnd)
268 : mStart(aStart), mEnd(aEnd) {}
269 explicit TextLeafRange(const TextLeafPoint& aStart)
270 : mStart(aStart), mEnd(aStart) {}
271 explicit TextLeafRange() {}
274 * A valid TextLeafRange evaluates to true. An invalid TextLeafRange
275 * evaluates to false.
277 explicit operator bool() const { return !!mStart && !!mEnd; }
279 bool operator!=(const TextLeafRange& aOther) const {
280 return mEnd != aOther.mEnd || mStart != aOther.mStart;
283 bool operator==(const TextLeafRange& aOther) const {
284 return mEnd == aOther.mEnd && mStart == aOther.mStart;
287 TextLeafPoint Start() const { return mStart; }
288 void SetStart(const TextLeafPoint& aStart) { mStart = aStart; }
289 TextLeafPoint End() const { return mEnd; }
290 void SetEnd(const TextLeafPoint& aEnd) { mEnd = aEnd; }
292 bool Crop(Accessible* aContainer);
295 * Returns a union rect (in dev pixels) of all character bounds in this range.
296 * This rect is screen-relative and inclusive of mEnd. This function only
297 * works on remote accessibles, and assumes caching is enabled.
299 LayoutDeviceIntRect Bounds() const;
302 * Get the ranges of text that are selected within this Accessible. The caret
303 * is not included as a collapsed range.
305 static void GetSelection(Accessible* aAcc, nsTArray<TextLeafRange>& aRanges);
308 * Set range as DOM selection.
309 * aSelectionNum is the selection index to use. If aSelectionNum is
310 * out of bounds for current selection ranges, or is -1, a new selection
311 * range is created.
313 MOZ_CAN_RUN_SCRIPT bool SetSelection(int32_t aSelectionNum) const;
315 MOZ_CAN_RUN_SCRIPT void ScrollIntoView(uint32_t aScrollType) const;
317 private:
318 TextLeafPoint mStart;
319 TextLeafPoint mEnd;
321 public:
323 * A TextLeafRange iterator will iterate through single leaf segments of the
324 * given range.
327 class Iterator {
328 public:
329 Iterator(Iterator&& aOther)
330 : mRange(aOther.mRange),
331 mSegmentStart(aOther.mSegmentStart),
332 mSegmentEnd(aOther.mSegmentEnd) {}
334 static Iterator BeginIterator(const TextLeafRange& aRange);
336 static Iterator EndIterator(const TextLeafRange& aRange);
338 Iterator& operator++();
340 bool operator!=(const Iterator& aOther) const {
341 return mRange != aOther.mRange || mSegmentStart != aOther.mSegmentStart ||
342 mSegmentEnd != aOther.mSegmentEnd;
345 TextLeafRange operator*() {
346 return TextLeafRange(mSegmentStart, mSegmentEnd);
349 private:
350 explicit Iterator(const TextLeafRange& aRange) : mRange(aRange) {}
352 Iterator() = delete;
353 Iterator(const Iterator&) = delete;
354 Iterator& operator=(const Iterator&) = delete;
355 Iterator& operator=(const Iterator&&) = delete;
357 const TextLeafRange& mRange;
358 TextLeafPoint mSegmentStart;
359 TextLeafPoint mSegmentEnd;
362 Iterator begin() const { return Iterator::BeginIterator(*this); }
363 Iterator end() const { return Iterator::EndIterator(*this); }
366 } // namespace a11y
367 } // namespace mozilla
369 #endif