Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/generic/HyperTextAccessible.h
Line
Count
Source (jump to first uncovered line)
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
class TextEditor;
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_INLINE_DECL_REFCOUNTING_INHERITED(HyperTextAccessible, AccessibleWrap)
55
56
  // Accessible
57
  virtual nsAtom* LandmarkRole() const override;
58
  virtual int32_t GetLevelInternal() override;
59
  virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
60
  virtual mozilla::a11y::role NativeRole() const override;
61
  virtual uint64_t NativeState() const 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) const 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
  uint32_t LinkCount()
85
0
    { return EmbeddedChildCount(); }
86
87
  /**
88
   * Return link accessible at the given index.
89
   */
90
  Accessible* LinkAt(uint32_t aIndex)
91
0
  {
92
0
    return GetEmbeddedChildAt(aIndex);
93
0
  }
94
95
  /**
96
   * Return index for the given link accessible.
97
   */
98
  int32_t LinkIndexOf(Accessible* aLink)
99
0
  {
100
0
    return GetIndexOfEmbeddedChild(aLink);
101
0
  }
102
103
  /**
104
   * Return link accessible at the given text offset.
105
   */
106
  int32_t LinkIndexAtOffset(uint32_t aOffset)
107
0
  {
108
0
    Accessible* child = GetChildAtOffset(aOffset);
109
0
    return child ? LinkIndexOf(child) : -1;
110
0
  }
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
  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
  bool CharAt(int32_t aOffset, nsAString& aChar,
185
              int32_t* aStartOffset = nullptr, int32_t* aEndOffset = nullptr)
186
0
  {
187
0
    NS_ASSERTION(!aStartOffset == !aEndOffset,
188
0
                 "Offsets should be both defined or both undefined!");
189
0
190
0
    int32_t childIdx = GetChildIndexAtOffset(aOffset);
191
0
    if (childIdx == -1)
192
0
      return false;
193
0
194
0
    Accessible* child = GetChildAt(childIdx);
195
0
    child->AppendTextTo(aChar, aOffset - GetChildOffset(childIdx), 1);
196
0
197
0
    if (aStartOffset && aEndOffset) {
198
0
      *aStartOffset = aOffset;
199
0
      *aEndOffset = aOffset + aChar.Length();
200
0
    }
201
0
    return true;
202
0
  }
203
204
  char16_t CharAt(int32_t aOffset)
205
0
  {
206
0
    nsAutoString charAtOffset;
207
0
    CharAt(aOffset, charAtOffset);
208
0
    return charAtOffset.CharAt(0);
209
0
  }
210
211
  /**
212
   * Return true if char at the given offset equals to given char.
213
   */
214
  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
  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
  int32_t GetChildOffset(const Accessible* aChild,
263
                         bool aInvalidateAfter = false) const
264
0
  {
265
0
    int32_t index = GetIndexOf(aChild);
266
0
    return index == -1 ? -1 : GetChildOffset(index, aInvalidateAfter);
267
0
  }
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
  Accessible* GetChildAtOffset(uint32_t aOffset) const
288
0
  {
289
0
    return GetChildAt(GetChildIndexAtOffset(aOffset));
290
0
  }
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
  nsIntRect CharBounds(int32_t aOffset, uint32_t aCoordType)
314
0
  {
315
0
    int32_t endOffset = aOffset == static_cast<int32_t>(CharacterCount()) ?
316
0
      aOffset : aOffset + 1;
317
0
    return TextBounds(aOffset, endOffset, aCoordType);
318
0
  }
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
   * The result may be either TextEditor or HTMLEditor.
426
   */
427
  virtual already_AddRefed<TextEditor> GetEditor() const;
428
429
  /**
430
   * Return DOM selection object for the accessible.
431
   */
432
  dom::Selection* DOMSelection() const;
433
434
protected:
435
0
  virtual ~HyperTextAccessible() { }
436
437
  // Accessible
438
  virtual ENameValueFlag NativeName(nsString& aName) const override;
439
440
  // HyperTextAccessible
441
442
  /**
443
   * Transform magic offset into text offset.
444
   */
445
  index_t ConvertMagicOffset(int32_t aOffset) const;
446
447
  /**
448
   * Adjust an offset the caret stays at to get a text by line boundary.
449
   */
450
  uint32_t AdjustCaretOffset(uint32_t aOffset) const;
451
452
  /**
453
   * Return true if caret is at end of line.
454
   */
455
  bool IsCaretAtEndOfLine() const;
456
457
  /**
458
   * Return true if the given offset points to terminal empty line if any.
459
   */
460
  bool IsEmptyLastLineOffset(int32_t aOffset)
461
0
  {
462
0
    return aOffset == static_cast<int32_t>(CharacterCount()) &&
463
0
      IsLineEndCharAt(aOffset - 1);
464
0
  }
465
466
  /**
467
   * Return an offset of the found word boundary.
468
   */
