tor-browser

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

TextLeafRange.h (15192B)


      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/. */
      6 
      7 #ifndef mozilla_a11y_TextLeafRange_h__
      8 #define mozilla_a11y_TextLeafRange_h__
      9 
     10 #include <stdint.h>
     11 
     12 #include "AccAttributes.h"
     13 #include "nsDirection.h"
     14 #include "nsIAccessibleText.h"
     15 #include "mozilla/FunctionRef.h"
     16 
     17 namespace mozilla {
     18 namespace dom {
     19 class AbstractRange;
     20 class Document;
     21 }  // namespace dom
     22 
     23 namespace a11y {
     24 class Accessible;
     25 class LocalAccessible;
     26 
     27 /**
     28 * Represents a point within accessible text.
     29 * This is stored as a leaf Accessible and an offset into that Accessible.
     30 * For an empty Accessible, the offset will always be 0.
     31 * This will eventually replace TextPoint. Unlike TextPoint, this does not
     32 * use HyperTextAccessible offsets.
     33 */
     34 class TextLeafPoint final {
     35 public:
     36  TextLeafPoint(Accessible* aAcc, int32_t aOffset);
     37 
     38  /**
     39   * Constructs an invalid TextPoint (mAcc is null).
     40   * A TextLeafPoint in this state will evaluate to false.
     41   * mAcc can be set later. Alternatively, this can be used to indicate an error
     42   * (e.g. if a requested point couldn't be found).
     43   */
     44  TextLeafPoint() : mAcc(nullptr), mOffset(0) {}
     45 
     46  /**
     47   * Construct a TextLeafPoint representing the caret.
     48   */
     49  static TextLeafPoint GetCaret(Accessible* aAcc);
     50 
     51  Accessible* mAcc;
     52  int32_t mOffset;
     53 
     54  /**
     55   * True if this point is the insertion point at the end of a line. This is the
     56   * point where the caret is positioned when pressing the end key, for example.
     57   * On the very last line, mOffset will be equal to the length of the text.
     58   * However, where text wraps across lines, this line end insertion point
     59   * doesn't have its own offset, so mOffset will be the offset for the first
     60   * character on the next line. This is where this flag becomes important.
     61   * Otherwise, for example, commanding a screen reader to read the current line
     62   * would read the next line instead of the current line in this case.
     63   */
     64  bool mIsEndOfLineInsertionPoint = false;
     65 
     66  bool operator==(const TextLeafPoint& aPoint) const {
     67    return mAcc == aPoint.mAcc && mOffset == aPoint.mOffset;
     68  }
     69 
     70  bool operator!=(const TextLeafPoint& aPoint) const {
     71    return !(*this == aPoint);
     72  }
     73 
     74  bool operator<(const TextLeafPoint& aPoint) const;
     75 
     76  bool operator<=(const TextLeafPoint& aPoint) const;
     77 
     78  /**
     79   * A valid TextLeafPoint evaluates to true. An invalid TextLeafPoint
     80   * evaluates to false.
     81   */
     82  explicit operator bool() const { return !!mAcc; }
     83 
     84  enum class BoundaryFlags : uint32_t {
     85    eDefaultBoundaryFlags = 0,
     86    // Return point unchanged if it is at the given boundary type.
     87    eIncludeOrigin = 1 << 0,
     88    // If current point is in editable, return point within same editable.
     89    eStopInEditable = 1 << 1,
     90    // Skip over list items in searches and don't consider them line or
     91    // paragraph starts.
     92    eIgnoreListItemMarker = 1 << 2,
     93  };
     94 
     95  /**
     96   * Find a boundary (word start, line start, etc.) in a specific direction.
     97   * If no boundary is found, the start/end of the document is returned
     98   * (depending on the direction).
     99   */
    100  TextLeafPoint FindBoundary(
    101      AccessibleTextBoundary aBoundaryType, nsDirection aDirection,
    102      BoundaryFlags aFlags = BoundaryFlags::eDefaultBoundaryFlags) const;
    103 
    104  /**
    105   * These two functions find a line start boundary within the same
    106   * LocalAccessible as this. That is, they do not cross Accessibles. If no
    107   * boundary is found, an invalid TextLeafPoint is returned.
    108   * These are used by FindBoundary. Most callers will want FindBoundary
    109   * instead.
    110   */
    111  TextLeafPoint FindPrevLineStartSameLocalAcc(bool aIncludeOrigin) const;
    112  TextLeafPoint FindNextLineStartSameLocalAcc(bool aIncludeOrigin) const;
    113 
    114  /**
    115   * These two functions find a word start boundary within the same
    116   * Accessible as this. That is, they do not cross Accessibles. If no
    117   * boundary is found, an invalid TextLeafPoint is returned.
    118   * These are used by FindBoundary. Most callers will want FindBoundary
    119   * instead.
    120   */
    121  TextLeafPoint FindPrevWordStartSameAcc(bool aIncludeOrigin) const;
    122  TextLeafPoint FindNextWordStartSameAcc(bool aIncludeOrigin) const;
    123 
    124  /**
    125   * Get the text attributes at this point.
    126   * If aIncludeDefaults is true, default attributes on the HyperTextAccessible
    127   * will be included.
    128   */
    129  already_AddRefed<AccAttributes> GetTextAttributes(
    130      bool aIncludeDefaults = true) const;
    131 
    132  /**
    133   * Get Get the text attributes at this point in a LocalAccessible.
    134   * This is used by GetTextAttributes. Most callers will want GetTextAttributes
    135   * instead.
    136   */
    137  already_AddRefed<AccAttributes> GetTextAttributesLocalAcc(
    138      bool aIncludeDefaults = true) const;
    139 
    140  /**
    141   * Get all the attributes that apply to offset ranges in a given text leaf
    142   * LocalAccessible. This should only be used when pushing the cache. Most
    143   * callers will want FindTextAttrsStart instead.
    144   */
    145  static nsTArray<TextOffsetAttribute> GetTextOffsetAttributes(
    146      LocalAccessible* aAcc);
    147 
    148  /**
    149   * Queue a cache update for text offset attributes in a given DOM range.
    150   */
    151  static void UpdateCachedTextOffsetAttributes(
    152      dom::Document* aDocument, const dom::AbstractRange& aRange);
    153 
    154  /**
    155   * Find the start of a run of text attributes in a specific direction.
    156   * A text attributes run is a span of text where the attributes are the same.
    157   * If no boundary is found, the function will walk out of the container and
    158   * into the next/previous leaf (if it exists) until it finds a start point.
    159   * If aIncludeOrigin is true and this is at a boundary, this will be
    160   * returned unchanged.
    161   */
    162  TextLeafPoint FindTextAttrsStart(nsDirection aDirection,
    163                                   bool aIncludeOrigin = false) const;
    164 
    165  /**
    166   * Returns a rect (in dev pixels) describing position and size of
    167   * the character at mOffset in mAcc. This rect is screen-relative.
    168   */
    169  LayoutDeviceIntRect CharBounds() const;
    170 
    171  /**
    172   * Returns true if the given point (in screen coords) is contained
    173   * in the char bounds of the current TextLeafPoint. Returns false otherwise.
    174   * If the current point is an empty container, we use the acc's bounds instead
    175   * of char bounds.
    176   */
    177  bool ContainsPoint(int32_t aX, int32_t aY);
    178 
    179  bool IsLineFeedChar() const { return GetChar() == '\n'; }
    180 
    181  bool IsSpace() const;
    182 
    183  bool IsParagraphStart(bool aIgnoreListItemMarker = false) const {
    184    return mOffset == 0 &&
    185           FindParagraphSameAcc(eDirPrevious, true, aIgnoreListItemMarker);
    186  }
    187 
    188  /**
    189   * Translate given TextLeafPoint into a DOM point.
    190   */
    191  MOZ_CAN_RUN_SCRIPT std::pair<nsIContent*, uint32_t> ToDOMPoint(
    192      bool aIncludeGenerated = true) const;
    193 
    194 private:
    195  /**
    196   * If this is the insertion point at the end of a line, return an adjusted
    197   * point such that word and line boundaries can be calculated correctly.
    198   */
    199  TextLeafPoint AdjustEndOfLine() const;
    200 
    201  bool IsEmptyLastLine() const;
    202 
    203  bool IsDocEdge(nsDirection aDirection) const;
    204 
    205  bool IsLeafAfterListItemMarker() const;
    206 
    207  char16_t GetChar() const;
    208 
    209  TextLeafPoint FindLineStartSameRemoteAcc(nsDirection aDirection,
    210                                           bool aIncludeOrigin) const;
    211 
    212  /**
    213   * Helper which just calls the appropriate function based on whether mAcc
    214   *is local or remote.
    215   */
    216  TextLeafPoint FindLineStartSameAcc(nsDirection aDirection,
    217                                     bool aIncludeOrigin,
    218                                     bool aIgnoreListItemMarker = false) const;
    219 
    220  TextLeafPoint FindLineEnd(nsDirection aDirection, BoundaryFlags aFlags) const;
    221  TextLeafPoint FindWordEnd(nsDirection aDirection, BoundaryFlags aFlags) const;
    222 
    223  TextLeafPoint FindParagraphSameAcc(nsDirection aDirection,
    224                                     bool aIncludeOrigin,
    225                                     bool aIgnoreListItemMarker = false) const;
    226 
    227  TextLeafPoint FindClusterSameAcc(nsDirection aDirection,
    228                                   bool aIncludeOrigin) const;
    229 
    230  void AddTextOffsetAttributes(AccAttributes* aAttrs) const;
    231 
    232  /**
    233   * Find a text offset attribute boundary in the same Accessible. This function
    234   * searches for either start or end points, since either means a change in
    235   * text attributes. This only considers attributes such as spelling errors
    236   * which are mapped to DOM selections. Most callers will want
    237   * FindTextAttrsStart instead.
    238   */
    239  TextLeafPoint FindTextOffsetAttributeSameAcc(nsDirection aDirection,
    240                                               bool aIncludeOrigin) const;
    241 
    242  // Return the point immediately succeeding or preceding this leaf depending
    243  // on given direction.
    244  TextLeafPoint NeighborLeafPoint(nsDirection aDirection, bool aIsEditable,
    245                                  bool aIgnoreListItemMarker) const;
    246 
    247  /**
    248   * This function assumes mAcc is a LocalAccessible.
    249   * It iterates the continuations of mAcc's primary frame until it locates
    250   * the continuation containing mOffset (a rendered offset). It then uses
    251   * GetScreenRectInAppUnits to compute screen coords for the frame, resizing
    252   * such that the resulting rect contains only one character.
    253   */
    254  LayoutDeviceIntRect ComputeBoundsFromFrame() const;
    255 
    256  LayoutDeviceIntRect InsertionPointBounds() const;
    257 
    258  friend class TextLeafRange;
    259 };
    260 
    261 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TextLeafPoint::BoundaryFlags)
    262 
    263 /**
    264 * Represents a range of accessible text.
    265 * This will eventually replace TextRange.
    266 */
    267 class TextLeafRange final {
    268 public:
    269  TextLeafRange(const TextLeafPoint& aStart, const TextLeafPoint& aEnd)
    270      : mStart(aStart), mEnd(aEnd) {}
    271  explicit TextLeafRange(const TextLeafPoint& aStart)
    272      : mStart(aStart), mEnd(aStart) {}
    273  explicit TextLeafRange() {}
    274 
    275  // Create a TextLeafRange spanning the entire leaf.
    276  static TextLeafRange FromAccessible(Accessible* aAcc) {
    277    return {{aAcc, 0}, {aAcc, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT}};
    278  }
    279 
    280  /**
    281   * A valid TextLeafRange evaluates to true. An invalid TextLeafRange
    282   * evaluates to false.
    283   */
    284  explicit operator bool() const { return !!mStart && !!mEnd; }
    285 
    286  bool operator!=(const TextLeafRange& aOther) const {
    287    return mEnd != aOther.mEnd || mStart != aOther.mStart;
    288  }
    289 
    290  bool operator==(const TextLeafRange& aOther) const {
    291    return mEnd == aOther.mEnd && mStart == aOther.mStart;
    292  }
    293 
    294  TextLeafPoint Start() const { return mStart; }
    295  void SetStart(const TextLeafPoint& aStart) { mStart = aStart; }
    296  TextLeafPoint End() const { return mEnd; }
    297  void SetEnd(const TextLeafPoint& aEnd) { mEnd = aEnd; }
    298 
    299  bool Crop(Accessible* aContainer);
    300 
    301  /**
    302   * Returns a union rect (in dev pixels) of all character bounds in this range.
    303   * This rect is screen-relative and exclusive of mEnd.
    304   */
    305  LayoutDeviceIntRect Bounds() const;
    306 
    307  /**
    308   * Returns an array of bounding rectangles, one for each visible text line in
    309   * this range. These rectangles are screen-relative and exclusive of mEnd.
    310   */
    311  nsTArray<LayoutDeviceIntRect> LineRects() const;
    312 
    313  /**
    314   * Returns a TextLeafPoint corresponding to the point in the TextLeafRange
    315   * containing the given screen point. The function returns a TextLeafPoint
    316   * constructed from mStart if it does not find a containing character.
    317   */
    318  TextLeafPoint TextLeafPointAtScreenPoint(int32_t aX, int32_t aY) const;
    319 
    320  /**
    321   * Get the ranges of text that are selected within this Accessible. The caret
    322   * is not included as a collapsed range.
    323   */
    324  static void GetSelection(Accessible* aAcc, nsTArray<TextLeafRange>& aRanges);
    325 
    326  static const int32_t kCreateNewSelectionRange = -1;
    327  static const int32_t kRemoveAllExistingSelectedRanges = -2;
    328 
    329  /**
    330   * Set range as DOM selection.
    331   * aSelectionNum is the selection index to use. If aSelectionNum is
    332   * out of bounds for current selection ranges, or is kCreateNewSelectionRange,
    333   * a new selection range is created. If aSelectionNum is
    334   * kRemoveAllExistingSelectedRanges, this will be set as the only range in the
    335   * selection; i.e. all existing ranges (if any) will be removed from the
    336   * selection first.
    337   * If aSetFocus is true, the element containing the start point will be
    338   * focused if appropriate. If aSetFocus is false, the focused element will
    339   * be left as is.
    340   */
    341  MOZ_CAN_RUN_SCRIPT bool SetSelection(int32_t aSelectionNum,
    342                                       bool aSetFocus = true) const;
    343 
    344  MOZ_CAN_RUN_SCRIPT void ScrollIntoView(uint32_t aScrollType) const;
    345 
    346  /**
    347   * Returns sub-ranges for all the lines in this range visible within the given
    348   * container Accessible.
    349   */
    350  nsTArray<TextLeafRange> VisibleLines(Accessible* aContainer) const;
    351 
    352 private:
    353  TextLeafPoint mStart;
    354  TextLeafPoint mEnd;
    355 
    356  /**
    357   * Walk all of the lines within the TextLeafRange. This function invokes the
    358   * given callback with the sub-range for each line and the line's bounding
    359   * rectangle. The bounds are inclusive of all characters in each line, except
    360   * that the first and last lines might be partial if the range begins or ends
    361   * in the middle of a line. They are exclusive of mEnd, since range ends are
    362   * always exclusive, so including mEnd would include the bounds for 1
    363   * character past the end of the range. Each rectangle is screen-relative. If
    364   * this range is collapsed, the callback is called with the insertion point
    365   * bounds. The function returns true if it walks any lines, and false if it
    366   * could not walk any lines, which could happen if the start and end points
    367   * are improperly positioned.
    368   */
    369  using LineRectCallback =
    370      FunctionRef<void(TextLeafRange, LayoutDeviceIntRect)>;
    371  bool WalkLineRects(LineRectCallback aCallback) const;
    372 
    373 public:
    374  /**
    375   * A TextLeafRange iterator will iterate through single leaf segments of the
    376   * given range.
    377   */
    378 
    379  class Iterator {
    380   public:
    381    Iterator(Iterator&& aOther)
    382        : mRange(aOther.mRange),
    383          mSegmentStart(aOther.mSegmentStart),
    384          mSegmentEnd(aOther.mSegmentEnd) {}
    385 
    386    static Iterator BeginIterator(const TextLeafRange& aRange);
    387 
    388    static Iterator EndIterator(const TextLeafRange& aRange);
    389 
    390    Iterator& operator++();
    391 
    392    bool operator!=(const Iterator& aOther) const {
    393      return mRange != aOther.mRange || mSegmentStart != aOther.mSegmentStart ||
    394             mSegmentEnd != aOther.mSegmentEnd;
    395    }
    396 
    397    TextLeafRange operator*() {
    398      return TextLeafRange(mSegmentStart, mSegmentEnd);
    399    }
    400 
    401   private:
    402    explicit Iterator(const TextLeafRange& aRange) : mRange(aRange) {}
    403 
    404    Iterator() = delete;
    405    Iterator(const Iterator&) = delete;
    406    Iterator& operator=(const Iterator&) = delete;
    407    Iterator& operator=(const Iterator&&) = delete;
    408 
    409    const TextLeafRange& mRange;
    410    TextLeafPoint mSegmentStart;
    411    TextLeafPoint mSegmentEnd;
    412  };
    413 
    414  Iterator begin() const { return Iterator::BeginIterator(*this); }
    415  Iterator end() const { return Iterator::EndIterator(*this); }
    416 };
    417 
    418 }  // namespace a11y
    419 }  // namespace mozilla
    420 
    421 #endif