Bug 1884547 - Skip Snap QA because of CAPCTHAs r=mboldan
[gecko.git] / accessible / mac / MOXSearchInfo.mm
blobce7a7dedf347e87fb367d5a8cc3a1663bcca1d37
1 /* clang-format off */
2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* clang-format on */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #import "MOXSearchInfo.h"
9 #import "MOXWebAreaAccessible.h"
10 #import "RotorRules.h"
12 #include "nsCocoaUtils.h"
13 #include "DocAccessibleParent.h"
14 #include "nsAccessibilityService.h"
16 using namespace mozilla::a11y;
18 @interface MOXSearchInfo ()
19 - (NSArray*)getMatchesForRule:(PivotRule&)rule;
21 - (Accessible*)rootGeckoAccessible;
23 - (Accessible*)startGeckoAccessible;
24 @end
26 @implementation MOXSearchInfo
28 - (id)initWithParameters:(NSDictionary*)params
29                  andRoot:(MOXAccessibleBase*)root {
30   if (id searchKeyParam = [params objectForKey:@"AXSearchKey"]) {
31     mSearchKeys = [searchKeyParam isKindOfClass:[NSString class]]
32                       ? [[NSArray alloc] initWithObjects:searchKeyParam, nil]
33                       : [searchKeyParam retain];
34   }
36   if (id startElemParam = [params objectForKey:@"AXStartElement"]) {
37     mStartElem = startElemParam;
38   } else {
39     mStartElem = root;
40   }
42   mRoot = root;
44   mResultLimit = [[params objectForKey:@"AXResultsLimit"] intValue];
46   mSearchForward =
47       [[params objectForKey:@"AXDirection"] isEqualToString:@"AXDirectionNext"];
49   mImmediateDescendantsOnly =
50       [[params objectForKey:@"AXImmediateDescendantsOnly"] boolValue];
52   mSearchText = [params objectForKey:@"AXSearchText"];
54   return [super init];
57 - (Accessible*)rootGeckoAccessible {
58   id root =
59       [mRoot isKindOfClass:[mozAccessible class]] ? mRoot : [mRoot moxParent];
61   return [static_cast<mozAccessible*>(root) geckoAccessible];
64 - (Accessible*)startGeckoAccessible {
65   if ([mStartElem isKindOfClass:[mozAccessible class]]) {
66     return [static_cast<mozAccessible*>(mStartElem) geckoAccessible];
67   }
69   // If it isn't a mozAccessible, it doesn't have a gecko accessible
70   // this is most likely the root group. Use the gecko doc as the start
71   // accessible.
72   return [self rootGeckoAccessible];
75 - (NSArray*)getMatchesForRule:(PivotRule&)rule {
76   int resultLimit = mResultLimit;
78   NSMutableArray<mozAccessible*>* matches =
79       [[[NSMutableArray alloc] init] autorelease];
80   Accessible* geckoRootAcc = [self rootGeckoAccessible];
81   Accessible* geckoStartAcc = [self startGeckoAccessible];
82   Pivot p = Pivot(geckoRootAcc);
83   Accessible* match;
84   if (mSearchForward) {
85     match = p.Next(geckoStartAcc, rule);
86   } else {
87     // Search backwards
88     if (geckoRootAcc == geckoStartAcc) {
89       // If we have no explicit start accessible, start from the last match.
90       match = p.Last(rule);
91     } else {
92       match = p.Prev(geckoStartAcc, rule);
93     }
94   }
96   while (match && resultLimit != 0) {
97     if (!mSearchForward && match == geckoRootAcc) {
98       // If searching backwards, don't include root.
99       break;
100     }
102     // we use mResultLimit != 0 to capture the case where mResultLimit is -1
103     // when it is set from the params dictionary. If that's true, we want
104     // to return all matches (ie. have no limit)
105     mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(match);
106     if (nativeMatch) {
107       // only add/count results for which there is a matching
108       // native accessible
109       [matches addObject:nativeMatch];
110       resultLimit -= 1;
111     }
113     match = mSearchForward ? p.Next(match, rule) : p.Prev(match, rule);
114   }
116   return matches;
119 - (NSArray*)performSearch {
120   Accessible* geckoRootAcc = [self rootGeckoAccessible];
121   Accessible* geckoStartAcc = [self startGeckoAccessible];
122   NSMutableArray* matches = [[[NSMutableArray alloc] init] autorelease];
123   nsString searchText;
124   nsCocoaUtils::GetStringForNSString(mSearchText, searchText);
125   for (id key in mSearchKeys) {
126     if ([key isEqualToString:@"AXAnyTypeSearchKey"]) {
127       RotorRule rule = mImmediateDescendantsOnly
128                            ? RotorRule(geckoRootAcc, searchText)
129                            : RotorRule(searchText);
131       if (searchText.IsEmpty() &&
132           [mStartElem isKindOfClass:[MOXWebAreaAccessible class]]) {
133         // Don't include the root group when a search text is defined.
134         if (id rootGroup =
135                 [static_cast<MOXWebAreaAccessible*>(mStartElem) rootGroup]) {
136           // Moving forward from web area, rootgroup; if it exists, is next.
137           [matches addObject:rootGroup];
138           if (mResultLimit == 1) {
139             // Found one match, continue in search keys for block.
140             continue;
141           }
142         }
143       }
145       if (mImmediateDescendantsOnly && mStartElem != mRoot &&
146           [mStartElem isKindOfClass:[MOXRootGroup class]]) {
147         // Moving forward from root group. If we don't match descendants,
148         // there is no match. Continue.
149         continue;
150       }
151       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
152     }
154     if ([key isEqualToString:@"AXHeadingSearchKey"]) {
155       RotorRoleRule rule =
156           mImmediateDescendantsOnly
157               ? RotorRoleRule(roles::HEADING, geckoRootAcc, searchText)
158               : RotorRoleRule(roles::HEADING, searchText);
159       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
160     }
162     if ([key isEqualToString:@"AXArticleSearchKey"]) {
163       RotorRoleRule rule =
164           mImmediateDescendantsOnly
165               ? RotorRoleRule(roles::ARTICLE, geckoRootAcc, searchText)
166               : RotorRoleRule(roles::ARTICLE, searchText);
167       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
168     }
170     if ([key isEqualToString:@"AXTableSearchKey"]) {
171       RotorRoleRule rule =
172           mImmediateDescendantsOnly
173               ? RotorRoleRule(roles::TABLE, geckoRootAcc, searchText)
174               : RotorRoleRule(roles::TABLE, searchText);
175       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
176     }
178     if ([key isEqualToString:@"AXLandmarkSearchKey"]) {
179       RotorRoleRule rule =
180           mImmediateDescendantsOnly
181               ? RotorRoleRule(roles::LANDMARK, geckoRootAcc, searchText)
182               : RotorRoleRule(roles::LANDMARK, searchText);
183       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
184     }
186     if ([key isEqualToString:@"AXListSearchKey"]) {
187       RotorRoleRule rule =
188           mImmediateDescendantsOnly
189               ? RotorRoleRule(roles::LIST, geckoRootAcc, searchText)
190               : RotorRoleRule(roles::LIST, searchText);
191       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
192     }
194     if ([key isEqualToString:@"AXLinkSearchKey"]) {
195       RotorLinkRule rule = mImmediateDescendantsOnly
196                                ? RotorLinkRule(geckoRootAcc, searchText)
197                                : RotorLinkRule(searchText);
198       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
199     }
201     if ([key isEqualToString:@"AXVisitedLinkSearchKey"]) {
202       RotorVisitedLinkRule rule =
203           mImmediateDescendantsOnly
204               ? RotorVisitedLinkRule(geckoRootAcc, searchText)
205               : RotorVisitedLinkRule(searchText);
206       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
207     }
209     if ([key isEqualToString:@"AXUnvisitedLinkSearchKey"]) {
210       RotorUnvisitedLinkRule rule =
211           mImmediateDescendantsOnly
212               ? RotorUnvisitedLinkRule(geckoRootAcc, searchText)
213               : RotorUnvisitedLinkRule(searchText);
214       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
215     }
217     if ([key isEqualToString:@"AXButtonSearchKey"]) {
218       RotorRoleRule rule =
219           mImmediateDescendantsOnly
220               ? RotorRoleRule(roles::PUSHBUTTON, geckoRootAcc, searchText)
221               : RotorRoleRule(roles::PUSHBUTTON, searchText);
222       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
223     }
225     if ([key isEqualToString:@"AXControlSearchKey"]) {
226       RotorControlRule rule = mImmediateDescendantsOnly
227                                   ? RotorControlRule(geckoRootAcc, searchText)
228                                   : RotorControlRule(searchText);
229       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
230     }
232     if ([key isEqualToString:@"AXSameTypeSearchKey"]) {
233       mozAccessible* native = GetNativeFromGeckoAccessible(geckoStartAcc);
234       NSString* macRole = [native moxRole];
235       RotorMacRoleRule rule =
236           mImmediateDescendantsOnly
237               ? RotorMacRoleRule(macRole, geckoRootAcc, searchText)
238               : RotorMacRoleRule(macRole, searchText);
239       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
240     }
242     if ([key isEqualToString:@"AXDifferentTypeSearchKey"]) {
243       mozAccessible* native = GetNativeFromGeckoAccessible(geckoStartAcc);
244       NSString* macRole = [native moxRole];
245       RotorNotMacRoleRule rule =
246           mImmediateDescendantsOnly
247               ? RotorNotMacRoleRule(macRole, geckoRootAcc, searchText)
248               : RotorNotMacRoleRule(macRole, searchText);
249       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
250     }
252     if ([key isEqualToString:@"AXRadioGroupSearchKey"]) {
253       RotorRoleRule rule =
254           mImmediateDescendantsOnly
255               ? RotorRoleRule(roles::RADIO_GROUP, geckoRootAcc, searchText)
256               : RotorRoleRule(roles::RADIO_GROUP, searchText);
257       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
258     }
260     if ([key isEqualToString:@"AXFrameSearchKey"]) {
261       RotorRoleRule rule =
262           mImmediateDescendantsOnly
263               ? RotorRoleRule(roles::DOCUMENT, geckoRootAcc, searchText)
264               : RotorRoleRule(roles::DOCUMENT, searchText);
265       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
266     }
268     if ([key isEqualToString:@"AXImageSearchKey"] ||
269         [key isEqualToString:@"AXGraphicSearchKey"]) {
270       RotorRoleRule rule =
271           mImmediateDescendantsOnly
272               ? RotorRoleRule(roles::GRAPHIC, geckoRootAcc, searchText)
273               : RotorRoleRule(roles::GRAPHIC, searchText);
274       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
275     }
277     if ([key isEqualToString:@"AXCheckBoxSearchKey"]) {
278       RotorRoleRule rule =
279           mImmediateDescendantsOnly
280               ? RotorRoleRule(roles::CHECKBUTTON, geckoRootAcc, searchText)
281               : RotorRoleRule(roles::CHECKBUTTON, searchText);
282       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
283     }
285     if ([key isEqualToString:@"AXStaticTextSearchKey"]) {
286       RotorStaticTextRule rule =
287           mImmediateDescendantsOnly
288               ? RotorStaticTextRule(geckoRootAcc, searchText)
289               : RotorStaticTextRule(searchText);
290       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
291     }
293     if ([key isEqualToString:@"AXHeadingLevel1SearchKey"]) {
294       RotorHeadingLevelRule rule =
295           mImmediateDescendantsOnly
296               ? RotorHeadingLevelRule(1, geckoRootAcc, searchText)
297               : RotorHeadingLevelRule(1, searchText);
298       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
299     }
301     if ([key isEqualToString:@"AXHeadingLevel2SearchKey"]) {
302       RotorHeadingLevelRule rule =
303           mImmediateDescendantsOnly
304               ? RotorHeadingLevelRule(2, geckoRootAcc, searchText)
305               : RotorHeadingLevelRule(2, searchText);
306       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
307     }
309     if ([key isEqualToString:@"AXHeadingLevel3SearchKey"]) {
310       RotorHeadingLevelRule rule =
311           mImmediateDescendantsOnly
312               ? RotorHeadingLevelRule(3, geckoRootAcc, searchText)
313               : RotorHeadingLevelRule(3, searchText);
314       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
315     }
317     if ([key isEqualToString:@"AXHeadingLevel4SearchKey"]) {
318       RotorHeadingLevelRule rule =
319           mImmediateDescendantsOnly
320               ? RotorHeadingLevelRule(4, geckoRootAcc, searchText)
321               : RotorHeadingLevelRule(4, searchText);
322       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
323     }
325     if ([key isEqualToString:@"AXHeadingLevel5SearchKey"]) {
326       RotorHeadingLevelRule rule =
327           mImmediateDescendantsOnly
328               ? RotorHeadingLevelRule(5, geckoRootAcc, searchText)
329               : RotorHeadingLevelRule(5, searchText);
330       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
331     }
333     if ([key isEqualToString:@"AXHeadingLevel6SearchKey"]) {
334       RotorHeadingLevelRule rule =
335           mImmediateDescendantsOnly
336               ? RotorHeadingLevelRule(6, geckoRootAcc, searchText)
337               : RotorHeadingLevelRule(6, searchText);
338       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
339     }
341     if ([key isEqualToString:@"AXBlockquoteSearchKey"]) {
342       RotorRoleRule rule =
343           mImmediateDescendantsOnly
344               ? RotorRoleRule(roles::BLOCKQUOTE, geckoRootAcc, searchText)
345               : RotorRoleRule(roles::BLOCKQUOTE, searchText);
346       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
347     }
349     if ([key isEqualToString:@"AXTextFieldSearchKey"]) {
350       RotorTextEntryRule rule =
351           mImmediateDescendantsOnly
352               ? RotorTextEntryRule(geckoRootAcc, searchText)
353               : RotorTextEntryRule(searchText);
354       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
355     }
357     if ([key isEqualToString:@"AXLiveRegionSearchKey"]) {
358       RotorLiveRegionRule rule =
359           mImmediateDescendantsOnly
360               ? RotorLiveRegionRule(geckoRootAcc, searchText)
361               : RotorLiveRegionRule(searchText);
362       [matches addObjectsFromArray:[self getMatchesForRule:rule]];
363     }
364   }
366   return matches;
369 - (void)dealloc {
370   [mSearchKeys release];
371   [super dealloc];
374 @end