2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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;
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];
36 if (id startElemParam = [params objectForKey:@"AXStartElement"]) {
37 mStartElem = startElemParam;
44 mResultLimit = [[params objectForKey:@"AXResultsLimit"] intValue];
47 [[params objectForKey:@"AXDirection"] isEqualToString:@"AXDirectionNext"];
49 mImmediateDescendantsOnly =
50 [[params objectForKey:@"AXImmediateDescendantsOnly"] boolValue];
52 mSearchText = [params objectForKey:@"AXSearchText"];
57 - (Accessible*)rootGeckoAccessible {
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];
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
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);
85 match = p.Next(geckoStartAcc, rule);
88 if (geckoRootAcc == geckoStartAcc) {
89 // If we have no explicit start accessible, start from the last match.
92 match = p.Prev(geckoStartAcc, rule);
96 while (match && resultLimit != 0) {
97 if (!mSearchForward && match == geckoRootAcc) {
98 // If searching backwards, don't include root.
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);
107 // only add/count results for which there is a matching
109 [matches addObject:nativeMatch];
113 match = mSearchForward ? p.Next(match, rule) : p.Prev(match, rule);
119 - (NSArray*)performSearch {
120 Accessible* geckoRootAcc = [self rootGeckoAccessible];
121 Accessible* geckoStartAcc = [self startGeckoAccessible];
122 NSMutableArray* matches = [[[NSMutableArray alloc] init] autorelease];
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.
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.
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.
151 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
154 if ([key isEqualToString:@"AXHeadingSearchKey"]) {
156 mImmediateDescendantsOnly
157 ? RotorRoleRule(roles::HEADING, geckoRootAcc, searchText)
158 : RotorRoleRule(roles::HEADING, searchText);
159 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
162 if ([key isEqualToString:@"AXArticleSearchKey"]) {
164 mImmediateDescendantsOnly
165 ? RotorRoleRule(roles::ARTICLE, geckoRootAcc, searchText)
166 : RotorRoleRule(roles::ARTICLE, searchText);
167 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
170 if ([key isEqualToString:@"AXTableSearchKey"]) {
172 mImmediateDescendantsOnly
173 ? RotorRoleRule(roles::TABLE, geckoRootAcc, searchText)
174 : RotorRoleRule(roles::TABLE, searchText);
175 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
178 if ([key isEqualToString:@"AXLandmarkSearchKey"]) {
180 mImmediateDescendantsOnly
181 ? RotorRoleRule(roles::LANDMARK, geckoRootAcc, searchText)
182 : RotorRoleRule(roles::LANDMARK, searchText);
183 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
186 if ([key isEqualToString:@"AXListSearchKey"]) {
188 mImmediateDescendantsOnly
189 ? RotorRoleRule(roles::LIST, geckoRootAcc, searchText)
190 : RotorRoleRule(roles::LIST, searchText);
191 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
194 if ([key isEqualToString:@"AXLinkSearchKey"]) {
195 RotorLinkRule rule = mImmediateDescendantsOnly
196 ? RotorLinkRule(geckoRootAcc, searchText)
197 : RotorLinkRule(searchText);
198 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
201 if ([key isEqualToString:@"AXVisitedLinkSearchKey"]) {
202 RotorVisitedLinkRule rule =
203 mImmediateDescendantsOnly
204 ? RotorVisitedLinkRule(geckoRootAcc, searchText)
205 : RotorVisitedLinkRule(searchText);
206 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
209 if ([key isEqualToString:@"AXUnvisitedLinkSearchKey"]) {
210 RotorUnvisitedLinkRule rule =
211 mImmediateDescendantsOnly
212 ? RotorUnvisitedLinkRule(geckoRootAcc, searchText)
213 : RotorUnvisitedLinkRule(searchText);
214 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
217 if ([key isEqualToString:@"AXButtonSearchKey"]) {
219 mImmediateDescendantsOnly
220 ? RotorRoleRule(roles::PUSHBUTTON, geckoRootAcc, searchText)
221 : RotorRoleRule(roles::PUSHBUTTON, searchText);
222 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
225 if ([key isEqualToString:@"AXControlSearchKey"]) {
226 RotorControlRule rule = mImmediateDescendantsOnly
227 ? RotorControlRule(geckoRootAcc, searchText)
228 : RotorControlRule(searchText);
229 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
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]];
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]];
252 if ([key isEqualToString:@"AXRadioGroupSearchKey"]) {
254 mImmediateDescendantsOnly
255 ? RotorRoleRule(roles::RADIO_GROUP, geckoRootAcc, searchText)
256 : RotorRoleRule(roles::RADIO_GROUP, searchText);
257 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
260 if ([key isEqualToString:@"AXFrameSearchKey"]) {
262 mImmediateDescendantsOnly
263 ? RotorRoleRule(roles::DOCUMENT, geckoRootAcc, searchText)
264 : RotorRoleRule(roles::DOCUMENT, searchText);
265 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
268 if ([key isEqualToString:@"AXImageSearchKey"] ||
269 [key isEqualToString:@"AXGraphicSearchKey"]) {
271 mImmediateDescendantsOnly
272 ? RotorRoleRule(roles::GRAPHIC, geckoRootAcc, searchText)
273 : RotorRoleRule(roles::GRAPHIC, searchText);
274 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
277 if ([key isEqualToString:@"AXCheckBoxSearchKey"]) {
279 mImmediateDescendantsOnly
280 ? RotorRoleRule(roles::CHECKBUTTON, geckoRootAcc, searchText)
281 : RotorRoleRule(roles::CHECKBUTTON, searchText);
282 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
285 if ([key isEqualToString:@"AXStaticTextSearchKey"]) {
286 RotorStaticTextRule rule =
287 mImmediateDescendantsOnly
288 ? RotorStaticTextRule(geckoRootAcc, searchText)
289 : RotorStaticTextRule(searchText);
290 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
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]];
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]];
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]];
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]];
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]];
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]];
341 if ([key isEqualToString:@"AXBlockquoteSearchKey"]) {
343 mImmediateDescendantsOnly
344 ? RotorRoleRule(roles::BLOCKQUOTE, geckoRootAcc, searchText)
345 : RotorRoleRule(roles::BLOCKQUOTE, searchText);
346 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
349 if ([key isEqualToString:@"AXTextFieldSearchKey"]) {
350 RotorTextEntryRule rule =
351 mImmediateDescendantsOnly
352 ? RotorTextEntryRule(geckoRootAcc, searchText)
353 : RotorTextEntryRule(searchText);
354 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
357 if ([key isEqualToString:@"AXLiveRegionSearchKey"]) {
358 RotorLiveRegionRule rule =
359 mImmediateDescendantsOnly
360 ? RotorLiveRegionRule(geckoRootAcc, searchText)
361 : RotorLiveRegionRule(searchText);
362 [matches addObjectsFromArray:[self getMatchesForRule:rule]];
370 [mSearchKeys release];