/* -*- 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 #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 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 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 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 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; } 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; } bool Crop(Accessible* aContainer); /** * 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