MOXSearchInfo.mm (14105B)
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/. */ 7 8 #import "MOXSearchInfo.h" 9 #import "MOXWebAreaAccessible.h" 10 #import "RotorRules.h" 11 12 #include "nsCocoaUtils.h" 13 #include "DocAccessibleParent.h" 14 #include "nsAccessibilityService.h" 15 16 using namespace mozilla::a11y; 17 18 @interface MOXSearchInfo () 19 - (NSArray*)getMatchesForRule:(PivotRule&)rule; 20 21 - (Accessible*)rootGeckoAccessible; 22 23 - (Accessible*)startGeckoAccessible; 24 @end 25 26 @implementation MOXSearchInfo 27 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 } 35 36 if (id startElemParam = [params objectForKey:@"AXStartElement"]) { 37 mStartElem = startElemParam; 38 } else { 39 mStartElem = root; 40 } 41 42 mRoot = root; 43 44 mResultLimit = [[params objectForKey:@"AXResultsLimit"] intValue]; 45 46 mSearchForward = 47 [[params objectForKey:@"AXDirection"] isEqualToString:@"AXDirectionNext"]; 48 49 mImmediateDescendantsOnly = 50 [[params objectForKey:@"AXImmediateDescendantsOnly"] boolValue]; 51 52 mSearchText = [params objectForKey:@"AXSearchText"]; 53 54 return [super init]; 55 } 56 57 - (Accessible*)rootGeckoAccessible { 58 id root = 59 [mRoot isKindOfClass:[mozAccessible class]] ? mRoot : [mRoot moxParent]; 60 61 return [static_cast<mozAccessible*>(root) geckoAccessible]; 62 } 63 64 - (Accessible*)startGeckoAccessible { 65 if ([mStartElem isKindOfClass:[mozAccessible class]]) { 66 return [static_cast<mozAccessible*>(mStartElem) geckoAccessible]; 67 } 68 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]; 73 } 74 75 - (NSArray*)getMatchesForRule:(PivotRule&)rule { 76 int resultLimit = mResultLimit; 77 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 } 95 96 while (match && resultLimit != 0) { 97 if (!mSearchForward && match == geckoRootAcc) { 98 // If searching backwards, don't include root. 99 break; 100 } 101 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 } 112 113 match = mSearchForward ? p.Next(match, rule) : p.Prev(match, rule); 114 } 115 116 return matches; 117 } 118 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); 130 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 } 144 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 } 153 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 } 161 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 } 169 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 } 177 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 } 185 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 } 193 194 if ([key isEqualToString:@"AXLinkSearchKey"]) { 195 RotorLinkRule rule = mImmediateDescendantsOnly 196 ? RotorLinkRule(geckoRootAcc, searchText) 197 : RotorLinkRule(searchText); 198 [matches addObjectsFromArray:[self getMatchesForRule:rule]]; 199 } 200 201 if ([key isEqualToString:@"AXVisitedLinkSearchKey"]) { 202 RotorVisitedLinkRule rule = 203 mImmediateDescendantsOnly 204 ? RotorVisitedLinkRule(geckoRootAcc, searchText) 205 : RotorVisitedLinkRule(searchText); 206 [matches addObjectsFromArray:[self getMatchesForRule:rule]]; 207 } 208 209 if ([key isEqualToString:@"AXUnvisitedLinkSearchKey"]) { 210 RotorUnvisitedLinkRule rule = 211 mImmediateDescendantsOnly 212 ? RotorUnvisitedLinkRule(geckoRootAcc, searchText) 213 : RotorUnvisitedLinkRule(searchText); 214 [matches addObjectsFromArray:[self getMatchesForRule:rule]]; 215 } 216 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 } 224 225 if ([key isEqualToString:@"AXControlSearchKey"]) { 226 RotorControlRule rule = mImmediateDescendantsOnly 227 ? RotorControlRule(geckoRootAcc, searchText) 228 : RotorControlRule(searchText); 229 [matches addObjectsFromArray:[self getMatchesForRule:rule]]; 230 } 231 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 } 241 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 } 251 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 } 259 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 } 267 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 } 276 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 } 284 285 if ([key isEqualToString:@"AXStaticTextSearchKey"]) { 286 RotorStaticTextRule rule = 287 mImmediateDescendantsOnly 288 ? RotorStaticTextRule(geckoRootAcc, searchText) 289 : RotorStaticTextRule(searchText); 290 [matches addObjectsFromArray:[self getMatchesForRule:rule]]; 291 } 292 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 } 300 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 } 308 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 } 316 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 } 324 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 } 332 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 } 340 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 } 348 349 if ([key isEqualToString:@"AXTextFieldSearchKey"]) { 350 RotorTextEntryRule rule = 351 mImmediateDescendantsOnly 352 ? RotorTextEntryRule(geckoRootAcc, searchText) 353 : RotorTextEntryRule(searchText); 354 [matches addObjectsFromArray:[self getMatchesForRule:rule]]; 355 } 356 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 365 if ([key isEqualToString:@"AXKeyboardFocusableSearchKey"]) { 366 RotorFocusableRule rule = 367 mImmediateDescendantsOnly 368 ? RotorFocusableRule(geckoRootAcc, searchText) 369 : RotorFocusableRule(searchText); 370 [matches addObjectsFromArray:[self getMatchesForRule:rule]]; 371 } 372 } 373 374 return matches; 375 } 376 377 - (void)dealloc { 378 [mSearchKeys release]; 379 [super dealloc]; 380 } 381 382 @end