mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
abdbe44b83
Differential Revision: https://phabricator.services.mozilla.com/D175591
399 lines
14 KiB
Plaintext
399 lines
14 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"
|
|
#include "nsIAccessiblePivot.h"
|
|
#include "nsAccUtils.h"
|
|
|
|
#include "mozilla/StaticPrefs_accessibility.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::a11y;
|
|
|
|
// Generic Rotor Rule
|
|
|
|
RotorRule::RotorRule(Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: mDirectDescendantsFrom(aDirectDescendantsFrom),
|
|
mSearchText(aSearchText) {}
|
|
|
|
RotorRule::RotorRule(const nsString& aSearchText)
|
|
: mDirectDescendantsFrom(nullptr), mSearchText(aSearchText) {}
|
|
|
|
uint16_t RotorRule::Match(Accessible* aAcc) {
|
|
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
|
|
|
if (nsAccUtils::MustPrune(aAcc)) {
|
|
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
}
|
|
|
|
if (mDirectDescendantsFrom && (aAcc != mDirectDescendantsFrom)) {
|
|
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
}
|
|
|
|
if ([GetNativeFromGeckoAccessible(aAcc) isAccessibilityElement]) {
|
|
result |= nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
|
|
if ((result & nsIAccessibleTraversalRule::FILTER_MATCH) &&
|
|
!mSearchText.IsEmpty()) {
|
|
// If we have a non-empty search text, there are some roles
|
|
// we can safely ignore.
|
|
switch (aAcc->Role()) {
|
|
case roles::LANDMARK:
|
|
case roles::COMBOBOX:
|
|
case roles::LISTITEM:
|
|
case roles::COMBOBOX_LIST:
|
|
case roles::MENUBAR:
|
|
case roles::MENUPOPUP:
|
|
case roles::DOCUMENT:
|
|
case roles::APPLICATION:
|
|
// XXX: These roles either have AXTitle/AXDescription overridden as
|
|
// empty, or should never be returned in search text results. This
|
|
// should be better mapped somewhere.
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
break;
|
|
default:
|
|
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
|
|
// If caching is enabled and we have a non-empty search text,
|
|
// we can query the cached name to do furhter filtering. If
|
|
// the cache is disabled this will happen in the post-filter stage
|
|
// where we send a sync message to content.
|
|
nsAutoString name;
|
|
aAcc->Name(name);
|
|
if (!CaseInsensitiveFindInReadable(mSearchText, name)) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Rotor Role Rule
|
|
|
|
RotorRoleRule::RotorRoleRule(role aRole, Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorRule(aDirectDescendantsFrom, aSearchText), mRole(aRole){};
|
|
|
|
RotorRoleRule::RotorRoleRule(role aRole, const nsString& aSearchText)
|
|
: RotorRule(aSearchText), mRole(aRole){};
|
|
|
|
uint16_t RotorRoleRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRule::Match(aAcc);
|
|
|
|
// 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) &&
|
|
aAcc->Role() != mRole) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Rotor Mac Role Rule
|
|
|
|
RotorMacRoleRule::RotorMacRoleRule(NSString* aMacRole,
|
|
Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorRule(aDirectDescendantsFrom, aSearchText), mMacRole(aMacRole) {
|
|
[mMacRole retain];
|
|
};
|
|
|
|
RotorMacRoleRule::RotorMacRoleRule(NSString* aMacRole,
|
|
const nsString& aSearchText)
|
|
: RotorRule(aSearchText), mMacRole(aMacRole) {
|
|
[mMacRole retain];
|
|
};
|
|
|
|
RotorMacRoleRule::~RotorMacRoleRule() { [mMacRole release]; }
|
|
|
|
uint16_t RotorMacRoleRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRule::Match(aAcc);
|
|
|
|
// 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(aAcc);
|
|
if (![[nativeMatch moxRole] isEqualToString:mMacRole]) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Rotor Control Rule
|
|
|
|
RotorControlRule::RotorControlRule(Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorRule(aDirectDescendantsFrom, aSearchText){};
|
|
|
|
RotorControlRule::RotorControlRule(const nsString& aSearchText)
|
|
: RotorRule(aSearchText){};
|
|
|
|
uint16_t RotorControlRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRule::Match(aAcc);
|
|
|
|
// 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 (aAcc->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:
|
|
case roles::BUTTONMENU:
|
|
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 < aAcc->ChildCount(); i++) {
|
|
Accessible* currChild = aAcc->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(Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorRule(aDirectDescendantsFrom, aSearchText){};
|
|
|
|
RotorTextEntryRule::RotorTextEntryRule(const nsString& aSearchText)
|
|
: RotorRule(aSearchText){};
|
|
|
|
uint16_t RotorTextEntryRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRule::Match(aAcc);
|
|
|
|
// 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 (aAcc->Role() != roles::PASSWORD_TEXT && aAcc->Role() != roles::ENTRY) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Rotor Link Rule
|
|
|
|
RotorLinkRule::RotorLinkRule(Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorRule(aDirectDescendantsFrom, aSearchText){};
|
|
|
|
RotorLinkRule::RotorLinkRule(const nsString& aSearchText)
|
|
: RotorRule(aSearchText){};
|
|
|
|
uint16_t RotorLinkRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRule::Match(aAcc);
|
|
|
|
// 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(aAcc);
|
|
if (![[nativeMatch moxRole] isEqualToString:@"AXLink"]) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
RotorVisitedLinkRule::RotorVisitedLinkRule(const nsString& aSearchText)
|
|
: RotorLinkRule(aSearchText) {}
|
|
|
|
RotorVisitedLinkRule::RotorVisitedLinkRule(Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorLinkRule(aDirectDescendantsFrom, aSearchText) {}
|
|
|
|
uint16_t RotorVisitedLinkRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorLinkRule::Match(aAcc);
|
|
|
|
if (result & nsIAccessibleTraversalRule::FILTER_MATCH) {
|
|
mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
|
|
if (![[nativeMatch moxVisited] boolValue]) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
RotorUnvisitedLinkRule::RotorUnvisitedLinkRule(const nsString& aSearchText)
|
|
: RotorLinkRule(aSearchText) {}
|
|
|
|
RotorUnvisitedLinkRule::RotorUnvisitedLinkRule(
|
|
Accessible* aDirectDescendantsFrom, const nsString& aSearchText)
|
|
: RotorLinkRule(aDirectDescendantsFrom, aSearchText) {}
|
|
|
|
uint16_t RotorUnvisitedLinkRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorLinkRule::Match(aAcc);
|
|
|
|
if (result & nsIAccessibleTraversalRule::FILTER_MATCH) {
|
|
mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
|
|
if ([[nativeMatch moxVisited] boolValue]) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Match Not Rule
|
|
|
|
RotorNotMacRoleRule::RotorNotMacRoleRule(NSString* aMacRole,
|
|
Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorMacRoleRule(aMacRole, aDirectDescendantsFrom, aSearchText) {}
|
|
|
|
RotorNotMacRoleRule::RotorNotMacRoleRule(NSString* aMacRole,
|
|
const nsString& aSearchText)
|
|
: RotorMacRoleRule(aMacRole, aSearchText) {}
|
|
|
|
uint16_t RotorNotMacRoleRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRule::Match(aAcc);
|
|
|
|
// 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(aAcc);
|
|
if ([[nativeMatch moxRole] isEqualToString:mMacRole]) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Rotor Static Text Rule
|
|
|
|
RotorStaticTextRule::RotorStaticTextRule(Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorRule(aDirectDescendantsFrom, aSearchText){};
|
|
|
|
RotorStaticTextRule::RotorStaticTextRule(const nsString& aSearchText)
|
|
: RotorRule(aSearchText){};
|
|
|
|
uint16_t RotorStaticTextRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRule::Match(aAcc);
|
|
|
|
// 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(aAcc);
|
|
if (![[nativeMatch moxRole] isEqualToString:@"AXStaticText"]) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Rotor Heading Level Rule
|
|
|
|
RotorHeadingLevelRule::RotorHeadingLevelRule(int32_t aLevel,
|
|
Accessible* aDirectDescendantsFrom,
|
|
const nsString& aSearchText)
|
|
: RotorRoleRule(roles::HEADING, aDirectDescendantsFrom, aSearchText),
|
|
mLevel(aLevel){};
|
|
|
|
RotorHeadingLevelRule::RotorHeadingLevelRule(int32_t aLevel,
|
|
const nsString& aSearchText)
|
|
: RotorRoleRule(roles::HEADING, aSearchText), mLevel(aLevel){};
|
|
|
|
uint16_t RotorHeadingLevelRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRoleRule::Match(aAcc);
|
|
|
|
// 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 = aAcc->GroupPosition().level;
|
|
|
|
if (currLevel != mLevel) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint16_t RotorLiveRegionRule::Match(Accessible* aAcc) {
|
|
uint16_t result = RotorRule::Match(aAcc);
|
|
|
|
if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
|
|
mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
|
|
if (![nativeMatch moxIsLiveRegion]) {
|
|
result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
}
|
|
}
|
|
return result;
|
|
}
|