1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
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/. */
7 #include "nsAccessiblePivot.h"
9 #include "HyperTextAccessible.h"
10 #include "nsAccUtils.h"
13 using namespace mozilla::a11y
;
17 * An object that stores a given traversal rule during the pivot movement.
22 RuleCache(nsIAccessibleTraversalRule
* aRule
) : mRule(aRule
),
23 mAcceptRoles(nullptr) { }
26 nsMemory::Free(mAcceptRoles
);
29 nsresult
ApplyFilter(Accessible
* aAccessible
, uint16_t* aResult
);
32 nsCOMPtr
<nsIAccessibleTraversalRule
> mRule
;
33 uint32_t* mAcceptRoles
;
34 uint32_t mAcceptRolesLength
;
38 ////////////////////////////////////////////////////////////////////////////////
41 nsAccessiblePivot::nsAccessiblePivot(Accessible
* aRoot
) :
42 mRoot(aRoot
), mModalRoot(nullptr), mPosition(nullptr),
43 mStartOffset(-1), mEndOffset(-1)
45 NS_ASSERTION(aRoot
, "A root accessible is required");
48 nsAccessiblePivot::~nsAccessiblePivot()
52 ////////////////////////////////////////////////////////////////////////////////
55 NS_IMPL_CYCLE_COLLECTION(nsAccessiblePivot
, mRoot
, mPosition
, mObservers
)
57 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot
)
58 NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot
)
59 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIAccessiblePivot
)
62 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot
)
63 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot
)
65 ////////////////////////////////////////////////////////////////////////////////
69 nsAccessiblePivot::GetRoot(nsIAccessible
** aRoot
)
71 NS_ENSURE_ARG_POINTER(aRoot
);
73 NS_IF_ADDREF(*aRoot
= mRoot
);
79 nsAccessiblePivot::GetPosition(nsIAccessible
** aPosition
)
81 NS_ENSURE_ARG_POINTER(aPosition
);
83 NS_IF_ADDREF(*aPosition
= mPosition
);
89 nsAccessiblePivot::SetPosition(nsIAccessible
* aPosition
)
91 nsRefPtr
<Accessible
> secondPosition
;
94 secondPosition
= do_QueryObject(aPosition
);
95 if (!secondPosition
|| !IsDescendantOf(secondPosition
, GetActiveRoot()))
96 return NS_ERROR_INVALID_ARG
;
99 // Swap old position with new position, saves us an AddRef/Release.
100 mPosition
.swap(secondPosition
);
101 int32_t oldStart
= mStartOffset
, oldEnd
= mEndOffset
;
102 mStartOffset
= mEndOffset
= -1;
103 NotifyOfPivotChange(secondPosition
, oldStart
, oldEnd
,
104 nsIAccessiblePivot::REASON_NONE
, false);
110 nsAccessiblePivot::GetModalRoot(nsIAccessible
** aModalRoot
)
112 NS_ENSURE_ARG_POINTER(aModalRoot
);
114 NS_IF_ADDREF(*aModalRoot
= mModalRoot
);
120 nsAccessiblePivot::SetModalRoot(nsIAccessible
* aModalRoot
)
122 nsRefPtr
<Accessible
> modalRoot
;
125 modalRoot
= do_QueryObject(aModalRoot
);
126 if (!modalRoot
|| !IsDescendantOf(modalRoot
, mRoot
))
127 return NS_ERROR_INVALID_ARG
;
130 mModalRoot
.swap(modalRoot
);
136 nsAccessiblePivot::GetStartOffset(int32_t* aStartOffset
)
138 NS_ENSURE_ARG_POINTER(aStartOffset
);
140 *aStartOffset
= mStartOffset
;
146 nsAccessiblePivot::GetEndOffset(int32_t* aEndOffset
)
148 NS_ENSURE_ARG_POINTER(aEndOffset
);
150 *aEndOffset
= mEndOffset
;
156 nsAccessiblePivot::SetTextRange(nsIAccessibleText
* aTextAccessible
,
157 int32_t aStartOffset
, int32_t aEndOffset
,
158 bool aIsFromUserInput
, uint8_t aArgc
)
160 NS_ENSURE_ARG(aTextAccessible
);
162 // Check that start offset is smaller than end offset, and that if a value is
163 // smaller than 0, both should be -1.
164 NS_ENSURE_TRUE(aStartOffset
<= aEndOffset
&&
165 (aStartOffset
>= 0 || (aStartOffset
!= -1 && aEndOffset
!= -1)),
166 NS_ERROR_INVALID_ARG
);
168 nsRefPtr
<Accessible
> acc(do_QueryObject(aTextAccessible
));
170 return NS_ERROR_INVALID_ARG
;
172 HyperTextAccessible
* newPosition
= acc
->AsHyperText();
173 if (!newPosition
|| !IsDescendantOf(newPosition
, GetActiveRoot()))
174 return NS_ERROR_INVALID_ARG
;
176 // Make sure the given offsets don't exceed the character count.
177 int32_t charCount
= newPosition
->CharacterCount();
179 if (aEndOffset
> charCount
)
180 return NS_ERROR_FAILURE
;
182 int32_t oldStart
= mStartOffset
, oldEnd
= mEndOffset
;
183 mStartOffset
= aStartOffset
;
184 mEndOffset
= aEndOffset
;
186 nsRefPtr
<Accessible
> oldPosition
= mPosition
.forget();
187 mPosition
= newPosition
;
189 NotifyOfPivotChange(oldPosition
, oldStart
, oldEnd
,
190 nsIAccessiblePivot::REASON_TEXT
,
191 (aArgc
> 0) ? aIsFromUserInput
: true);
196 // Traversal functions
199 nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule
* aRule
,
200 nsIAccessible
* aAnchor
, bool aIncludeStart
,
201 bool aIsFromUserInput
, uint8_t aArgc
, bool* aResult
)
203 NS_ENSURE_ARG(aResult
);
204 NS_ENSURE_ARG(aRule
);
208 Accessible
* root
= GetActiveRoot();
209 nsRefPtr
<Accessible
> anchor
=
210 (aArgc
> 0) ? do_QueryObject(aAnchor
) : mPosition
;
211 if (anchor
&& (anchor
->IsDefunct() || !IsDescendantOf(anchor
, root
)))
212 return NS_ERROR_NOT_IN_TREE
;
215 Accessible
* accessible
=
216 SearchForward(anchor
, aRule
, (aArgc
> 1) ? aIncludeStart
: false, &rv
);
217 NS_ENSURE_SUCCESS(rv
, rv
);
220 *aResult
= MovePivotInternal(accessible
, nsIAccessiblePivot::REASON_NEXT
,
221 (aArgc
> 2) ? aIsFromUserInput
: true);
227 nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule
* aRule
,
228 nsIAccessible
* aAnchor
,
229 bool aIncludeStart
, bool aIsFromUserInput
,
230 uint8_t aArgc
, bool* aResult
)
232 NS_ENSURE_ARG(aResult
);
233 NS_ENSURE_ARG(aRule
);
237 Accessible
* root
= GetActiveRoot();
238 nsRefPtr
<Accessible
> anchor
=
239 (aArgc
> 0) ? do_QueryObject(aAnchor
) : mPosition
;
240 if (anchor
&& (anchor
->IsDefunct() || !IsDescendantOf(anchor
, root
)))
241 return NS_ERROR_NOT_IN_TREE
;
244 Accessible
* accessible
=
245 SearchBackward(anchor
, aRule
, (aArgc
> 1) ? aIncludeStart
: false, &rv
);
246 NS_ENSURE_SUCCESS(rv
, rv
);
249 *aResult
= MovePivotInternal(accessible
, nsIAccessiblePivot::REASON_PREV
,
250 (aArgc
> 2) ? aIsFromUserInput
: true);
256 nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule
* aRule
,
257 bool aIsFromUserInput
,
258 uint8_t aArgc
, bool* aResult
)
260 NS_ENSURE_ARG(aResult
);
261 NS_ENSURE_ARG(aRule
);
263 Accessible
* root
= GetActiveRoot();
264 NS_ENSURE_TRUE(root
&& !root
->IsDefunct(), NS_ERROR_NOT_IN_TREE
);
267 Accessible
* accessible
= SearchForward(root
, aRule
, true, &rv
);
268 NS_ENSURE_SUCCESS(rv
, rv
);
271 *aResult
= MovePivotInternal(accessible
, nsIAccessiblePivot::REASON_FIRST
,
272 (aArgc
> 0) ? aIsFromUserInput
: true);
278 nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule
* aRule
,
279 bool aIsFromUserInput
,
280 uint8_t aArgc
, bool* aResult
)
282 NS_ENSURE_ARG(aResult
);
283 NS_ENSURE_ARG(aRule
);
285 Accessible
* root
= GetActiveRoot();
286 NS_ENSURE_TRUE(root
&& !root
->IsDefunct(), NS_ERROR_NOT_IN_TREE
);
290 Accessible
* lastAccessible
= root
;
291 Accessible
* accessible
= nullptr;
293 // First go to the last accessible in pre-order
294 while (lastAccessible
->HasChildren())
295 lastAccessible
= lastAccessible
->LastChild();
297 // Search backwards from last accessible and find the last occurrence in the doc
298 accessible
= SearchBackward(lastAccessible
, aRule
, true, &rv
);
299 NS_ENSURE_SUCCESS(rv
, rv
);
302 *aResult
= MovePivotInternal(accessible
, nsAccessiblePivot::REASON_LAST
,
303 (aArgc
> 0) ? aIsFromUserInput
: true);
309 nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary
,
310 bool aIsFromUserInput
, uint8_t aArgc
,
313 NS_ENSURE_ARG(aResult
);
317 int32_t tempStart
= mStartOffset
, tempEnd
= mEndOffset
;
318 Accessible
* tempPosition
= mPosition
;
319 Accessible
* root
= GetActiveRoot();
321 Accessible
* curPosition
= tempPosition
;
322 HyperTextAccessible
* text
= nullptr;
323 // Find the nearest text node using a preorder traversal starting from
325 if (!(text
= tempPosition
->AsHyperText())) {
326 text
= SearchForText(tempPosition
, false);
329 if (text
!= curPosition
)
330 tempStart
= tempEnd
= -1;
334 // If the search led to the parent of the node we started on (e.g. when
335 // starting on a text leaf), start the text movement from the end of that
336 // node, otherwise we just default to 0.
338 tempEnd
= text
== curPosition
->Parent() ?
339 text
->GetChildOffset(curPosition
) : 0;
341 // If there's no more text on the current node, try to find the next text
342 // node; if there isn't one, bail out.
343 if (tempEnd
== static_cast<int32_t>(text
->CharacterCount())) {
344 if (tempPosition
== root
)
347 // If we're currently sitting on a link, try move to either the next
348 // sibling or the parent, whichever is closer to the current end
349 // offset. Otherwise, do a forward search for the next node to land on
350 // (we don't do this in the first case because we don't want to go to the
352 Accessible
* sibling
= tempPosition
->NextSibling();
353 if (tempPosition
->IsLink()) {
354 if (sibling
&& sibling
->IsLink()) {
355 tempStart
= tempEnd
= -1;
356 tempPosition
= sibling
;
358 tempStart
= tempPosition
->StartOffset();
359 tempEnd
= tempPosition
->EndOffset();
360 tempPosition
= tempPosition
->Parent();
363 tempPosition
= SearchForText(tempPosition
, false);
366 tempStart
= tempEnd
= -1;
371 AccessibleTextBoundary startBoundary
, endBoundary
;
374 startBoundary
= nsIAccessibleText::BOUNDARY_CHAR
;
375 endBoundary
= nsIAccessibleText::BOUNDARY_CHAR
;
378 startBoundary
= nsIAccessibleText::BOUNDARY_WORD_START
;
379 endBoundary
= nsIAccessibleText::BOUNDARY_WORD_END
;
382 return NS_ERROR_INVALID_ARG
;
385 nsAutoString unusedText
;
386 int32_t newStart
= 0, newEnd
= 0, currentEnd
= tempEnd
;
387 text
->TextAtOffset(tempEnd
, endBoundary
, &newStart
, &tempEnd
, unusedText
);
388 text
->TextBeforeOffset(tempEnd
, startBoundary
, &newStart
, &newEnd
, unusedText
);
389 int32_t potentialStart
= newEnd
== tempEnd
? newStart
: newEnd
;
390 tempStart
= potentialStart
> tempStart
? potentialStart
: currentEnd
;
392 // The offset range we've obtained might have embedded characters in it,
393 // limit the range to the start of the first occurrence of an embedded
395 Accessible
* childAtOffset
= nullptr;
396 for (int32_t i
= tempStart
; i
< tempEnd
; i
++) {
397 childAtOffset
= text
->GetChildAtOffset(i
);
398 if (childAtOffset
&& nsAccUtils::IsEmbeddedObject(childAtOffset
)) {
403 // If there's an embedded character at the very start of the range, we
404 // instead want to traverse into it. So restart the movement with
405 // the child as the starting point.
406 if (childAtOffset
&& nsAccUtils::IsEmbeddedObject(childAtOffset
) &&
407 tempStart
== static_cast<int32_t>(childAtOffset
->StartOffset())) {
408 tempPosition
= childAtOffset
;
409 tempStart
= tempEnd
= -1;
415 Accessible
* startPosition
= mPosition
;
416 int32_t oldStart
= mStartOffset
, oldEnd
= mEndOffset
;
417 mPosition
= tempPosition
;
418 mStartOffset
= tempStart
;
419 mEndOffset
= tempEnd
;
420 NotifyOfPivotChange(startPosition
, oldStart
, oldEnd
,
421 nsIAccessiblePivot::REASON_TEXT
,
422 (aArgc
> 0) ? aIsFromUserInput
: true);
428 nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary
,
429 bool aIsFromUserInput
, uint8_t aArgc
,
432 NS_ENSURE_ARG(aResult
);
436 int32_t tempStart
= mStartOffset
, tempEnd
= mEndOffset
;
437 Accessible
* tempPosition
= mPosition
;
438 Accessible
* root
= GetActiveRoot();
440 Accessible
* curPosition
= tempPosition
;
441 HyperTextAccessible
* text
;
442 // Find the nearest text node using a reverse preorder traversal starting
443 // from the current node.
444 if (!(text
= tempPosition
->AsHyperText())) {
445 text
= SearchForText(tempPosition
, true);
448 if (text
!= curPosition
)
449 tempStart
= tempEnd
= -1;
453 // If the search led to the parent of the node we started on (e.g. when
454 // starting on a text leaf), start the text movement from the end of that
455 // node, otherwise we just default to 0.
456 if (tempStart
== -1) {
457 if (tempPosition
!= curPosition
)
458 tempStart
= text
== curPosition
->Parent() ?
459 text
->GetChildOffset(curPosition
) : text
->CharacterCount();
464 // If there's no more text on the current node, try to find the previous
465 // text node; if there isn't one, bail out.
466 if (tempStart
== 0) {
467 if (tempPosition
== root
)
470 // If we're currently sitting on a link, try move to either the previous
471 // sibling or the parent, whichever is closer to the current end
472 // offset. Otherwise, do a forward search for the next node to land on
473 // (we don't do this in the first case because we don't want to go to the
475 Accessible
* sibling
= tempPosition
->PrevSibling();
476 if (tempPosition
->IsLink()) {
477 if (sibling
&& sibling
->IsLink()) {
478 HyperTextAccessible
* siblingText
= sibling
->AsHyperText();
479 tempStart
= tempEnd
= siblingText
?
480 siblingText
->CharacterCount() : -1;
481 tempPosition
= sibling
;
483 tempStart
= tempPosition
->StartOffset();
484 tempEnd
= tempPosition
->EndOffset();
485 tempPosition
= tempPosition
->Parent();
488 HyperTextAccessible
* tempText
= SearchForText(tempPosition
, true);
491 tempPosition
= tempText
;
492 tempStart
= tempEnd
= tempText
->CharacterCount();
497 AccessibleTextBoundary startBoundary
, endBoundary
;
500 startBoundary
= nsIAccessibleText::BOUNDARY_CHAR
;
501 endBoundary
= nsIAccessibleText::BOUNDARY_CHAR
;
504 startBoundary
= nsIAccessibleText::BOUNDARY_WORD_START
;
505 endBoundary
= nsIAccessibleText::BOUNDARY_WORD_END
;
508 return NS_ERROR_INVALID_ARG
;
511 nsAutoString unusedText
;
512 int32_t newStart
= 0, newEnd
= 0, currentStart
= tempStart
, potentialEnd
= 0;
513 text
->TextBeforeOffset(tempStart
, startBoundary
, &newStart
, &newEnd
, unusedText
);
514 if (newStart
< tempStart
)
515 tempStart
= newEnd
>= currentStart
? newStart
: newEnd
;
516 else // XXX: In certain odd cases newStart is equal to tempStart
517 text
->TextBeforeOffset(tempStart
- 1, startBoundary
, &newStart
,
518 &tempStart
, unusedText
);
519 text
->TextAtOffset(tempStart
, endBoundary
, &newStart
, &potentialEnd
,
521 tempEnd
= potentialEnd
< tempEnd
? potentialEnd
: currentStart
;
523 // The offset range we've obtained might have embedded characters in it,
524 // limit the range to the start of the last occurrence of an embedded
526 Accessible
* childAtOffset
= nullptr;
527 for (int32_t i
= tempEnd
- 1; i
>= tempStart
; i
--) {
528 childAtOffset
= text
->GetChildAtOffset(i
);
529 if (childAtOffset
&& nsAccUtils::IsEmbeddedObject(childAtOffset
)) {
530 tempStart
= childAtOffset
->EndOffset();
534 // If there's an embedded character at the very end of the range, we
535 // instead want to traverse into it. So restart the movement with
536 // the child as the starting point.
537 if (childAtOffset
&& nsAccUtils::IsEmbeddedObject(childAtOffset
) &&
538 tempEnd
== static_cast<int32_t>(childAtOffset
->EndOffset())) {
539 tempPosition
= childAtOffset
;
540 tempStart
= tempEnd
= childAtOffset
->AsHyperText()->CharacterCount();
546 Accessible
* startPosition
= mPosition
;
547 int32_t oldStart
= mStartOffset
, oldEnd
= mEndOffset
;
548 mPosition
= tempPosition
;
549 mStartOffset
= tempStart
;
550 mEndOffset
= tempEnd
;
552 NotifyOfPivotChange(startPosition
, oldStart
, oldEnd
,
553 nsIAccessiblePivot::REASON_TEXT
,
554 (aArgc
> 0) ? aIsFromUserInput
: true);
560 nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule
* aRule
,
561 int32_t aX
, int32_t aY
, bool aIgnoreNoMatch
,
562 bool aIsFromUserInput
, uint8_t aArgc
,
565 NS_ENSURE_ARG_POINTER(aResult
);
566 NS_ENSURE_ARG_POINTER(aRule
);
570 Accessible
* root
= GetActiveRoot();
571 NS_ENSURE_TRUE(root
&& !root
->IsDefunct(), NS_ERROR_NOT_IN_TREE
);
573 RuleCache
cache(aRule
);
574 Accessible
* match
= nullptr;
575 Accessible
* child
= root
->ChildAtPoint(aX
, aY
, Accessible::eDeepestChild
);
576 while (child
&& root
!= child
) {
577 uint16_t filtered
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
578 nsresult rv
= cache
.ApplyFilter(child
, &filtered
);
579 NS_ENSURE_SUCCESS(rv
, rv
);
581 // Ignore any matching nodes that were below this one
582 if (filtered
& nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
)
585 // Match if no node below this is a match
586 if ((filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
) && !match
) {
587 int32_t childX
, childY
, childWidth
, childHeight
;
588 child
->GetBounds(&childX
, &childY
, &childWidth
, &childHeight
);
589 // Double-check child's bounds since the deepest child may have been out
590 // of bounds. This assures we don't return a false positive.
591 if (aX
>= childX
&& aX
< childX
+ childWidth
&&
592 aY
>= childY
&& aY
< childY
+ childHeight
)
596 child
= child
->Parent();
599 if (match
|| !aIgnoreNoMatch
)
600 *aResult
= MovePivotInternal(match
, nsIAccessiblePivot::REASON_POINT
,
601 (aArgc
> 0) ? aIsFromUserInput
: true);
606 // Observer functions
609 nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver
* aObserver
)
611 NS_ENSURE_ARG(aObserver
);
613 mObservers
.AppendElement(aObserver
);
619 nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver
* aObserver
)
621 NS_ENSURE_ARG(aObserver
);
623 return mObservers
.RemoveElement(aObserver
) ? NS_OK
: NS_ERROR_FAILURE
;
626 // Private utility methods
629 nsAccessiblePivot::IsDescendantOf(Accessible
* aAccessible
, Accessible
* aAncestor
)
631 if (!aAncestor
|| aAncestor
->IsDefunct())
634 // XXX Optimize with IsInDocument() when appropriate. Blocked by bug 759875.
635 Accessible
* accessible
= aAccessible
;
637 if (accessible
== aAncestor
)
639 } while ((accessible
= accessible
->Parent()));
645 nsAccessiblePivot::MovePivotInternal(Accessible
* aPosition
,
646 PivotMoveReason aReason
,
647 bool aIsFromUserInput
)
649 nsRefPtr
<Accessible
> oldPosition
= mPosition
.forget();
650 mPosition
= aPosition
;
651 int32_t oldStart
= mStartOffset
, oldEnd
= mEndOffset
;
652 mStartOffset
= mEndOffset
= -1;
654 return NotifyOfPivotChange(oldPosition
, oldStart
, oldEnd
, aReason
,
659 nsAccessiblePivot::AdjustStartPosition(Accessible
* aAccessible
,
661 uint16_t* aFilterResult
,
664 Accessible
* matched
= aAccessible
;
665 *aResult
= aCache
.ApplyFilter(aAccessible
, aFilterResult
);
667 if (aAccessible
!= mRoot
&& aAccessible
!= mModalRoot
) {
668 for (Accessible
* temp
= aAccessible
->Parent();
669 temp
&& temp
!= mRoot
&& temp
!= mModalRoot
; temp
= temp
->Parent()) {
670 uint16_t filtered
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
671 *aResult
= aCache
.ApplyFilter(temp
, &filtered
);
672 NS_ENSURE_SUCCESS(*aResult
, nullptr);
673 if (filtered
& nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
) {
674 *aFilterResult
= filtered
;
680 if (aAccessible
== mPosition
&& mStartOffset
!= -1 && mEndOffset
!= -1) {
681 HyperTextAccessible
* text
= aAccessible
->AsHyperText();
683 matched
= text
->GetChildAtOffset(mStartOffset
);
691 nsAccessiblePivot::SearchBackward(Accessible
* aAccessible
,
692 nsIAccessibleTraversalRule
* aRule
,
698 // Initial position could be unset, in that case return null.
702 RuleCache
cache(aRule
);
703 uint16_t filtered
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
704 Accessible
* accessible
= AdjustStartPosition(aAccessible
, cache
,
706 NS_ENSURE_SUCCESS(*aResult
, nullptr);
708 if (aSearchCurrent
&& (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
)) {
712 Accessible
* root
= GetActiveRoot();
713 while (accessible
!= root
) {
714 Accessible
* parent
= accessible
->Parent();
715 int32_t idxInParent
= accessible
->IndexInParent();
716 while (idxInParent
> 0) {
717 if (!(accessible
= parent
->GetChildAt(--idxInParent
)))
720 *aResult
= cache
.ApplyFilter(accessible
, &filtered
);
721 NS_ENSURE_SUCCESS(*aResult
, nullptr);
723 Accessible
* lastChild
= nullptr;
724 while (!(filtered
& nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
) &&
725 (lastChild
= accessible
->LastChild())) {
727 accessible
= lastChild
;
728 idxInParent
= accessible
->IndexInParent();
729 *aResult
= cache
.ApplyFilter(accessible
, &filtered
);
730 NS_ENSURE_SUCCESS(*aResult
, nullptr);
733 if (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
)
737 if (!(accessible
= parent
))
740 *aResult
= cache
.ApplyFilter(accessible
, &filtered
);
741 NS_ENSURE_SUCCESS(*aResult
, nullptr);
743 if (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
)
751 nsAccessiblePivot::SearchForward(Accessible
* aAccessible
,
752 nsIAccessibleTraversalRule
* aRule
,
758 // Initial position could be not set, in that case begin search from root.
759 Accessible
* root
= GetActiveRoot();
760 Accessible
* accessible
= (!aAccessible
) ? root
: aAccessible
;
762 RuleCache
cache(aRule
);
764 uint16_t filtered
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
765 accessible
= AdjustStartPosition(accessible
, cache
, &filtered
, aResult
);
766 NS_ENSURE_SUCCESS(*aResult
, nullptr);
767 if (aSearchCurrent
&& (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
))
771 Accessible
* firstChild
= nullptr;
772 while (!(filtered
& nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
) &&
773 (firstChild
= accessible
->FirstChild())) {
774 accessible
= firstChild
;
775 *aResult
= cache
.ApplyFilter(accessible
, &filtered
);
776 NS_ENSURE_SUCCESS(*aResult
, nullptr);
778 if (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
)
782 Accessible
* sibling
= nullptr;
783 Accessible
* temp
= accessible
;
788 sibling
= temp
->NextSibling();
792 } while ((temp
= temp
->Parent()));
797 accessible
= sibling
;
798 *aResult
= cache
.ApplyFilter(accessible
, &filtered
);
799 NS_ENSURE_SUCCESS(*aResult
, nullptr);
801 if (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
)
809 nsAccessiblePivot::SearchForText(Accessible
* aAccessible
, bool aBackward
)
811 Accessible
* root
= GetActiveRoot();
812 Accessible
* accessible
= aAccessible
;
814 Accessible
* child
= nullptr;
816 while ((child
= (aBackward
? accessible
->LastChild() :
817 accessible
->FirstChild()))) {
819 if (child
->IsHyperText())
820 return child
->AsHyperText();
823 Accessible
* sibling
= nullptr;
824 Accessible
* temp
= accessible
;
829 if (temp
!= aAccessible
&& temp
->IsHyperText())
830 return temp
->AsHyperText();
832 sibling
= aBackward
? temp
->PrevSibling() : temp
->NextSibling();
836 } while ((temp
= temp
->Parent()));
841 accessible
= sibling
;
842 if (accessible
->IsHyperText())
843 return accessible
->AsHyperText();
851 nsAccessiblePivot::NotifyOfPivotChange(Accessible
* aOldPosition
,
852 int32_t aOldStart
, int32_t aOldEnd
,
853 int16_t aReason
, bool aIsFromUserInput
)
855 if (aOldPosition
== mPosition
&&
856 aOldStart
== mStartOffset
&& aOldEnd
== mEndOffset
)
859 nsTObserverArray
<nsCOMPtr
<nsIAccessiblePivotObserver
> >::ForwardIterator
iter(mObservers
);
860 while (iter
.HasMore()) {
861 nsIAccessiblePivotObserver
* obs
= iter
.GetNext();
862 obs
->OnPivotChanged(this, aOldPosition
, aOldStart
, aOldEnd
, aReason
,
870 RuleCache::ApplyFilter(Accessible
* aAccessible
, uint16_t* aResult
)
872 *aResult
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
875 nsresult rv
= mRule
->GetMatchRoles(&mAcceptRoles
, &mAcceptRolesLength
);
876 NS_ENSURE_SUCCESS(rv
, rv
);
877 rv
= mRule
->GetPreFilter(&mPreFilter
);
878 NS_ENSURE_SUCCESS(rv
, rv
);
882 uint64_t state
= aAccessible
->State();
884 if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE
& mPreFilter
) &&
885 (state
& states::INVISIBLE
))
888 if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN
& mPreFilter
) &&
889 (state
& states::OFFSCREEN
))
892 if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE
& mPreFilter
) &&
893 !(state
& states::FOCUSABLE
))
896 if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN
& mPreFilter
) {
897 nsIContent
* content
= aAccessible
->GetContent();
899 nsAccUtils::HasDefinedARIAToken(content
, nsGkAtoms::aria_hidden
) &&
900 !content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::aria_hidden
,
901 nsGkAtoms::_false
, eCaseMatters
)) {
902 *aResult
|= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
907 if ((nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT
& mPreFilter
) &&
908 !(state
& states::OPAQUE1
)) {
909 nsIFrame
* frame
= aAccessible
->GetFrame();
910 if (frame
->StyleDisplay()->mOpacity
== 0.0f
) {
911 *aResult
|= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
917 if (mAcceptRolesLength
> 0) {
918 uint32_t accessibleRole
= aAccessible
->Role();
919 bool matchesRole
= false;
920 for (uint32_t idx
= 0; idx
< mAcceptRolesLength
; idx
++) {
921 matchesRole
= mAcceptRoles
[idx
] == accessibleRole
;
929 return mRule
->Match(aAccessible
, aResult
);