accessible/src/atk/Platform.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/accessible/src/atk/Platform.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,352 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "Platform.h"
    1.11 +
    1.12 +#include "nsIAccessibleEvent.h"
    1.13 +#include "nsIGConfService.h"
    1.14 +#include "nsIServiceManager.h"
    1.15 +#include "nsMai.h"
    1.16 +#include "AtkSocketAccessible.h"
    1.17 +#include "prenv.h"
    1.18 +#include "prlink.h"
    1.19 +
    1.20 +#ifdef MOZ_ENABLE_DBUS
    1.21 +#include <dbus/dbus.h>
    1.22 +#endif
    1.23 +#include <gtk/gtk.h>
    1.24 +
    1.25 +using namespace mozilla;
    1.26 +using namespace mozilla::a11y;
    1.27 +
    1.28 +int atkMajorVersion = 1, atkMinorVersion = 12;
    1.29 +
    1.30 +extern "C" {
    1.31 +typedef GType (* AtkGetTypeType) (void);
    1.32 +typedef void (*GnomeAccessibilityInit) (void);
    1.33 +typedef void (*GnomeAccessibilityShutdown) (void);
    1.34 +}
    1.35 +
    1.36 +static PRLibrary* sATKLib = nullptr;
    1.37 +static const char sATKLibName[] = "libatk-1.0.so.0";
    1.38 +static const char sATKHyperlinkImplGetTypeSymbol[] =
    1.39 +  "atk_hyperlink_impl_get_type";
    1.40 +
    1.41 +gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
    1.42 +                                gpointer);
    1.43 +static bool sToplevel_event_hook_added = false;
    1.44 +static gulong sToplevel_show_hook = 0;
    1.45 +static gulong sToplevel_hide_hook = 0;
    1.46 +
    1.47 +GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
    1.48 +
    1.49 +struct GnomeAccessibilityModule
    1.50 +{
    1.51 +    const char *libName;
    1.52 +    PRLibrary *lib;
    1.53 +    const char *initName;
    1.54 +    GnomeAccessibilityInit init;
    1.55 +    const char *shutdownName;
    1.56 +    GnomeAccessibilityShutdown shutdown;
    1.57 +};
    1.58 +
    1.59 +static GnomeAccessibilityModule sAtkBridge = {
    1.60 +#ifdef AIX
    1.61 +    "libatk-bridge.a(libatk-bridge.so.0)", nullptr,
    1.62 +#else
    1.63 +    "libatk-bridge.so", nullptr,
    1.64 +#endif
    1.65 +    "gnome_accessibility_module_init", nullptr,
    1.66 +    "gnome_accessibility_module_shutdown", nullptr
    1.67 +};
    1.68 +
    1.69 +static GnomeAccessibilityModule sGail = {
    1.70 +    "libgail.so", nullptr,
    1.71 +    "gnome_accessibility_module_init", nullptr,
    1.72 +    "gnome_accessibility_module_shutdown", nullptr
    1.73 +};
    1.74 +
    1.75 +static nsresult
    1.76 +LoadGtkModule(GnomeAccessibilityModule& aModule)
    1.77 +{
    1.78 +    NS_ENSURE_ARG(aModule.libName);
    1.79 +
    1.80 +    if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
    1.81 +        //try to load the module with "gtk-2.0/modules" appended
    1.82 +        char *curLibPath = PR_GetLibraryPath();
    1.83 +        nsAutoCString libPath(curLibPath);
    1.84 +#if defined(LINUX) && defined(__x86_64__)
    1.85 +        libPath.Append(":/usr/lib64:/usr/lib");
    1.86 +#else
    1.87 +        libPath.Append(":/usr/lib");
    1.88 +#endif
    1.89 +        PR_FreeLibraryName(curLibPath);
    1.90 +
    1.91 +        int16_t loc1 = 0, loc2 = 0;
    1.92 +        int16_t subLen = 0;
    1.93 +        while (loc2 >= 0) {
    1.94 +            loc2 = libPath.FindChar(':', loc1);
    1.95 +            if (loc2 < 0)
    1.96 +                subLen = libPath.Length() - loc1;
    1.97 +            else
    1.98 +                subLen = loc2 - loc1;
    1.99 +            nsAutoCString sub(Substring(libPath, loc1, subLen));
   1.100 +            sub.Append("/gtk-2.0/modules/");
   1.101 +            sub.Append(aModule.libName);
   1.102 +            aModule.lib = PR_LoadLibrary(sub.get());
   1.103 +            if (aModule.lib)
   1.104 +                break;
   1.105 +
   1.106 +            loc1 = loc2+1;
   1.107 +        }
   1.108 +        if (!aModule.lib)
   1.109 +            return NS_ERROR_FAILURE;
   1.110 +    }
   1.111 +
   1.112 +    //we have loaded the library, try to get the function ptrs
   1.113 +    if (!(aModule.init = PR_FindFunctionSymbol(aModule.lib,
   1.114 +                                               aModule.initName)) ||
   1.115 +        !(aModule.shutdown = PR_FindFunctionSymbol(aModule.lib,
   1.116 +                                                   aModule.shutdownName))) {
   1.117 +
   1.118 +        //fail, :(
   1.119 +        PR_UnloadLibrary(aModule.lib);
   1.120 +        aModule.lib = nullptr;
   1.121 +        return NS_ERROR_FAILURE;
   1.122 +    }
   1.123 +    return NS_OK;
   1.124 +}
   1.125 +
   1.126 +void
   1.127 +a11y::PlatformInit()
   1.128 +{
   1.129 +  if (!ShouldA11yBeEnabled())
   1.130 +    return;
   1.131 +
   1.132 +  sATKLib = PR_LoadLibrary(sATKLibName);
   1.133 +  if (!sATKLib)
   1.134 +    return;
   1.135 +
   1.136 +  AtkGetTypeType pfn_atk_hyperlink_impl_get_type =
   1.137 +    (AtkGetTypeType) PR_FindFunctionSymbol(sATKLib, sATKHyperlinkImplGetTypeSymbol);
   1.138 +  if (pfn_atk_hyperlink_impl_get_type)
   1.139 +    g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
   1.140 +
   1.141 +  AtkGetTypeType pfn_atk_socket_get_type = (AtkGetTypeType)
   1.142 +    PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible::sATKSocketGetTypeSymbol);
   1.143 +  if (pfn_atk_socket_get_type) {
   1.144 +    AtkSocketAccessible::g_atk_socket_type = pfn_atk_socket_get_type();
   1.145 +    AtkSocketAccessible::g_atk_socket_embed = (AtkSocketEmbedType)
   1.146 +      PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible ::sATKSocketEmbedSymbol);
   1.147 +    AtkSocketAccessible::gCanEmbed =
   1.148 +      AtkSocketAccessible::g_atk_socket_type != G_TYPE_INVALID &&
   1.149 +      AtkSocketAccessible::g_atk_socket_embed;
   1.150 +  }
   1.151 +
   1.152 +  const char* (*atkGetVersion)() =
   1.153 +    (const char* (*)()) PR_FindFunctionSymbol(sATKLib, "atk_get_version");
   1.154 +  if (atkGetVersion) {
   1.155 +    const char* version = atkGetVersion();
   1.156 +    if (version) {
   1.157 +      char* endPtr = nullptr;
   1.158 +      atkMajorVersion = strtol(version, &endPtr, 10);
   1.159 +      if (*endPtr == '.')
   1.160 +        atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
   1.161 +    }
   1.162 +  }
   1.163 +
   1.164 +  // Load and initialize gail library.
   1.165 +  nsresult rv = LoadGtkModule(sGail);
   1.166 +  if (NS_SUCCEEDED(rv))
   1.167 +    (*sGail.init)();
   1.168 +
   1.169 +  // Initialize the MAI Utility class, it will overwrite gail_util.
   1.170 +  g_type_class_unref(g_type_class_ref(mai_util_get_type()));
   1.171 +
   1.172 +  // Init atk-bridge now
   1.173 +  PR_SetEnv("NO_AT_BRIDGE=0");
   1.174 +  rv = LoadGtkModule(sAtkBridge);
   1.175 +  if (NS_SUCCEEDED(rv)) {
   1.176 +    (*sAtkBridge.init)();
   1.177 +  }
   1.178 +
   1.179 +  if (!sToplevel_event_hook_added) {
   1.180 +    sToplevel_event_hook_added = true;
   1.181 +    sToplevel_show_hook =
   1.182 +      g_signal_add_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
   1.183 +                                 0, toplevel_event_watcher,
   1.184 +                                 reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW),
   1.185 +                                 nullptr);
   1.186 +    sToplevel_hide_hook =
   1.187 +      g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW), 0,
   1.188 +                                 toplevel_event_watcher,
   1.189 +                                 reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE),
   1.190 +                                 nullptr);
   1.191 +  }
   1.192 +}
   1.193 +
   1.194 +void
   1.195 +a11y::PlatformShutdown()
   1.196 +{
   1.197 +    if (sToplevel_event_hook_added) {
   1.198 +      sToplevel_event_hook_added = false;
   1.199 +      g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
   1.200 +                                    sToplevel_show_hook);
   1.201 +      g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
   1.202 +                                    sToplevel_hide_hook);
   1.203 +    }
   1.204 +
   1.205 +    if (sAtkBridge.lib) {
   1.206 +        // Do not shutdown/unload atk-bridge,
   1.207 +        // an exit function registered will take care of it
   1.208 +        // if (sAtkBridge.shutdown)
   1.209 +        //     (*sAtkBridge.shutdown)();
   1.210 +        // PR_UnloadLibrary(sAtkBridge.lib);
   1.211 +        sAtkBridge.lib = nullptr;
   1.212 +        sAtkBridge.init = nullptr;
   1.213 +        sAtkBridge.shutdown = nullptr;
   1.214 +    }
   1.215 +    if (sGail.lib) {
   1.216 +        // Do not shutdown gail because
   1.217 +        // 1) Maybe it's not init-ed by us. e.g. GtkEmbed
   1.218 +        // 2) We need it to avoid assert in spi_atk_tidy_windows
   1.219 +        // if (sGail.shutdown)
   1.220 +        //   (*sGail.shutdown)();
   1.221 +        // PR_UnloadLibrary(sGail.lib);
   1.222 +        sGail.lib = nullptr;
   1.223 +        sGail.init = nullptr;
   1.224 +        sGail.shutdown = nullptr;
   1.225 +    }
   1.226 +    // if (sATKLib) {
   1.227 +    //     PR_UnloadLibrary(sATKLib);
   1.228 +    //     sATKLib = nullptr;
   1.229 +    // }
   1.230 +}
   1.231 +
   1.232 +  static const char sAccEnv [] = "GNOME_ACCESSIBILITY";
   1.233 +#ifdef MOZ_ENABLE_DBUS
   1.234 +static DBusPendingCall *sPendingCall = nullptr;
   1.235 +#endif
   1.236 +
   1.237 +void
   1.238 +a11y::PreInit()
   1.239 +{
   1.240 +#ifdef MOZ_ENABLE_DBUS
   1.241 +  static bool sChecked = FALSE;
   1.242 +  if (sChecked)
   1.243 +    return;
   1.244 +
   1.245 +  sChecked = TRUE;
   1.246 +
   1.247 +  // dbus is only checked if GNOME_ACCESSIBILITY is unset
   1.248 +  // also make sure that a session bus address is available to prevent dbus from
   1.249 +  // starting a new one.  Dbus confuses the test harness when it creates a new
   1.250 +  // process (see bug 693343)
   1.251 +  if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS"))
   1.252 +    return;
   1.253 +
   1.254 +  DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nullptr);
   1.255 +  if (!bus)
   1.256 +    return;
   1.257 +
   1.258 +  dbus_connection_set_exit_on_disconnect(bus, FALSE);
   1.259 +
   1.260 +  static const char* iface = "org.a11y.Status";
   1.261 +  static const char* member = "IsEnabled";
   1.262 +  DBusMessage *message;
   1.263 +  message = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
   1.264 +                                         "org.freedesktop.DBus.Properties",
   1.265 +                                         "Get");
   1.266 +  if (!message)
   1.267 +    goto dbus_done;
   1.268 +
   1.269 +  dbus_message_append_args(message, DBUS_TYPE_STRING, &iface,
   1.270 +                           DBUS_TYPE_STRING, &member, DBUS_TYPE_INVALID);
   1.271 +  dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
   1.272 +  dbus_message_unref(message);
   1.273 +
   1.274 +dbus_done:
   1.275 +  dbus_connection_unref(bus);
   1.276 +#endif
   1.277 +}
   1.278 +
   1.279 +bool
   1.280 +a11y::ShouldA11yBeEnabled()
   1.281 +{
   1.282 +  static bool sChecked = false, sShouldEnable = false;
   1.283 +  if (sChecked)
   1.284 +    return sShouldEnable;
   1.285 +
   1.286 +  sChecked = true;
   1.287 +
   1.288 +  EPlatformDisabledState disabledState = PlatformDisabledState();
   1.289 +  if (disabledState == ePlatformIsDisabled)
   1.290 +    return sShouldEnable = false;
   1.291 +
   1.292 +  // check if accessibility enabled/disabled by environment variable
   1.293 +  const char* envValue = PR_GetEnv(sAccEnv);
   1.294 +  if (envValue)
   1.295 +    return sShouldEnable = !!atoi(envValue);
   1.296 +
   1.297 +#ifdef MOZ_ENABLE_DBUS
   1.298 +  PreInit();
   1.299 +  bool dbusSuccess = false;
   1.300 +  DBusMessage *reply = nullptr;
   1.301 +  if (!sPendingCall)
   1.302 +    goto dbus_done;
   1.303 +
   1.304 +  dbus_pending_call_block(sPendingCall);
   1.305 +  reply = dbus_pending_call_steal_reply(sPendingCall);
   1.306 +  dbus_pending_call_unref(sPendingCall);
   1.307 +  sPendingCall = nullptr;
   1.308 +  if (!reply ||
   1.309 +      dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
   1.310 +      strcmp(dbus_message_get_signature (reply), DBUS_TYPE_VARIANT_AS_STRING))
   1.311 +    goto dbus_done;
   1.312 +
   1.313 +  DBusMessageIter iter, iter_variant, iter_struct;
   1.314 +  dbus_bool_t dResult;
   1.315 +  dbus_message_iter_init(reply, &iter);
   1.316 +  dbus_message_iter_recurse (&iter, &iter_variant);
   1.317 +  switch (dbus_message_iter_get_arg_type(&iter_variant)) {
   1.318 +    case DBUS_TYPE_STRUCT:
   1.319 +      // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
   1.320 +      dbus_message_iter_recurse(&iter_variant, &iter_struct);
   1.321 +      if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
   1.322 +        dbus_message_iter_get_basic(&iter_struct, &dResult);
   1.323 +        sShouldEnable = dResult;
   1.324 +        dbusSuccess = true;
   1.325 +      }
   1.326 +
   1.327 +      break;
   1.328 +    case DBUS_TYPE_BOOLEAN:
   1.329 +      dbus_message_iter_get_basic(&iter_variant, &dResult);
   1.330 +      sShouldEnable = dResult;
   1.331 +      dbusSuccess = true;
   1.332 +      break;
   1.333 +    default:
   1.334 +      break;
   1.335 +  }
   1.336 +
   1.337 +dbus_done:
   1.338 +  if (reply)
   1.339 +    dbus_message_unref(reply);
   1.340 +
   1.341 +  if (dbusSuccess)
   1.342 +    return sShouldEnable;
   1.343 +#endif
   1.344 +
   1.345 +  //check gconf-2 setting
   1.346 +static const char sGconfAccessibilityKey[] =
   1.347 +    "/desktop/gnome/interface/accessibility";
   1.348 +  nsresult rv = NS_OK;
   1.349 +  nsCOMPtr<nsIGConfService> gconf =
   1.350 +    do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv);
   1.351 +  if (NS_SUCCEEDED(rv) && gconf)
   1.352 +    gconf->GetBool(NS_LITERAL_CSTRING(sGconfAccessibilityKey), &sShouldEnable);
   1.353 +
   1.354 +  return sShouldEnable;
   1.355 +}

mercurial