summaryrefslogtreecommitdiffstats
path: root/accessible/atk/Platform.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/atk/Platform.cpp')
-rw-r--r--accessible/atk/Platform.cpp377
1 files changed, 377 insertions, 0 deletions
diff --git a/accessible/atk/Platform.cpp b/accessible/atk/Platform.cpp
new file mode 100644
index 000000000..e64084f5a
--- /dev/null
+++ b/accessible/atk/Platform.cpp
@@ -0,0 +1,377 @@
+/* -*- 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 "Platform.h"
+
+#include "nsIAccessibleEvent.h"
+#include "nsIGConfService.h"
+#include "nsIServiceManager.h"
+#include "nsMai.h"
+#include "AtkSocketAccessible.h"
+#include "prenv.h"
+#include "prlink.h"
+
+#ifdef MOZ_ENABLE_DBUS
+#include <dbus/dbus.h>
+#endif
+#include <gtk/gtk.h>
+
+#if (MOZ_WIDGET_GTK == 3)
+extern "C" __attribute__((weak,visibility("default"))) int atk_bridge_adaptor_init(int*, char **[]);
+#endif
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+int atkMajorVersion = 1, atkMinorVersion = 12;
+
+GType (*gAtkTableCellGetTypeFunc)();
+
+extern "C" {
+typedef GType (* AtkGetTypeType) (void);
+typedef void (*GnomeAccessibilityInit) (void);
+typedef void (*GnomeAccessibilityShutdown) (void);
+}
+
+static PRLibrary* sATKLib = nullptr;
+static const char sATKLibName[] = "libatk-1.0.so.0";
+static const char sATKHyperlinkImplGetTypeSymbol[] =
+ "atk_hyperlink_impl_get_type";
+
+gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
+ gpointer);
+static bool sToplevel_event_hook_added = false;
+static gulong sToplevel_show_hook = 0;
+static gulong sToplevel_hide_hook = 0;
+
+GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
+
+struct GnomeAccessibilityModule
+{
+ const char *libName;
+ PRLibrary *lib;
+ const char *initName;
+ GnomeAccessibilityInit init;
+ const char *shutdownName;
+ GnomeAccessibilityShutdown shutdown;
+};
+
+static GnomeAccessibilityModule sAtkBridge = {
+#ifdef AIX
+ "libatk-bridge.a(libatk-bridge.so.0)", nullptr,
+#else
+ "libatk-bridge.so", nullptr,
+#endif
+ "gnome_accessibility_module_init", nullptr,
+ "gnome_accessibility_module_shutdown", nullptr
+};
+
+#if (MOZ_WIDGET_GTK == 2)
+static GnomeAccessibilityModule sGail = {
+ "libgail.so", nullptr,
+ "gnome_accessibility_module_init", nullptr,
+ "gnome_accessibility_module_shutdown", nullptr
+};
+#endif
+
+static nsresult
+LoadGtkModule(GnomeAccessibilityModule& aModule)
+{
+ NS_ENSURE_ARG(aModule.libName);
+
+ if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
+ //try to load the module with "gtk-2.0/modules" appended
+ char *curLibPath = PR_GetLibraryPath();
+ nsAutoCString libPath(curLibPath);
+#if defined(LINUX) && defined(__x86_64__)
+ libPath.AppendLiteral(":/usr/lib64:/usr/lib");
+#else
+ libPath.AppendLiteral(":/usr/lib");
+#endif
+ PR_FreeLibraryName(curLibPath);
+
+ int16_t loc1 = 0, loc2 = 0;
+ int16_t subLen = 0;
+ while (loc2 >= 0) {
+ loc2 = libPath.FindChar(':', loc1);
+ if (loc2 < 0)
+ subLen = libPath.Length() - loc1;
+ else
+ subLen = loc2 - loc1;
+ nsAutoCString sub(Substring(libPath, loc1, subLen));
+#if (MOZ_WIDGET_GTK == 2)
+ sub.AppendLiteral("/gtk-2.0/modules/");
+#else
+ sub.AppendLiteral("/gtk-3.0/modules/");
+#endif
+ sub.Append(aModule.libName);
+ aModule.lib = PR_LoadLibrary(sub.get());
+ if (aModule.lib)
+ break;
+
+ loc1 = loc2+1;
+ }
+ if (!aModule.lib)
+ return NS_ERROR_FAILURE;
+ }
+
+ //we have loaded the library, try to get the function ptrs
+ if (!(aModule.init = PR_FindFunctionSymbol(aModule.lib,
+ aModule.initName)) ||
+ !(aModule.shutdown = PR_FindFunctionSymbol(aModule.lib,
+ aModule.shutdownName))) {
+
+ //fail, :(
+ PR_UnloadLibrary(aModule.lib);
+ aModule.lib = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+void
+a11y::PlatformInit()
+{
+ if (!ShouldA11yBeEnabled())
+ return;
+
+ sATKLib = PR_LoadLibrary(sATKLibName);
+ if (!sATKLib)
+ return;
+
+ AtkGetTypeType pfn_atk_hyperlink_impl_get_type =
+ (AtkGetTypeType) PR_FindFunctionSymbol(sATKLib, sATKHyperlinkImplGetTypeSymbol);
+ if (pfn_atk_hyperlink_impl_get_type)
+ g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
+
+ AtkGetTypeType pfn_atk_socket_get_type = (AtkGetTypeType)
+ PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible::sATKSocketGetTypeSymbol);
+ if (pfn_atk_socket_get_type) {
+ AtkSocketAccessible::g_atk_socket_type = pfn_atk_socket_get_type();
+ AtkSocketAccessible::g_atk_socket_embed = (AtkSocketEmbedType)
+ PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible ::sATKSocketEmbedSymbol);
+ AtkSocketAccessible::gCanEmbed =
+ AtkSocketAccessible::g_atk_socket_type != G_TYPE_INVALID &&
+ AtkSocketAccessible::g_atk_socket_embed;
+ }
+
+ gAtkTableCellGetTypeFunc = (GType (*)())
+ PR_FindFunctionSymbol(sATKLib, "atk_table_cell_get_type");
+
+ const char* (*atkGetVersion)() =
+ (const char* (*)()) PR_FindFunctionSymbol(sATKLib, "atk_get_version");
+ if (atkGetVersion) {
+ const char* version = atkGetVersion();
+ if (version) {
+ char* endPtr = nullptr;
+ atkMajorVersion = strtol(version, &endPtr, 10);
+ if (*endPtr == '.')
+ atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
+ }
+ }
+
+#if (MOZ_WIDGET_GTK == 2)
+ // Load and initialize gail library.
+ nsresult rv = LoadGtkModule(sGail);
+ if (NS_SUCCEEDED(rv))
+ (*sGail.init)();
+#endif
+
+ // Initialize the MAI Utility class, it will overwrite gail_util.
+ g_type_class_unref(g_type_class_ref(mai_util_get_type()));
+
+ // Init atk-bridge now
+ PR_SetEnv("NO_AT_BRIDGE=0");
+#if (MOZ_WIDGET_GTK == 3)
+ if (atk_bridge_adaptor_init) {
+ atk_bridge_adaptor_init(nullptr, nullptr);
+ } else
+#endif
+ {
+ nsresult rv = LoadGtkModule(sAtkBridge);
+ if (NS_SUCCEEDED(rv)) {
+ (*sAtkBridge.init)();
+ }
+ }
+
+ if (!sToplevel_event_hook_added) {
+ sToplevel_event_hook_added = true;
+ sToplevel_show_hook =
+ g_signal_add_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
+ 0, toplevel_event_watcher,
+ reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW),
+ nullptr);
+ sToplevel_hide_hook =
+ g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW), 0,
+ toplevel_event_watcher,
+ reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE),
+ nullptr);
+ }
+}
+
+void
+a11y::PlatformShutdown()
+{
+ if (sToplevel_event_hook_added) {
+ sToplevel_event_hook_added = false;
+ g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
+ sToplevel_show_hook);
+ g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
+ sToplevel_hide_hook);
+ }
+
+ if (sAtkBridge.lib) {
+ // Do not shutdown/unload atk-bridge,
+ // an exit function registered will take care of it
+ // if (sAtkBridge.shutdown)
+ // (*sAtkBridge.shutdown)();
+ // PR_UnloadLibrary(sAtkBridge.lib);
+ sAtkBridge.lib = nullptr;
+ sAtkBridge.init = nullptr;
+ sAtkBridge.shutdown = nullptr;
+ }
+#if (MOZ_WIDGET_GTK == 2)
+ if (sGail.lib) {
+ // Do not shutdown gail because
+ // 1) Maybe it's not init-ed by us. e.g. GtkEmbed
+ // 2) We need it to avoid assert in spi_atk_tidy_windows
+ // if (sGail.shutdown)
+ // (*sGail.shutdown)();
+ // PR_UnloadLibrary(sGail.lib);
+ sGail.lib = nullptr;
+ sGail.init = nullptr;
+ sGail.shutdown = nullptr;
+ }
+#endif
+ // if (sATKLib) {
+ // PR_UnloadLibrary(sATKLib);
+ // sATKLib = nullptr;
+ // }
+}
+
+ static const char sAccEnv [] = "GNOME_ACCESSIBILITY";
+#ifdef MOZ_ENABLE_DBUS
+static DBusPendingCall *sPendingCall = nullptr;
+#endif
+
+void
+a11y::PreInit()
+{
+#ifdef MOZ_ENABLE_DBUS
+ static bool sChecked = FALSE;
+ if (sChecked)
+ return;
+
+ sChecked = TRUE;
+
+ // dbus is only checked if GNOME_ACCESSIBILITY is unset
+ // also make sure that a session bus address is available to prevent dbus from
+ // starting a new one. Dbus confuses the test harness when it creates a new
+ // process (see bug 693343)
+ if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS"))
+ return;
+
+ DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nullptr);
+ if (!bus)
+ return;
+
+ dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
+ static const char* iface = "org.a11y.Status";
+ static const char* member = "IsEnabled";
+ DBusMessage *message;
+ message = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
+ "org.freedesktop.DBus.Properties",
+ "Get");
+ if (!message)
+ goto dbus_done;
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &iface,
+ DBUS_TYPE_STRING, &member, DBUS_TYPE_INVALID);
+ dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
+ dbus_message_unref(message);
+
+dbus_done:
+ dbus_connection_unref(bus);
+#endif
+}
+
+bool
+a11y::ShouldA11yBeEnabled()
+{
+ static bool sChecked = false, sShouldEnable = false;
+ if (sChecked)
+ return sShouldEnable;
+
+ sChecked = true;
+
+ EPlatformDisabledState disabledState = PlatformDisabledState();
+ if (disabledState == ePlatformIsDisabled)
+ return sShouldEnable = false;
+
+ // check if accessibility enabled/disabled by environment variable
+ const char* envValue = PR_GetEnv(sAccEnv);
+ if (envValue)
+ return sShouldEnable = !!atoi(envValue);
+
+#ifdef MOZ_ENABLE_DBUS
+ PreInit();
+ bool dbusSuccess = false;
+ DBusMessage *reply = nullptr;
+ if (!sPendingCall)
+ goto dbus_done;
+
+ dbus_pending_call_block(sPendingCall);
+ reply = dbus_pending_call_steal_reply(sPendingCall);
+ dbus_pending_call_unref(sPendingCall);
+ sPendingCall = nullptr;
+ if (!reply ||
+ dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
+ strcmp(dbus_message_get_signature (reply), DBUS_TYPE_VARIANT_AS_STRING))
+ goto dbus_done;
+
+ DBusMessageIter iter, iter_variant, iter_struct;
+ dbus_bool_t dResult;
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse (&iter, &iter_variant);
+ switch (dbus_message_iter_get_arg_type(&iter_variant)) {
+ case DBUS_TYPE_STRUCT:
+ // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
+ dbus_message_iter_recurse(&iter_variant, &iter_struct);
+ if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
+ dbus_message_iter_get_basic(&iter_struct, &dResult);
+ sShouldEnable = dResult;
+ dbusSuccess = true;
+ }
+
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ dbus_message_iter_get_basic(&iter_variant, &dResult);
+ sShouldEnable = dResult;
+ dbusSuccess = true;
+ break;
+ default:
+ break;
+ }
+
+dbus_done:
+ if (reply)
+ dbus_message_unref(reply);
+
+ if (dbusSuccess)
+ return sShouldEnable;
+#endif
+
+ //check gconf-2 setting
+#define GCONF_A11Y_KEY "/desktop/gnome/interface/accessibility"
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIGConfService> gconf =
+ do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && gconf)
+ gconf->GetBool(NS_LITERAL_CSTRING(GCONF_A11Y_KEY), &sShouldEnable);
+
+ return sShouldEnable;
+}