tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Compatibility.cpp (7294B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "Compatibility.h"
      8 
      9 #include "mozilla/WindowsVersion.h"
     10 #include "mozilla/WinHeaderOnlyUtils.h"
     11 #include "mozilla/StaticPrefs_accessibility.h"
     12 #include "nsExceptionHandler.h"
     13 #include "nsIXULRuntime.h"
     14 #include "nsPrintfCString.h"
     15 #include "nsUnicharUtils.h"
     16 #include "nsWinUtils.h"
     17 #include "Statistics.h"
     18 #include "AccessibleWrap.h"
     19 
     20 #include "mozilla/Preferences.h"
     21 
     22 #include <shlobj.h>
     23 
     24 using namespace mozilla;
     25 using namespace mozilla::a11y;
     26 
     27 /**
     28 * String versions of consumer flags. See GetHumanReadableConsumersStr.
     29 */
     30 static const wchar_t* ConsumerStringMap[CONSUMERS_ENUM_LEN + 1] = {
     31    L"NVDA",    L"JAWS",         L"OLDJAWS",       L"WE",       L"DOLPHIN",
     32    L"SEROTEK", L"COBRA",        L"ZOOMTEXT",      L"KAZAGURU", L"YOUDAO",
     33    L"UNKNOWN", L"UIAUTOMATION", L"VISPEROSHARED", L"\0"};
     34 
     35 bool Compatibility::IsModuleVersionLessThan(HMODULE aModuleHandle,
     36                                            unsigned long long aVersion) {
     37  LauncherResult<ModuleVersion> version = GetModuleVersion(aModuleHandle);
     38  if (version.isErr()) {
     39    return true;
     40  }
     41 
     42  return version.unwrap() < aVersion;
     43 }
     44 
     45 ////////////////////////////////////////////////////////////////////////////////
     46 // Compatibility
     47 ////////////////////////////////////////////////////////////////////////////////
     48 
     49 uint32_t Compatibility::sConsumers = Compatibility::UNKNOWN;
     50 
     51 /**
     52 * This function is safe to call multiple times.
     53 */
     54 /* static */
     55 void Compatibility::InitConsumers() {
     56  HMODULE jawsHandle = ::GetModuleHandleW(L"jhook");
     57  if (jawsHandle) {
     58    sConsumers |=
     59        IsModuleVersionLessThan(jawsHandle, MAKE_FILE_VERSION(19, 0, 0, 0))
     60            ? OLDJAWS
     61            : JAWS;
     62  }
     63 
     64  if (::GetModuleHandleW(L"gwm32inc")) sConsumers |= WE;
     65 
     66  if (::GetModuleHandleW(L"dolwinhk")) sConsumers |= DOLPHIN;
     67 
     68  if (::GetModuleHandleW(L"STSA32")) sConsumers |= SEROTEK;
     69 
     70  if (::GetModuleHandleW(L"nvdaHelperRemote")) sConsumers |= NVDA;
     71 
     72  if (::GetModuleHandleW(L"OsmHooks") || ::GetModuleHandleW(L"OsmHks64"))
     73    sConsumers |= COBRA;
     74 
     75  if (::GetModuleHandleW(L"WebFinderRemote")) sConsumers |= ZOOMTEXT;
     76 
     77  if (::GetModuleHandleW(L"Kazahook")) sConsumers |= KAZAGURU;
     78 
     79  if (::GetModuleHandleW(L"TextExtractorImpl32") ||
     80      ::GetModuleHandleW(L"TextExtractorImpl64"))
     81    sConsumers |= YOUDAO;
     82 
     83  if (::GetModuleHandleW(L"uiautomation") ||
     84      ::GetModuleHandleW(L"uiautomationcore"))
     85    sConsumers |= UIAUTOMATION;
     86 
     87  if (::GetModuleHandleW(L"AccEventCache")) {
     88    sConsumers |= VISPEROSHARED;
     89  }
     90 
     91  // If we have a known consumer remove the unknown bit.
     92  if (sConsumers != Compatibility::UNKNOWN)
     93    sConsumers &= ~Compatibility::UNKNOWN;
     94 }
     95 
     96 /* static */
     97 bool Compatibility::HasKnownNonUiaConsumer() {
     98  InitConsumers();
     99  return sConsumers & ~(Compatibility::UNKNOWN | UIAUTOMATION);
    100 }
    101 
    102 void Compatibility::Init() {
    103  // Note we collect some AT statistics/telemetry here for convenience.
    104  InitConsumers();
    105 
    106  CrashReporter::RecordAnnotationNSCString(
    107      CrashReporter::Annotation::AccessibilityInProcClient,
    108      nsPrintfCString("0x%X", sConsumers));
    109 
    110  // Gather telemetry
    111  uint32_t temp = sConsumers;
    112  for (int i = 0; temp; i++) {
    113    if (temp & 0x1) statistics::A11yConsumers(i);
    114 
    115    temp >>= 1;
    116  }
    117 
    118  // Turn off new tab switching for Jaws and WE.
    119  if (sConsumers & (JAWS | OLDJAWS | WE)) {
    120    // Check to see if the pref for disallowing CtrlTab is already set. If so,
    121    // bail out (respect the user settings). If not, set it.
    122    if (!Preferences::HasUserValue("browser.ctrlTab.disallowForScreenReaders"))
    123      Preferences::SetBool("browser.ctrlTab.disallowForScreenReaders", true);
    124  }
    125 }
    126 
    127 // static
    128 void Compatibility::GetHumanReadableConsumersStr(nsAString& aResult) {
    129  bool appened = false;
    130  uint32_t index = 0;
    131  for (uint32_t consumers = sConsumers; consumers; consumers = consumers >> 1) {
    132    if (consumers & 0x1) {
    133      if (appened) {
    134        aResult.AppendLiteral(",");
    135      }
    136      aResult.Append(ConsumerStringMap[index]);
    137      appened = true;
    138    }
    139    if (++index > CONSUMERS_ENUM_LEN) {
    140      break;
    141    }
    142  }
    143 }
    144 
    145 struct SuppressionTimer {
    146  constexpr SuppressionTimer() = default;
    147  void Start() { mStart = ::GetTickCount(); }
    148  bool IsActive(DWORD aTickCount) const {
    149    return mStart && aTickCount - mStart < kSuppressTimeout;
    150  }
    151  // Last time Start() was called, as returned by ::GetTickCount().
    152  DWORD mStart = 0;
    153 
    154  static constexpr DWORD kSuppressTimeout = 1500;  // ms
    155 };
    156 
    157 static SuppressionTimer sClipboardSuppressionTimer;
    158 static SuppressionTimer sSnapLayoutsSuppressionTimer;
    159 
    160 /* static */
    161 void Compatibility::SuppressA11yForClipboardCopy() {
    162  // Bug 1774285: Windows Suggested Actions (introduced in Windows 11 22H2)
    163  // might walk the a11y tree using UIA whenever anything is copied to the
    164  // clipboard. This causes an unacceptable hang, particularly when the cache
    165  // is disabled.
    166  bool doSuppress = [&] {
    167    switch (
    168        StaticPrefs::accessibility_windows_suppress_after_clipboard_copy()) {
    169      case 0:
    170        return false;
    171      case 1:
    172        return true;
    173      default:
    174        // Our workaround for Suggested Actions is needed from Windows 11 22H2
    175        return IsWin1122H2OrLater();
    176    }
    177  }();
    178 
    179  if (doSuppress) {
    180    sClipboardSuppressionTimer.Start();
    181  }
    182 }
    183 
    184 /* static */
    185 void Compatibility::SuppressA11yForSnapLayouts() {
    186  // Bug 1883132: Snap Layouts might enable a11y to find the maximize button
    187  // position.
    188  bool doSuppress = [&] {
    189    switch (StaticPrefs::accessibility_windows_suppress_for_snap_layout()) {
    190      case 0:
    191        return false;
    192      case 1:
    193        return true;
    194      default:
    195        // Our workaround for Snap Layouts is needed from Windows 11 22H2
    196        return IsWin1122H2OrLater();
    197    }
    198  }();
    199 
    200  if (doSuppress) {
    201    sSnapLayoutsSuppressionTimer.Start();
    202  }
    203 }
    204 
    205 /* static */
    206 SuppressionReasons Compatibility::A11ySuppressionReasons() {
    207  const auto now = ::GetTickCount();
    208  auto reasons = SuppressionReasons::None;
    209  if (sClipboardSuppressionTimer.IsActive(now)) {
    210    reasons |= SuppressionReasons::Clipboard;
    211  }
    212  if (sSnapLayoutsSuppressionTimer.IsActive(now)) {
    213    reasons |= SuppressionReasons::SnapLayouts;
    214  }
    215  return reasons;
    216 }
    217 
    218 /* static */
    219 bool Compatibility::IsUiaEnabled() {
    220  // This is the only function which should call the UIA pref function directly.
    221  const uint32_t pref =
    222      StaticPrefs::accessibility_uia_enable_DoNotUseDirectly();
    223  if (pref == 0) {
    224    return false;  // Never enable.
    225  }
    226  if (pref == 1) {
    227    return true;  // Always enable.
    228  }
    229  // Bug 1956415: Some screen readers break when native UIA is enabled, so don't
    230  // enable it when they're detected.
    231  // Call InitConsumers() if we haven't already. It's safe to call this multiple
    232  // times, but it still does a small amount of work and we can easily avoid
    233  // unnecessary calls here.
    234  if (!(sConsumers & UIAUTOMATION)) {
    235    InitConsumers();
    236  }
    237  return !IsJAWS() && !IsOldJAWS() && !IsVisperoShared() &&
    238         !(sConsumers & NVDA);
    239 }