239 lines
7.1 KiB
C++
239 lines
7.1 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* 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/. */
|
|
|
|
#include "Compatibility.h"
|
|
|
|
#include "mozilla/WindowsVersion.h"
|
|
#include "mozilla/WinHeaderOnlyUtils.h"
|
|
#include "mozilla/StaticPrefs_accessibility.h"
|
|
#include "nsExceptionHandler.h"
|
|
#include "nsIXULRuntime.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsWinUtils.h"
|
|
#include "Statistics.h"
|
|
#include "AccessibleWrap.h"
|
|
|
|
#include "mozilla/Preferences.h"
|
|
|
|
#include <shlobj.h>
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::a11y;
|
|
|
|
/**
|
|
* String versions of consumer flags. See GetHumanReadableConsumersStr.
|
|
*/
|
|
static const wchar_t* ConsumerStringMap[CONSUMERS_ENUM_LEN + 1] = {
|
|
L"NVDA", L"JAWS", L"OLDJAWS", L"WE", L"DOLPHIN",
|
|
L"SEROTEK", L"COBRA", L"ZOOMTEXT", L"KAZAGURU", L"YOUDAO",
|
|
L"UNKNOWN", L"UIAUTOMATION", L"VISPEROSHARED", L"\0"};
|
|
|
|
bool Compatibility::IsModuleVersionLessThan(HMODULE aModuleHandle,
|
|
unsigned long long aVersion) {
|
|
LauncherResult<ModuleVersion> version = GetModuleVersion(aModuleHandle);
|
|
if (version.isErr()) {
|
|
return true;
|
|
}
|
|
|
|
return version.unwrap() < aVersion;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Compatibility
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
uint32_t Compatibility::sConsumers = Compatibility::UNKNOWN;
|
|
|
|
/**
|
|
* This function is safe to call multiple times.
|
|
*/
|
|
/* static */
|
|
void Compatibility::InitConsumers() {
|
|
HMODULE jawsHandle = ::GetModuleHandleW(L"jhook");
|
|
if (jawsHandle) {
|
|
sConsumers |=
|
|
IsModuleVersionLessThan(jawsHandle, MAKE_FILE_VERSION(19, 0, 0, 0))
|
|
? OLDJAWS
|
|
: JAWS;
|
|
}
|
|
|
|
if (::GetModuleHandleW(L"gwm32inc")) sConsumers |= WE;
|
|
|
|
if (::GetModuleHandleW(L"dolwinhk")) sConsumers |= DOLPHIN;
|
|
|
|
if (::GetModuleHandleW(L"STSA32")) sConsumers |= SEROTEK;
|
|
|
|
if (::GetModuleHandleW(L"nvdaHelperRemote")) sConsumers |= NVDA;
|
|
|
|
if (::GetModuleHandleW(L"OsmHooks") || ::GetModuleHandleW(L"OsmHks64"))
|
|
sConsumers |= COBRA;
|
|
|
|
if (::GetModuleHandleW(L"WebFinderRemote")) sConsumers |= ZOOMTEXT;
|
|
|
|
if (::GetModuleHandleW(L"Kazahook")) sConsumers |= KAZAGURU;
|
|
|
|
if (::GetModuleHandleW(L"TextExtractorImpl32") ||
|
|
::GetModuleHandleW(L"TextExtractorImpl64"))
|
|
sConsumers |= YOUDAO;
|
|
|
|
if (::GetModuleHandleW(L"uiautomation") ||
|
|
::GetModuleHandleW(L"uiautomationcore"))
|
|
sConsumers |= UIAUTOMATION;
|
|
|
|
if (::GetModuleHandleW(L"AccEventCache")) {
|
|
sConsumers |= VISPEROSHARED;
|
|
}
|
|
|
|
// If we have a known consumer remove the unknown bit.
|
|
if (sConsumers != Compatibility::UNKNOWN)
|
|
sConsumers &= ~Compatibility::UNKNOWN;
|
|
}
|
|
|
|
/* static */
|
|
bool Compatibility::HasKnownNonUiaConsumer() {
|
|
InitConsumers();
|
|
return sConsumers & ~(Compatibility::UNKNOWN | UIAUTOMATION);
|
|
}
|
|
|
|
void Compatibility::Init() {
|
|
// Note we collect some AT statistics/telemetry here for convenience.
|
|
InitConsumers();
|
|
|
|
CrashReporter::RecordAnnotationNSCString(
|
|
CrashReporter::Annotation::AccessibilityInProcClient,
|
|
nsPrintfCString("0x%X", sConsumers));
|
|
|
|
// Gather telemetry
|
|
uint32_t temp = sConsumers;
|
|
for (int i = 0; temp; i++) {
|
|
if (temp & 0x1) statistics::A11yConsumers(i);
|
|
|
|
temp >>= 1;
|
|
}
|
|
|
|
// Turn off new tab switching for Jaws and WE.
|
|
if (sConsumers & (JAWS | OLDJAWS | WE)) {
|
|
// Check to see if the pref for disallowing CtrlTab is already set. If so,
|
|
// bail out (respect the user settings). If not, set it.
|
|
if (!Preferences::HasUserValue("browser.ctrlTab.disallowForScreenReaders"))
|
|
Preferences::SetBool("browser.ctrlTab.disallowForScreenReaders", true);
|
|
}
|
|
}
|
|
|
|
// static
|
|
void Compatibility::GetHumanReadableConsumersStr(nsAString& aResult) {
|
|
bool appened = false;
|
|
uint32_t index = 0;
|
|
for (uint32_t consumers = sConsumers; consumers; consumers = consumers >> 1) {
|
|
if (consumers & 0x1) {
|
|
if (appened) {
|
|
aResult.AppendLiteral(",");
|
|
}
|
|
aResult.Append(ConsumerStringMap[index]);
|
|
appened = true;
|
|
}
|
|
if (++index > CONSUMERS_ENUM_LEN) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SuppressionTimer {
|
|
constexpr SuppressionTimer() = default;
|
|
void Start() { mStart = ::GetTickCount(); }
|
|
bool IsActive(DWORD aTickCount) const {
|
|
return mStart && aTickCount - mStart < kSuppressTimeout;
|
|
}
|
|
// Last time Start() was called, as returned by ::GetTickCount().
|
|
DWORD mStart = 0;
|
|
|
|
static constexpr DWORD kSuppressTimeout = 1500; // ms
|
|
};
|
|
|
|
static SuppressionTimer sClipboardSuppressionTimer;
|
|
static SuppressionTimer sSnapLayoutsSuppressionTimer;
|
|
|
|
/* static */
|
|
void Compatibility::SuppressA11yForClipboardCopy() {
|
|
// Bug 1774285: Windows Suggested Actions (introduced in Windows 11 22H2)
|
|
// might walk the a11y tree using UIA whenever anything is copied to the
|
|
// clipboard. This causes an unacceptable hang, particularly when the cache
|
|
// is disabled.
|
|
bool doSuppress = [&] {
|
|
switch (
|
|
StaticPrefs::accessibility_windows_suppress_after_clipboard_copy()) {
|
|
case 0:
|
|
return false;
|
|
case 1:
|
|
return true;
|
|
default:
|
|
// Our workaround for Suggested Actions is needed from Windows 11 22H2
|
|
return IsWin1122H2OrLater();
|
|
}
|
|
}();
|
|
|
|
if (doSuppress) {
|
|
sClipboardSuppressionTimer.Start();
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
void Compatibility::SuppressA11yForSnapLayouts() {
|
|
// Bug 1883132: Snap Layouts might enable a11y to find the maximize button
|
|
// position.
|
|
bool doSuppress = [&] {
|
|
switch (StaticPrefs::accessibility_windows_suppress_for_snap_layout()) {
|
|
case 0:
|
|
return false;
|
|
case 1:
|
|
return true;
|
|
default:
|
|
// Our workaround for Snap Layouts is needed from Windows 11 22H2
|
|
return IsWin1122H2OrLater();
|
|
}
|
|
}();
|
|
|
|
if (doSuppress) {
|
|
sSnapLayoutsSuppressionTimer.Start();
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
SuppressionReasons Compatibility::A11ySuppressionReasons() {
|
|
const auto now = ::GetTickCount();
|
|
auto reasons = SuppressionReasons::None;
|
|
if (sClipboardSuppressionTimer.IsActive(now)) {
|
|
reasons |= SuppressionReasons::Clipboard;
|
|
}
|
|
if (sSnapLayoutsSuppressionTimer.IsActive(now)) {
|
|
reasons |= SuppressionReasons::SnapLayouts;
|
|
}
|
|
return reasons;
|
|
}
|
|
|
|
/* static */
|
|
bool Compatibility::IsUiaEnabled() {
|
|
// This is the only function which should call the UIA pref function directly.
|
|
const uint32_t pref =
|
|
StaticPrefs::accessibility_uia_enable_DoNotUseDirectly();
|
|
if (pref == 0) {
|
|
return false; // Never enable.
|
|
}
|
|
if (pref == 1) {
|
|
return true; // Always enable.
|
|
}
|
|
// Bug 1956415: Some screen readers break when native UIA is enabled, so don't
|
|
// enable it when they're detected.
|
|
// Call InitConsumers() if we haven't already. It's safe to call this multiple
|
|
// times, but it still does a small amount of work and we can easily avoid
|
|
// unnecessary calls here.
|
|
if (!(sConsumers & UIAUTOMATION)) {
|
|
InitConsumers();
|
|
}
|
|
return !IsJAWS() && !IsOldJAWS() && !IsVisperoShared() &&
|
|
!(sConsumers & NVDA);
|
|
}
|