469
  uint32_t FindWordBoundary(uint32_t aOffset, nsDirection aDirection,
470
                           EWordMovementType aWordMovementType)
471
0
  {
472
0
    return FindOffset(aOffset, aDirection, eSelectWord, aWordMovementType);
473
0
  }
474
475
  /**
476
   * Used to get begin/end of previous/this/next line. Note: end of line
477
   * is an offset right before '\n' character if any, the offset is right after
478
   * '\n' character is begin of line. In case of wrap word breaks these offsets
479
   * are equal.
480
   */
481
  enum EWhichLineBoundary {
482
    ePrevLineBegin,
483
    ePrevLineEnd,
484
    eThisLineBegin,
485
    eThisLineEnd,
486
    eNextLineBegin,
487
    eNextLineEnd
488
  };
489
490
  /**
491
   * Return an offset for requested line boundary. See constants above.
492
   */
493
  uint32_t FindLineBoundary(uint32_t aOffset,
494
                            EWhichLineBoundary aWhichLineBoundary);
495
496
  /**
497
   * Return an offset corresponding to the given direction and selection amount
498
   * relative the given offset. A helper used to find word or line boundaries.
499
   */
500
  uint32_t FindOffset(uint32_t aOffset, nsDirection aDirection,
501
                      nsSelectionAmount aAmount,
502
                      EWordMovementType aWordMovementType = eDefaultBehavior);
503
504
  /**
505
   * Return the boundaries of the substring in case of textual frame or
506
   * frame boundaries in case of non textual frame, offsets are ignored.
507
   */
508
  nsIntRect GetBoundsInFrame(nsIFrame* aFrame,
509
                             uint32_t aStartRenderedOffset,
510
                             uint32_t aEndRenderedOffset);
511
512
  // Selection helpers
513
514
  /**
515
   * Return frame selection object for the accessible.
516
   */
517
  already_AddRefed<nsFrameSelection> FrameSelection() const;
518
519
  /**
520
   * Return selection ranges within the accessible subtree.
521
   */
522
  void GetSelectionDOMRanges(SelectionType aSelectionType,
523
                             nsTArray<nsRange*>* aRanges);
524
525
  nsresult SetSelectionRange(int32_t aStartPos, int32_t aEndPos);
526
527
  /**
528
   * Convert the given DOM point to a DOM point in non-generated contents.
529
   *
530
   * If aDOMPoint is in ::before, the result is immediately after it.
531
   * If aDOMPoint is in ::after, the result is immediately before it.
532
   *
533
   * @param aDOMPoint       [in] A DOM node and an index of its child. This may
534
   *                             be in a generated content such as ::before or
535
   *                             ::after.
536
   * @param aElementContent [in] An nsIContent representing an element of
537
   *                             aDOMPoint.node.
538
   * @return                An DOM point which must not be in generated
539
   *                        contents.
540
   */
541
  DOMPoint ClosestNotGeneratedDOMPoint(const DOMPoint& aDOMPoint,
542
                                       nsIContent* aElementContent);
543
544
  // Helpers
545
  nsresult GetDOMPointByFrameOffset(nsIFrame* aFrame, int32_t aOffset,
546
                                    Accessible* aAccessible,
547
                                    mozilla::a11y::DOMPoint* aPoint);
548
549
  /**
550
   * Set 'misspelled' text attribute and return range offsets where the
551
   * attibute is stretched. If the text is not misspelled at the given offset
552
   * then we expose only range offsets where text is not misspelled. The method
553
   * is used by TextAttributes() method.
554
   *
555
   * @param aIncludeDefAttrs  [in] points whether text attributes having default
556
   *                          values of attributes should be included
557
   * @param aSourceNode       [in] the node we start to traverse from
558
   * @param aStartOffset      [in, out] the start offset
559
   * @param aEndOffset        [in, out] the end offset
560
   * @param aAttributes       [out, optional] result attributes
561
   */
562
  void GetSpellTextAttr(nsINode* aNode, int32_t aNodeOffset,
563
                        uint32_t* aStartOffset, uint32_t* aEndOffset,
564
                        nsIPersistentProperties* aAttributes);
565
566
  /**
567
   * Set xml-roles attributes for MathML elements.
568
   * @param aAttributes
569
   */
570
  void SetMathMLXMLRoles(nsIPersistentProperties* aAttributes);
571
572
private:
573
  /**
574
   * End text offsets array.
575
   */
576
  mutable nsTArray<uint32_t> mOffsets;
577
};
578
579
580
////////////////////////////////////////////////////////////////////////////////
581
// Accessible downcasting method
582
583
inline HyperTextAccessible*
584
Accessible::AsHyperText()
585
0
{
586
0
  return IsHyperText() ? static_cast<HyperTextAccessible*>(this) : nullptr;
587
0
}
588
589
} // namespace a11y
590
} // namespace mozilla
591
592
#endif
593