|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifndef mozilla_a11y_HyperTextAccessible_h__ |
|
7 #define mozilla_a11y_HyperTextAccessible_h__ |
|
8 |
|
9 #include "AccessibleWrap.h" |
|
10 #include "nsIAccessibleTypes.h" |
|
11 #include "xpcAccessibleHyperText.h" |
|
12 |
|
13 #include "nsFrameSelection.h" |
|
14 #include "nsISelectionController.h" |
|
15 |
|
16 namespace mozilla { |
|
17 namespace a11y { |
|
18 |
|
19 class TextRange; |
|
20 |
|
21 struct DOMPoint { |
|
22 DOMPoint() : node(nullptr), idx(0) { } |
|
23 DOMPoint(nsINode* aNode, int32_t aIdx) : node(aNode), idx(aIdx) { } |
|
24 |
|
25 nsINode* node; |
|
26 int32_t idx; |
|
27 }; |
|
28 |
|
29 // This character marks where in the text returned via nsIAccessibleText(), |
|
30 // that embedded object characters exist |
|
31 const char16_t kEmbeddedObjectChar = 0xfffc; |
|
32 const char16_t kImaginaryEmbeddedObjectChar = ' '; |
|
33 const char16_t kForcedNewLineChar = '\n'; |
|
34 |
|
35 /** |
|
36 * Special Accessible that knows how contain both text and embedded objects |
|
37 */ |
|
38 class HyperTextAccessible : public AccessibleWrap, |
|
39 public xpcAccessibleHyperText |
|
40 { |
|
41 public: |
|
42 HyperTextAccessible(nsIContent* aContent, DocAccessible* aDoc); |
|
43 virtual ~HyperTextAccessible() { } |
|
44 |
|
45 NS_DECL_ISUPPORTS_INHERITED |
|
46 |
|
47 // Accessible |
|
48 virtual int32_t GetLevelInternal(); |
|
49 virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() MOZ_OVERRIDE; |
|
50 virtual mozilla::a11y::role NativeRole(); |
|
51 virtual uint64_t NativeState(); |
|
52 |
|
53 virtual void InvalidateChildren(); |
|
54 virtual bool RemoveChild(Accessible* aAccessible); |
|
55 |
|
56 // HyperTextAccessible (static helper method) |
|
57 |
|
58 // Convert content offset to rendered text offset |
|
59 nsresult ContentToRenderedOffset(nsIFrame *aFrame, int32_t aContentOffset, |
|
60 uint32_t *aRenderedOffset) const; |
|
61 |
|
62 // Convert rendered text offset to content offset |
|
63 nsresult RenderedToContentOffset(nsIFrame *aFrame, uint32_t aRenderedOffset, |
|
64 int32_t *aContentOffset) const; |
|
65 |
|
66 ////////////////////////////////////////////////////////////////////////////// |
|
67 // HyperLinkAccessible |
|
68 |
|
69 /** |
|
70 * Return link count within this hypertext accessible. |
|
71 */ |
|
72 uint32_t LinkCount() |
|
73 { return EmbeddedChildCount(); } |
|
74 |
|
75 /** |
|
76 * Return link accessible at the given index. |
|
77 */ |
|
78 Accessible* LinkAt(uint32_t aIndex) |
|
79 { |
|
80 return GetEmbeddedChildAt(aIndex); |
|
81 } |
|
82 |
|
83 /** |
|
84 * Return index for the given link accessible. |
|
85 */ |
|
86 int32_t LinkIndexOf(Accessible* aLink) |
|
87 { |
|
88 return GetIndexOfEmbeddedChild(aLink); |
|
89 } |
|
90 |
|
91 /** |
|
92 * Return link accessible at the given text offset. |
|
93 */ |
|
94 int32_t LinkIndexAtOffset(uint32_t aOffset) |
|
95 { |
|
96 Accessible* child = GetChildAtOffset(aOffset); |
|
97 return child ? LinkIndexOf(child) : -1; |
|
98 } |
|
99 |
|
100 ////////////////////////////////////////////////////////////////////////////// |
|
101 // HyperTextAccessible: DOM point to text offset conversions. |
|
102 |
|
103 /** |
|
104 * Turn a DOM point (node and offset) into a character offset of this |
|
105 * hypertext. Will look for closest match when the DOM node does not have |
|
106 * an accessible object associated with it. Will return an offset for the end |
|
107 * of the string if the node is not found. |
|
108 * |
|
109 * @param aNode [in] the node to look for |
|
110 * @param aNodeOffset [in] the offset to look for |
|
111 * if -1 just look directly for the node |
|
112 * if >=0 and aNode is text, this represents a char offset |
|
113 * if >=0 and aNode is not text, this represents a child node offset |
|
114 * @param aIsEndOffset [in] if true, then then this offset is not inclusive. The character |
|
115 * indicated by the offset returned is at [offset - 1]. This means |
|
116 * if the passed-in offset is really in a descendant, then the offset returned |
|
117 * will come just after the relevant embedded object characer. |
|
118 * If false, then the offset is inclusive. The character indicated |
|
119 * by the offset returned is at [offset]. If the passed-in offset in inside a |
|
120 * descendant, then the returned offset will be on the relevant embedded object char. |
|
121 */ |
|
122 int32_t DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset, |
|
123 bool aIsEndOffset = false) const; |
|
124 |
|
125 /** |
|
126 * Transform the given a11y point into the offset relative this hypertext. |
|
127 */ |
|
128 int32_t TransformOffset(Accessible* aDescendant, int32_t aOffset, |
|
129 bool aIsEndOffset) const; |
|
130 |
|
131 /** |
|
132 * Convert start and end hypertext offsets into DOM range. |
|
133 * |
|
134 * @param aStartOffset [in] the given start hypertext offset |
|
135 * @param aEndOffset [in] the given end hypertext offset |
|
136 * @param aRange [in, out] the range whose bounds to set |
|
137 * @return true if conversion was successful |
|
138 */ |
|
139 bool OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset, |
|
140 nsRange* aRange); |
|
141 |
|
142 /** |
|
143 * Convert the given offset into DOM point. |
|
144 * |
|
145 * If offset is at text leaf then DOM point is (text node, offsetInTextNode), |
|
146 * if before embedded object then (parent node, indexInParent), if after then |
|
147 * (parent node, indexInParent + 1). |
|
148 */ |
|
149 DOMPoint OffsetToDOMPoint(int32_t aOffset); |
|
150 |
|
151 /** |
|
152 * Return true if the used ARIA role (if any) allows the hypertext accessible |
|
153 * to expose text interfaces. |
|
154 */ |
|
155 bool IsTextRole(); |
|
156 |
|
157 ////////////////////////////////////////////////////////////////////////////// |
|
158 // TextAccessible |
|
159 |
|
160 /** |
|
161 * Return character count within the hypertext accessible. |
|
162 */ |
|
163 uint32_t CharacterCount() const |
|
164 { return GetChildOffset(ChildCount()); } |
|
165 |
|
166 /** |
|
167 * Get a character at the given offset (don't support magic offsets). |
|
168 */ |
|
169 bool CharAt(int32_t aOffset, nsAString& aChar, |
|
170 int32_t* aStartOffset = nullptr, int32_t* aEndOffset = nullptr) |
|
171 { |
|
172 NS_ASSERTION(!aStartOffset == !aEndOffset, |
|
173 "Offsets should be both defined or both undefined!"); |
|
174 |
|
175 int32_t childIdx = GetChildIndexAtOffset(aOffset); |
|
176 if (childIdx == -1) |
|
177 return false; |
|
178 |
|
179 Accessible* child = GetChildAt(childIdx); |
|
180 child->AppendTextTo(aChar, aOffset - GetChildOffset(childIdx), 1); |
|
181 |
|
182 if (aStartOffset && aEndOffset) { |
|
183 *aStartOffset = aOffset; |
|
184 *aEndOffset = aOffset + aChar.Length(); |
|
185 } |
|
186 return true; |
|
187 } |
|
188 |
|
189 char16_t CharAt(int32_t aOffset) |
|
190 { |
|
191 nsAutoString charAtOffset; |
|
192 CharAt(aOffset, charAtOffset); |
|
193 return charAtOffset.CharAt(0); |
|
194 } |
|
195 |
|
196 /** |
|
197 * Return true if char at the given offset equals to given char. |
|
198 */ |
|
199 bool IsCharAt(int32_t aOffset, char16_t aChar) |
|
200 { return CharAt(aOffset) == aChar; } |
|
201 |
|
202 /** |
|
203 * Return true if terminal char is at the given offset. |
|
204 */ |
|
205 bool IsLineEndCharAt(int32_t aOffset) |
|
206 { return IsCharAt(aOffset, '\n'); } |
|
207 |
|
208 /** |
|
209 * Return text between given offsets. |
|
210 */ |
|
211 void TextSubstring(int32_t aStartOffset, int32_t aEndOffset, nsAString& aText); |
|
212 |
|
213 /** |
|
214 * Return text before/at/after the given offset corresponding to |
|
215 * the boundary type. |
|
216 */ |
|
217 void TextBeforeOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType, |
|
218 int32_t* aStartOffset, int32_t* aEndOffset, |
|
219 nsAString& aText); |
|
220 void TextAtOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType, |
|
221 int32_t* aStartOffset, int32_t* aEndOffset, |
|
222 nsAString& aText); |
|
223 void TextAfterOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType, |
|
224 int32_t* aStartOffset, int32_t* aEndOffset, |
|
225 nsAString& aText); |
|
226 |
|
227 /** |
|
228 * Return text attributes for the given text range. |
|
229 */ |
|
230 already_AddRefed<nsIPersistentProperties> |
|
231 TextAttributes(bool aIncludeDefAttrs, int32_t aOffset, |
|
232 int32_t* aStartOffset, int32_t* aEndOffset); |
|
233 |
|
234 /** |
|
235 * Return text attributes applied to the accessible. |
|
236 */ |
|
237 already_AddRefed<nsIPersistentProperties> DefaultTextAttributes(); |
|
238 |
|
239 /** |
|
240 * Return text offset of the given child accessible within hypertext |
|
241 * accessible. |
|
242 * |
|
243 * @param aChild [in] accessible child to get text offset for |
|
244 * @param aInvalidateAfter [in, optional] indicates whether invalidate |
|
245 * cached offsets for next siblings of the child |
|
246 */ |
|
247 int32_t GetChildOffset(Accessible* aChild, |
|
248 bool aInvalidateAfter = false) const |
|
249 { |
|
250 int32_t index = GetIndexOf(aChild); |
|
251 return index == -1 ? -1 : GetChildOffset(index, aInvalidateAfter); |
|
252 } |
|
253 |
|
254 /** |
|
255 * Return text offset for the child accessible index. |
|
256 */ |
|
257 int32_t GetChildOffset(uint32_t aChildIndex, |
|
258 bool aInvalidateAfter = false) const; |
|
259 |
|
260 /** |
|
261 * Return child accessible at the given text offset. |
|
262 * |
|
263 * @param aOffset [in] the given text offset |
|
264 */ |
|
265 int32_t GetChildIndexAtOffset(uint32_t aOffset) const; |
|
266 |
|
267 /** |
|
268 * Return child accessible at the given text offset. |
|
269 * |
|
270 * @param aOffset [in] the given text offset |
|
271 */ |
|
272 Accessible* GetChildAtOffset(uint32_t aOffset) const |
|
273 { |
|
274 return GetChildAt(GetChildIndexAtOffset(aOffset)); |
|
275 } |
|
276 |
|
277 /** |
|
278 * Return true if the given offset/range is valid. |
|
279 */ |
|
280 bool IsValidOffset(int32_t aOffset); |
|
281 bool IsValidRange(int32_t aStartOffset, int32_t aEndOffset); |
|
282 |
|
283 /** |
|
284 * Return an offset at the given point. |
|
285 */ |
|
286 int32_t OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType); |
|
287 |
|
288 /** |
|
289 * Return a rect of the given text range relative given coordinate system. |
|
290 */ |
|
291 nsIntRect TextBounds(int32_t aStartOffset, int32_t aEndOffset, |
|
292 uint32_t aCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE); |
|
293 |
|
294 /** |
|
295 * Return a rect for character at given offset relative given coordinate |
|
296 * system. |
|
297 */ |
|
298 nsIntRect CharBounds(int32_t aOffset, uint32_t aCoordType) |
|
299 { return TextBounds(aOffset, aOffset + 1, aCoordType); } |
|
300 |
|
301 /** |
|
302 * Get/set caret offset, if no caret then -1. |
|
303 */ |
|
304 int32_t CaretOffset() const; |
|
305 void SetCaretOffset(int32_t aOffset) { SetSelectionRange(aOffset, aOffset); } |
|
306 |
|
307 /** |
|
308 * Provide the line number for the caret. |
|
309 * @return 1-based index for the line number with the caret |
|
310 */ |
|
311 int32_t CaretLineNumber(); |
|
312 |
|
313 /** |
|
314 * Return the caret rect and the widget containing the caret within this |
|
315 * text accessible. |
|
316 * |
|
317 * @param [out] the widget containing the caret |
|
318 * @return the caret rect |
|
319 */ |
|
320 nsIntRect GetCaretRect(nsIWidget** aWidget); |
|
321 |
|
322 /** |
|
323 * Return selected regions count within the accessible. |
|
324 */ |
|
325 int32_t SelectionCount(); |
|
326 |
|
327 /** |
|
328 * Return the start and end offset of the specified selection. |
|
329 */ |
|
330 bool SelectionBoundsAt(int32_t aSelectionNum, |
|
331 int32_t* aStartOffset, int32_t* aEndOffset); |
|
332 |
|
333 /* |
|
334 * Changes the start and end offset of the specified selection. |
|
335 * @return true if succeeded |
|
336 */ |
|
337 bool SetSelectionBoundsAt(int32_t aSelectionNum, |
|
338 int32_t aStartOffset, int32_t aEndOffset); |
|
339 |
|
340 /** |
|
341 * Adds a selection bounded by the specified offsets. |
|
342 * @return true if succeeded |
|
343 */ |
|
344 bool AddToSelection(int32_t aStartOffset, int32_t aEndOffset); |
|
345 |
|
346 /* |
|
347 * Removes the specified selection. |
|
348 * @return true if succeeded |
|
349 */ |
|
350 bool RemoveFromSelection(int32_t aSelectionNum); |
|
351 |
|
352 /** |
|
353 * Scroll the given text range into view. |
|
354 */ |
|
355 void ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset, |
|
356 uint32_t aScrollType); |
|
357 |
|
358 /** |
|
359 * Scroll the given text range to the given point. |
|
360 */ |
|
361 void ScrollSubstringToPoint(int32_t aStartOffset, |
|
362 int32_t aEndOffset, |
|
363 uint32_t aCoordinateType, |
|
364 int32_t aX, int32_t aY); |
|
365 |
|
366 /** |
|
367 * Return a range that encloses the text control or the document this |
|
368 * accessible belongs to. |
|
369 */ |
|
370 void EnclosingRange(TextRange& aRange) const; |
|
371 |
|
372 /** |
|
373 * Return an array of disjoint ranges for selected text within the text control |
|
374 * or the document this accessible belongs to. |
|
375 */ |
|
376 void SelectionRanges(nsTArray<TextRange>* aRanges) const; |
|
377 |
|
378 /** |
|
379 * Return an array of disjoint ranges of visible text within the text control |
|
380 * or the document this accessible belongs to. |
|
381 */ |
|
382 void VisibleRanges(nsTArray<TextRange>* aRanges) const; |
|
383 |
|
384 /** |
|
385 * Return a range containing the given accessible. |
|
386 */ |
|
387 void RangeByChild(Accessible* aChild, TextRange& aRange) const; |
|
388 |
|
389 /** |
|
390 * Return a range containing an accessible at the given point. |
|
391 */ |
|
392 void RangeAtPoint(int32_t aX, int32_t aY, TextRange& aRange) const; |
|
393 |
|
394 ////////////////////////////////////////////////////////////////////////////// |
|
395 // EditableTextAccessible |
|
396 |
|
397 void ReplaceText(const nsAString& aText); |
|
398 void InsertText(const nsAString& aText, int32_t aPosition); |
|
399 void CopyText(int32_t aStartPos, int32_t aEndPos); |
|
400 void CutText(int32_t aStartPos, int32_t aEndPos); |
|
401 void DeleteText(int32_t aStartPos, int32_t aEndPos); |
|
402 void PasteText(int32_t aPosition); |
|
403 |
|
404 /** |
|
405 * Return the editor associated with the accessible. |
|
406 */ |
|
407 virtual already_AddRefed<nsIEditor> GetEditor() const; |
|
408 |
|
409 protected: |
|
410 // Accessible |
|
411 virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE; |
|
412 virtual void CacheChildren() MOZ_OVERRIDE; |
|
413 |
|
414 // HyperTextAccessible |
|
415 |
|
416 /** |
|
417 * Transform magic offset into text offset. |
|
418 */ |
|
419 int32_t ConvertMagicOffset(int32_t aOffset); |
|
420 |
|
421 /** |
|
422 * Adjust an offset the caret stays at to get a text by line boundary. |
|
423 */ |
|
424 int32_t AdjustCaretOffset(int32_t aOffset) const; |
|
425 |
|
426 /** |
|
427 * Return true if caret is at end of line. |
|
428 */ |
|
429 bool IsCaretAtEndOfLine() const; |
|
430 |
|
431 /** |
|
432 * Return true if the given offset points to terminal empty line if any. |
|
433 */ |
|
434 bool IsEmptyLastLineOffset(int32_t aOffset) |
|
435 { |
|
436 return aOffset == static_cast<int32_t>(CharacterCount()) && |
|
437 IsLineEndCharAt(aOffset - 1); |
|
438 } |
|
439 |
|
440 /** |
|
441 * Return an offset of the found word boundary. |
|
442 */ |
|
443 int32_t FindWordBoundary(int32_t aOffset, nsDirection aDirection, |
|
444 EWordMovementType aWordMovementType) |
|
445 { |
|
446 return FindOffset(aOffset, aDirection, eSelectWord, aWordMovementType); |
|
447 } |
|
448 |
|
449 /** |
|
450 * Used to get begin/end of previous/this/next line. Note: end of line |
|
451 * is an offset right before '\n' character if any, the offset is right after |
|
452 * '\n' character is begin of line. In case of wrap word breaks these offsets |
|
453 * are equal. |
|
454 */ |
|
455 enum EWhichLineBoundary { |
|
456 ePrevLineBegin, |
|
457 ePrevLineEnd, |
|
458 eThisLineBegin, |
|
459 eThisLineEnd, |
|
460 eNextLineBegin, |
|
461 eNextLineEnd |
|
462 }; |
|
463 |
|
464 /** |
|
465 * Return an offset for requested line boundary. See constants above. |
|
466 */ |
|
467 int32_t FindLineBoundary(int32_t aOffset, |
|
468 EWhichLineBoundary aWhichLineBoundary); |
|
469 |
|
470 /** |
|
471 * Return an offset corresponding to the given direction and selection amount |
|
472 * relative the given offset. A helper used to find word or line boundaries. |
|
473 */ |
|
474 int32_t FindOffset(int32_t aOffset, nsDirection aDirection, |
|
475 nsSelectionAmount aAmount, |
|
476 EWordMovementType aWordMovementType = eDefaultBehavior); |
|
477 |
|
478 /** |
|
479 * Return the boundaries of the substring in case of textual frame or |
|
480 * frame boundaries in case of non textual frame, offsets are ignored. |
|
481 */ |
|
482 nsIntRect GetBoundsInFrame(nsIFrame* aFrame, |
|
483 uint32_t aStartRenderedOffset, |
|
484 uint32_t aEndRenderedOffset); |
|
485 |
|
486 // Selection helpers |
|
487 |
|
488 /** |
|
489 * Return frame/DOM selection object for the accessible. |
|
490 */ |
|
491 already_AddRefed<nsFrameSelection> FrameSelection() const; |
|
492 dom::Selection* DOMSelection() const; |
|
493 |
|
494 /** |
|
495 * Return selection ranges within the accessible subtree. |
|
496 */ |
|
497 void GetSelectionDOMRanges(int16_t aType, nsTArray<nsRange*>* aRanges); |
|
498 |
|
499 nsresult SetSelectionRange(int32_t aStartPos, int32_t aEndPos); |
|
500 |
|
501 // Helpers |
|
502 nsresult GetDOMPointByFrameOffset(nsIFrame* aFrame, int32_t aOffset, |
|
503 Accessible* aAccessible, |
|
504 mozilla::a11y::DOMPoint* aPoint); |
|
505 |
|
506 /** |
|
507 * Set 'misspelled' text attribute and return range offsets where the |
|
508 * attibute is stretched. If the text is not misspelled at the given offset |
|
509 * then we expose only range offsets where text is not misspelled. The method |
|
510 * is used by TextAttributes() method. |
|
511 * |
|
512 * @param aIncludeDefAttrs [in] points whether text attributes having default |
|
513 * values of attributes should be included |
|
514 * @param aSourceNode [in] the node we start to traverse from |
|
515 * @param aStartOffset [in, out] the start offset |
|
516 * @param aEndOffset [in, out] the end offset |
|
517 * @param aAttributes [out, optional] result attributes |
|
518 */ |
|
519 nsresult GetSpellTextAttribute(nsINode* aNode, int32_t aNodeOffset, |
|
520 int32_t *aStartOffset, |
|
521 int32_t *aEndOffset, |
|
522 nsIPersistentProperties *aAttributes); |
|
523 |
|
524 private: |
|
525 /** |
|
526 * End text offsets array. |
|
527 */ |
|
528 mutable nsTArray<uint32_t> mOffsets; |
|
529 }; |
|
530 |
|
531 |
|
532 //////////////////////////////////////////////////////////////////////////////// |
|
533 // Accessible downcasting method |
|
534 |
|
535 inline HyperTextAccessible* |
|
536 Accessible::AsHyperText() |
|
537 { |
|
538 return IsHyperText() ? static_cast<HyperTextAccessible*>(this) : nullptr; |
|
539 } |
|
540 |
|
541 } // namespace a11y |
|
542 } // namespace mozilla |
|
543 |
|
544 #endif |
|
545 |