From c5ff480218fbb4bd00831d3edbe2ed5929b77990 Mon Sep 17 00:00:00 2001 From: Morgan Reschenberg Date: Thu, 15 Oct 2020 16:42:14 +0000 Subject: [PATCH] Bug 1668101: Support same/different type naviagation with VO r=eeejay Differential Revision: https://phabricator.services.mozilla.com/D91833 --- accessible/mac/MOXSearchInfo.mm | 19 ++++++ accessible/mac/RotorRules.h | 14 +++- accessible/mac/RotorRules.mm | 25 ++++++++ .../tests/browser/mac/browser_navigate.js | 64 +++++++++++++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/accessible/mac/MOXSearchInfo.mm b/accessible/mac/MOXSearchInfo.mm index d6ccf50b5c59..16f93df7663d 100644 --- a/accessible/mac/MOXSearchInfo.mm +++ b/accessible/mac/MOXSearchInfo.mm @@ -113,6 +113,7 @@ using namespace mozilla::a11y; - (NSArray*)performSearch { AccessibleOrProxy geckoRootAcc = [self rootGeckoAccessible]; + AccessibleOrProxy geckoStartAcc = [self startGeckoAccessible]; NSMutableArray* matches = [[NSMutableArray alloc] init]; for (id key in mSearchKeys) { if ([key isEqualToString:@"AXAnyTypeSearchKey"]) { @@ -218,6 +219,24 @@ using namespace mozilla::a11y; [matches addObjectsFromArray:[self getMatchesForRule:rule]]; } + if ([key isEqualToString:@"AXSameTypeSearchKey"]) { + mozAccessible* native = GetNativeFromGeckoAccessible(geckoStartAcc); + NSString* macRole = [native moxRole]; + RotorMacRoleRule rule = mImmediateDescendantsOnly + ? RotorMacRoleRule(macRole, geckoRootAcc) + : RotorMacRoleRule(macRole); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXDifferentTypeSearchKey"]) { + mozAccessible* native = GetNativeFromGeckoAccessible(geckoStartAcc); + NSString* macRole = [native moxRole]; + RotorNotMacRoleRule rule = + mImmediateDescendantsOnly ? RotorNotMacRoleRule(macRole, geckoRootAcc) + : RotorNotMacRoleRule(macRole); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + if ([key isEqualToString:@"AXRadioGroupSearchKey"]) { RotorRoleRule rule = mImmediateDescendantsOnly ? RotorRoleRule(roles::RADIO_GROUP, geckoRootAcc) diff --git a/accessible/mac/RotorRules.h b/accessible/mac/RotorRules.h index 3424372ca9e8..536b84556bda 100644 --- a/accessible/mac/RotorRules.h +++ b/accessible/mac/RotorRules.h @@ -45,7 +45,7 @@ class RotorMacRoleRule : public RotorRule { ~RotorMacRoleRule(); virtual uint16_t Match(const AccessibleOrProxy& aAccOrProxy) override; - private: + protected: NSString* mMacRole; }; @@ -81,6 +81,18 @@ class RotorUnvisitedLinkRule final : public RotorLinkRule { virtual uint16_t Match(const AccessibleOrProxy& aAccOrProxy) override; }; +/** + * This rule matches all accessibles that satisfy the "boilerplate" + * pivot conditions and have a corresponding native accessible. + */ +class RotorNotMacRoleRule : public RotorMacRoleRule { + public: + explicit RotorNotMacRoleRule(NSString* aMacRole, + AccessibleOrProxy& aDirectDescendantsFrom); + explicit RotorNotMacRoleRule(NSString* aMacRole); + uint16_t Match(const AccessibleOrProxy& aAccOrProxy) override; +}; + class RotorStaticTextRule : public RotorRule { public: explicit RotorStaticTextRule(); diff --git a/accessible/mac/RotorRules.mm b/accessible/mac/RotorRules.mm index 82991a49988b..0b99334f4300 100644 --- a/accessible/mac/RotorRules.mm +++ b/accessible/mac/RotorRules.mm @@ -226,6 +226,31 @@ uint16_t RotorUnvisitedLinkRule::Match(const AccessibleOrProxy& aAccOrProxy) { return result; } +// Match Not Rule + +RotorNotMacRoleRule::RotorNotMacRoleRule( + NSString* aMacRole, AccessibleOrProxy& aDirectDescendantsFrom) + : RotorMacRoleRule(aMacRole, aDirectDescendantsFrom) {} + +RotorNotMacRoleRule::RotorNotMacRoleRule(NSString* aMacRole) + : RotorMacRoleRule(aMacRole) {} + +uint16_t RotorNotMacRoleRule::Match(const AccessibleOrProxy& aAccOrProxy) { + uint16_t result = RotorRule::Match(aAccOrProxy); + + // if a match was found in the base-class's Match function, + // it is valid to consider that match again here. if it is + // not different from the desired role, we flip the + // match bit to "unmatch" otherwise, the match persists. + if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) { + mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAccOrProxy); + if ([[nativeMatch moxRole] isEqualToString:mMacRole]) { + result &= ~nsIAccessibleTraversalRule::FILTER_MATCH; + } + } + return result; +} + // Rotor Static Text Rule RotorStaticTextRule::RotorStaticTextRule( diff --git a/accessible/tests/browser/mac/browser_navigate.js b/accessible/tests/browser/mac/browser_navigate.js index da6c87f5f8f3..69486676e49d 100644 --- a/accessible/tests/browser/mac/browser_navigate.js +++ b/accessible/tests/browser/mac/browser_navigate.js @@ -4,6 +4,70 @@ "use strict"; +/** + * Test navigation of same/different type content + */ +addAccessibleTask( + `

hello

+ world
+ I am a link +

goodbye

`, + async (browser, accDoc) => { + const searchPred = { + AXSearchKey: "AXSameTypeSearchKey", + AXImmediateDescendantsOnly: 0, + AXResultsLimit: 1, + AXDirection: "AXDirectionNext", + }; + + const hello = getNativeInterface(accDoc, "hello"); + const goodbye = getNativeInterface(accDoc, "goodbye"); + const webArea = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + + searchPred.AXStartElement = hello; + + let sameItem = webArea.getParameterizedAttributeValue( + "AXUIElementsForSearchPredicate", + NSDictionary(searchPred) + ); + + is(sameItem.length, 1, "Found one item"); + is( + "goodbye", + sameItem[0].getAttributeValue("AXTitle"), + "Found correct item of same type" + ); + + searchPred.AXDirection = "AXDirectionPrevious"; + searchPred.AXStartElement = goodbye; + sameItem = webArea.getParameterizedAttributeValue( + "AXUIElementsForSearchPredicate", + NSDictionary(searchPred) + ); + + is(sameItem.length, 1, "Found one item"); + is( + "hello", + sameItem[0].getAttributeValue("AXTitle"), + "Found correct item of same type" + ); + + searchPred.AXSearchKey = "AXDifferentTypeSearchKey"; + let diffItem = webArea.getParameterizedAttributeValue( + "AXUIElementsForSearchPredicate", + NSDictionary(searchPred) + ); + is(diffItem.length, 1, "Found one item"); + is( + "I am a link", + diffItem[0].getAttributeValue("AXValue"), + "Found correct item of different type" + ); + } +); + /** * Test navigation of heading levels */