HyperTextAccessible.h 19 KB

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