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