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_TextRange_h__
8 #define mozilla_a11y_TextRange_h__
9 
10 #include <utility>
11 
12 #include "nsCaseTreatment.h"
13 #include "nsRect.h"
14 #include "nsTArray.h"
15 
16 class nsIVariant;
17 class nsRange;
18 
19 namespace mozilla {
20 namespace dom {
21 class Selection;
22 }  // namespace dom
23 namespace a11y {
24 
25 class LocalAccessible;
26 class HyperTextAccessible;
27 
28 /**
29  * A text point (hyper text + offset), represents a boundary of text range.
30  */
31 struct TextPoint final {
TextPointfinal32   TextPoint(HyperTextAccessible* aContainer, int32_t aOffset)
33       : mContainer(aContainer), mOffset(aOffset) {}
TextPointfinal34   TextPoint(const TextPoint& aPoint)
35       : mContainer(aPoint.mContainer), mOffset(aPoint.mOffset) {}
36 
37   HyperTextAccessible* mContainer;
38   int32_t mOffset;
39 
40   bool operator==(const TextPoint& aPoint) const {
41     return mContainer == aPoint.mContainer && mOffset == aPoint.mOffset;
42   }
43   bool operator<(const TextPoint& aPoint) const;
44 };
45 
46 /**
47  * Represents a text range within the text control or document.
48  */
49 class TextRange final {
50  public:
51   TextRange(HyperTextAccessible* aRoot, HyperTextAccessible* aStartContainer,
52             int32_t aStartOffset, HyperTextAccessible* aEndContainer,
53             int32_t aEndOffset);
TextRange()54   TextRange() : mStartOffset{0}, mEndOffset{0} {}
TextRange(TextRange && aRange)55   TextRange(TextRange&& aRange)
56       : mRoot(std::move(aRange.mRoot)),
57         mStartContainer(std::move(aRange.mStartContainer)),
58         mEndContainer(std::move(aRange.mEndContainer)),
59         mStartOffset(aRange.mStartOffset),
60         mEndOffset(aRange.mEndOffset) {}
61 
62   TextRange& operator=(TextRange&& aRange) {
63     mRoot = std::move(aRange.mRoot);
64     mStartContainer = std::move(aRange.mStartContainer);
65     mEndContainer = std::move(aRange.mEndContainer);
66     mStartOffset = aRange.mStartOffset;
67     mEndOffset = aRange.mEndOffset;
68     return *this;
69   }
70 
StartContainer()71   HyperTextAccessible* StartContainer() const { return mStartContainer; }
StartOffset()72   int32_t StartOffset() const { return mStartOffset; }
EndContainer()73   HyperTextAccessible* EndContainer() const { return mEndContainer; }
EndOffset()74   int32_t EndOffset() const { return mEndOffset; }
75 
76   bool operator==(const TextRange& aRange) const {
77     return mStartContainer == aRange.mStartContainer &&
78            mStartOffset == aRange.mStartOffset &&
79            mEndContainer == aRange.mEndContainer &&
80            mEndOffset == aRange.mEndOffset;
81   }
82 
StartPoint()83   TextPoint StartPoint() const {
84     return TextPoint(mStartContainer, mStartOffset);
85   }
EndPoint()86   TextPoint EndPoint() const { return TextPoint(mEndContainer, mEndOffset); }
87 
88   /**
89    * Return a container containing both start and end points.
90    */
91   LocalAccessible* Container() const;
92 
93   /**
94    * Return a list of embedded objects enclosed by the text range (includes
95    * partially overlapped objects).
96    */
97   void EmbeddedChildren(nsTArray<LocalAccessible*>* aChildren) const;
98 
99   /**
100    * Return text enclosed by the range.
101    */
102   void Text(nsAString& aText) const;
103 
104   /**
105    * Return list of bounding rects of the text range by lines.
106    */
107   void Bounds(nsTArray<nsIntRect> aRects) const;
108 
109   enum ETextUnit { eFormat, eWord, eLine, eParagraph, ePage, eDocument };
110 
111   /**
112    * Move the range or its points on specified amount of given units.
113    */
Move(ETextUnit aUnit,int32_t aCount)114   void Move(ETextUnit aUnit, int32_t aCount) {
115     MoveEnd(aUnit, aCount);
116     MoveStart(aUnit, aCount);
117   }
MoveStart(ETextUnit aUnit,int32_t aCount)118   void MoveStart(ETextUnit aUnit, int32_t aCount) {
119     MoveInternal(aUnit, aCount, *mStartContainer, mStartOffset, mEndContainer,
120                  mEndOffset);
121   }
MoveEnd(ETextUnit aUnit,int32_t aCount)122   void MoveEnd(ETextUnit aUnit, int32_t aCount) {
123     MoveInternal(aUnit, aCount, *mEndContainer, mEndOffset);
124   }
125 
126   /**
127    * Move the range points to the closest unit boundaries.
128    */
129   void Normalize(ETextUnit aUnit);
130 
131   /**
132    * Crops the range if it overlaps the given accessible element boundaries,
133    * returns true if the range was cropped successfully.
134    */
135   bool Crop(LocalAccessible* aContainer);
136 
137   enum EDirection { eBackward, eForward };
138 
139   /**
140    * Return range enclosing the found text.
141    */
142   void FindText(const nsAString& aText, EDirection aDirection,
143                 nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const;
144 
145   enum EAttr {
146     eAnimationStyleAttr,
147     eAnnotationObjectsAttr,
148     eAnnotationTypesAttr,
149     eBackgroundColorAttr,
150     eBulletStyleAttr,
151     eCapStyleAttr,
152     eCaretBidiModeAttr,
153     eCaretPositionAttr,
154     eCultureAttr,
155     eFontNameAttr,
156     eFontSizeAttr,
157     eFontWeightAttr,
158     eForegroundColorAttr,
159     eHorizontalTextAlignmentAttr,
160     eIndentationFirstLineAttr,
161     eIndentationLeadingAttr,
162     eIndentationTrailingAttr,
163     eIsActiveAttr,
164     eIsHiddenAttr,
165     eIsItalicAttr,
166     eIsReadOnlyAttr,
167     eIsSubscriptAttr,
168     eIsSuperscriptAttr,
169     eLinkAttr,
170     eMarginBottomAttr,
171     eMarginLeadingAttr,
172     eMarginTopAttr,
173     eMarginTrailingAttr,
174     eOutlineStylesAttr,
175     eOverlineColorAttr,
176     eOverlineStyleAttr,
177     eSelectionActiveEndAttr,
178     eStrikethroughColorAttr,
179     eStrikethroughStyleAttr,
180     eStyleIdAttr,
181     eStyleNameAttr,
182     eTabsAttr,
183     eTextFlowDirectionsAttr,
184     eUnderlineColorAttr,
185     eUnderlineStyleAttr
186   };
187 
188   /**
189    * Return range enclosing text having requested attribute.
190    */
191   void FindAttr(EAttr aAttr, nsIVariant* aValue, EDirection aDirection,
192                 TextRange* aFoundRange) const;
193 
194   /**
195    * Add/remove the text range from selection.
196    */
197   void AddToSelection() const;
198   void RemoveFromSelection() const;
199   MOZ_CAN_RUN_SCRIPT bool SetSelectionAt(int32_t aSelectionNum) const;
200 
201   /**
202    * Scroll the text range into view.
203    */
204   void ScrollIntoView(uint32_t aScrollType) const;
205 
206   /**
207    * Convert stored hypertext offsets into DOM offsets and assign it to DOM
208    * range.
209    *
210    * Note that if start and/or end accessible offsets are in generated content
211    * such as ::before or
212    * ::after, the result range excludes the generated content.  See also
213    * ClosestNotGeneratedDOMPoint() for more information.
214    *
215    * @param  aRange        [in, out] the range whose bounds to set
216    * @param  aReversed     [out] whether the start/end offsets were reversed.
217    * @return true   if conversion was successful
218    */
219   bool AssignDOMRange(nsRange* aRange, bool* aReversed = nullptr) const;
220 
221   /**
222    * Return true if this TextRange object represents an actual range of text.
223    */
IsValid()224   bool IsValid() const { return mRoot; }
225 
SetStartPoint(HyperTextAccessible * aContainer,int32_t aOffset)226   void SetStartPoint(HyperTextAccessible* aContainer, int32_t aOffset) {
227     mStartContainer = aContainer;
228     mStartOffset = aOffset;
229   }
SetEndPoint(HyperTextAccessible * aContainer,int32_t aOffset)230   void SetEndPoint(HyperTextAccessible* aContainer, int32_t aOffset) {
231     mStartContainer = aContainer;
232     mStartOffset = aOffset;
233   }
234 
235   static void TextRangesFromSelection(dom::Selection* aSelection,
236                                       nsTArray<TextRange>* aRanges);
237 
238  private:
239   TextRange(const TextRange& aRange) = delete;
240   TextRange& operator=(const TextRange& aRange) = delete;
241 
242   friend class HyperTextAccessible;
243   friend class xpcAccessibleTextRange;
244 
245   void Set(HyperTextAccessible* aRoot, HyperTextAccessible* aStartContainer,
246            int32_t aStartOffset, HyperTextAccessible* aEndContainer,
247            int32_t aEndOffset);
248 
249   /**
250    * Text() method helper.
251    * @param  aText            [in,out] calculated text
252    * @param  aCurrent         [in] currently traversed node
253    * @param  aStartIntlOffset [in] start offset if current node is a text node
254    * @return                   true if calculation is not finished yet
255    */
256   bool TextInternal(nsAString& aText, LocalAccessible* aCurrent,
257                     uint32_t aStartIntlOffset) const;
258 
259   void MoveInternal(ETextUnit aUnit, int32_t aCount,
260                     HyperTextAccessible& aContainer, int32_t aOffset,
261                     HyperTextAccessible* aStopContainer = nullptr,
262                     int32_t aStopOffset = 0);
263 
264   /**
265    * A helper method returning a common parent for two given accessible
266    * elements.
267    */
268   LocalAccessible* CommonParent(LocalAccessible* aAcc1, LocalAccessible* aAcc2,
269                                 nsTArray<LocalAccessible*>* aParents1,
270                                 uint32_t* aPos1,
271                                 nsTArray<LocalAccessible*>* aParents2,
272                                 uint32_t* aPos2) const;
273 
274   RefPtr<HyperTextAccessible> mRoot;
275   RefPtr<HyperTextAccessible> mStartContainer;
276   RefPtr<HyperTextAccessible> mEndContainer;
277   int32_t mStartOffset;
278   int32_t mEndOffset;
279 };
280 
281 }  // namespace a11y
282 }  // namespace mozilla
283 
284 #endif
285