diff options
Diffstat (limited to '')
-rw-r--r-- | accessible/base/TextLeafRange.h | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/accessible/base/TextLeafRange.h b/accessible/base/TextLeafRange.h new file mode 100644 index 0000000000..d3f33b5622 --- /dev/null +++ b/accessible/base/TextLeafRange.h @@ -0,0 +1,354 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=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_a11y_TextLeafRange_h__ +#define mozilla_a11y_TextLeafRange_h__ + +#include <stdint.h> + +#include "AccAttributes.h" +#include "nsDirection.h" +#include "nsIAccessibleText.h" + +class nsRange; + +namespace mozilla { +namespace dom { +class Document; +} + +namespace a11y { +class Accessible; +class LocalAccessible; + +/** + * Represents a point within accessible text. + * This is stored as a leaf Accessible and an offset into that Accessible. + * For an empty Accessible, the offset will always be 0. + * This will eventually replace TextPoint. Unlike TextPoint, this does not + * use HyperTextAccessible offsets. + */ +class TextLeafPoint final { + public: + TextLeafPoint(Accessible* aAcc, int32_t aOffset); + + /** + * Constructs an invalid TextPoint (mAcc is null). + * A TextLeafPoint in this state will evaluate to false. + * mAcc can be set later. Alternatively, this can be used to indicate an error + * (e.g. if a requested point couldn't be found). + */ + TextLeafPoint() : mAcc(nullptr), mOffset(0) {} + + /** + * Construct a TextLeafPoint representing the caret. + * The actual offset used for the caret differs depending on whether the + * caret is at the end of a line and the query being made. Thus, mOffset on + * the returned TextLeafPoint is not a valid offset. + */ + static TextLeafPoint GetCaret(Accessible* aAcc) { + return TextLeafPoint(aAcc, nsIAccessibleText::TEXT_OFFSET_CARET); + } + + Accessible* mAcc; + int32_t mOffset; + + bool operator==(const TextLeafPoint& aPoint) const { + return mAcc == aPoint.mAcc && mOffset == aPoint.mOffset; + } + + bool operator!=(const TextLeafPoint& aPoint) const { + return !(*this == aPoint); + } + + bool operator<(const TextLeafPoint& aPoint) const; + + bool operator<=(const TextLeafPoint& aPoint) const; + + /** + * A valid TextLeafPoint evaluates to true. An invalid TextLeafPoint + * evaluates to false. + */ + explicit operator bool() const { return !!mAcc; } + + bool IsCaret() const { + return mOffset == nsIAccessibleText::TEXT_OFFSET_CARET; + } + + bool IsCaretAtEndOfLine() const; + + /** + * Get a TextLeafPoint at the actual caret offset. + * This should only be called on a TextLeafPoint created with GetCaret. + * If aAdjustAtEndOfLine is true, the point will be adjusted if the caret is + * at the end of a line so that word and line boundaries can be calculated + * correctly. + */ + TextLeafPoint ActualizeCaret(bool aAdjustAtEndOfLine = true) const; + + enum class BoundaryFlags : uint32_t { + eDefaultBoundaryFlags = 0, + // Return point unchanged if it is at the given boundary type. + eIncludeOrigin = 1 << 0, + // If current point is in editable, return point within samme editable. + eStopInEditable = 1 << 1, + // Skip over list items in searches and don't consider them line or + // paragraph starts. + eIgnoreListItemMarker = 1 << 2, + }; + + /** + * Find a boundary (word start, line start, etc.) in a specific direction. + * If no boundary is found, the start/end of the document is returned + * (depending on the direction). + */ + TextLeafPoint FindBoundary( + AccessibleTextBoundary aBoundaryType, nsDirection aDirection, + BoundaryFlags aFlags = BoundaryFlags::eDefaultBoundaryFlags) const; + + /** + * These two functions find a line start boundary within the same + * LocalAccessible as this. That is, they do not cross Accessibles. If no + * boundary is found, an invalid TextLeafPoint is returned. + * These are used by FindBoundary. Most callers will want FindBoundary + * instead. + */ + TextLeafPoint FindPrevLineStartSameLocalAcc(bool aIncludeOrigin) const; + TextLeafPoint FindNextLineStartSameLocalAcc(bool aIncludeOrigin) const; + + /** + * These two functions find a word start boundary within the same + * Accessible as this. That is, they do not cross Accessibles. If no + * boundary is found, an invalid TextLeafPoint is returned. + * These are used by FindBoundary. Most callers will want FindBoundary + * instead. + */ + TextLeafPoint FindPrevWordStartSameAcc(bool aIncludeOrigin) const; + TextLeafPoint FindNextWordStartSameAcc(bool aIncludeOrigin) const; + + /** + * Get the text attributes at this point. + * If aIncludeDefaults is true, default attributes on the HyperTextAccessible + * will be included. + */ + already_AddRefed<AccAttributes> GetTextAttributes( + bool aIncludeDefaults = true) const; + + /** + * Get Get the text attributes at this point in a LocalAccessible. + * This is used by GetTextAttributes. Most callers will want GetTextAttributes + * instead. + */ + already_AddRefed<AccAttributes> GetTextAttributesLocalAcc( + bool aIncludeDefaults = true) const; + + /** + * Get the offsets of all spelling errors in a given LocalAccessible. This + * should only be used when pushing the cache. Most callers will want + * FindTextAttrsStart instead. + */ + static nsTArray<int32_t> GetSpellingErrorOffsets(LocalAccessible* aAcc); + + /** + * Queue a cache update for a spelling error in a given DOM range. + */ + static void UpdateCachedSpellingError(dom::Document* aDocument, + const nsRange& aRange); + + /** + * Find the start of a run of text attributes in a specific direction. + * A text attributes run is a span of text where the attributes are the same. + * If no boundary is found, the start/end of the container is returned + * (depending on the direction). + * If aIncludeorigin is true and this is at a boundary, this will be + * returned unchanged. + */ + TextLeafPoint FindTextAttrsStart(nsDirection aDirection, + bool aIncludeOrigin = false) const; + + /** + * Returns a rect (in dev pixels) describing position and size of + * the character at mOffset in mAcc. This rect is screen-relative. + * This function only works on remote accessibles, and assumes caching + * is enabled. + */ + LayoutDeviceIntRect CharBounds(); + + /** + * Returns true if the given point (in screen coords) is contained + * in the char bounds of the current TextLeafPoint. Returns false otherwise. + * If the current point is an empty container, we use the acc's bounds instead + * of char bounds. Because this depends on CharBounds, this function only + * works on remote accessibles, and assumes caching is enabled. + */ + bool ContainsPoint(int32_t aX, int32_t aY); + + bool IsLineFeedChar() const { return GetChar() == '\n'; } + + bool IsSpace() const; + + bool IsParagraphStart(bool aIgnoreListItemMarker = false) const { + return mOffset == 0 && + FindParagraphSameAcc(eDirPrevious, true, aIgnoreListItemMarker); + } + + /** + * Translate given TextLeafPoint into a DOM point. + */ + MOZ_CAN_RUN_SCRIPT std::pair<nsIContent*, int32_t> ToDOMPoint( + bool aIncludeGenerated = true) const; + + private: + bool IsEmptyLastLine() const; + + bool IsDocEdge(nsDirection aDirection) const; + + bool IsLeafAfterListItemMarker() const; + + char16_t GetChar() const; + + TextLeafPoint FindLineStartSameRemoteAcc(nsDirection aDirection, + bool aIncludeOrigin) const; + + /** + * Helper which just calls the appropriate function based on whether mAcc + *is local or remote. + */ + TextLeafPoint FindLineStartSameAcc(nsDirection aDirection, + bool aIncludeOrigin, + bool aIgnoreListItemMarker = false) const; + + TextLeafPoint FindLineEnd(nsDirection aDirection, BoundaryFlags aFlags) const; + TextLeafPoint FindWordEnd(nsDirection aDirection, BoundaryFlags aFlags) const; + + TextLeafPoint FindParagraphSameAcc(nsDirection aDirection, + bool aIncludeOrigin, + bool aIgnoreListItemMarker = false) const; + + bool IsInSpellingError() const; + + /** + * Find a spelling error boundary in the same Accessible. This function + * searches for either start or end points, since either means a change in + * text attributes. + */ + TextLeafPoint FindSpellingErrorSameAcc(nsDirection aDirection, + bool aIncludeOrigin) const; + + // Return the point immediately succeeding or preceding this leaf depending + // on given direction. + TextLeafPoint NeighborLeafPoint(nsDirection aDirection, bool aIsEditable, + bool aIgnoreListItemMarker) const; + + /** + * This function assumes mAcc is a LocalAccessible. + * It iterates the continuations of mAcc's primary frame until it locates + * the continuation containing mOffset (a rendered offset). It then uses + * GetScreenRectInAppUnits to compute screen coords for the frame, resizing + * such that the resulting rect contains only one character. + */ + LayoutDeviceIntRect ComputeBoundsFromFrame() const; +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TextLeafPoint::BoundaryFlags) + +/** + * Represents a range of accessible text. + * This will eventually replace TextRange. + */ +class TextLeafRange final { + public: + TextLeafRange(const TextLeafPoint& aStart, const TextLeafPoint& aEnd) + : mStart(aStart), mEnd(aEnd) {} + explicit TextLeafRange(const TextLeafPoint& aStart) + : mStart(aStart), mEnd(aStart) {} + explicit TextLeafRange() {} + + /** + * A valid TextLeafRange evaluates to true. An invalid TextLeafRange + * evaluates to false. + */ + explicit operator bool() const { return !!mStart && !!mEnd; } + + bool operator!=(const TextLeafRange& aOther) const { + return mEnd != aOther.mEnd || mStart != aOther.mStart; + } + + TextLeafPoint Start() const { return mStart; } + void SetStart(const TextLeafPoint& aStart) { mStart = aStart; } + TextLeafPoint End() const { return mEnd; } + void SetEnd(const TextLeafPoint& aEnd) { mEnd = aEnd; } + + /** + * Returns a union rect (in dev pixels) of all character bounds in this range. + * This rect is screen-relative and inclusive of mEnd. This function only + * works on remote accessibles, and assumes caching is enabled. + */ + LayoutDeviceIntRect Bounds() const; + + /** + * Set range as DOM selection. + * aSelectionNum is the selection index to use. If aSelectionNum is + * out of bounds for current selection ranges, or is -1, a new selection + * range is created. + */ + MOZ_CAN_RUN_SCRIPT bool SetSelection(int32_t aSelectionNum) const; + + MOZ_CAN_RUN_SCRIPT void ScrollIntoView(uint32_t aScrollType) const; + + private: + TextLeafPoint mStart; + TextLeafPoint mEnd; + + public: + /** + * A TextLeafRange iterator will iterate through single leaf segments of the + * given range. + */ + + class Iterator { + public: + Iterator(Iterator&& aOther) + : mRange(aOther.mRange), + mSegmentStart(aOther.mSegmentStart), + mSegmentEnd(aOther.mSegmentEnd) {} + + static Iterator BeginIterator(const TextLeafRange& aRange); + + static Iterator EndIterator(const TextLeafRange& aRange); + + Iterator& operator++(); + + bool operator!=(const Iterator& aOther) const { + return mRange != aOther.mRange || mSegmentStart != aOther.mSegmentStart || + mSegmentEnd != aOther.mSegmentEnd; + } + + TextLeafRange operator*() { + return TextLeafRange(mSegmentStart, mSegmentEnd); + } + + private: + explicit Iterator(const TextLeafRange& aRange) : mRange(aRange) {} + + Iterator() = delete; + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + Iterator& operator=(const Iterator&&) = delete; + + const TextLeafRange& mRange; + TextLeafPoint mSegmentStart; + TextLeafPoint mSegmentEnd; + }; + + Iterator begin() const { return Iterator::BeginIterator(*this); } + Iterator end() const { return Iterator::EndIterator(*this); } +}; + +} // namespace a11y +} // namespace mozilla + +#endif |