diff options
Diffstat (limited to '')
-rw-r--r-- | accessible/mac/MOXSearchInfo.mm | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/accessible/mac/MOXSearchInfo.mm b/accessible/mac/MOXSearchInfo.mm new file mode 100644 index 0000000000..ce7a7dedf3 --- /dev/null +++ b/accessible/mac/MOXSearchInfo.mm @@ -0,0 +1,374 @@ +/* clang-format off */ +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* clang-format on */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#import "MOXSearchInfo.h" +#import "MOXWebAreaAccessible.h" +#import "RotorRules.h" + +#include "nsCocoaUtils.h" +#include "DocAccessibleParent.h" +#include "nsAccessibilityService.h" + +using namespace mozilla::a11y; + +@interface MOXSearchInfo () +- (NSArray*)getMatchesForRule:(PivotRule&)rule; + +- (Accessible*)rootGeckoAccessible; + +- (Accessible*)startGeckoAccessible; +@end + +@implementation MOXSearchInfo + +- (id)initWithParameters:(NSDictionary*)params + andRoot:(MOXAccessibleBase*)root { + if (id searchKeyParam = [params objectForKey:@"AXSearchKey"]) { + mSearchKeys = [searchKeyParam isKindOfClass:[NSString class]] + ? [[NSArray alloc] initWithObjects:searchKeyParam, nil] + : [searchKeyParam retain]; + } + + if (id startElemParam = [params objectForKey:@"AXStartElement"]) { + mStartElem = startElemParam; + } else { + mStartElem = root; + } + + mRoot = root; + + mResultLimit = [[params objectForKey:@"AXResultsLimit"] intValue]; + + mSearchForward = + [[params objectForKey:@"AXDirection"] isEqualToString:@"AXDirectionNext"]; + + mImmediateDescendantsOnly = + [[params objectForKey:@"AXImmediateDescendantsOnly"] boolValue]; + + mSearchText = [params objectForKey:@"AXSearchText"]; + + return [super init]; +} + +- (Accessible*)rootGeckoAccessible { + id root = + [mRoot isKindOfClass:[mozAccessible class]] ? mRoot : [mRoot moxParent]; + + return [static_cast<mozAccessible*>(root) geckoAccessible]; +} + +- (Accessible*)startGeckoAccessible { + if ([mStartElem isKindOfClass:[mozAccessible class]]) { + return [static_cast<mozAccessible*>(mStartElem) geckoAccessible]; + } + + // If it isn't a mozAccessible, it doesn't have a gecko accessible + // this is most likely the root group. Use the gecko doc as the start + // accessible. + return [self rootGeckoAccessible]; +} + +- (NSArray*)getMatchesForRule:(PivotRule&)rule { + int resultLimit = mResultLimit; + + NSMutableArray<mozAccessible*>* matches = + [[[NSMutableArray alloc] init] autorelease]; + Accessible* geckoRootAcc = [self rootGeckoAccessible]; + Accessible* geckoStartAcc = [self startGeckoAccessible]; + Pivot p = Pivot(geckoRootAcc); + Accessible* match; + if (mSearchForward) { + match = p.Next(geckoStartAcc, rule); + } else { + // Search backwards + if (geckoRootAcc == geckoStartAcc) { + // If we have no explicit start accessible, start from the last match. + match = p.Last(rule); + } else { + match = p.Prev(geckoStartAcc, rule); + } + } + + while (match && resultLimit != 0) { + if (!mSearchForward && match == geckoRootAcc) { + // If searching backwards, don't include root. + break; + } + + // we use mResultLimit != 0 to capture the case where mResultLimit is -1 + // when it is set from the params dictionary. If that's true, we want + // to return all matches (ie. have no limit) + mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(match); + if (nativeMatch) { + // only add/count results for which there is a matching + // native accessible + [matches addObject:nativeMatch]; + resultLimit -= 1; + } + + match = mSearchForward ? p.Next(match, rule) : p.Prev(match, rule); + } + + return matches; +} + +- (NSArray*)performSearch { + Accessible* geckoRootAcc = [self rootGeckoAccessible]; + Accessible* geckoStartAcc = [self startGeckoAccessible]; + NSMutableArray* matches = [[[NSMutableArray alloc] init] autorelease]; + nsString searchText; + nsCocoaUtils::GetStringForNSString(mSearchText, searchText); + for (id key in mSearchKeys) { + if ([key isEqualToString:@"AXAnyTypeSearchKey"]) { + RotorRule rule = mImmediateDescendantsOnly + ? RotorRule(geckoRootAcc, searchText) + : RotorRule(searchText); + + if (searchText.IsEmpty() && + [mStartElem isKindOfClass:[MOXWebAreaAccessible class]]) { + // Don't include the root group when a search text is defined. + if (id rootGroup = + [static_cast<MOXWebAreaAccessible*>(mStartElem) rootGroup]) { + // Moving forward from web area, rootgroup; if it exists, is next. + [matches addObject:rootGroup]; + if (mResultLimit == 1) { + // Found one match, continue in search keys for block. + continue; + } + } + } + + if (mImmediateDescendantsOnly && mStartElem != mRoot && + [mStartElem isKindOfClass:[MOXRootGroup class]]) { + // Moving forward from root group. If we don't match descendants, + // there is no match. Continue. + continue; + } + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXHeadingSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::HEADING, geckoRootAcc, searchText) + : RotorRoleRule(roles::HEADING, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXArticleSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::ARTICLE, geckoRootAcc, searchText) + : RotorRoleRule(roles::ARTICLE, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXTableSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::TABLE, geckoRootAcc, searchText) + : RotorRoleRule(roles::TABLE, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXLandmarkSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::LANDMARK, geckoRootAcc, searchText) + : RotorRoleRule(roles::LANDMARK, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXListSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::LIST, geckoRootAcc, searchText) + : RotorRoleRule(roles::LIST, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXLinkSearchKey"]) { + RotorLinkRule rule = mImmediateDescendantsOnly + ? RotorLinkRule(geckoRootAcc, searchText) + : RotorLinkRule(searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXVisitedLinkSearchKey"]) { + RotorVisitedLinkRule rule = + mImmediateDescendantsOnly + ? RotorVisitedLinkRule(geckoRootAcc, searchText) + : RotorVisitedLinkRule(searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXUnvisitedLinkSearchKey"]) { + RotorUnvisitedLinkRule rule = + mImmediateDescendantsOnly + ? RotorUnvisitedLinkRule(geckoRootAcc, searchText) + : RotorUnvisitedLinkRule(searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXButtonSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::PUSHBUTTON, geckoRootAcc, searchText) + : RotorRoleRule(roles::PUSHBUTTON, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXControlSearchKey"]) { + RotorControlRule rule = mImmediateDescendantsOnly + ? RotorControlRule(geckoRootAcc, searchText) + : RotorControlRule(searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXSameTypeSearchKey"]) { + mozAccessible* native = GetNativeFromGeckoAccessible(geckoStartAcc); + NSString* macRole = [native moxRole]; + RotorMacRoleRule rule = + mImmediateDescendantsOnly + ? RotorMacRoleRule(macRole, geckoRootAcc, searchText) + : RotorMacRoleRule(macRole, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXDifferentTypeSearchKey"]) { + mozAccessible* native = GetNativeFromGeckoAccessible(geckoStartAcc); + NSString* macRole = [native moxRole]; + RotorNotMacRoleRule rule = + mImmediateDescendantsOnly + ? RotorNotMacRoleRule(macRole, geckoRootAcc, searchText) + : RotorNotMacRoleRule(macRole, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXRadioGroupSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::RADIO_GROUP, geckoRootAcc, searchText) + : RotorRoleRule(roles::RADIO_GROUP, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXFrameSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::DOCUMENT, geckoRootAcc, searchText) + : RotorRoleRule(roles::DOCUMENT, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXImageSearchKey"] || + [key isEqualToString:@"AXGraphicSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::GRAPHIC, geckoRootAcc, searchText) + : RotorRoleRule(roles::GRAPHIC, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXCheckBoxSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::CHECKBUTTON, geckoRootAcc, searchText) + : RotorRoleRule(roles::CHECKBUTTON, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXStaticTextSearchKey"]) { + RotorStaticTextRule rule = + mImmediateDescendantsOnly + ? RotorStaticTextRule(geckoRootAcc, searchText) + : RotorStaticTextRule(searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXHeadingLevel1SearchKey"]) { + RotorHeadingLevelRule rule = + mImmediateDescendantsOnly + ? RotorHeadingLevelRule(1, geckoRootAcc, searchText) + : RotorHeadingLevelRule(1, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXHeadingLevel2SearchKey"]) { + RotorHeadingLevelRule rule = + mImmediateDescendantsOnly + ? RotorHeadingLevelRule(2, geckoRootAcc, searchText) + : RotorHeadingLevelRule(2, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXHeadingLevel3SearchKey"]) { + RotorHeadingLevelRule rule = + mImmediateDescendantsOnly + ? RotorHeadingLevelRule(3, geckoRootAcc, searchText) + : RotorHeadingLevelRule(3, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXHeadingLevel4SearchKey"]) { + RotorHeadingLevelRule rule = + mImmediateDescendantsOnly + ? RotorHeadingLevelRule(4, geckoRootAcc, searchText) + : RotorHeadingLevelRule(4, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXHeadingLevel5SearchKey"]) { + RotorHeadingLevelRule rule = + mImmediateDescendantsOnly + ? RotorHeadingLevelRule(5, geckoRootAcc, searchText) + : RotorHeadingLevelRule(5, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXHeadingLevel6SearchKey"]) { + RotorHeadingLevelRule rule = + mImmediateDescendantsOnly + ? RotorHeadingLevelRule(6, geckoRootAcc, searchText) + : RotorHeadingLevelRule(6, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXBlockquoteSearchKey"]) { + RotorRoleRule rule = + mImmediateDescendantsOnly + ? RotorRoleRule(roles::BLOCKQUOTE, geckoRootAcc, searchText) + : RotorRoleRule(roles::BLOCKQUOTE, searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXTextFieldSearchKey"]) { + RotorTextEntryRule rule = + mImmediateDescendantsOnly + ? RotorTextEntryRule(geckoRootAcc, searchText) + : RotorTextEntryRule(searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXLiveRegionSearchKey"]) { + RotorLiveRegionRule rule = + mImmediateDescendantsOnly + ? RotorLiveRegionRule(geckoRootAcc, searchText) + : RotorLiveRegionRule(searchText); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + } + + return matches; +} + +- (void)dealloc { + [mSearchKeys release]; + [super dealloc]; +} + +@end |