tor-browser

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

Platform.cpp (8637B)


      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 "Platform.h"
      8 
      9 #include "mozilla/ClearOnShutdown.h"
     10 #include "mozilla/GRefPtr.h"
     11 #include "mozilla/GUniquePtr.h"
     12 #include "mozilla/widget/GSettings.h"
     13 #include "nsIAccessibleEvent.h"
     14 #include "nsMai.h"
     15 #include "nsServiceManagerUtils.h"
     16 #include "nsWindow.h"
     17 #include "prenv.h"
     18 #include "prlink.h"
     19 
     20 #ifdef MOZ_ENABLE_DBUS
     21 #  include "mozilla/widget/AsyncDBus.h"
     22 #endif
     23 #include <gtk/gtk.h>
     24 
     25 using namespace mozilla;
     26 using namespace mozilla::a11y;
     27 
     28 int atkMajorVersion = 1, atkMinorVersion = 12, atkMicroVersion = 0;
     29 
     30 GType (*gAtkTableCellGetTypeFunc)();
     31 
     32 extern "C" {
     33 typedef GType (*AtkGetTypeType)(void);
     34 typedef void (*AtkBridgeAdaptorInit)(int*, char**[]);
     35 }
     36 
     37 static PRLibrary* sATKLib = nullptr;
     38 static const char sATKLibName[] = "libatk-1.0.so.0";
     39 static const char sATKHyperlinkImplGetTypeSymbol[] =
     40    "atk_hyperlink_impl_get_type";
     41 
     42 gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
     43                                gpointer);
     44 static bool sToplevel_event_hook_added = false;
     45 static gulong sToplevel_show_hook = 0;
     46 static gulong sToplevel_hide_hook = 0;
     47 
     48 GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
     49 
     50 struct AtkBridgeModule {
     51  const char* libName;
     52  PRLibrary* lib;
     53  const char* initName;
     54  AtkBridgeAdaptorInit init;
     55 };
     56 
     57 static AtkBridgeModule sAtkBridge = {"libatk-bridge-2.0.so.0", nullptr,
     58                                     "atk_bridge_adaptor_init", nullptr};
     59 
     60 static nsresult LoadGtkModule(AtkBridgeModule& aModule) {
     61  NS_ENSURE_ARG(aModule.libName);
     62 
     63  if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
     64    return NS_ERROR_FAILURE;
     65  }
     66 
     67  // we have loaded the library, try to get the function ptrs
     68  if (!(aModule.init = (AtkBridgeAdaptorInit)PR_FindFunctionSymbol(
     69            aModule.lib, aModule.initName))) {
     70    // fail, :(
     71    PR_UnloadLibrary(aModule.lib);
     72    aModule.lib = nullptr;
     73    return NS_ERROR_FAILURE;
     74  }
     75  return NS_OK;
     76 }
     77 
     78 void a11y::PlatformInit() {
     79  if (!ShouldA11yBeEnabled()) return;
     80 
     81  sATKLib = PR_LoadLibrary(sATKLibName);
     82  if (!sATKLib) return;
     83 
     84  AtkGetTypeType pfn_atk_hyperlink_impl_get_type =
     85      (AtkGetTypeType)PR_FindFunctionSymbol(sATKLib,
     86                                            sATKHyperlinkImplGetTypeSymbol);
     87  if (pfn_atk_hyperlink_impl_get_type) {
     88    g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
     89  }
     90 
     91  gAtkTableCellGetTypeFunc =
     92      (GType(*)())PR_FindFunctionSymbol(sATKLib, "atk_table_cell_get_type");
     93 
     94  const char* (*atkGetVersion)() =
     95      (const char* (*)())PR_FindFunctionSymbol(sATKLib, "atk_get_version");
     96  if (atkGetVersion) {
     97    const char* version = atkGetVersion();
     98    if (version) {
     99      char* endPtr = nullptr;
    100      atkMajorVersion = strtol(version, &endPtr, 10);
    101      if (atkMajorVersion != 0L) {
    102        atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
    103        if (atkMinorVersion != 0L) {
    104          atkMicroVersion = strtol(endPtr + 1, &endPtr, 10);
    105        }
    106      }
    107    }
    108  }
    109 
    110  // Initialize the MAI Utility class, it will overwrite gail_util.
    111  g_type_class_unref(g_type_class_ref(mai_util_get_type()));
    112 
    113  // Init atk-bridge now
    114  PR_SetEnv("NO_AT_BRIDGE=0");
    115  nsresult rv = LoadGtkModule(sAtkBridge);
    116  if (NS_SUCCEEDED(rv)) {
    117    (*sAtkBridge.init)(nullptr, nullptr);
    118  }
    119 
    120  if (!sToplevel_event_hook_added) {
    121    sToplevel_event_hook_added = true;
    122    sToplevel_show_hook = g_signal_add_emission_hook(
    123        g_signal_lookup("show", GTK_TYPE_WINDOW), 0, toplevel_event_watcher,
    124        reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW), nullptr);
    125    sToplevel_hide_hook = g_signal_add_emission_hook(
    126        g_signal_lookup("hide", GTK_TYPE_WINDOW), 0, toplevel_event_watcher,
    127        reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE), nullptr);
    128  }
    129 }
    130 
    131 void a11y::PlatformShutdown() {
    132  if (sToplevel_event_hook_added) {
    133    sToplevel_event_hook_added = false;
    134    g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
    135                                  sToplevel_show_hook);
    136    g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
    137                                  sToplevel_hide_hook);
    138  }
    139 
    140  if (sAtkBridge.lib) {
    141    // Do not shutdown/unload atk-bridge,
    142    // an exit function registered will take care of it
    143    // PR_UnloadLibrary(sAtkBridge.lib);
    144    sAtkBridge.lib = nullptr;
    145    sAtkBridge.init = nullptr;
    146  }
    147  // if (sATKLib) {
    148  //     PR_UnloadLibrary(sATKLib);
    149  //     sATKLib = nullptr;
    150  // }
    151 }
    152 
    153 static const char sAccEnv[] = "GNOME_ACCESSIBILITY";
    154 #ifdef MOZ_ENABLE_DBUS
    155 StaticRefPtr<GDBusProxy> sA11yBusProxy;
    156 StaticRefPtr<GCancellable> sCancellable;
    157 #endif
    158 
    159 static void StartAccessibility() {
    160  if (nsWindow* window = nsWindow::GetFocusedWindow()) {
    161    window->DispatchActivateEventAccessible();
    162  }
    163 }
    164 
    165 static void A11yBusProxyPropertyChanged(GDBusProxy* aProxy,
    166                                        GVariant* aChangedProperties,
    167                                        char** aInvalidatedProperties,
    168                                        gpointer aUserData) {
    169  gboolean isEnabled;
    170  g_variant_lookup(aChangedProperties, "IsEnabled", "b", &isEnabled);
    171  if (isEnabled) {
    172    StartAccessibility();
    173  }
    174 }
    175 
    176 // Called from `nsWindow::Create()` before the window is shown.
    177 void a11y::PreInit() {
    178 #ifdef MOZ_ENABLE_DBUS
    179  static bool sInited = false;
    180  if (sInited) {
    181    return;
    182  }
    183  sInited = true;
    184  sCancellable = dont_AddRef(g_cancellable_new());
    185 
    186  widget::CreateDBusProxyForBus(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE,
    187                                /* aInterfaceInfo = */ nullptr, "org.a11y.Bus",
    188                                "/org/a11y/bus", "org.a11y.Status",
    189                                sCancellable)
    190      ->Then(
    191          GetCurrentSerialEventTarget(), __func__,
    192 
    193          [](RefPtr<GDBusProxy>&& aProxy) {
    194            sA11yBusProxy = std::move(aProxy);
    195            sCancellable = nullptr;
    196            g_signal_connect(sA11yBusProxy, "g-properties-changed",
    197                             G_CALLBACK(A11yBusProxyPropertyChanged), nullptr);
    198            RefPtr<GVariant> isEnabled = dont_AddRef(
    199                g_dbus_proxy_get_cached_property(sA11yBusProxy, "IsEnabled"));
    200            if (isEnabled && g_variant_get_boolean(isEnabled)) {
    201              // If a window is already focused, `StartAccessibility()` will
    202              // initialize a11y by sending an activate event. If the window has
    203              // not yet been shown/focused, nothing will happen. We now have
    204              // the `IsEnabled` property cached so when `ShouldA11yBeEnabled()`
    205              // is called from `nsWindow::Show()`, a root accessible will be
    206              // created and events will be dispatched henceforth.
    207              StartAccessibility();
    208            }
    209          },
    210          [](GUniquePtr<GError>&& aError) {
    211            sCancellable = nullptr;
    212            if (!g_error_matches(aError.get(), G_IO_ERROR,
    213                                 G_IO_ERROR_CANCELLED)) {
    214              g_warning(
    215                  "Failed to create DBus proxy for org.a11y.Bus: "
    216                  "%s\n",
    217                  aError->message);
    218            }
    219          });
    220 
    221  RunOnShutdown([] {
    222    if (sCancellable) {
    223      g_cancellable_cancel(sCancellable);
    224      sCancellable = nullptr;
    225    }
    226 
    227    if (sA11yBusProxy) {
    228      sA11yBusProxy = nullptr;
    229    }
    230  });
    231 #endif
    232 }
    233 
    234 bool a11y::ShouldA11yBeEnabled() {
    235  EPlatformDisabledState disabledState = PlatformDisabledState();
    236  if (disabledState == ePlatformIsDisabled) {
    237    return false;
    238  }
    239  if (disabledState == ePlatformIsForceEnabled) {
    240    return true;
    241  }
    242 
    243  // check if accessibility enabled/disabled by environment variable
    244  if (const char* envValue = PR_GetEnv(sAccEnv)) {
    245    return !!atoi(envValue);
    246  }
    247 
    248 #ifdef MOZ_ENABLE_DBUS
    249  if (sA11yBusProxy) {
    250    RefPtr<GVariant> isEnabled = dont_AddRef(
    251        g_dbus_proxy_get_cached_property(sA11yBusProxy, "IsEnabled"));
    252    // result can be null if proxy isn't actually working.
    253    if (isEnabled) {
    254      return g_variant_get_boolean(isEnabled);
    255    }
    256  }
    257 #endif
    258 
    259  // check GSettings
    260  return widget::GSettings::GetBoolean("org.gnome.desktop.interface"_ns,
    261                                       "toolkit-accessibility"_ns)
    262      .valueOr(false);
    263 }
    264 
    265 uint64_t a11y::GetCacheDomainsForKnownClients(uint64_t aCacheDomains) {
    266  return aCacheDomains;
    267 }