Bug 1794974: Part 1: Add improved client detection, automatic domain setting, r=Jamie
This revision is a step towards ensuring we get the right domains for known accessibility instantiators. We want to anticipate the needs of known clients and make sure we have all the cache domains ready, especially for heavy-use clients like screen readers. The domains we'll need are a bit of a work in progress, but can be updated easily. For now, err on the side of caution. This revision also implements better client detection on macOS, so we can handle multiple clients. Differential Revision: https://phabricator.services.mozilla.com/D220035
This commit is contained in:
@@ -232,3 +232,11 @@ bool a11y::LocalizeString(const nsAString& aToken, nsAString& aLocalized) {
|
||||
|
||||
return !!str;
|
||||
}
|
||||
|
||||
uint64_t a11y::GetCacheDomainsForKnownClients(uint64_t aCacheDomains) {
|
||||
Unused << aCacheDomains;
|
||||
|
||||
// XXX: Respond to clients such as TalkBack. For now, be safe and default to
|
||||
// caching all domains.
|
||||
return CacheDomain::All;
|
||||
}
|
||||
|
||||
@@ -277,3 +277,7 @@ bool a11y::ShouldA11yBeEnabled() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t a11y::GetCacheDomainsForKnownClients(uint64_t aCacheDomains) {
|
||||
return aCacheDomains;
|
||||
}
|
||||
|
||||
@@ -130,6 +130,12 @@ void PlatformRoleChangedEvent(Accessible* aTarget, const a11y::role& aRole,
|
||||
uint8_t aRoleMapEntryIndex);
|
||||
#endif
|
||||
|
||||
// Get the cache domains needed by any known clients interacting with Gecko. If
|
||||
// any known clients are found, the return value is aCacheDomains bitwise OR'd
|
||||
// with the required cache domains for those clients. Otherwise, the return
|
||||
// value is aCacheDomains unaltered.
|
||||
uint64_t GetCacheDomainsForKnownClients(uint64_t aCacheDomains);
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
@@ -340,6 +340,15 @@ LocalAccessible* CreateMenupopupAccessible(Element* aElement,
|
||||
return new XULMenupopupAccessible(aElement, aContext->Document());
|
||||
}
|
||||
|
||||
static uint64_t GetCacheDomainsForKnownClients(uint64_t aCacheDomains) {
|
||||
// Only check clients in the parent process.
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return aCacheDomains;
|
||||
}
|
||||
|
||||
return a11y::GetCacheDomainsForKnownClients(aCacheDomains);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// LocalAccessible constructors
|
||||
|
||||
|
||||
@@ -53,5 +53,9 @@ void PlatformShowHideEvent(Accessible*, Accessible*, bool, bool) {}
|
||||
|
||||
void PlatformSelectionEvent(Accessible*, Accessible*, uint32_t) {}
|
||||
|
||||
uint64_t GetCacheDomainsForKnownClients(uint64_t aCacheDomains) {
|
||||
return aCacheDomains;
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "nsAppShell.h"
|
||||
#include "nsCocoaUtils.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
// Available from 10.13 onwards; test availability at runtime before using
|
||||
@@ -200,6 +201,102 @@ void PlatformRoleChangedEvent(Accessible* aTarget, const a11y::role& aRole,
|
||||
}
|
||||
}
|
||||
|
||||
// This enum lists possible assistive technology clients. It's intended for use
|
||||
// in an EnumSet since there can be multiple ATs active at once.
|
||||
enum class Client : uint64_t {
|
||||
Unknown,
|
||||
VoiceOver,
|
||||
SwitchControl,
|
||||
FullKeyboardAccess,
|
||||
VoiceControl
|
||||
};
|
||||
|
||||
// Get the set of currently-active clients and the client to log.
|
||||
// XXX: We should log all clients, but default to the first one encountered.
|
||||
std::pair<EnumSet<Client>, Client> GetClients() {
|
||||
EnumSet<Client> clients;
|
||||
std::optional<Client> clientToLog;
|
||||
auto AddClient = [&clients, &clientToLog](Client client) {
|
||||
clients += client;
|
||||
if (!clientToLog.has_value()) {
|
||||
clientToLog = client;
|
||||
}
|
||||
};
|
||||
if ([[NSWorkspace sharedWorkspace]
|
||||
respondsToSelector:@selector(isVoiceOverEnabled)] &&
|
||||
[[NSWorkspace sharedWorkspace] isVoiceOverEnabled]) {
|
||||
AddClient(Client::VoiceOver);
|
||||
} else if ([[NSWorkspace sharedWorkspace]
|
||||
respondsToSelector:@selector(isSwitchControlEnabled)] &&
|
||||
[[NSWorkspace sharedWorkspace] isSwitchControlEnabled]) {
|
||||
AddClient(Client::SwitchControl);
|
||||
} else {
|
||||
// This is more complicated than the NSWorkspace queries above
|
||||
// because (a) there is no "full keyboard access" query for NSWorkspace
|
||||
// and (b) the [NSApplication fullKeyboardAccessEnabled] query checks
|
||||
// the pre-Monterey version of full keyboard access, which is not what
|
||||
// we're looking for here. For more info, see bug 1772375 comment 7.
|
||||
Boolean exists;
|
||||
int val = CFPreferencesGetAppIntegerValue(
|
||||
CFSTR("FullKeyboardAccessEnabled"), CFSTR("com.apple.Accessibility"),
|
||||
&exists);
|
||||
if (exists && val == 1) {
|
||||
AddClient(Client::FullKeyboardAccess);
|
||||
} else {
|
||||
val = CFPreferencesGetAppIntegerValue(CFSTR("CommandAndControlEnabled"),
|
||||
CFSTR("com.apple.Accessibility"),
|
||||
&exists);
|
||||
if (exists && val == 1) {
|
||||
AddClient(Client::VoiceControl);
|
||||
} else {
|
||||
AddClient(Client::Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(clients, clientToLog.value());
|
||||
}
|
||||
|
||||
// Expects a single client, returns a string representation of that client.
|
||||
constexpr const char* GetStringForClient(Client aClient) {
|
||||
switch (aClient) {
|
||||
case Client::Unknown:
|
||||
return "Unknown";
|
||||
case Client::VoiceOver:
|
||||
return "VoiceOver";
|
||||
case Client::SwitchControl:
|
||||
return "SwitchControl";
|
||||
case Client::FullKeyboardAccess:
|
||||
return "FullKeyboardAccess";
|
||||
case Client::VoiceControl:
|
||||
return "VoiceControl";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown Client enum value!");
|
||||
return "";
|
||||
}
|
||||
|
||||
uint64_t GetCacheDomainsForKnownClients(uint64_t aCacheDomains) {
|
||||
auto [clients, _] = GetClients();
|
||||
// We expect VoiceOver will require all information we have.
|
||||
if (clients.contains(Client::VoiceOver)) {
|
||||
return CacheDomain::All;
|
||||
}
|
||||
if (clients.contains(Client::FullKeyboardAccess)) {
|
||||
aCacheDomains |= CacheDomain::Bounds;
|
||||
}
|
||||
if (clients.contains(Client::SwitchControl)) {
|
||||
// XXX: Find minimum set of domains required for SwitchControl.
|
||||
// SwitchControl can give up if we don't furnish it certain information.
|
||||
return CacheDomain::All;
|
||||
}
|
||||
if (clients.contains(Client::VoiceControl)) {
|
||||
// XXX: Find minimum set of domains required for VoiceControl.
|
||||
return CacheDomain::All;
|
||||
}
|
||||
return aCacheDomains;
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -224,46 +321,16 @@ void PlatformRoleChangedEvent(Accessible* aTarget, const a11y::role& aRole,
|
||||
mozilla::a11y::sA11yShouldBeEnabled = ([value intValue] == 1);
|
||||
if (sA11yShouldBeEnabled) {
|
||||
// If accessibility should be enabled, log the appropriate client
|
||||
nsAutoString client;
|
||||
if ([[NSWorkspace sharedWorkspace]
|
||||
respondsToSelector:@selector(isVoiceOverEnabled)] &&
|
||||
[[NSWorkspace sharedWorkspace] isVoiceOverEnabled]) {
|
||||
client.Assign(u"VoiceOver"_ns);
|
||||
} else if ([[NSWorkspace sharedWorkspace]
|
||||
respondsToSelector:@selector(isSwitchControlEnabled)] &&
|
||||
[[NSWorkspace sharedWorkspace] isSwitchControlEnabled]) {
|
||||
client.Assign(u"SwitchControl"_ns);
|
||||
} else {
|
||||
// This is more complicated than the NSWorkspace queries above
|
||||
// because (a) there is no "full keyboard access" query for NSWorkspace
|
||||
// and (b) the [NSApplication fullKeyboardAccessEnabled] query checks
|
||||
// the pre-Monterey version of full keyboard access, which is not what
|
||||
// we're looking for here. For more info, see bug 1772375 comment 7.
|
||||
Boolean exists;
|
||||
int val = CFPreferencesGetAppIntegerValue(
|
||||
CFSTR("FullKeyboardAccessEnabled"),
|
||||
CFSTR("com.apple.Accessibility"), &exists);
|
||||
if (exists && val == 1) {
|
||||
client.Assign(u"FullKeyboardAccess"_ns);
|
||||
} else {
|
||||
val = CFPreferencesGetAppIntegerValue(
|
||||
CFSTR("CommandAndControlEnabled"),
|
||||
CFSTR("com.apple.Accessibility"), &exists);
|
||||
if (exists && val == 1) {
|
||||
client.Assign(u"VoiceControl"_ns);
|
||||
} else {
|
||||
client.Assign(u"Unknown"_ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto [_, clientToLog] = GetClients();
|
||||
const char* client = GetStringForClient(clientToLog);
|
||||
|
||||
#if defined(MOZ_TELEMETRY_REPORTING)
|
||||
mozilla::Telemetry::ScalarSet(
|
||||
mozilla::Telemetry::ScalarID::A11Y_INSTANTIATORS, client);
|
||||
mozilla::Telemetry::ScalarID::A11Y_INSTANTIATORS,
|
||||
NS_ConvertASCIItoUTF16(client));
|
||||
#endif // defined(MOZ_TELEMETRY_REPORTING)
|
||||
CrashReporter::RecordAnnotationNSCString(
|
||||
CrashReporter::Annotation::AccessibilityClient,
|
||||
NS_ConvertUTF16toUTF8(client));
|
||||
CrashReporter::RecordAnnotationCString(
|
||||
CrashReporter::Annotation::AccessibilityClient, client);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,14 @@ class Compatibility {
|
||||
*/
|
||||
static bool IsVisperoShared() { return !!(sConsumers & VISPEROSHARED); }
|
||||
|
||||
/*
|
||||
* Returns true if the instantiator is a known screen reader.
|
||||
*/
|
||||
static bool IsKnownScreenReader() {
|
||||
return IsJAWS() || IsDolphin() || IsVisperoShared() ||
|
||||
!!(sConsumers & NVDA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string describing sConsumers suitable for about:support.
|
||||
* Exposed through nsIXULRuntime.accessibilityInstantiator.
|
||||
|
||||
@@ -271,3 +271,12 @@ bool a11y::GetInstantiator(nsIFile** aOutInstantiator) {
|
||||
|
||||
return NS_SUCCEEDED(gInstantiator->Clone(aOutInstantiator));
|
||||
}
|
||||
|
||||
uint64_t a11y::GetCacheDomainsForKnownClients(uint64_t aCacheDomains) {
|
||||
// If we're instantiating because of a screen reader, enable all cache
|
||||
// domains. We expect that demanding ATs will need all information we have.
|
||||
if (Compatibility::IsKnownScreenReader()) {
|
||||
return CacheDomain::All;
|
||||
}
|
||||
return aCacheDomains;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user