Bug 1906091 - Adjust MLS timeout fallback value to 5 r=emilio
[gecko.git] / accessible / base / Pivot.cpp
blob146d9207cfac4afc9671ac71d71df5121d9a7ce9
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/. */
6 #include "Pivot.h"
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 ////////////////////////////////////////////////////////////////////////////////
20 // Pivot
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;
38 matched = temp;
43 return matched;
46 Accessible* Pivot::SearchBackward(Accessible* aAnchor, PivotRule& aRule,
47 bool aSearchCurrent) {
48 // Initial position could be unset, in that case return null.
49 if (!aAnchor) {
50 return nullptr;
53 uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
55 Accessible* acc = AdjustStartPosition(aAnchor, aRule, &filtered);
57 if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
58 return acc;
61 while (acc && acc != mRoot) {
62 Accessible* parent = acc->Parent();
63 #if defined(ANDROID)
64 MOZ_ASSERT(
65 acc->IsLocal() || (acc->IsRemote() && parent->IsRemote()),
66 "Pivot::SearchBackward climbed out of remote subtree in Android!");
67 #endif
68 int32_t idxInParent = acc->IndexInParent();
69 while (idxInParent > 0 && parent) {
70 acc = parent->ChildAt(--idxInParent);
71 if (!acc) {
72 continue;
75 filtered = aRule.Match(acc);
77 Accessible* lastChild = acc->LastChild();
78 while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
79 lastChild) {
80 parent = acc;
81 acc = lastChild;
82 idxInParent = acc->IndexInParent();
83 filtered = aRule.Match(acc);
84 lastChild = acc->LastChild();
87 if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
88 return acc;
92 acc = parent;
93 if (!acc) {
94 break;
97 filtered = aRule.Match(acc);
99 if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
100 return acc;
104 return nullptr;
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)) {
115 return acc;
118 while (acc) {
119 Accessible* firstChild = acc->FirstChild();
120 while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
121 firstChild) {
122 acc = firstChild;
123 filtered = aRule.Match(acc);
125 if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
126 return acc;
128 firstChild = acc->FirstChild();
131 Accessible* sibling = nullptr;
132 Accessible* temp = acc;
133 do {
134 if (temp == mRoot) {
135 break;
138 sibling = temp->NextSibling();
140 if (sibling) {
141 break;
143 temp = temp->Parent();
144 #if defined(ANDROID)
145 MOZ_ASSERT(
146 acc->IsLocal() || (acc->IsRemote() && temp->IsRemote()),
147 "Pivot::SearchForward climbed out of remote subtree in Android!");
148 #endif
150 } while (temp);
152 if (!sibling) {
153 break;
156 acc = sibling;
157 filtered = aRule.Match(acc);
158 if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
159 return acc;
163 return nullptr;
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
189 // doc
190 return SearchBackward(lastAcc, aRule, true);
193 Accessible* Pivot::AtPoint(int32_t aX, int32_t aY, PivotRule& aRule) {
194 Accessible* match = nullptr;
195 Accessible* child =
196 mRoot ? mRoot->ChildAtPoint(aX, aY,
197 Accessible::EWhichChildAtPoint::DeepestChild)
198 : nullptr;
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) {
204 match = nullptr;
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)) {
215 match = child;
219 child = child->Parent();
222 return match;
225 // Role Rule
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;
256 return result;
259 // State Rule
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;
275 return result;
278 // LocalAccInSameDocRule
280 uint16_t LocalAccInSameDocRule::Match(Accessible* aAcc) {
281 LocalAccessible* acc = aAcc ? aAcc->AsLocal() : nullptr;
282 if (!acc) {
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();
299 if (!remote) {
300 // We need the cache to be able to fetch the name attribute below.
301 return result;
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;
315 return result;
318 // MustPruneSameDocRule
320 uint16_t MustPruneSameDocRule::Match(Accessible* aAcc) {
321 if (!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;