1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "AccIterator.h"
9 #include "LocalAccessible.h"
10 #include "RemoteAccessible.h"
11 #include "nsAccUtils.h"
12 #include "nsIAccessiblePivot.h"
14 #include "mozilla/a11y/Accessible.h"
16 using namespace mozilla
;
17 using namespace mozilla::a11y
;
19 ////////////////////////////////////////////////////////////////////////////////
21 ////////////////////////////////////////////////////////////////////////////////
23 Pivot::Pivot(Accessible
* aRoot
) : mRoot(aRoot
) { MOZ_COUNT_CTOR(Pivot
); }
25 Pivot::~Pivot() { MOZ_COUNT_DTOR(Pivot
); }
27 Accessible
* Pivot::AdjustStartPosition(Accessible
* aAnchor
, PivotRule
& aRule
,
28 uint16_t* aFilterResult
) {
29 Accessible
* matched
= aAnchor
;
30 *aFilterResult
= aRule
.Match(aAnchor
);
32 if (aAnchor
&& aAnchor
!= mRoot
) {
33 for (Accessible
* temp
= aAnchor
->Parent(); temp
&& temp
!= mRoot
;
34 temp
= temp
->Parent()) {
35 uint16_t filtered
= aRule
.Match(temp
);
36 if (filtered
& nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
) {
37 *aFilterResult
= filtered
;
46 Accessible
* Pivot::SearchBackward(Accessible
* aAnchor
, PivotRule
& aRule
,
47 bool aSearchCurrent
) {
48 // Initial position could be unset, in that case return null.
53 uint16_t filtered
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
55 Accessible
* acc
= AdjustStartPosition(aAnchor
, aRule
, &filtered
);
57 if (aSearchCurrent
&& (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
)) {
61 while (acc
&& acc
!= mRoot
) {
62 Accessible
* parent
= acc
->Parent();
65 acc
->IsLocal() || (acc
->IsRemote() && parent
->IsRemote()),
66 "Pivot::SearchBackward climbed out of remote subtree in Android!");
68 int32_t idxInParent
= acc
->IndexInParent();
69 while (idxInParent
> 0 && parent
) {
70 acc
= parent
->ChildAt(--idxInParent
);
75 filtered
= aRule
.Match(acc
);
77 Accessible
* lastChild
= acc
->LastChild();
78 while (!(filtered
& nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
) &&
82 idxInParent
= acc
->IndexInParent();
83 filtered
= aRule
.Match(acc
);
84 lastChild
= acc
->LastChild();
87 if (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
) {
97 filtered
= aRule
.Match(acc
);
99 if (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
) {
107 Accessible
* Pivot::SearchForward(Accessible
* aAnchor
, PivotRule
& aRule
,
108 bool aSearchCurrent
) {
109 // Initial position could be not set, in that case begin search from root.
110 Accessible
* acc
= aAnchor
? aAnchor
: mRoot
;
112 uint16_t filtered
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
113 acc
= AdjustStartPosition(acc
, aRule
, &filtered
);
114 if (aSearchCurrent
&& (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
)) {
119 Accessible
* firstChild
= acc
->FirstChild();
120 while (!(filtered
& nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
) &&
123 filtered
= aRule
.Match(acc
);
125 if (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
) {
128 firstChild
= acc
->FirstChild();
131 Accessible
* sibling
= nullptr;
132 Accessible
* temp
= acc
;
138 sibling
= temp
->NextSibling();
143 temp
= temp
->Parent();
146 acc
->IsLocal() || (acc
->IsRemote() && temp
->IsRemote()),
147 "Pivot::SearchForward climbed out of remote subtree in Android!");
157 filtered
= aRule
.Match(acc
);
158 if (filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
) {
166 Accessible
* Pivot::Next(Accessible
* aAnchor
, PivotRule
& aRule
,
167 bool aIncludeStart
) {
168 return SearchForward(aAnchor
, aRule
, aIncludeStart
);
171 Accessible
* Pivot::Prev(Accessible
* aAnchor
, PivotRule
& aRule
,
172 bool aIncludeStart
) {
173 return SearchBackward(aAnchor
, aRule
, aIncludeStart
);
176 Accessible
* Pivot::First(PivotRule
& aRule
) {
177 return SearchForward(mRoot
, aRule
, true);
180 Accessible
* Pivot::Last(PivotRule
& aRule
) {
181 Accessible
* lastAcc
= mRoot
;
183 // First go to the last accessible in pre-order
184 while (lastAcc
&& lastAcc
->HasChildren()) {
185 lastAcc
= lastAcc
->LastChild();
188 // Search backwards from last accessible and find the last occurrence in the
190 return SearchBackward(lastAcc
, aRule
, true);
193 Accessible
* Pivot::AtPoint(int32_t aX
, int32_t aY
, PivotRule
& aRule
) {
194 Accessible
* match
= nullptr;
196 mRoot
? mRoot
->ChildAtPoint(aX
, aY
,
197 Accessible::EWhichChildAtPoint::DeepestChild
)
199 while (child
&& (mRoot
!= child
)) {
200 uint16_t filtered
= aRule
.Match(child
);
202 // Ignore any matching nodes that were below this one
203 if (filtered
& nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
) {
207 // Match if no node below this is a match
208 if ((filtered
& nsIAccessibleTraversalRule::FILTER_MATCH
) && !match
) {
209 LayoutDeviceIntRect childRect
= child
->IsLocal()
210 ? child
->AsLocal()->Bounds()
211 : child
->AsRemote()->Bounds();
212 // Double-check child's bounds since the deepest child may have been out
213 // of bounds. This assures we don't return a false positive.
214 if (childRect
.Contains(aX
, aY
)) {
219 child
= child
->Parent();
227 PivotRoleRule::PivotRoleRule(mozilla::a11y::role aRole
)
228 : mRole(aRole
), mDirectDescendantsFrom(nullptr) {}
230 PivotRoleRule::PivotRoleRule(mozilla::a11y::role aRole
,
231 Accessible
* aDirectDescendantsFrom
)
232 : mRole(aRole
), mDirectDescendantsFrom(aDirectDescendantsFrom
) {}
234 uint16_t PivotRoleRule::Match(Accessible
* aAcc
) {
235 uint16_t result
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
237 if (nsAccUtils::MustPrune(aAcc
)) {
238 result
|= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
241 if (mDirectDescendantsFrom
&& (aAcc
!= mDirectDescendantsFrom
)) {
242 // If we've specified mDirectDescendantsFrom, we should ignore
243 // non-direct descendants of from the specified AoP. Because
244 // pivot performs a preorder traversal, the first aAcc
245 // object(s) that don't equal mDirectDescendantsFrom will be
246 // mDirectDescendantsFrom's children. We'll process them, but ignore
247 // their subtrees thereby processing direct descendants of
248 // mDirectDescendantsFrom only.
249 result
|= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
252 if (aAcc
&& aAcc
->Role() == mRole
) {
253 result
|= nsIAccessibleTraversalRule::FILTER_MATCH
;
261 PivotStateRule::PivotStateRule(uint64_t aState
) : mState(aState
) {}
263 uint16_t PivotStateRule::Match(Accessible
* aAcc
) {
264 uint16_t result
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
266 if (nsAccUtils::MustPrune(aAcc
)) {
267 result
|= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
270 if (aAcc
&& (aAcc
->State() & mState
)) {
271 result
= nsIAccessibleTraversalRule::FILTER_MATCH
|
272 nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
278 // LocalAccInSameDocRule
280 uint16_t LocalAccInSameDocRule::Match(Accessible
* aAcc
) {
281 LocalAccessible
* acc
= aAcc
? aAcc
->AsLocal() : nullptr;
283 return nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
285 if (acc
->IsOuterDoc()) {
286 return nsIAccessibleTraversalRule::FILTER_MATCH
|
287 nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
289 return nsIAccessibleTraversalRule::FILTER_MATCH
;
292 // Radio Button Name Rule
294 PivotRadioNameRule::PivotRadioNameRule(const nsString
& aName
) : mName(aName
) {}
296 uint16_t PivotRadioNameRule::Match(Accessible
* aAcc
) {
297 uint16_t result
= nsIAccessibleTraversalRule::FILTER_IGNORE
;
298 RemoteAccessible
* remote
= aAcc
->AsRemote();
300 // We need the cache to be able to fetch the name attribute below.
304 if (nsAccUtils::MustPrune(aAcc
) || aAcc
->IsOuterDoc()) {
305 result
|= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
308 if (remote
->IsHTMLRadioButton()) {
309 nsString currName
= remote
->GetCachedHTMLNameAttribute();
310 if (!currName
.IsEmpty() && mName
.Equals(currName
)) {
311 result
|= nsIAccessibleTraversalRule::FILTER_MATCH
;
318 // MustPruneSameDocRule
320 uint16_t MustPruneSameDocRule::Match(Accessible
* aAcc
) {
322 return nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
325 if (nsAccUtils::MustPrune(aAcc
) || aAcc
->IsOuterDoc()) {
326 return nsIAccessibleTraversalRule::FILTER_MATCH
|
327 nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE
;
330 return nsIAccessibleTraversalRule::FILTER_MATCH
;