Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=78: */
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 : #include "HyperTextAccessible-inl.h"
8 :
9 : #include "Accessible-inl.h"
10 : #include "nsAccessibilityService.h"
11 : #include "nsIAccessibleTypes.h"
12 : #include "DocAccessible.h"
13 : #include "HTMLListAccessible.h"
14 : #include "Role.h"
15 : #include "States.h"
16 : #include "TextAttrs.h"
17 : #include "TextRange.h"
18 : #include "TreeWalker.h"
19 :
20 : #include "nsCaret.h"
21 : #include "nsContentUtils.h"
22 : #include "nsFocusManager.h"
23 : #include "nsIDOMRange.h"
24 : #include "nsIEditingSession.h"
25 : #include "nsContainerFrame.h"
26 : #include "nsFrameSelection.h"
27 : #include "nsILineIterator.h"
28 : #include "nsIInterfaceRequestorUtils.h"
29 : #include "nsIPersistentProperties2.h"
30 : #include "nsIScrollableFrame.h"
31 : #include "nsIServiceManager.h"
32 : #include "nsITextControlElement.h"
33 : #include "nsIMathMLFrame.h"
34 : #include "nsTextFragment.h"
35 : #include "mozilla/BinarySearch.h"
36 : #include "mozilla/dom/Element.h"
37 : #include "mozilla/EventStates.h"
38 : #include "mozilla/dom/Selection.h"
39 : #include "mozilla/MathAlgorithms.h"
40 : #include "gfxSkipChars.h"
41 : #include <algorithm>
42 :
43 : using namespace mozilla;
44 : using namespace mozilla::a11y;
45 :
46 : ////////////////////////////////////////////////////////////////////////////////
47 : // HyperTextAccessible
48 : ////////////////////////////////////////////////////////////////////////////////
49 :
50 0 : HyperTextAccessible::
51 0 : HyperTextAccessible(nsIContent* aNode, DocAccessible* aDoc) :
52 0 : AccessibleWrap(aNode, aDoc)
53 : {
54 0 : mType = eHyperTextType;
55 0 : mGenericTypes |= eHyperText;
56 0 : }
57 :
58 0 : NS_IMPL_ISUPPORTS_INHERITED0(HyperTextAccessible, Accessible)
59 :
60 : role
61 0 : HyperTextAccessible::NativeRole()
62 : {
63 0 : a11y::role r = GetAccService()->MarkupRole(mContent);
64 0 : if (r != roles::NOTHING)
65 0 : return r;
66 :
67 0 : nsIFrame* frame = GetFrame();
68 0 : if (frame && frame->IsInlineFrame())
69 0 : return roles::TEXT;
70 :
71 0 : return roles::TEXT_CONTAINER;
72 : }
73 :
74 : uint64_t
75 0 : HyperTextAccessible::NativeState()
76 : {
77 0 : uint64_t states = AccessibleWrap::NativeState();
78 :
79 0 : if (mContent->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
80 0 : states |= states::EDITABLE;
81 :
82 0 : } else if (mContent->IsHTMLElement(nsGkAtoms::article)) {
83 : // We want <article> to behave like a document in terms of readonly state.
84 0 : states |= states::READONLY;
85 : }
86 :
87 0 : if (HasChildren())
88 0 : states |= states::SELECTABLE_TEXT;
89 :
90 0 : return states;
91 : }
92 :
93 : nsIntRect
94 0 : HyperTextAccessible::GetBoundsInFrame(nsIFrame* aFrame,
95 : uint32_t aStartRenderedOffset,
96 : uint32_t aEndRenderedOffset)
97 : {
98 0 : nsPresContext* presContext = mDoc->PresContext();
99 0 : if (!aFrame->IsTextFrame()) {
100 0 : return aFrame->GetScreenRectInAppUnits().
101 0 : ToNearestPixels(presContext->AppUnitsPerDevPixel());
102 : }
103 :
104 : // Substring must be entirely within the same text node.
105 : int32_t startContentOffset, endContentOffset;
106 0 : nsresult rv = RenderedToContentOffset(aFrame, aStartRenderedOffset, &startContentOffset);
107 0 : NS_ENSURE_SUCCESS(rv, nsIntRect());
108 0 : rv = RenderedToContentOffset(aFrame, aEndRenderedOffset, &endContentOffset);
109 0 : NS_ENSURE_SUCCESS(rv, nsIntRect());
110 :
111 : nsIFrame *frame;
112 : int32_t startContentOffsetInFrame;
113 : // Get the right frame continuation -- not really a child, but a sibling of
114 : // the primary frame passed in
115 0 : rv = aFrame->GetChildFrameContainingOffset(startContentOffset, false,
116 0 : &startContentOffsetInFrame, &frame);
117 0 : NS_ENSURE_SUCCESS(rv, nsIntRect());
118 :
119 0 : nsRect screenRect;
120 0 : while (frame && startContentOffset < endContentOffset) {
121 : // Start with this frame's screen rect, which we will shrink based on
122 : // the substring we care about within it. We will then add that frame to
123 : // the total screenRect we are returning.
124 0 : nsRect frameScreenRect = frame->GetScreenRectInAppUnits();
125 :
126 : // Get the length of the substring in this frame that we want the bounds for
127 : int32_t startFrameTextOffset, endFrameTextOffset;
128 0 : frame->GetOffsets(startFrameTextOffset, endFrameTextOffset);
129 0 : int32_t frameTotalTextLength = endFrameTextOffset - startFrameTextOffset;
130 0 : int32_t seekLength = endContentOffset - startContentOffset;
131 0 : int32_t frameSubStringLength = std::min(frameTotalTextLength - startContentOffsetInFrame, seekLength);
132 :
133 : // Add the point where the string starts to the frameScreenRect
134 0 : nsPoint frameTextStartPoint;
135 0 : rv = frame->GetPointFromOffset(startContentOffset, &frameTextStartPoint);
136 0 : NS_ENSURE_SUCCESS(rv, nsIntRect());
137 :
138 : // Use the point for the end offset to calculate the width
139 0 : nsPoint frameTextEndPoint;
140 0 : rv = frame->GetPointFromOffset(startContentOffset + frameSubStringLength, &frameTextEndPoint);
141 0 : NS_ENSURE_SUCCESS(rv, nsIntRect());
142 :
143 0 : frameScreenRect.x += std::min(frameTextStartPoint.x, frameTextEndPoint.x);
144 0 : frameScreenRect.width = mozilla::Abs(frameTextStartPoint.x - frameTextEndPoint.x);
145 :
146 0 : screenRect.UnionRect(frameScreenRect, screenRect);
147 :
148 : // Get ready to loop back for next frame continuation
149 0 : startContentOffset += frameSubStringLength;
150 0 : startContentOffsetInFrame = 0;
151 0 : frame = frame->GetNextContinuation();
152 : }
153 :
154 0 : return screenRect.ToNearestPixels(presContext->AppUnitsPerDevPixel());
155 : }
156 :
157 : void
158 0 : HyperTextAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
159 : nsAString& aText)
160 : {
161 0 : aText.Truncate();
162 :
163 0 : index_t startOffset = ConvertMagicOffset(aStartOffset);
164 0 : index_t endOffset = ConvertMagicOffset(aEndOffset);
165 0 : if (!startOffset.IsValid() || !endOffset.IsValid() ||
166 0 : startOffset > endOffset || endOffset > CharacterCount()) {
167 0 : NS_ERROR("Wrong in offset");
168 0 : return;
169 : }
170 :
171 0 : int32_t startChildIdx = GetChildIndexAtOffset(startOffset);
172 0 : if (startChildIdx == -1)
173 0 : return;
174 :
175 0 : int32_t endChildIdx = GetChildIndexAtOffset(endOffset);
176 0 : if (endChildIdx == -1)
177 0 : return;
178 :
179 0 : if (startChildIdx == endChildIdx) {
180 0 : int32_t childOffset = GetChildOffset(startChildIdx);
181 0 : if (childOffset == -1)
182 0 : return;
183 :
184 0 : Accessible* child = GetChildAt(startChildIdx);
185 0 : child->AppendTextTo(aText, startOffset - childOffset,
186 0 : endOffset - startOffset);
187 0 : return;
188 : }
189 :
190 0 : int32_t startChildOffset = GetChildOffset(startChildIdx);
191 0 : if (startChildOffset == -1)
192 0 : return;
193 :
194 0 : Accessible* startChild = GetChildAt(startChildIdx);
195 0 : startChild->AppendTextTo(aText, startOffset - startChildOffset);
196 :
197 0 : for (int32_t childIdx = startChildIdx + 1; childIdx < endChildIdx; childIdx++) {
198 0 : Accessible* child = GetChildAt(childIdx);
199 0 : child->AppendTextTo(aText);
200 : }
201 :
202 0 : int32_t endChildOffset = GetChildOffset(endChildIdx);
203 0 : if (endChildOffset == -1)
204 0 : return;
205 :
206 0 : Accessible* endChild = GetChildAt(endChildIdx);
207 0 : endChild->AppendTextTo(aText, 0, endOffset - endChildOffset);
208 : }
209 :
210 : uint32_t
211 0 : HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
212 : bool aIsEndOffset) const
213 : {
214 0 : if (!aNode)
215 0 : return 0;
216 :
217 0 : uint32_t offset = 0;
218 0 : nsINode* findNode = nullptr;
219 :
220 0 : if (aNodeOffset == -1) {
221 0 : findNode = aNode;
222 :
223 0 : } else if (aNode->IsNodeOfType(nsINode::eTEXT)) {
224 : // For text nodes, aNodeOffset comes in as a character offset
225 : // Text offset will be added at the end, if we find the offset in this hypertext
226 : // We want the "skipped" offset into the text (rendered text without the extra whitespace)
227 0 : nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
228 0 : NS_ENSURE_TRUE(frame, 0);
229 :
230 0 : nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &offset);
231 0 : NS_ENSURE_SUCCESS(rv, 0);
232 :
233 0 : findNode = aNode;
234 :
235 : } else {
236 : // findNode could be null if aNodeOffset == # of child nodes, which means
237 : // one of two things:
238 : // 1) there are no children, and the passed-in node is not mContent -- use
239 : // parentContent for the node to find
240 : // 2) there are no children and the passed-in node is mContent, which means
241 : // we're an empty nsIAccessibleText
242 : // 3) there are children and we're at the end of the children
243 :
244 0 : findNode = aNode->GetChildAt(aNodeOffset);
245 0 : if (!findNode) {
246 0 : if (aNodeOffset == 0) {
247 0 : if (aNode == GetNode()) {
248 : // Case #1: this accessible has no children and thus has empty text,
249 : // we can only be at hypertext offset 0.
250 0 : return 0;
251 : }
252 :
253 : // Case #2: there are no children, we're at this node.
254 0 : findNode = aNode;
255 0 : } else if (aNodeOffset == static_cast<int32_t>(aNode->GetChildCount())) {
256 : // Case #3: we're after the last child, get next node to this one.
257 0 : for (nsINode* tmpNode = aNode;
258 0 : !findNode && tmpNode && tmpNode != mContent;
259 : tmpNode = tmpNode->GetParent()) {
260 0 : findNode = tmpNode->GetNextSibling();
261 : }
262 : }
263 : }
264 : }
265 :
266 : // Get accessible for this findNode, or if that node isn't accessible, use the
267 : // accessible for the next DOM node which has one (based on forward depth first search)
268 0 : Accessible* descendant = nullptr;
269 0 : if (findNode) {
270 0 : nsCOMPtr<nsIContent> findContent(do_QueryInterface(findNode));
271 0 : if (findContent && findContent->IsHTMLElement() &&
272 0 : findContent->NodeInfo()->Equals(nsGkAtoms::br) &&
273 0 : findContent->AttrValueIs(kNameSpaceID_None,
274 : nsGkAtoms::mozeditorbogusnode,
275 : nsGkAtoms::_true,
276 : eIgnoreCase)) {
277 : // This <br> is the hacky "bogus node" used when there is no text in a control
278 0 : return 0;
279 : }
280 :
281 0 : descendant = mDoc->GetAccessible(findNode);
282 0 : if (!descendant && findNode->IsContent()) {
283 0 : Accessible* container = mDoc->GetContainerAccessible(findNode);
284 0 : if (container) {
285 : TreeWalker walker(container, findNode->AsContent(),
286 0 : TreeWalker::eWalkContextTree);
287 0 : descendant = walker.Next();
288 0 : if (!descendant)
289 0 : descendant = container;
290 : }
291 : }
292 : }
293 :
294 0 : return TransformOffset(descendant, offset, aIsEndOffset);
295 : }
296 :
297 : uint32_t
298 0 : HyperTextAccessible::TransformOffset(Accessible* aDescendant,
299 : uint32_t aOffset, bool aIsEndOffset) const
300 : {
301 : // From the descendant, go up and get the immediate child of this hypertext.
302 0 : uint32_t offset = aOffset;
303 0 : Accessible* descendant = aDescendant;
304 0 : while (descendant) {
305 0 : Accessible* parent = descendant->Parent();
306 0 : if (parent == this)
307 0 : return GetChildOffset(descendant) + offset;
308 :
309 : // This offset no longer applies because the passed-in text object is not
310 : // a child of the hypertext. This happens when there are nested hypertexts,
311 : // e.g. <div>abc<h1>def</h1>ghi</div>. Thus we need to adjust the offset
312 : // to make it relative the hypertext.
313 : // If the end offset is not supposed to be inclusive and the original point
314 : // is not at 0 offset then the returned offset should be after an embedded
315 : // character the original point belongs to.
316 0 : if (aIsEndOffset)
317 0 : offset = (offset > 0 || descendant->IndexInParent() > 0) ? 1 : 0;
318 : else
319 0 : offset = 0;
320 :
321 0 : descendant = parent;
322 : }
323 :
324 : // If the given a11y point cannot be mapped into offset relative this hypertext
325 : // offset then return length as fallback value.
326 0 : return CharacterCount();
327 : }
328 :
329 : /**
330 : * GetElementAsContentOf() returns a content representing an element which is
331 : * or includes aNode.
332 : *
333 : * XXX This method is enough to retrieve ::before or ::after pseudo element.
334 : * So, if you want to use this for other purpose, you might need to check
335 : * ancestors too.
336 : */
337 0 : static nsIContent* GetElementAsContentOf(nsINode* aNode)
338 : {
339 0 : if (aNode->IsElement()) {
340 0 : return aNode->AsContent();
341 : }
342 0 : nsIContent* parent = aNode->GetParent();
343 0 : return parent && parent->IsElement() ? parent : nullptr;
344 : }
345 :
346 : bool
347 0 : HyperTextAccessible::OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
348 : nsRange* aRange)
349 : {
350 0 : DOMPoint startPoint = OffsetToDOMPoint(aStartOffset);
351 0 : if (!startPoint.node)
352 0 : return false;
353 :
354 : // HyperTextAccessible manages pseudo elements generated by ::before or
355 : // ::after. However, contents of them are not in the DOM tree normally.
356 : // Therefore, they are not selectable and editable. So, when this creates
357 : // a DOM range, it should not start from nor end in any pseudo contents.
358 :
359 0 : nsIContent* container = GetElementAsContentOf(startPoint.node);
360 : DOMPoint startPointForDOMRange =
361 0 : ClosestNotGeneratedDOMPoint(startPoint, container);
362 0 : aRange->SetStart(startPointForDOMRange.node, startPointForDOMRange.idx);
363 :
364 : // If the caller wants collapsed range, let's collapse the range to its start.
365 0 : if (aStartOffset == aEndOffset) {
366 0 : aRange->Collapse(true);
367 0 : return true;
368 : }
369 :
370 0 : DOMPoint endPoint = OffsetToDOMPoint(aEndOffset);
371 0 : if (!endPoint.node)
372 0 : return false;
373 :
374 0 : if (startPoint.node != endPoint.node) {
375 0 : container = GetElementAsContentOf(endPoint.node);
376 : }
377 :
378 : DOMPoint endPointForDOMRange =
379 0 : ClosestNotGeneratedDOMPoint(endPoint, container);
380 0 : aRange->SetEnd(endPointForDOMRange.node, endPointForDOMRange.idx);
381 0 : return true;
382 : }
383 :
384 : DOMPoint
385 0 : HyperTextAccessible::OffsetToDOMPoint(int32_t aOffset)
386 : {
387 : // 0 offset is valid even if no children. In this case the associated editor
388 : // is empty so return a DOM point for editor root element.
389 0 : if (aOffset == 0) {
390 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
391 0 : if (editor) {
392 0 : bool isEmpty = false;
393 0 : editor->GetDocumentIsEmpty(&isEmpty);
394 0 : if (isEmpty) {
395 0 : nsCOMPtr<nsIDOMElement> editorRootElm;
396 0 : editor->GetRootElement(getter_AddRefs(editorRootElm));
397 :
398 0 : nsCOMPtr<nsINode> editorRoot(do_QueryInterface(editorRootElm));
399 0 : return DOMPoint(editorRoot, 0);
400 : }
401 : }
402 : }
403 :
404 0 : int32_t childIdx = GetChildIndexAtOffset(aOffset);
405 0 : if (childIdx == -1)
406 0 : return DOMPoint();
407 :
408 0 : Accessible* child = GetChildAt(childIdx);
409 0 : int32_t innerOffset = aOffset - GetChildOffset(childIdx);
410 :
411 : // A text leaf case.
412 0 : if (child->IsTextLeaf()) {
413 : // The point is inside the text node. This is always true for any text leaf
414 : // except a last child one. See assertion below.
415 0 : if (aOffset < GetChildOffset(childIdx + 1)) {
416 0 : nsIContent* content = child->GetContent();
417 0 : int32_t idx = 0;
418 0 : if (NS_FAILED(RenderedToContentOffset(content->GetPrimaryFrame(),
419 : innerOffset, &idx)))
420 0 : return DOMPoint();
421 :
422 0 : return DOMPoint(content, idx);
423 : }
424 :
425 : // Set the DOM point right after the text node.
426 0 : MOZ_ASSERT(static_cast<uint32_t>(aOffset) == CharacterCount());
427 0 : innerOffset = 1;
428 : }
429 :
430 : // Case of embedded object. The point is either before or after the element.
431 0 : NS_ASSERTION(innerOffset == 0 || innerOffset == 1, "A wrong inner offset!");
432 0 : nsINode* node = child->GetNode();
433 0 : nsINode* parentNode = node->GetParentNode();
434 : return parentNode ?
435 0 : DOMPoint(parentNode, parentNode->IndexOf(node) + innerOffset) :
436 0 : DOMPoint();
437 : }
438 :
439 : DOMPoint
440 0 : HyperTextAccessible::ClosestNotGeneratedDOMPoint(const DOMPoint& aDOMPoint,
441 : nsIContent* aElementContent)
442 : {
443 0 : MOZ_ASSERT(aDOMPoint.node, "The node must not be null");
444 :
445 : // ::before pseudo element
446 0 : if (aElementContent &&
447 0 : aElementContent->IsGeneratedContentContainerForBefore()) {
448 0 : MOZ_ASSERT(aElementContent->GetParent(),
449 : "::before must have parent element");
450 : // The first child of its parent (i.e., immediately after the ::before) is
451 : // good point for a DOM range.
452 0 : return DOMPoint(aElementContent->GetParent(), 0);
453 : }
454 :
455 : // ::after pseudo element
456 0 : if (aElementContent &&
457 0 : aElementContent->IsGeneratedContentContainerForAfter()) {
458 0 : MOZ_ASSERT(aElementContent->GetParent(),
459 : "::after must have parent element");
460 : // The end of its parent (i.e., immediately before the ::after) is good
461 : // point for a DOM range.
462 0 : return DOMPoint(aElementContent->GetParent(),
463 0 : aElementContent->GetParent()->GetChildCount());
464 : }
465 :
466 0 : return aDOMPoint;
467 : }
468 :
469 : uint32_t
470 0 : HyperTextAccessible::FindOffset(uint32_t aOffset, nsDirection aDirection,
471 : nsSelectionAmount aAmount,
472 : EWordMovementType aWordMovementType)
473 : {
474 0 : NS_ASSERTION(aDirection == eDirPrevious || aAmount != eSelectBeginLine,
475 : "eSelectBeginLine should only be used with eDirPrevious");
476 :
477 : // Find a leaf accessible frame to start with. PeekOffset wants this.
478 0 : HyperTextAccessible* text = this;
479 0 : Accessible* child = nullptr;
480 0 : int32_t innerOffset = aOffset;
481 :
482 0 : do {
483 0 : int32_t childIdx = text->GetChildIndexAtOffset(innerOffset);
484 :
485 : // We can have an empty text leaf as our only child. Since empty text
486 : // leaves are not accessible we then have no children, but 0 is a valid
487 : // innerOffset.
488 0 : if (childIdx == -1) {
489 0 : NS_ASSERTION(innerOffset == 0 && !text->ChildCount(), "No childIdx?");
490 0 : return DOMPointToOffset(text->GetNode(), 0, aDirection == eDirNext);
491 : }
492 :
493 0 : child = text->GetChildAt(childIdx);
494 :
495 : // HTML list items may need special processing because PeekOffset doesn't
496 : // work with list bullets.
497 0 : if (text->IsHTMLListItem()) {
498 0 : HTMLLIAccessible* li = text->AsHTMLListItem();
499 0 : if (child == li->Bullet()) {
500 : // XXX: the logic is broken for multichar bullets in moving by
501 : // char/cluster/word cases.
502 0 : if (text != this) {
503 0 : return aDirection == eDirPrevious ?
504 : TransformOffset(text, 0, false) :
505 0 : TransformOffset(text, 1, true);
506 : }
507 0 : if (aDirection == eDirPrevious)
508 0 : return 0;
509 :
510 0 : uint32_t nextOffset = GetChildOffset(1);
511 0 : if (nextOffset == 0)
512 0 : return 0;
513 :
514 0 : switch (aAmount) {
515 : case eSelectLine:
516 : case eSelectEndLine:
517 : // Ask a text leaf next (if not empty) to the bullet for an offset
518 : // since list item may be multiline.
519 0 : return nextOffset < CharacterCount() ?
520 : FindOffset(nextOffset, aDirection, aAmount, aWordMovementType) :
521 0 : nextOffset;
522 :
523 : default:
524 0 : return nextOffset;
525 : }
526 : }
527 : }
528 :
529 0 : innerOffset -= text->GetChildOffset(childIdx);
530 :
531 0 : text = child->AsHyperText();
532 0 : } while (text);
533 :
534 0 : nsIFrame* childFrame = child->GetFrame();
535 0 : if (!childFrame) {
536 0 : NS_ERROR("No child frame");
537 0 : return 0;
538 : }
539 :
540 0 : int32_t innerContentOffset = innerOffset;
541 0 : if (child->IsTextLeaf()) {
542 0 : NS_ASSERTION(childFrame->IsTextFrame(), "Wrong frame!");
543 0 : RenderedToContentOffset(childFrame, innerOffset, &innerContentOffset);
544 : }
545 :
546 0 : nsIFrame* frameAtOffset = childFrame;
547 0 : int32_t unusedOffsetInFrame = 0;
548 : childFrame->GetChildFrameContainingOffset(innerContentOffset, true,
549 : &unusedOffsetInFrame,
550 0 : &frameAtOffset);
551 :
552 0 : const bool kIsJumpLinesOk = true; // okay to jump lines
553 0 : const bool kIsScrollViewAStop = false; // do not stop at scroll views
554 0 : const bool kIsKeyboardSelect = true; // is keyboard selection
555 0 : const bool kIsVisualBidi = false; // use visual order for bidi text
556 : nsPeekOffsetStruct pos(aAmount, aDirection, innerContentOffset,
557 0 : nsPoint(0, 0), kIsJumpLinesOk, kIsScrollViewAStop,
558 : kIsKeyboardSelect, kIsVisualBidi,
559 0 : false, aWordMovementType);
560 0 : nsresult rv = frameAtOffset->PeekOffset(&pos);
561 :
562 : // PeekOffset fails on last/first lines of the text in certain cases.
563 0 : if (NS_FAILED(rv) && aAmount == eSelectLine) {
564 0 : pos.mAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
565 0 : frameAtOffset->PeekOffset(&pos);
566 : }
567 0 : if (!pos.mResultContent) {
568 0 : NS_ERROR("No result content!");
569 0 : return 0;
570 : }
571 :
572 : // Turn the resulting DOM point into an offset.
573 0 : uint32_t hyperTextOffset = DOMPointToOffset(pos.mResultContent,
574 : pos.mContentOffset,
575 0 : aDirection == eDirNext);
576 :
577 0 : if (aDirection == eDirPrevious) {
578 : // If we reached the end during search, this means we didn't find the DOM point
579 : // and we're actually at the start of the paragraph
580 0 : if (hyperTextOffset == CharacterCount())
581 0 : return 0;
582 :
583 : // PeekOffset stops right before bullet so return 0 to workaround it.
584 0 : if (IsHTMLListItem() && aAmount == eSelectBeginLine &&
585 : hyperTextOffset > 0) {
586 0 : Accessible* prevOffsetChild = GetChildAtOffset(hyperTextOffset - 1);
587 0 : if (prevOffsetChild == AsHTMLListItem()->Bullet())
588 0 : return 0;
589 : }
590 : }
591 :
592 0 : return hyperTextOffset;
593 : }
594 :
595 : uint32_t
596 0 : HyperTextAccessible::FindLineBoundary(uint32_t aOffset,
597 : EWhichLineBoundary aWhichLineBoundary)
598 : {
599 : // Note: empty last line doesn't have own frame (a previous line contains '\n'
600 : // character instead) thus when it makes a difference we need to process this
601 : // case separately (otherwise operations are performed on previous line).
602 0 : switch (aWhichLineBoundary) {
603 : case ePrevLineBegin: {
604 : // Fetch a previous line and move to its start (as arrow up and home keys
605 : // were pressed).
606 0 : if (IsEmptyLastLineOffset(aOffset))
607 0 : return FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
608 :
609 0 : uint32_t tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectLine);
610 0 : return FindOffset(tmpOffset, eDirPrevious, eSelectBeginLine);
611 : }
612 :
613 : case ePrevLineEnd: {
614 0 : if (IsEmptyLastLineOffset(aOffset))
615 0 : return aOffset - 1;
616 :
617 : // If offset is at first line then return 0 (first line start).
618 0 : uint32_t tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
619 0 : if (tmpOffset == 0)
620 0 : return 0;
621 :
622 : // Otherwise move to end of previous line (as arrow up and end keys were
623 : // pressed).
624 0 : tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectLine);
625 0 : return FindOffset(tmpOffset, eDirNext, eSelectEndLine);
626 : }
627 :
628 : case eThisLineBegin:
629 0 : if (IsEmptyLastLineOffset(aOffset))
630 0 : return aOffset;
631 :
632 : // Move to begin of the current line (as home key was pressed).
633 0 : return FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
634 :
635 : case eThisLineEnd:
636 0 : if (IsEmptyLastLineOffset(aOffset))
637 0 : return aOffset;
638 :
639 : // Move to end of the current line (as end key was pressed).
640 0 : return FindOffset(aOffset, eDirNext, eSelectEndLine);
641 :
642 : case eNextLineBegin: {
643 0 : if (IsEmptyLastLineOffset(aOffset))
644 0 : return aOffset;
645 :
646 : // Move to begin of the next line if any (arrow down and home keys),
647 : // otherwise end of the current line (arrow down only).
648 0 : uint32_t tmpOffset = FindOffset(aOffset, eDirNext, eSelectLine);
649 0 : if (tmpOffset == CharacterCount())
650 0 : return tmpOffset;
651 :
652 0 : return FindOffset(tmpOffset, eDirPrevious, eSelectBeginLine);
653 : }
654 :
655 : case eNextLineEnd: {
656 0 : if (IsEmptyLastLineOffset(aOffset))
657 0 : return aOffset;
658 :
659 : // Move to next line end (as down arrow and end key were pressed).
660 0 : uint32_t tmpOffset = FindOffset(aOffset, eDirNext, eSelectLine);
661 0 : if (tmpOffset == CharacterCount())
662 0 : return tmpOffset;
663 :
664 0 : return FindOffset(tmpOffset, eDirNext, eSelectEndLine);
665 : }
666 : }
667 :
668 0 : return 0;
669 : }
670 :
671 : void
672 0 : HyperTextAccessible::TextBeforeOffset(int32_t aOffset,
673 : AccessibleTextBoundary aBoundaryType,
674 : int32_t* aStartOffset, int32_t* aEndOffset,
675 : nsAString& aText)
676 : {
677 0 : *aStartOffset = *aEndOffset = 0;
678 0 : aText.Truncate();
679 :
680 0 : index_t convertedOffset = ConvertMagicOffset(aOffset);
681 0 : if (!convertedOffset.IsValid() || convertedOffset > CharacterCount()) {
682 0 : NS_ERROR("Wrong in offset!");
683 0 : return;
684 : }
685 :
686 0 : uint32_t adjustedOffset = convertedOffset;
687 0 : if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
688 0 : adjustedOffset = AdjustCaretOffset(adjustedOffset);
689 :
690 0 : switch (aBoundaryType) {
691 : case nsIAccessibleText::BOUNDARY_CHAR:
692 0 : if (convertedOffset != 0)
693 0 : CharAt(convertedOffset - 1, aText, aStartOffset, aEndOffset);
694 0 : break;
695 :
696 : case nsIAccessibleText::BOUNDARY_WORD_START: {
697 : // If the offset is a word start (except text length offset) then move
698 : // backward to find a start offset (end offset is the given offset).
699 : // Otherwise move backward twice to find both start and end offsets.
700 0 : if (adjustedOffset == CharacterCount()) {
701 0 : *aEndOffset = FindWordBoundary(adjustedOffset, eDirPrevious, eStartWord);
702 0 : *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
703 : } else {
704 0 : *aStartOffset = FindWordBoundary(adjustedOffset, eDirPrevious, eStartWord);
705 0 : *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eStartWord);
706 0 : if (*aEndOffset != static_cast<int32_t>(adjustedOffset)) {
707 0 : *aEndOffset = *aStartOffset;
708 0 : *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
709 : }
710 : }
711 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
712 0 : break;
713 : }
714 :
715 : case nsIAccessibleText::BOUNDARY_WORD_END: {
716 : // Move word backward twice to find start and end offsets.
717 0 : *aEndOffset = FindWordBoundary(convertedOffset, eDirPrevious, eEndWord);
718 0 : *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
719 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
720 0 : break;
721 : }
722 :
723 : case nsIAccessibleText::BOUNDARY_LINE_START:
724 0 : *aStartOffset = FindLineBoundary(adjustedOffset, ePrevLineBegin);
725 0 : *aEndOffset = FindLineBoundary(adjustedOffset, eThisLineBegin);
726 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
727 0 : break;
728 :
729 : case nsIAccessibleText::BOUNDARY_LINE_END: {
730 0 : *aEndOffset = FindLineBoundary(adjustedOffset, ePrevLineEnd);
731 0 : int32_t tmpOffset = *aEndOffset;
732 : // Adjust offset if line is wrapped.
733 0 : if (*aEndOffset != 0 && !IsLineEndCharAt(*aEndOffset))
734 0 : tmpOffset--;
735 :
736 0 : *aStartOffset = FindLineBoundary(tmpOffset, ePrevLineEnd);
737 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
738 0 : break;
739 : }
740 : }
741 : }
742 :
743 : void
744 0 : HyperTextAccessible::TextAtOffset(int32_t aOffset,
745 : AccessibleTextBoundary aBoundaryType,
746 : int32_t* aStartOffset, int32_t* aEndOffset,
747 : nsAString& aText)
748 : {
749 0 : *aStartOffset = *aEndOffset = 0;
750 0 : aText.Truncate();
751 :
752 0 : uint32_t adjustedOffset = ConvertMagicOffset(aOffset);
753 0 : if (adjustedOffset == std::numeric_limits<uint32_t>::max()) {
754 0 : NS_ERROR("Wrong given offset!");
755 0 : return;
756 : }
757 :
758 0 : switch (aBoundaryType) {
759 : case nsIAccessibleText::BOUNDARY_CHAR:
760 : // Return no char if caret is at the end of wrapped line (case of no line
761 : // end character). Returning a next line char is confusing for AT.
762 0 : if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET && IsCaretAtEndOfLine())
763 0 : *aStartOffset = *aEndOffset = adjustedOffset;
764 : else
765 0 : CharAt(adjustedOffset, aText, aStartOffset, aEndOffset);
766 0 : break;
767 :
768 : case nsIAccessibleText::BOUNDARY_WORD_START:
769 0 : if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
770 0 : adjustedOffset = AdjustCaretOffset(adjustedOffset);
771 :
772 0 : *aEndOffset = FindWordBoundary(adjustedOffset, eDirNext, eStartWord);
773 0 : *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
774 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
775 0 : break;
776 :
777 : case nsIAccessibleText::BOUNDARY_WORD_END:
778 : // Ignore the spec and follow what WebKitGtk does because Orca expects it,
779 : // i.e. return a next word at word end offset of the current word
780 : // (WebKitGtk behavior) instead the current word (AKT spec).
781 0 : *aEndOffset = FindWordBoundary(adjustedOffset, eDirNext, eEndWord);
782 0 : *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
783 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
784 0 : break;
785 :
786 : case nsIAccessibleText::BOUNDARY_LINE_START:
787 0 : if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
788 0 : adjustedOffset = AdjustCaretOffset(adjustedOffset);
789 :
790 0 : *aStartOffset = FindLineBoundary(adjustedOffset, eThisLineBegin);
791 0 : *aEndOffset = FindLineBoundary(adjustedOffset, eNextLineBegin);
792 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
793 0 : break;
794 :
795 : case nsIAccessibleText::BOUNDARY_LINE_END:
796 0 : if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
797 0 : adjustedOffset = AdjustCaretOffset(adjustedOffset);
798 :
799 : // In contrast to word end boundary we follow the spec here.
800 0 : *aStartOffset = FindLineBoundary(adjustedOffset, ePrevLineEnd);
801 0 : *aEndOffset = FindLineBoundary(adjustedOffset, eThisLineEnd);
802 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
803 0 : break;
804 : }
805 : }
806 :
807 : void
808 0 : HyperTextAccessible::TextAfterOffset(int32_t aOffset,
809 : AccessibleTextBoundary aBoundaryType,
810 : int32_t* aStartOffset, int32_t* aEndOffset,
811 : nsAString& aText)
812 : {
813 0 : *aStartOffset = *aEndOffset = 0;
814 0 : aText.Truncate();
815 :
816 0 : index_t convertedOffset = ConvertMagicOffset(aOffset);
817 0 : if (!convertedOffset.IsValid() || convertedOffset > CharacterCount()) {
818 0 : NS_ERROR("Wrong in offset!");
819 0 : return;
820 : }
821 :
822 0 : uint32_t adjustedOffset = convertedOffset;
823 0 : if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
824 0 : adjustedOffset = AdjustCaretOffset(adjustedOffset);
825 :
826 0 : switch (aBoundaryType) {
827 : case nsIAccessibleText::BOUNDARY_CHAR:
828 : // If caret is at the end of wrapped line (case of no line end character)
829 : // then char after the offset is a first char at next line.
830 0 : if (adjustedOffset >= CharacterCount())
831 0 : *aStartOffset = *aEndOffset = CharacterCount();
832 : else
833 0 : CharAt(adjustedOffset + 1, aText, aStartOffset, aEndOffset);
834 0 : break;
835 :
836 : case nsIAccessibleText::BOUNDARY_WORD_START:
837 : // Move word forward twice to find start and end offsets.
838 0 : *aStartOffset = FindWordBoundary(adjustedOffset, eDirNext, eStartWord);
839 0 : *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eStartWord);
840 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
841 0 : break;
842 :
843 : case nsIAccessibleText::BOUNDARY_WORD_END:
844 : // If the offset is a word end (except 0 offset) then move forward to find
845 : // end offset (start offset is the given offset). Otherwise move forward
846 : // twice to find both start and end offsets.
847 0 : if (convertedOffset == 0) {
848 0 : *aStartOffset = FindWordBoundary(convertedOffset, eDirNext, eEndWord);
849 0 : *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
850 : } else {
851 0 : *aEndOffset = FindWordBoundary(convertedOffset, eDirNext, eEndWord);
852 0 : *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
853 0 : if (*aStartOffset != static_cast<int32_t>(convertedOffset)) {
854 0 : *aStartOffset = *aEndOffset;
855 0 : *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
856 : }
857 : }
858 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
859 0 : break;
860 :
861 : case nsIAccessibleText::BOUNDARY_LINE_START:
862 0 : *aStartOffset = FindLineBoundary(adjustedOffset, eNextLineBegin);
863 0 : *aEndOffset = FindLineBoundary(*aStartOffset, eNextLineBegin);
864 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
865 0 : break;
866 :
867 : case nsIAccessibleText::BOUNDARY_LINE_END:
868 0 : *aStartOffset = FindLineBoundary(adjustedOffset, eThisLineEnd);
869 0 : *aEndOffset = FindLineBoundary(adjustedOffset, eNextLineEnd);
870 0 : TextSubstring(*aStartOffset, *aEndOffset, aText);
871 0 : break;
872 : }
873 : }
874 :
875 : already_AddRefed<nsIPersistentProperties>
876 0 : HyperTextAccessible::TextAttributes(bool aIncludeDefAttrs, int32_t aOffset,
877 : int32_t* aStartOffset,
878 : int32_t* aEndOffset)
879 : {
880 : // 1. Get each attribute and its ranges one after another.
881 : // 2. As we get each new attribute, we pass the current start and end offsets
882 : // as in/out parameters. In other words, as attributes are collected,
883 : // the attribute range itself can only stay the same or get smaller.
884 :
885 0 : *aStartOffset = *aEndOffset = 0;
886 0 : index_t offset = ConvertMagicOffset(aOffset);
887 0 : if (!offset.IsValid() || offset > CharacterCount()) {
888 0 : NS_ERROR("Wrong in offset!");
889 0 : return nullptr;
890 : }
891 :
892 : nsCOMPtr<nsIPersistentProperties> attributes =
893 0 : do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
894 :
895 0 : Accessible* accAtOffset = GetChildAtOffset(offset);
896 0 : if (!accAtOffset) {
897 : // Offset 0 is correct offset when accessible has empty text. Include
898 : // default attributes if they were requested, otherwise return empty set.
899 0 : if (offset == 0) {
900 0 : if (aIncludeDefAttrs) {
901 0 : TextAttrsMgr textAttrsMgr(this);
902 0 : textAttrsMgr.GetAttributes(attributes);
903 : }
904 0 : return attributes.forget();
905 : }
906 0 : return nullptr;
907 : }
908 :
909 0 : int32_t accAtOffsetIdx = accAtOffset->IndexInParent();
910 0 : uint32_t startOffset = GetChildOffset(accAtOffsetIdx);
911 0 : uint32_t endOffset = GetChildOffset(accAtOffsetIdx + 1);
912 0 : int32_t offsetInAcc = offset - startOffset;
913 :
914 : TextAttrsMgr textAttrsMgr(this, aIncludeDefAttrs, accAtOffset,
915 0 : accAtOffsetIdx);
916 0 : textAttrsMgr.GetAttributes(attributes, &startOffset, &endOffset);
917 :
918 : // Compute spelling attributes on text accessible only.
919 0 : nsIFrame *offsetFrame = accAtOffset->GetFrame();
920 0 : if (offsetFrame && offsetFrame->IsTextFrame()) {
921 0 : int32_t nodeOffset = 0;
922 0 : RenderedToContentOffset(offsetFrame, offsetInAcc, &nodeOffset);
923 :
924 : // Set 'misspelled' text attribute.
925 0 : GetSpellTextAttr(accAtOffset->GetNode(), nodeOffset,
926 0 : &startOffset, &endOffset, attributes);
927 : }
928 :
929 0 : *aStartOffset = startOffset;
930 0 : *aEndOffset = endOffset;
931 0 : return attributes.forget();
932 : }
933 :
934 : already_AddRefed<nsIPersistentProperties>
935 0 : HyperTextAccessible::DefaultTextAttributes()
936 : {
937 : nsCOMPtr<nsIPersistentProperties> attributes =
938 0 : do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
939 :
940 0 : TextAttrsMgr textAttrsMgr(this);
941 0 : textAttrsMgr.GetAttributes(attributes);
942 0 : return attributes.forget();
943 : }
944 :
945 : int32_t
946 0 : HyperTextAccessible::GetLevelInternal()
947 : {
948 0 : if (mContent->IsHTMLElement(nsGkAtoms::h1))
949 0 : return 1;
950 0 : if (mContent->IsHTMLElement(nsGkAtoms::h2))
951 0 : return 2;
952 0 : if (mContent->IsHTMLElement(nsGkAtoms::h3))
953 0 : return 3;
954 0 : if (mContent->IsHTMLElement(nsGkAtoms::h4))
955 0 : return 4;
956 0 : if (mContent->IsHTMLElement(nsGkAtoms::h5))
957 0 : return 5;
958 0 : if (mContent->IsHTMLElement(nsGkAtoms::h6))
959 0 : return 6;
960 :
961 0 : return AccessibleWrap::GetLevelInternal();
962 : }
963 :
964 : void
965 0 : HyperTextAccessible::SetMathMLXMLRoles(nsIPersistentProperties* aAttributes)
966 : {
967 : // Add MathML xmlroles based on the position inside the parent.
968 0 : Accessible* parent = Parent();
969 0 : if (parent) {
970 0 : switch (parent->Role()) {
971 : case roles::MATHML_CELL:
972 : case roles::MATHML_ENCLOSED:
973 : case roles::MATHML_ERROR:
974 : case roles::MATHML_MATH:
975 : case roles::MATHML_ROW:
976 : case roles::MATHML_SQUARE_ROOT:
977 : case roles::MATHML_STYLE:
978 0 : if (Role() == roles::MATHML_OPERATOR) {
979 : // This is an operator inside an <mrow> (or an inferred <mrow>).
980 : // See http://www.w3.org/TR/MathML3/chapter3.html#presm.inferredmrow
981 : // XXX We should probably do something similar for MATHML_FENCED, but
982 : // operators do not appear in the accessible tree. See bug 1175747.
983 0 : nsIMathMLFrame* mathMLFrame = do_QueryFrame(GetFrame());
984 0 : if (mathMLFrame) {
985 0 : nsEmbellishData embellishData;
986 0 : mathMLFrame->GetEmbellishData(embellishData);
987 0 : if (NS_MATHML_EMBELLISH_IS_FENCE(embellishData.flags)) {
988 0 : if (!PrevSibling()) {
989 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
990 0 : nsGkAtoms::open_fence);
991 0 : } else if (!NextSibling()) {
992 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
993 0 : nsGkAtoms::close_fence);
994 : }
995 : }
996 0 : if (NS_MATHML_EMBELLISH_IS_SEPARATOR(embellishData.flags)) {
997 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
998 0 : nsGkAtoms::separator_);
999 : }
1000 : }
1001 : }
1002 0 : break;
1003 : case roles::MATHML_FRACTION:
1004 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1005 0 : IndexInParent() == 0 ?
1006 : nsGkAtoms::numerator :
1007 0 : nsGkAtoms::denominator);
1008 0 : break;
1009 : case roles::MATHML_ROOT:
1010 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1011 0 : IndexInParent() == 0 ? nsGkAtoms::base :
1012 0 : nsGkAtoms::root_index);
1013 0 : break;
1014 : case roles::MATHML_SUB:
1015 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1016 0 : IndexInParent() == 0 ? nsGkAtoms::base :
1017 0 : nsGkAtoms::subscript);
1018 0 : break;
1019 : case roles::MATHML_SUP:
1020 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1021 0 : IndexInParent() == 0 ? nsGkAtoms::base :
1022 0 : nsGkAtoms::superscript);
1023 0 : break;
1024 : case roles::MATHML_SUB_SUP: {
1025 0 : int32_t index = IndexInParent();
1026 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1027 : index == 0 ? nsGkAtoms::base :
1028 0 : (index == 1 ? nsGkAtoms::subscript :
1029 0 : nsGkAtoms::superscript));
1030 0 : } break;
1031 : case roles::MATHML_UNDER:
1032 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1033 0 : IndexInParent() == 0 ? nsGkAtoms::base :
1034 0 : nsGkAtoms::underscript);
1035 0 : break;
1036 : case roles::MATHML_OVER:
1037 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1038 0 : IndexInParent() == 0 ? nsGkAtoms::base :
1039 0 : nsGkAtoms::overscript);
1040 0 : break;
1041 : case roles::MATHML_UNDER_OVER: {
1042 0 : int32_t index = IndexInParent();
1043 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1044 : index == 0 ? nsGkAtoms::base :
1045 0 : (index == 1 ? nsGkAtoms::underscript :
1046 0 : nsGkAtoms::overscript));
1047 0 : } break;
1048 : case roles::MATHML_MULTISCRIPTS: {
1049 : // Get the <multiscripts> base.
1050 : nsIContent* child;
1051 0 : bool baseFound = false;
1052 0 : for (child = parent->GetContent()->GetFirstChild(); child;
1053 0 : child = child->GetNextSibling()) {
1054 0 : if (child->IsMathMLElement()) {
1055 0 : baseFound = true;
1056 0 : break;
1057 : }
1058 : }
1059 0 : if (baseFound) {
1060 0 : nsIContent* content = GetContent();
1061 0 : if (child == content) {
1062 : // We are the base.
1063 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1064 0 : nsGkAtoms::base);
1065 : } else {
1066 : // Browse the list of scripts to find us and determine our type.
1067 0 : bool postscript = true;
1068 0 : bool subscript = true;
1069 0 : for (child = child->GetNextSibling(); child;
1070 0 : child = child->GetNextSibling()) {
1071 0 : if (!child->IsMathMLElement())
1072 0 : continue;
1073 0 : if (child->IsMathMLElement(nsGkAtoms::mprescripts_)) {
1074 0 : postscript = false;
1075 0 : subscript = true;
1076 0 : continue;
1077 : }
1078 0 : if (child == content) {
1079 0 : if (postscript) {
1080 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1081 : subscript ?
1082 : nsGkAtoms::subscript :
1083 0 : nsGkAtoms::superscript);
1084 : } else {
1085 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1086 : subscript ?
1087 : nsGkAtoms::presubscript :
1088 0 : nsGkAtoms::presuperscript);
1089 : }
1090 0 : break;
1091 : }
1092 0 : subscript = !subscript;
1093 : }
1094 : }
1095 : }
1096 0 : } break;
1097 : default:
1098 0 : break;
1099 : }
1100 : }
1101 0 : }
1102 :
1103 : already_AddRefed<nsIPersistentProperties>
1104 0 : HyperTextAccessible::NativeAttributes()
1105 : {
1106 : nsCOMPtr<nsIPersistentProperties> attributes =
1107 0 : AccessibleWrap::NativeAttributes();
1108 :
1109 : // 'formatting' attribute is deprecated, 'display' attribute should be
1110 : // instead.
1111 0 : nsIFrame *frame = GetFrame();
1112 0 : if (frame && frame->IsBlockFrame()) {
1113 0 : nsAutoString unused;
1114 0 : attributes->SetStringProperty(NS_LITERAL_CSTRING("formatting"),
1115 0 : NS_LITERAL_STRING("block"), unused);
1116 : }
1117 :
1118 0 : if (FocusMgr()->IsFocused(this)) {
1119 0 : int32_t lineNumber = CaretLineNumber();
1120 0 : if (lineNumber >= 1) {
1121 0 : nsAutoString strLineNumber;
1122 0 : strLineNumber.AppendInt(lineNumber);
1123 0 : nsAccUtils::SetAccAttr(attributes, nsGkAtoms::lineNumber, strLineNumber);
1124 : }
1125 : }
1126 :
1127 0 : if (HasOwnContent()) {
1128 0 : GetAccService()->MarkupAttributes(mContent, attributes);
1129 0 : if (mContent->IsMathMLElement())
1130 0 : SetMathMLXMLRoles(attributes);
1131 : }
1132 :
1133 0 : return attributes.forget();
1134 : }
1135 :
1136 : nsIAtom*
1137 0 : HyperTextAccessible::LandmarkRole() const
1138 : {
1139 0 : if (!HasOwnContent())
1140 0 : return nullptr;
1141 :
1142 : // For the html landmark elements we expose them like we do ARIA landmarks to
1143 : // make AT navigation schemes "just work".
1144 0 : if (mContent->IsHTMLElement(nsGkAtoms::nav)) {
1145 0 : return nsGkAtoms::navigation;
1146 : }
1147 :
1148 0 : if (mContent->IsAnyOfHTMLElements(nsGkAtoms::header,
1149 : nsGkAtoms::footer)) {
1150 : // Only map header and footer if they are not descendants of an article
1151 : // or section tag.
1152 0 : nsIContent* parent = mContent->GetParent();
1153 0 : while (parent) {
1154 0 : if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::section)) {
1155 0 : break;
1156 : }
1157 0 : parent = parent->GetParent();
1158 : }
1159 :
1160 : // No article or section elements found.
1161 0 : if (!parent) {
1162 0 : if (mContent->IsHTMLElement(nsGkAtoms::header)) {
1163 0 : return nsGkAtoms::banner;
1164 : }
1165 :
1166 0 : if (mContent->IsHTMLElement(nsGkAtoms::footer)) {
1167 0 : return nsGkAtoms::contentinfo;
1168 : }
1169 : }
1170 0 : return nullptr;
1171 : }
1172 :
1173 0 : if (mContent->IsHTMLElement(nsGkAtoms::aside)) {
1174 0 : return nsGkAtoms::complementary;
1175 : }
1176 :
1177 0 : if (mContent->IsHTMLElement(nsGkAtoms::main)) {
1178 0 : return nsGkAtoms::main;
1179 : }
1180 :
1181 0 : return nullptr;
1182 : }
1183 :
1184 : int32_t
1185 0 : HyperTextAccessible::OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType)
1186 : {
1187 0 : nsIFrame* hyperFrame = GetFrame();
1188 0 : if (!hyperFrame)
1189 0 : return -1;
1190 :
1191 : nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType,
1192 0 : this);
1193 :
1194 0 : nsPresContext* presContext = mDoc->PresContext();
1195 : nsPoint coordsInAppUnits =
1196 0 : ToAppUnits(coords, presContext->AppUnitsPerDevPixel());
1197 :
1198 0 : nsRect frameScreenRect = hyperFrame->GetScreenRectInAppUnits();
1199 0 : if (!frameScreenRect.Contains(coordsInAppUnits.x, coordsInAppUnits.y))
1200 0 : return -1; // Not found
1201 :
1202 0 : nsPoint pointInHyperText(coordsInAppUnits.x - frameScreenRect.x,
1203 0 : coordsInAppUnits.y - frameScreenRect.y);
1204 :
1205 : // Go through the frames to check if each one has the point.
1206 : // When one does, add up the character offsets until we have a match
1207 :
1208 : // We have an point in an accessible child of this, now we need to add up the
1209 : // offsets before it to what we already have
1210 0 : int32_t offset = 0;
1211 0 : uint32_t childCount = ChildCount();
1212 0 : for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
1213 0 : Accessible* childAcc = mChildren[childIdx];
1214 :
1215 0 : nsIFrame *primaryFrame = childAcc->GetFrame();
1216 0 : NS_ENSURE_TRUE(primaryFrame, -1);
1217 :
1218 0 : nsIFrame *frame = primaryFrame;
1219 0 : while (frame) {
1220 0 : nsIContent *content = frame->GetContent();
1221 0 : NS_ENSURE_TRUE(content, -1);
1222 0 : nsPoint pointInFrame = pointInHyperText - frame->GetOffsetTo(hyperFrame);
1223 0 : nsSize frameSize = frame->GetSize();
1224 0 : if (pointInFrame.x < frameSize.width && pointInFrame.y < frameSize.height) {
1225 : // Finished
1226 0 : if (frame->IsTextFrame()) {
1227 : nsIFrame::ContentOffsets contentOffsets =
1228 0 : frame->GetContentOffsetsFromPointExternal(pointInFrame, nsIFrame::IGNORE_SELECTION_STYLE);
1229 0 : if (contentOffsets.IsNull() || contentOffsets.content != content) {
1230 0 : return -1; // Not found
1231 : }
1232 : uint32_t addToOffset;
1233 0 : nsresult rv = ContentToRenderedOffset(primaryFrame,
1234 : contentOffsets.offset,
1235 0 : &addToOffset);
1236 0 : NS_ENSURE_SUCCESS(rv, -1);
1237 0 : offset += addToOffset;
1238 : }
1239 0 : return offset;
1240 : }
1241 0 : frame = frame->GetNextContinuation();
1242 : }
1243 :
1244 0 : offset += nsAccUtils::TextLength(childAcc);
1245 : }
1246 :
1247 0 : return -1; // Not found
1248 : }
1249 :
1250 : nsIntRect
1251 0 : HyperTextAccessible::TextBounds(int32_t aStartOffset, int32_t aEndOffset,
1252 : uint32_t aCoordType)
1253 : {
1254 0 : index_t startOffset = ConvertMagicOffset(aStartOffset);
1255 0 : index_t endOffset = ConvertMagicOffset(aEndOffset);
1256 0 : if (!startOffset.IsValid() || !endOffset.IsValid() ||
1257 0 : startOffset > endOffset || endOffset > CharacterCount()) {
1258 0 : NS_ERROR("Wrong in offset");
1259 0 : return nsIntRect();
1260 : }
1261 :
1262 :
1263 0 : int32_t childIdx = GetChildIndexAtOffset(startOffset);
1264 0 : if (childIdx == -1)
1265 0 : return nsIntRect();
1266 :
1267 0 : nsIntRect bounds;
1268 0 : int32_t prevOffset = GetChildOffset(childIdx);
1269 0 : int32_t offset1 = startOffset - prevOffset;
1270 :
1271 0 : while (childIdx < static_cast<int32_t>(ChildCount())) {
1272 0 : nsIFrame* frame = GetChildAt(childIdx++)->GetFrame();
1273 0 : if (!frame) {
1274 0 : NS_NOTREACHED("No frame for a child!");
1275 0 : continue;
1276 : }
1277 :
1278 0 : int32_t nextOffset = GetChildOffset(childIdx);
1279 0 : if (nextOffset >= static_cast<int32_t>(endOffset)) {
1280 0 : bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
1281 0 : endOffset - prevOffset));
1282 0 : break;
1283 : }
1284 :
1285 0 : bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
1286 0 : nextOffset - prevOffset));
1287 :
1288 0 : prevOffset = nextOffset;
1289 0 : offset1 = 0;
1290 : }
1291 :
1292 0 : nsAccUtils::ConvertScreenCoordsTo(&bounds.x, &bounds.y, aCoordType, this);
1293 0 : return bounds;
1294 : }
1295 :
1296 : already_AddRefed<nsIEditor>
1297 0 : HyperTextAccessible::GetEditor() const
1298 : {
1299 0 : if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
1300 : // If we're inside an editable container, then return that container's editor
1301 0 : Accessible* ancestor = Parent();
1302 0 : while (ancestor) {
1303 0 : HyperTextAccessible* hyperText = ancestor->AsHyperText();
1304 0 : if (hyperText) {
1305 : // Recursion will stop at container doc because it has its own impl
1306 : // of GetEditor()
1307 0 : return hyperText->GetEditor();
1308 : }
1309 :
1310 0 : ancestor = ancestor->Parent();
1311 : }
1312 :
1313 0 : return nullptr;
1314 : }
1315 :
1316 0 : nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mContent);
1317 0 : nsCOMPtr<nsIEditingSession> editingSession;
1318 0 : docShell->GetEditingSession(getter_AddRefs(editingSession));
1319 0 : if (!editingSession)
1320 0 : return nullptr; // No editing session interface
1321 :
1322 0 : nsCOMPtr<nsIEditor> editor;
1323 0 : nsIDocument* docNode = mDoc->DocumentNode();
1324 0 : editingSession->GetEditorForWindow(docNode->GetWindow(),
1325 0 : getter_AddRefs(editor));
1326 0 : return editor.forget();
1327 : }
1328 :
1329 : /**
1330 : * =================== Caret & Selection ======================
1331 : */
1332 :
1333 : nsresult
1334 0 : HyperTextAccessible::SetSelectionRange(int32_t aStartPos, int32_t aEndPos)
1335 : {
1336 : // Before setting the selection range, we need to ensure that the editor
1337 : // is initialized. (See bug 804927.)
1338 : // Otherwise, it's possible that lazy editor initialization will override
1339 : // the selection we set here and leave the caret at the end of the text.
1340 : // By calling GetEditor here, we ensure that editor initialization is
1341 : // completed before we set the selection.
1342 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
1343 :
1344 0 : bool isFocusable = InteractiveState() & states::FOCUSABLE;
1345 :
1346 : // If accessible is focusable then focus it before setting the selection to
1347 : // neglect control's selection changes on focus if any (for example, inputs
1348 : // that do select all on focus).
1349 : // some input controls
1350 0 : if (isFocusable)
1351 0 : TakeFocus();
1352 :
1353 0 : dom::Selection* domSel = DOMSelection();
1354 0 : NS_ENSURE_STATE(domSel);
1355 :
1356 : // Set up the selection.
1357 0 : for (int32_t idx = domSel->RangeCount() - 1; idx > 0; idx--)
1358 0 : domSel->RemoveRange(domSel->GetRangeAt(idx));
1359 0 : SetSelectionBoundsAt(0, aStartPos, aEndPos);
1360 :
1361 : // When selection is done, move the focus to the selection if accessible is
1362 : // not focusable. That happens when selection is set within hypertext
1363 : // accessible.
1364 0 : if (isFocusable)
1365 0 : return NS_OK;
1366 :
1367 0 : nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
1368 0 : if (DOMFocusManager) {
1369 0 : NS_ENSURE_TRUE(mDoc, NS_ERROR_FAILURE);
1370 0 : nsIDocument* docNode = mDoc->DocumentNode();
1371 0 : NS_ENSURE_TRUE(docNode, NS_ERROR_FAILURE);
1372 0 : nsCOMPtr<nsPIDOMWindowOuter> window = docNode->GetWindow();
1373 0 : nsCOMPtr<nsIDOMElement> result;
1374 0 : DOMFocusManager->MoveFocus(window, nullptr, nsIFocusManager::MOVEFOCUS_CARET,
1375 0 : nsIFocusManager::FLAG_BYMOVEFOCUS, getter_AddRefs(result));
1376 : }
1377 :
1378 0 : return NS_OK;
1379 : }
1380 :
1381 : int32_t
1382 0 : HyperTextAccessible::CaretOffset() const
1383 : {
1384 : // Not focused focusable accessible except document accessible doesn't have
1385 : // a caret.
1386 0 : if (!IsDoc() && !FocusMgr()->IsFocused(this) &&
1387 0 : (InteractiveState() & states::FOCUSABLE)) {
1388 0 : return -1;
1389 : }
1390 :
1391 : // Check cached value.
1392 0 : int32_t caretOffset = -1;
1393 0 : HyperTextAccessible* text = SelectionMgr()->AccessibleWithCaret(&caretOffset);
1394 :
1395 : // Use cached value if it corresponds to this accessible.
1396 0 : if (caretOffset != -1) {
1397 0 : if (text == this)
1398 0 : return caretOffset;
1399 :
1400 0 : nsINode* textNode = text->GetNode();
1401 : // Ignore offset if cached accessible isn't a text leaf.
1402 0 : if (nsCoreUtils::IsAncestorOf(GetNode(), textNode))
1403 0 : return TransformOffset(text,
1404 0 : textNode->IsNodeOfType(nsINode::eTEXT) ? caretOffset : 0, false);
1405 : }
1406 :
1407 : // No caret if the focused node is not inside this DOM node and this DOM node
1408 : // is not inside of focused node.
1409 : FocusManager::FocusDisposition focusDisp =
1410 0 : FocusMgr()->IsInOrContainsFocus(this);
1411 0 : if (focusDisp == FocusManager::eNone)
1412 0 : return -1;
1413 :
1414 : // Turn the focus node and offset of the selection into caret hypretext
1415 : // offset.
1416 0 : dom::Selection* domSel = DOMSelection();
1417 0 : NS_ENSURE_TRUE(domSel, -1);
1418 :
1419 0 : nsINode* focusNode = domSel->GetFocusNode();
1420 0 : uint32_t focusOffset = domSel->FocusOffset();
1421 :
1422 : // No caret if this DOM node is inside of focused node but the selection's
1423 : // focus point is not inside of this DOM node.
1424 0 : if (focusDisp == FocusManager::eContainedByFocus) {
1425 : nsINode* resultNode =
1426 0 : nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
1427 :
1428 0 : nsINode* thisNode = GetNode();
1429 0 : if (resultNode != thisNode &&
1430 0 : !nsCoreUtils::IsAncestorOf(thisNode, resultNode))
1431 0 : return -1;
1432 : }
1433 :
1434 0 : return DOMPointToOffset(focusNode, focusOffset);
1435 : }
1436 :
1437 : int32_t
1438 0 : HyperTextAccessible::CaretLineNumber()
1439 : {
1440 : // Provide the line number for the caret, relative to the
1441 : // currently focused node. Use a 1-based index
1442 0 : RefPtr<nsFrameSelection> frameSelection = FrameSelection();
1443 0 : if (!frameSelection)
1444 0 : return -1;
1445 :
1446 0 : dom::Selection* domSel = frameSelection->GetSelection(SelectionType::eNormal);
1447 0 : if (!domSel)
1448 0 : return - 1;
1449 :
1450 0 : nsINode* caretNode = domSel->GetFocusNode();
1451 0 : if (!caretNode || !caretNode->IsContent())
1452 0 : return -1;
1453 :
1454 0 : nsIContent* caretContent = caretNode->AsContent();
1455 0 : if (!nsCoreUtils::IsAncestorOf(GetNode(), caretContent))
1456 0 : return -1;
1457 :
1458 : int32_t returnOffsetUnused;
1459 0 : uint32_t caretOffset = domSel->FocusOffset();
1460 0 : CaretAssociationHint hint = frameSelection->GetHint();
1461 0 : nsIFrame *caretFrame = frameSelection->GetFrameForNodeOffset(caretContent, caretOffset,
1462 0 : hint, &returnOffsetUnused);
1463 0 : NS_ENSURE_TRUE(caretFrame, -1);
1464 :
1465 0 : int32_t lineNumber = 1;
1466 0 : nsAutoLineIterator lineIterForCaret;
1467 0 : nsIContent *hyperTextContent = IsContent() ? mContent.get() : nullptr;
1468 0 : while (caretFrame) {
1469 0 : if (hyperTextContent == caretFrame->GetContent()) {
1470 0 : return lineNumber; // Must be in a single line hyper text, there is no line iterator
1471 : }
1472 0 : nsContainerFrame *parentFrame = caretFrame->GetParent();
1473 0 : if (!parentFrame)
1474 0 : break;
1475 :
1476 : // Add lines for the sibling frames before the caret
1477 0 : nsIFrame *sibling = parentFrame->PrincipalChildList().FirstChild();
1478 0 : while (sibling && sibling != caretFrame) {
1479 0 : nsAutoLineIterator lineIterForSibling = sibling->GetLineIterator();
1480 0 : if (lineIterForSibling) {
1481 : // For the frames before that grab all the lines
1482 0 : int32_t addLines = lineIterForSibling->GetNumLines();
1483 0 : lineNumber += addLines;
1484 : }
1485 0 : sibling = sibling->GetNextSibling();
1486 : }
1487 :
1488 : // Get the line number relative to the container with lines
1489 0 : if (!lineIterForCaret) { // Add the caret line just once
1490 0 : lineIterForCaret = parentFrame->GetLineIterator();
1491 0 : if (lineIterForCaret) {
1492 : // Ancestor of caret
1493 0 : int32_t addLines = lineIterForCaret->FindLineContaining(caretFrame);
1494 0 : lineNumber += addLines;
1495 : }
1496 : }
1497 :
1498 0 : caretFrame = parentFrame;
1499 : }
1500 :
1501 0 : NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
1502 0 : return lineNumber;
1503 : }
1504 :
1505 : LayoutDeviceIntRect
1506 0 : HyperTextAccessible::GetCaretRect(nsIWidget** aWidget)
1507 : {
1508 0 : *aWidget = nullptr;
1509 :
1510 0 : RefPtr<nsCaret> caret = mDoc->PresShell()->GetCaret();
1511 0 : NS_ENSURE_TRUE(caret, LayoutDeviceIntRect());
1512 :
1513 0 : bool isVisible = caret->IsVisible();
1514 0 : if (!isVisible)
1515 0 : return LayoutDeviceIntRect();
1516 :
1517 0 : nsRect rect;
1518 0 : nsIFrame* frame = caret->GetGeometry(&rect);
1519 0 : if (!frame || rect.IsEmpty())
1520 0 : return LayoutDeviceIntRect();
1521 :
1522 0 : nsPoint offset;
1523 : // Offset from widget origin to the frame origin, which includes chrome
1524 : // on the widget.
1525 0 : *aWidget = frame->GetNearestWidget(offset);
1526 0 : NS_ENSURE_TRUE(*aWidget, LayoutDeviceIntRect());
1527 0 : rect.MoveBy(offset);
1528 :
1529 : LayoutDeviceIntRect caretRect = LayoutDeviceIntRect::FromUnknownRect(
1530 0 : rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel()));
1531 : // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
1532 0 : caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
1533 :
1534 : // Correct for character size, so that caret always matches the size of
1535 : // the character. This is important for font size transitions, and is
1536 : // necessary because the Gecko caret uses the previous character's size as
1537 : // the user moves forward in the text by character.
1538 : nsIntRect charRect = CharBounds(CaretOffset(),
1539 0 : nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
1540 0 : if (!charRect.IsEmpty()) {
1541 0 : caretRect.height -= charRect.y - caretRect.y;
1542 0 : caretRect.y = charRect.y;
1543 : }
1544 0 : return caretRect;
1545 : }
1546 :
1547 : void
1548 0 : HyperTextAccessible::GetSelectionDOMRanges(SelectionType aSelectionType,
1549 : nsTArray<nsRange*>* aRanges)
1550 : {
1551 : // Ignore selection if it is not visible.
1552 0 : RefPtr<nsFrameSelection> frameSelection = FrameSelection();
1553 0 : if (!frameSelection ||
1554 0 : frameSelection->GetDisplaySelection() <= nsISelectionController::SELECTION_HIDDEN)
1555 0 : return;
1556 :
1557 0 : dom::Selection* domSel = frameSelection->GetSelection(aSelectionType);
1558 0 : if (!domSel)
1559 0 : return;
1560 :
1561 0 : nsCOMPtr<nsINode> startNode = GetNode();
1562 :
1563 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
1564 0 : if (editor) {
1565 0 : nsCOMPtr<nsIDOMElement> editorRoot;
1566 0 : editor->GetRootElement(getter_AddRefs(editorRoot));
1567 0 : startNode = do_QueryInterface(editorRoot);
1568 : }
1569 :
1570 0 : if (!startNode)
1571 0 : return;
1572 :
1573 0 : uint32_t childCount = startNode->GetChildCount();
1574 : nsresult rv = domSel->
1575 0 : GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
1576 0 : NS_ENSURE_SUCCESS_VOID(rv);
1577 :
1578 : // Remove collapsed ranges
1579 0 : uint32_t numRanges = aRanges->Length();
1580 0 : for (uint32_t idx = 0; idx < numRanges; idx ++) {
1581 0 : if ((*aRanges)[idx]->Collapsed()) {
1582 0 : aRanges->RemoveElementAt(idx);
1583 0 : --numRanges;
1584 0 : --idx;
1585 : }
1586 : }
1587 : }
1588 :
1589 : int32_t
1590 0 : HyperTextAccessible::SelectionCount()
1591 : {
1592 0 : nsTArray<nsRange*> ranges;
1593 0 : GetSelectionDOMRanges(SelectionType::eNormal, &ranges);
1594 0 : return ranges.Length();
1595 : }
1596 :
1597 : bool
1598 0 : HyperTextAccessible::SelectionBoundsAt(int32_t aSelectionNum,
1599 : int32_t* aStartOffset,
1600 : int32_t* aEndOffset)
1601 : {
1602 0 : *aStartOffset = *aEndOffset = 0;
1603 :
1604 0 : nsTArray<nsRange*> ranges;
1605 0 : GetSelectionDOMRanges(SelectionType::eNormal, &ranges);
1606 :
1607 0 : uint32_t rangeCount = ranges.Length();
1608 0 : if (aSelectionNum < 0 || aSelectionNum >= static_cast<int32_t>(rangeCount))
1609 0 : return false;
1610 :
1611 0 : nsRange* range = ranges[aSelectionNum];
1612 :
1613 : // Get start and end points.
1614 0 : nsINode* startNode = range->GetStartContainer();
1615 0 : nsINode* endNode = range->GetEndContainer();
1616 0 : int32_t startOffset = range->StartOffset(), endOffset = range->EndOffset();
1617 :
1618 : // Make sure start is before end, by swapping DOM points. This occurs when
1619 : // the user selects backwards in the text.
1620 : int32_t rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
1621 0 : startNode, startOffset);
1622 0 : if (rangeCompare < 0) {
1623 0 : nsINode* tempNode = startNode;
1624 0 : startNode = endNode;
1625 0 : endNode = tempNode;
1626 0 : int32_t tempOffset = startOffset;
1627 0 : startOffset = endOffset;
1628 0 : endOffset = tempOffset;
1629 : }
1630 :
1631 0 : if (!nsContentUtils::ContentIsDescendantOf(startNode, mContent))
1632 0 : *aStartOffset = 0;
1633 : else
1634 0 : *aStartOffset = DOMPointToOffset(startNode, startOffset);
1635 :
1636 0 : if (!nsContentUtils::ContentIsDescendantOf(endNode, mContent))
1637 0 : *aEndOffset = CharacterCount();
1638 : else
1639 0 : *aEndOffset = DOMPointToOffset(endNode, endOffset, true);
1640 0 : return true;
1641 : }
1642 :
1643 : bool
1644 0 : HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
1645 : int32_t aStartOffset,
1646 : int32_t aEndOffset)
1647 : {
1648 0 : index_t startOffset = ConvertMagicOffset(aStartOffset);
1649 0 : index_t endOffset = ConvertMagicOffset(aEndOffset);
1650 0 : if (!startOffset.IsValid() || !endOffset.IsValid() ||
1651 0 : startOffset > endOffset || endOffset > CharacterCount()) {
1652 0 : NS_ERROR("Wrong in offset");
1653 0 : return false;
1654 : }
1655 :
1656 0 : dom::Selection* domSel = DOMSelection();
1657 0 : if (!domSel)
1658 0 : return false;
1659 :
1660 0 : RefPtr<nsRange> range;
1661 0 : uint32_t rangeCount = domSel->RangeCount();
1662 0 : if (aSelectionNum == static_cast<int32_t>(rangeCount))
1663 0 : range = new nsRange(mContent);
1664 : else
1665 0 : range = domSel->GetRangeAt(aSelectionNum);
1666 :
1667 0 : if (!range)
1668 0 : return false;
1669 :
1670 0 : if (!OffsetsToDOMRange(startOffset, endOffset, range))
1671 0 : return false;
1672 :
1673 : // If new range was created then add it, otherwise notify selection listeners
1674 : // that existing selection range was changed.
1675 0 : if (aSelectionNum == static_cast<int32_t>(rangeCount))
1676 0 : return NS_SUCCEEDED(domSel->AddRange(range));
1677 :
1678 0 : domSel->RemoveRange(range);
1679 0 : return NS_SUCCEEDED(domSel->AddRange(range));
1680 : }
1681 :
1682 : bool
1683 0 : HyperTextAccessible::RemoveFromSelection(int32_t aSelectionNum)
1684 : {
1685 0 : dom::Selection* domSel = DOMSelection();
1686 0 : if (!domSel)
1687 0 : return false;
1688 :
1689 0 : if (aSelectionNum < 0 || aSelectionNum >= static_cast<int32_t>(domSel->RangeCount()))
1690 0 : return false;
1691 :
1692 0 : domSel->RemoveRange(domSel->GetRangeAt(aSelectionNum));
1693 0 : return true;
1694 : }
1695 :
1696 : void
1697 0 : HyperTextAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
1698 : uint32_t aScrollType)
1699 : {
1700 0 : RefPtr<nsRange> range = new nsRange(mContent);
1701 0 : if (OffsetsToDOMRange(aStartOffset, aEndOffset, range))
1702 0 : nsCoreUtils::ScrollSubstringTo(GetFrame(), range, aScrollType);
1703 0 : }
1704 :
1705 : void
1706 0 : HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
1707 : int32_t aEndOffset,
1708 : uint32_t aCoordinateType,
1709 : int32_t aX, int32_t aY)
1710 : {
1711 0 : nsIFrame *frame = GetFrame();
1712 0 : if (!frame)
1713 0 : return;
1714 :
1715 : nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
1716 0 : this);
1717 :
1718 0 : RefPtr<nsRange> range = new nsRange(mContent);
1719 0 : if (!OffsetsToDOMRange(aStartOffset, aEndOffset, range))
1720 0 : return;
1721 :
1722 0 : nsPresContext* presContext = frame->PresContext();
1723 : nsPoint coordsInAppUnits =
1724 0 : ToAppUnits(coords, presContext->AppUnitsPerDevPixel());
1725 :
1726 0 : bool initialScrolled = false;
1727 0 : nsIFrame *parentFrame = frame;
1728 0 : while ((parentFrame = parentFrame->GetParent())) {
1729 0 : nsIScrollableFrame *scrollableFrame = do_QueryFrame(parentFrame);
1730 0 : if (scrollableFrame) {
1731 0 : if (!initialScrolled) {
1732 : // Scroll substring to the given point. Turn the point into percents
1733 : // relative scrollable area to use nsCoreUtils::ScrollSubstringTo.
1734 0 : nsRect frameRect = parentFrame->GetScreenRectInAppUnits();
1735 0 : nscoord offsetPointX = coordsInAppUnits.x - frameRect.x;
1736 0 : nscoord offsetPointY = coordsInAppUnits.y - frameRect.y;
1737 :
1738 0 : nsSize size(parentFrame->GetSize());
1739 :
1740 : // avoid divide by zero
1741 0 : size.width = size.width ? size.width : 1;
1742 0 : size.height = size.height ? size.height : 1;
1743 :
1744 0 : int16_t hPercent = offsetPointX * 100 / size.width;
1745 0 : int16_t vPercent = offsetPointY * 100 / size.height;
1746 :
1747 0 : nsresult rv = nsCoreUtils::ScrollSubstringTo(frame, range,
1748 : nsIPresShell::ScrollAxis(vPercent),
1749 0 : nsIPresShell::ScrollAxis(hPercent));
1750 0 : if (NS_FAILED(rv))
1751 0 : return;
1752 :
1753 0 : initialScrolled = true;
1754 : } else {
1755 : // Substring was scrolled to the given point already inside its closest
1756 : // scrollable area. If there are nested scrollable areas then make
1757 : // sure we scroll lower areas to the given point inside currently
1758 : // traversed scrollable area.
1759 0 : nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
1760 : }
1761 : }
1762 0 : frame = parentFrame;
1763 : }
1764 : }
1765 :
1766 : void
1767 0 : HyperTextAccessible::EnclosingRange(a11y::TextRange& aRange) const
1768 : {
1769 0 : if (IsTextField()) {
1770 0 : aRange.Set(mDoc, const_cast<HyperTextAccessible*>(this), 0,
1771 0 : const_cast<HyperTextAccessible*>(this), CharacterCount());
1772 : } else {
1773 0 : aRange.Set(mDoc, mDoc, 0, mDoc, mDoc->CharacterCount());
1774 : }
1775 0 : }
1776 :
1777 : void
1778 0 : HyperTextAccessible::SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const
1779 : {
1780 0 : MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty");
1781 :
1782 0 : dom::Selection* sel = DOMSelection();
1783 0 : if (!sel)
1784 0 : return;
1785 :
1786 0 : aRanges->SetCapacity(sel->RangeCount());
1787 :
1788 0 : for (uint32_t idx = 0; idx < sel->RangeCount(); idx++) {
1789 0 : nsRange* DOMRange = sel->GetRangeAt(idx);
1790 : HyperTextAccessible* startContainer =
1791 0 : nsAccUtils::GetTextContainer(DOMRange->GetStartContainer());
1792 : HyperTextAccessible* endContainer =
1793 0 : nsAccUtils::GetTextContainer(DOMRange->GetEndContainer());
1794 0 : if (!startContainer || !endContainer) {
1795 0 : continue;
1796 : }
1797 :
1798 : int32_t startOffset =
1799 0 : startContainer->DOMPointToOffset(DOMRange->GetStartContainer(),
1800 0 : DOMRange->StartOffset(), false);
1801 : int32_t endOffset =
1802 0 : endContainer->DOMPointToOffset(DOMRange->GetEndContainer(),
1803 0 : DOMRange->EndOffset(), true);
1804 :
1805 0 : TextRange tr(IsTextField() ? const_cast<HyperTextAccessible*>(this) : mDoc,
1806 0 : startContainer, startOffset, endContainer, endOffset);
1807 0 : *(aRanges->AppendElement()) = Move(tr);
1808 : }
1809 : }
1810 :
1811 : void
1812 0 : HyperTextAccessible::VisibleRanges(nsTArray<a11y::TextRange>* aRanges) const
1813 : {
1814 0 : }
1815 :
1816 : void
1817 0 : HyperTextAccessible::RangeByChild(Accessible* aChild,
1818 : a11y::TextRange& aRange) const
1819 : {
1820 0 : HyperTextAccessible* ht = aChild->AsHyperText();
1821 0 : if (ht) {
1822 0 : aRange.Set(mDoc, ht, 0, ht, ht->CharacterCount());
1823 0 : return;
1824 : }
1825 :
1826 0 : Accessible* child = aChild;
1827 0 : Accessible* parent = nullptr;
1828 0 : while ((parent = child->Parent()) && !(ht = parent->AsHyperText()))
1829 0 : child = parent;
1830 :
1831 : // If no text then return collapsed text range, otherwise return a range
1832 : // containing the text enclosed by the given child.
1833 0 : if (ht) {
1834 0 : int32_t childIdx = child->IndexInParent();
1835 0 : int32_t startOffset = ht->GetChildOffset(childIdx);
1836 0 : int32_t endOffset = child->IsTextLeaf() ?
1837 0 : ht->GetChildOffset(childIdx + 1) : startOffset;
1838 0 : aRange.Set(mDoc, ht, startOffset, ht, endOffset);
1839 : }
1840 : }
1841 :
1842 : void
1843 0 : HyperTextAccessible::RangeAtPoint(int32_t aX, int32_t aY,
1844 : a11y::TextRange& aRange) const
1845 : {
1846 0 : Accessible* child = mDoc->ChildAtPoint(aX, aY, eDeepestChild);
1847 0 : if (!child)
1848 0 : return;
1849 :
1850 0 : Accessible* parent = nullptr;
1851 0 : while ((parent = child->Parent()) && !parent->IsHyperText())
1852 0 : child = parent;
1853 :
1854 : // Return collapsed text range for the point.
1855 0 : if (parent) {
1856 0 : HyperTextAccessible* ht = parent->AsHyperText();
1857 0 : int32_t offset = ht->GetChildOffset(child);
1858 0 : aRange.Set(mDoc, ht, offset, ht, offset);
1859 : }
1860 : }
1861 :
1862 : ////////////////////////////////////////////////////////////////////////////////
1863 : // Accessible public
1864 :
1865 : // Accessible protected
1866 : ENameValueFlag
1867 0 : HyperTextAccessible::NativeName(nsString& aName)
1868 : {
1869 : // Check @alt attribute for invalid img elements.
1870 0 : bool hasImgAlt = false;
1871 0 : if (mContent->IsHTMLElement(nsGkAtoms::img)) {
1872 0 : hasImgAlt = mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName);
1873 0 : if (!aName.IsEmpty())
1874 0 : return eNameOK;
1875 : }
1876 :
1877 0 : ENameValueFlag nameFlag = AccessibleWrap::NativeName(aName);
1878 0 : if (!aName.IsEmpty())
1879 0 : return nameFlag;
1880 :
1881 : // Get name from title attribute for HTML abbr and acronym elements making it
1882 : // a valid name from markup. Otherwise their name isn't picked up by recursive
1883 : // name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP.
1884 0 : if (IsAbbreviation() &&
1885 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName))
1886 0 : aName.CompressWhitespace();
1887 :
1888 0 : return hasImgAlt ? eNoNameOnPurpose : eNameOK;
1889 : }
1890 :
1891 : void
1892 0 : HyperTextAccessible::Shutdown()
1893 : {
1894 0 : mOffsets.Clear();
1895 0 : AccessibleWrap::Shutdown();
1896 0 : }
1897 :
1898 : bool
1899 0 : HyperTextAccessible::RemoveChild(Accessible* aAccessible)
1900 : {
1901 0 : int32_t childIndex = aAccessible->IndexInParent();
1902 0 : int32_t count = mOffsets.Length() - childIndex;
1903 0 : if (count > 0)
1904 0 : mOffsets.RemoveElementsAt(childIndex, count);
1905 :
1906 0 : return AccessibleWrap::RemoveChild(aAccessible);
1907 : }
1908 :
1909 : bool
1910 0 : HyperTextAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
1911 : {
1912 0 : int32_t count = mOffsets.Length() - aIndex;
1913 0 : if (count > 0 ) {
1914 0 : mOffsets.RemoveElementsAt(aIndex, count);
1915 : }
1916 0 : return AccessibleWrap::InsertChildAt(aIndex, aChild);
1917 : }
1918 :
1919 : Relation
1920 0 : HyperTextAccessible::RelationByType(RelationType aType)
1921 : {
1922 0 : Relation rel = Accessible::RelationByType(aType);
1923 :
1924 0 : switch (aType) {
1925 : case RelationType::NODE_CHILD_OF:
1926 0 : if (HasOwnContent() && mContent->IsMathMLElement()) {
1927 0 : Accessible* parent = Parent();
1928 0 : if (parent) {
1929 0 : nsIContent* parentContent = parent->GetContent();
1930 0 : if (parentContent &&
1931 0 : parentContent->IsMathMLElement(nsGkAtoms::mroot_)) {
1932 : // Add a relation pointing to the parent <mroot>.
1933 0 : rel.AppendTarget(parent);
1934 : }
1935 : }
1936 : }
1937 0 : break;
1938 : case RelationType::NODE_PARENT_OF:
1939 0 : if (HasOwnContent() && mContent->IsMathMLElement(nsGkAtoms::mroot_)) {
1940 0 : Accessible* base = GetChildAt(0);
1941 0 : Accessible* index = GetChildAt(1);
1942 0 : if (base && index) {
1943 : // Append the <mroot> children in the order index, base.
1944 0 : rel.AppendTarget(index);
1945 0 : rel.AppendTarget(base);
1946 : }
1947 : }
1948 0 : break;
1949 : default:
1950 0 : break;
1951 : }
1952 :
1953 0 : return rel;
1954 : }
1955 :
1956 : ////////////////////////////////////////////////////////////////////////////////
1957 : // HyperTextAccessible public static
1958 :
1959 : nsresult
1960 0 : HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentOffset,
1961 : uint32_t* aRenderedOffset) const
1962 : {
1963 0 : if (!aFrame) {
1964 : // Current frame not rendered -- this can happen if text is set on
1965 : // something with display: none
1966 0 : *aRenderedOffset = 0;
1967 0 : return NS_OK;
1968 : }
1969 :
1970 0 : if (IsTextField()) {
1971 0 : *aRenderedOffset = aContentOffset;
1972 0 : return NS_OK;
1973 : }
1974 :
1975 0 : NS_ASSERTION(aFrame->IsTextFrame(), "Need text frame for offset conversion");
1976 0 : NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
1977 : "Call on primary frame only");
1978 :
1979 : nsIFrame::RenderedText text = aFrame->GetRenderedText(aContentOffset,
1980 0 : aContentOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
1981 0 : nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
1982 0 : *aRenderedOffset = text.mOffsetWithinNodeRenderedText;
1983 :
1984 0 : return NS_OK;
1985 : }
1986 :
1987 : nsresult
1988 0 : HyperTextAccessible::RenderedToContentOffset(nsIFrame* aFrame, uint32_t aRenderedOffset,
1989 : int32_t* aContentOffset) const
1990 : {
1991 0 : if (IsTextField()) {
1992 0 : *aContentOffset = aRenderedOffset;
1993 0 : return NS_OK;
1994 : }
1995 :
1996 0 : *aContentOffset = 0;
1997 0 : NS_ENSURE_TRUE(aFrame, NS_ERROR_FAILURE);
1998 :
1999 0 : NS_ASSERTION(aFrame->IsTextFrame(), "Need text frame for offset conversion");
2000 0 : NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
2001 : "Call on primary frame only");
2002 :
2003 : nsIFrame::RenderedText text = aFrame->GetRenderedText(aRenderedOffset,
2004 : aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT,
2005 0 : nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
2006 0 : *aContentOffset = text.mOffsetWithinNodeText;
2007 :
2008 0 : return NS_OK;
2009 : }
2010 :
2011 : ////////////////////////////////////////////////////////////////////////////////
2012 : // HyperTextAccessible public
2013 :
2014 : int32_t
2015 0 : HyperTextAccessible::GetChildOffset(uint32_t aChildIndex,
2016 : bool aInvalidateAfter) const
2017 : {
2018 0 : if (aChildIndex == 0) {
2019 0 : if (aInvalidateAfter)
2020 0 : mOffsets.Clear();
2021 :
2022 0 : return aChildIndex;
2023 : }
2024 :
2025 0 : int32_t count = mOffsets.Length() - aChildIndex;
2026 0 : if (count > 0) {
2027 0 : if (aInvalidateAfter)
2028 0 : mOffsets.RemoveElementsAt(aChildIndex, count);
2029 :
2030 0 : return mOffsets[aChildIndex - 1];
2031 : }
2032 :
2033 0 : uint32_t lastOffset = mOffsets.IsEmpty() ?
2034 0 : 0 : mOffsets[mOffsets.Length() - 1];
2035 :
2036 0 : while (mOffsets.Length() < aChildIndex) {
2037 0 : Accessible* child = mChildren[mOffsets.Length()];
2038 0 : lastOffset += nsAccUtils::TextLength(child);
2039 0 : mOffsets.AppendElement(lastOffset);
2040 : }
2041 :
2042 0 : return mOffsets[aChildIndex - 1];
2043 : }
2044 :
2045 : int32_t
2046 0 : HyperTextAccessible::GetChildIndexAtOffset(uint32_t aOffset) const
2047 : {
2048 0 : uint32_t lastOffset = 0;
2049 0 : const uint32_t offsetCount = mOffsets.Length();
2050 :
2051 0 : if (offsetCount > 0) {
2052 0 : lastOffset = mOffsets[offsetCount - 1];
2053 0 : if (aOffset < lastOffset) {
2054 : size_t index;
2055 0 : if (BinarySearch(mOffsets, 0, offsetCount, aOffset, &index)) {
2056 0 : return (index < (offsetCount - 1)) ? index + 1 : index;
2057 : }
2058 :
2059 0 : return (index == offsetCount) ? -1 : index;
2060 : }
2061 : }
2062 :
2063 0 : uint32_t childCount = ChildCount();
2064 0 : while (mOffsets.Length() < childCount) {
2065 0 : Accessible* child = GetChildAt(mOffsets.Length());
2066 0 : lastOffset += nsAccUtils::TextLength(child);
2067 0 : mOffsets.AppendElement(lastOffset);
2068 0 : if (aOffset < lastOffset)
2069 0 : return mOffsets.Length() - 1;
2070 : }
2071 :
2072 0 : if (aOffset == lastOffset)
2073 0 : return mOffsets.Length() - 1;
2074 :
2075 0 : return -1;
2076 : }
2077 :
2078 : ////////////////////////////////////////////////////////////////////////////////
2079 : // HyperTextAccessible protected
2080 :
2081 : nsresult
2082 0 : HyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame* aFrame, int32_t aOffset,
2083 : Accessible* aAccessible,
2084 : DOMPoint* aPoint)
2085 : {
2086 0 : NS_ENSURE_ARG(aAccessible);
2087 :
2088 0 : if (!aFrame) {
2089 : // If the given frame is null then set offset after the DOM node of the
2090 : // given accessible.
2091 0 : NS_ASSERTION(!aAccessible->IsDoc(),
2092 : "Shouldn't be called on document accessible!");
2093 :
2094 0 : nsIContent* content = aAccessible->GetContent();
2095 0 : NS_ASSERTION(content, "Shouldn't operate on defunct accessible!");
2096 :
2097 0 : nsIContent* parent = content->GetParent();
2098 :
2099 0 : aPoint->idx = parent->IndexOf(content) + 1;
2100 0 : aPoint->node = parent;
2101 :
2102 0 : } else if (aFrame->IsTextFrame()) {
2103 0 : nsIContent* content = aFrame->GetContent();
2104 0 : NS_ENSURE_STATE(content);
2105 :
2106 0 : nsIFrame *primaryFrame = content->GetPrimaryFrame();
2107 0 : nsresult rv = RenderedToContentOffset(primaryFrame, aOffset, &(aPoint->idx));
2108 0 : NS_ENSURE_SUCCESS(rv, rv);
2109 :
2110 0 : aPoint->node = content;
2111 :
2112 : } else {
2113 0 : nsIContent* content = aFrame->GetContent();
2114 0 : NS_ENSURE_STATE(content);
2115 :
2116 0 : nsIContent* parent = content->GetParent();
2117 0 : NS_ENSURE_STATE(parent);
2118 :
2119 0 : aPoint->idx = parent->IndexOf(content);
2120 0 : aPoint->node = parent;
2121 : }
2122 :
2123 0 : return NS_OK;
2124 : }
2125 :
2126 : // HyperTextAccessible
2127 : void
2128 0 : HyperTextAccessible::GetSpellTextAttr(nsINode* aNode,
2129 : int32_t aNodeOffset,
2130 : uint32_t* aStartOffset,
2131 : uint32_t* aEndOffset,
2132 : nsIPersistentProperties* aAttributes)
2133 : {
2134 0 : RefPtr<nsFrameSelection> fs = FrameSelection();
2135 0 : if (!fs)
2136 0 : return;
2137 :
2138 0 : dom::Selection* domSel = fs->GetSelection(SelectionType::eSpellCheck);
2139 0 : if (!domSel)
2140 0 : return;
2141 :
2142 0 : int32_t rangeCount = domSel->RangeCount();
2143 0 : if (rangeCount <= 0)
2144 0 : return;
2145 :
2146 0 : uint32_t startOffset = 0, endOffset = 0;
2147 0 : for (int32_t idx = 0; idx < rangeCount; idx++) {
2148 0 : nsRange* range = domSel->GetRangeAt(idx);
2149 0 : if (range->Collapsed())
2150 0 : continue;
2151 :
2152 : // See if the point comes after the range in which case we must continue in
2153 : // case there is another range after this one.
2154 0 : nsINode* endNode = range->GetEndContainer();
2155 0 : int32_t endNodeOffset = range->EndOffset();
2156 0 : if (nsContentUtils::ComparePoints(aNode, aNodeOffset,
2157 : endNode, endNodeOffset) >= 0)
2158 0 : continue;
2159 :
2160 : // At this point our point is either in this range or before it but after
2161 : // the previous range. So we check to see if the range starts before the
2162 : // point in which case the point is in the missspelled range, otherwise it
2163 : // must be before the range and after the previous one if any.
2164 0 : nsINode* startNode = range->GetStartContainer();
2165 0 : int32_t startNodeOffset = range->StartOffset();
2166 0 : if (nsContentUtils::ComparePoints(startNode, startNodeOffset, aNode,
2167 : aNodeOffset) <= 0) {
2168 0 : startOffset = DOMPointToOffset(startNode, startNodeOffset);
2169 :
2170 0 : endOffset = DOMPointToOffset(endNode, endNodeOffset);
2171 :
2172 0 : if (startOffset > *aStartOffset)
2173 0 : *aStartOffset = startOffset;
2174 :
2175 0 : if (endOffset < *aEndOffset)
2176 0 : *aEndOffset = endOffset;
2177 :
2178 0 : if (aAttributes) {
2179 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
2180 0 : NS_LITERAL_STRING("spelling"));
2181 : }
2182 :
2183 0 : return;
2184 : }
2185 :
2186 : // This range came after the point.
2187 0 : endOffset = DOMPointToOffset(startNode, startNodeOffset);
2188 :
2189 0 : if (idx > 0) {
2190 0 : nsRange* prevRange = domSel->GetRangeAt(idx - 1);
2191 0 : startOffset = DOMPointToOffset(prevRange->GetEndContainer(),
2192 0 : prevRange->EndOffset());
2193 : }
2194 :
2195 0 : if (startOffset > *aStartOffset)
2196 0 : *aStartOffset = startOffset;
2197 :
2198 0 : if (endOffset < *aEndOffset)
2199 0 : *aEndOffset = endOffset;
2200 :
2201 0 : return;
2202 : }
2203 :
2204 : // We never found a range that ended after the point, therefore we know that
2205 : // the point is not in a range, that we do not need to compute an end offset,
2206 : // and that we should use the end offset of the last range to compute the
2207 : // start offset of the text attribute range.
2208 0 : nsRange* prevRange = domSel->GetRangeAt(rangeCount - 1);
2209 0 : startOffset = DOMPointToOffset(prevRange->GetEndContainer(),
2210 0 : prevRange->EndOffset());
2211 :
2212 0 : if (startOffset > *aStartOffset)
2213 0 : *aStartOffset = startOffset;
2214 : }
2215 :
2216 : bool
2217 0 : HyperTextAccessible::IsTextRole()
2218 : {
2219 0 : const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
2220 0 : if (roleMapEntry &&
2221 0 : (roleMapEntry->role == roles::GRAPHIC ||
2222 0 : roleMapEntry->role == roles::IMAGE_MAP ||
2223 0 : roleMapEntry->role == roles::SLIDER ||
2224 0 : roleMapEntry->role == roles::PROGRESSBAR ||
2225 0 : roleMapEntry->role == roles::SEPARATOR))
2226 0 : return false;
2227 :
2228 0 : return true;
2229 : }
|