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 +}