gecko-dev/accessible/mac/RotorRules.mm

371 строка
12 KiB
Plaintext

/* 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 "RotorRules.h"
#include "nsCocoaUtils.h"
#include "DocAccessibleParent.h"
using namespace mozilla::a11y;
// Generic Rotor Rule
RotorRule::RotorRule(AccessibleOrProxy& aDirectDescendantsFrom)
: mDirectDescendantsFrom(aDirectDescendantsFrom) {}
RotorRule::RotorRule() : mDirectDescendantsFrom(nullptr) {}
uint16_t RotorRule::Match(const AccessibleOrProxy& aAccOrProxy) {
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
if (nsAccUtils::MustPrune(aAccOrProxy)) {
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
if (!mDirectDescendantsFrom.IsNull() &&
(aAccOrProxy != mDirectDescendantsFrom)) {
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
if ([GetNativeFromGeckoAccessible(aAccOrProxy) isAccessibilityElement]) {
result |= nsIAccessibleTraversalRule::FILTER_MATCH;
}
return result;
}
// Rotor Role Rule
RotorRoleRule::RotorRoleRule(role aRole,
AccessibleOrProxy& aDirectDescendantsFrom)
: RotorRule(aDirectDescendantsFrom), mRole(aRole){};
RotorRoleRule::RotorRoleRule(role aRole) : RotorRule(), mRole(aRole){};
uint16_t RotorRoleRule::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 of the desired role, we flip the match bit to "unmatch"
// otherwise, the match persists.
if ((result & nsIAccessibleTraversalRule::FILTER_MATCH) &&
aAccOrProxy.Role() != mRole) {
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
return result;
}
// Rotor Mac Role Rule
RotorMacRoleRule::RotorMacRoleRule(NSString* aMacRole,
AccessibleOrProxy& aDirectDescendantsFrom)
: RotorRule(aDirectDescendantsFrom), mMacRole(aMacRole) {
[mMacRole retain];
};
RotorMacRoleRule::RotorMacRoleRule(NSString* aMacRole)
: RotorRule(), mMacRole(aMacRole) {
[mMacRole retain];
};
RotorMacRoleRule::~RotorMacRoleRule() { [mMacRole release]; }
uint16_t RotorMacRoleRule::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 of 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 Control Rule
RotorControlRule::RotorControlRule(AccessibleOrProxy& aDirectDescendantsFrom)
: RotorRule(aDirectDescendantsFrom){};
RotorControlRule::RotorControlRule() : RotorRule(){};
uint16_t RotorControlRule::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 of the desired role, we flip the match bit to "unmatch"
// otherwise, the match persists.
if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
switch (aAccOrProxy.Role()) {
case roles::PUSHBUTTON:
case roles::SPINBUTTON:
case roles::DETAILS:
case roles::CHECKBUTTON:
case roles::COLOR_CHOOSER:
case roles::BUTTONDROPDOWNGRID: // xul colorpicker
case roles::LISTBOX:
case roles::COMBOBOX:
case roles::EDITCOMBOBOX:
case roles::RADIOBUTTON:
case roles::RADIO_GROUP:
case roles::PAGETAB:
case roles::SLIDER:
case roles::SWITCH:
case roles::ENTRY:
case roles::OUTLINE:
case roles::PASSWORD_TEXT:
return result;
case roles::DATE_EDITOR:
case roles::TIME_EDITOR:
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
return result;
case roles::GROUPING: {
// Groupings are sometimes used (like radio groups) to denote
// sets of controls. If that's the case, we want to surface
// them. We also want to surface grouped time and date controls.
for (unsigned int i = 0; i < aAccOrProxy.ChildCount(); i++) {
AccessibleOrProxy currChild = aAccOrProxy.ChildAt(i);
if (currChild.Role() == roles::CHECKBUTTON ||
currChild.Role() == roles::SWITCH ||
currChild.Role() == roles::SPINBUTTON ||
currChild.Role() == roles::RADIOBUTTON) {
return result;
}
}
// if we iterated through the groups children and didn't
// find a control with one of the roles above, we should
// ignore this grouping
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
return result;
}
default:
// if we did not match on any above role, we should
// ignore this accessible.
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
return result;
}
// Rotor TextEntry Rule
RotorTextEntryRule::RotorTextEntryRule(
AccessibleOrProxy& aDirectDescendantsFrom)
: RotorRule(aDirectDescendantsFrom){};
RotorTextEntryRule::RotorTextEntryRule() : RotorRule(){};
uint16_t RotorTextEntryRule::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 of the desired role, we flip the match bit to "unmatch"
// otherwise, the match persists.
if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
if (aAccOrProxy.Role() != roles::PASSWORD_TEXT &&
aAccOrProxy.Role() != roles::ENTRY) {
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
return result;
}
// Rotor Link Rule
RotorLinkRule::RotorLinkRule(AccessibleOrProxy& aDirectDescendantsFrom)
: RotorRule(aDirectDescendantsFrom){};
RotorLinkRule::RotorLinkRule() : RotorRule(){};
uint16_t RotorLinkRule::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 of 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:@"AXLink"]) {
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
return result;
}
RotorVisitedLinkRule::RotorVisitedLinkRule() : RotorLinkRule() {}
RotorVisitedLinkRule::RotorVisitedLinkRule(
AccessibleOrProxy& aDirectDescendantsFrom)
: RotorLinkRule(aDirectDescendantsFrom) {}
uint16_t RotorVisitedLinkRule::Match(const AccessibleOrProxy& aAccOrProxy) {
uint16_t result = RotorLinkRule::Match(aAccOrProxy);
if (result & nsIAccessibleTraversalRule::FILTER_MATCH) {
mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAccOrProxy);
if (![[nativeMatch moxVisited] boolValue]) {
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
return result;
}
RotorUnvisitedLinkRule::RotorUnvisitedLinkRule() : RotorLinkRule() {}
RotorUnvisitedLinkRule::RotorUnvisitedLinkRule(
AccessibleOrProxy& aDirectDescendantsFrom)
: RotorLinkRule(aDirectDescendantsFrom) {}
uint16_t RotorUnvisitedLinkRule::Match(const AccessibleOrProxy& aAccOrProxy) {
uint16_t result = RotorLinkRule::Match(aAccOrProxy);
if (result & nsIAccessibleTraversalRule::FILTER_MATCH) {
mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAccOrProxy);
if ([[nativeMatch moxVisited] boolValue]) {
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
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(
AccessibleOrProxy& aDirectDescendantsFrom)
: RotorRule(aDirectDescendantsFrom){};
RotorStaticTextRule::RotorStaticTextRule() : RotorRule(){};
uint16_t RotorStaticTextRule::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 of 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:@"AXStaticText"]) {
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
return result;
}
// Rotor Heading Level Rule
RotorHeadingLevelRule::RotorHeadingLevelRule(
int32_t aLevel, AccessibleOrProxy& aDirectDescendantsFrom)
: RotorRoleRule(roles::HEADING, aDirectDescendantsFrom), mLevel(aLevel){};
RotorHeadingLevelRule::RotorHeadingLevelRule(int32_t aLevel)
: RotorRoleRule(roles::HEADING), mLevel(aLevel){};
uint16_t RotorHeadingLevelRule::Match(const AccessibleOrProxy& aAccOrProxy) {
uint16_t result = RotorRoleRule::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 of the desired heading level, we flip the match bit to
// "unmatch" otherwise, the match persists.
if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
int32_t currLevel = 0;
if (Accessible* acc = aAccOrProxy.AsAccessible()) {
currLevel = acc->GroupPosition().level;
} else if (ProxyAccessible* proxy = aAccOrProxy.AsProxy()) {
currLevel = proxy->GroupPosition().level;
}
if (currLevel != mLevel) {
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
return result;
}
uint16_t RotorLiveRegionRule::Match(const AccessibleOrProxy& aAccOrProxy) {
uint16_t result = RotorRule::Match(aAccOrProxy);
if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAccOrProxy);
if (![nativeMatch moxIsLiveRegion]) {
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
return result;
}
// Outline Rule
OutlineRule::OutlineRule() : RotorRule(){};
uint16_t OutlineRule::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 (result & nsIAccessibleTraversalRule::FILTER_MATCH) {
if (aAccOrProxy.Role() == roles::OUTLINE) {
// if the match is an outline, we ignore all children here
// and unmatch the outline itself
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
} else if (aAccOrProxy.Role() != roles::OUTLINEITEM) {
// if the match is not an outline item, we unmatch here
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
}
}
return result;
}