Bumping manifests a=b2g-bump
[gecko.git] / accessible / atk / Platform.cpp
bloba2afd9667fc678aa06c98b2cb492456b7e0c4152
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/. */
7 #include "Platform.h"
9 #include "nsIAccessibleEvent.h"
10 #include "nsIGConfService.h"
11 #include "nsIServiceManager.h"
12 #include "nsMai.h"
13 #include "AtkSocketAccessible.h"
14 #include "prenv.h"
15 #include "prlink.h"
17 #ifdef MOZ_ENABLE_DBUS
18 #include <dbus/dbus.h>
19 #endif
20 #include <gtk/gtk.h>
21 #if (MOZ_WIDGET_GTK == 3)
22 #include <atk-bridge.h>
23 #endif
25 using namespace mozilla;
26 using namespace mozilla::a11y;
28 int atkMajorVersion = 1, atkMinorVersion = 12;
30 extern "C" {
31 typedef GType (* AtkGetTypeType) (void);
32 typedef void (*GnomeAccessibilityInit) (void);
33 typedef void (*GnomeAccessibilityShutdown) (void);
36 static PRLibrary* sATKLib = nullptr;
37 static const char sATKLibName[] = "libatk-1.0.so.0";
38 static const char sATKHyperlinkImplGetTypeSymbol[] =
39 "atk_hyperlink_impl_get_type";
41 gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
42 gpointer);
43 static bool sToplevel_event_hook_added = false;
44 static gulong sToplevel_show_hook = 0;
45 static gulong sToplevel_hide_hook = 0;
47 GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
49 #if (MOZ_WIDGET_GTK == 2)
50 struct GnomeAccessibilityModule
52 const char *libName;
53 PRLibrary *lib;
54 const char *initName;
55 GnomeAccessibilityInit init;
56 const char *shutdownName;
57 GnomeAccessibilityShutdown shutdown;
60 static GnomeAccessibilityModule sAtkBridge = {
61 #ifdef AIX
62 "libatk-bridge.a(libatk-bridge.so.0)", nullptr,
63 #else
64 "libatk-bridge.so", nullptr,
65 #endif
66 "gnome_accessibility_module_init", nullptr,
67 "gnome_accessibility_module_shutdown", nullptr
70 static GnomeAccessibilityModule sGail = {
71 "libgail.so", nullptr,
72 "gnome_accessibility_module_init", nullptr,
73 "gnome_accessibility_module_shutdown", nullptr
76 static nsresult
77 LoadGtkModule(GnomeAccessibilityModule& aModule)
79 NS_ENSURE_ARG(aModule.libName);
81 if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
82 //try to load the module with "gtk-2.0/modules" appended
83 char *curLibPath = PR_GetLibraryPath();
84 nsAutoCString libPath(curLibPath);
85 #if defined(LINUX) && defined(__x86_64__)
86 libPath.AppendLiteral(":/usr/lib64:/usr/lib");
87 #else
88 libPath.AppendLiteral(":/usr/lib");
89 #endif
90 PR_FreeLibraryName(curLibPath);
92 int16_t loc1 = 0, loc2 = 0;
93 int16_t subLen = 0;
94 while (loc2 >= 0) {
95 loc2 = libPath.FindChar(':', loc1);
96 if (loc2 < 0)
97 subLen = libPath.Length() - loc1;
98 else
99 subLen = loc2 - loc1;
100 nsAutoCString sub(Substring(libPath, loc1, subLen));
101 sub.AppendLiteral("/gtk-2.0/modules/");
102 sub.Append(aModule.libName);
103 aModule.lib = PR_LoadLibrary(sub.get());
104 if (aModule.lib)
105 break;
107 loc1 = loc2+1;
109 if (!aModule.lib)
110 return NS_ERROR_FAILURE;
113 //we have loaded the library, try to get the function ptrs
114 if (!(aModule.init = PR_FindFunctionSymbol(aModule.lib,
115 aModule.initName)) ||
116 !(aModule.shutdown = PR_FindFunctionSymbol(aModule.lib,
117 aModule.shutdownName))) {
119 //fail, :(
120 PR_UnloadLibrary(aModule.lib);
121 aModule.lib = nullptr;
122 return NS_ERROR_FAILURE;
124 return NS_OK;
126 #endif // (MOZ_WIDGET_GTK == 2)
128 void
129 a11y::PlatformInit()
131 if (!ShouldA11yBeEnabled())
132 return;
134 sATKLib = PR_LoadLibrary(sATKLibName);
135 if (!sATKLib)
136 return;
138 AtkGetTypeType pfn_atk_hyperlink_impl_get_type =
139 (AtkGetTypeType) PR_FindFunctionSymbol(sATKLib, sATKHyperlinkImplGetTypeSymbol);
140 if (pfn_atk_hyperlink_impl_get_type)
141 g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
143 AtkGetTypeType pfn_atk_socket_get_type = (AtkGetTypeType)
144 PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible::sATKSocketGetTypeSymbol);
145 if (pfn_atk_socket_get_type) {
146 AtkSocketAccessible::g_atk_socket_type = pfn_atk_socket_get_type();
147 AtkSocketAccessible::g_atk_socket_embed = (AtkSocketEmbedType)
148 PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible ::sATKSocketEmbedSymbol);
149 AtkSocketAccessible::gCanEmbed =
150 AtkSocketAccessible::g_atk_socket_type != G_TYPE_INVALID &&
151 AtkSocketAccessible::g_atk_socket_embed;
154 const char* (*atkGetVersion)() =
155 (const char* (*)()) PR_FindFunctionSymbol(sATKLib, "atk_get_version");
156 if (atkGetVersion) {
157 const char* version = atkGetVersion();
158 if (version) {
159 char* endPtr = nullptr;
160 atkMajorVersion = strtol(version, &endPtr, 10);
161 if (*endPtr == '.')
162 atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
166 #if (MOZ_WIDGET_GTK == 2)
167 // Load and initialize gail library.
168 nsresult rv = LoadGtkModule(sGail);
169 if (NS_SUCCEEDED(rv))
170 (*sGail.init)();
171 #endif
173 // Initialize the MAI Utility class, it will overwrite gail_util.
174 g_type_class_unref(g_type_class_ref(mai_util_get_type()));
176 // Init atk-bridge now
177 PR_SetEnv("NO_AT_BRIDGE=0");
178 #if (MOZ_WIDGET_GTK == 2)
179 rv = LoadGtkModule(sAtkBridge);
180 if (NS_SUCCEEDED(rv)) {
181 (*sAtkBridge.init)();
183 #else
184 atk_bridge_adaptor_init(nullptr, nullptr);
185 #endif
187 if (!sToplevel_event_hook_added) {
188 sToplevel_event_hook_added = true;
189 sToplevel_show_hook =
190 g_signal_add_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
191 0, toplevel_event_watcher,
192 reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW),
193 nullptr);
194 sToplevel_hide_hook =
195 g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW), 0,
196 toplevel_event_watcher,
197 reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE),
198 nullptr);
202 void
203 a11y::PlatformShutdown()
205 if (sToplevel_event_hook_added) {
206 sToplevel_event_hook_added = false;
207 g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
208 sToplevel_show_hook);
209 g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
210 sToplevel_hide_hook);
213 #if (MOZ_WIDGET_GTK == 2)
214 if (sAtkBridge.lib) {
215 // Do not shutdown/unload atk-bridge,
216 // an exit function registered will take care of it
217 // if (sAtkBridge.shutdown)
218 // (*sAtkBridge.shutdown)();
219 // PR_UnloadLibrary(sAtkBridge.lib);
220 sAtkBridge.lib = nullptr;
221 sAtkBridge.init = nullptr;
222 sAtkBridge.shutdown = nullptr;
224 if (sGail.lib) {
225 // Do not shutdown gail because
226 // 1) Maybe it's not init-ed by us. e.g. GtkEmbed
227 // 2) We need it to avoid assert in spi_atk_tidy_windows
228 // if (sGail.shutdown)
229 // (*sGail.shutdown)();
230 // PR_UnloadLibrary(sGail.lib);
231 sGail.lib = nullptr;
232 sGail.init = nullptr;
233 sGail.shutdown = nullptr;
235 #endif
236 // if (sATKLib) {
237 // PR_UnloadLibrary(sATKLib);
238 // sATKLib = nullptr;
239 // }
242 static const char sAccEnv [] = "GNOME_ACCESSIBILITY";
243 #ifdef MOZ_ENABLE_DBUS
244 static DBusPendingCall *sPendingCall = nullptr;
245 #endif
247 void
248 a11y::PreInit()
250 #ifdef MOZ_ENABLE_DBUS
251 static bool sChecked = FALSE;
252 if (sChecked)
253 return;
255 sChecked = TRUE;
257 // dbus is only checked if GNOME_ACCESSIBILITY is unset
258 // also make sure that a session bus address is available to prevent dbus from
259 // starting a new one. Dbus confuses the test harness when it creates a new
260 // process (see bug 693343)
261 if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS"))
262 return;
264 DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nullptr);
265 if (!bus)
266 return;
268 dbus_connection_set_exit_on_disconnect(bus, FALSE);
270 static const char* iface = "org.a11y.Status";
271 static const char* member = "IsEnabled";
272 DBusMessage *message;
273 message = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
274 "org.freedesktop.DBus.Properties",
275 "Get");
276 if (!message)
277 goto dbus_done;
279 dbus_message_append_args(message, DBUS_TYPE_STRING, &iface,
280 DBUS_TYPE_STRING, &member, DBUS_TYPE_INVALID);
281 dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
282 dbus_message_unref(message);
284 dbus_done:
285 dbus_connection_unref(bus);
286 #endif
289 bool
290 a11y::ShouldA11yBeEnabled()
292 static bool sChecked = false, sShouldEnable = false;
293 if (sChecked)
294 return sShouldEnable;
296 sChecked = true;
298 EPlatformDisabledState disabledState = PlatformDisabledState();
299 if (disabledState == ePlatformIsDisabled)
300 return sShouldEnable = false;
302 // check if accessibility enabled/disabled by environment variable
303 const char* envValue = PR_GetEnv(sAccEnv);
304 if (envValue)
305 return sShouldEnable = !!atoi(envValue);
307 #ifdef MOZ_ENABLE_DBUS
308 PreInit();
309 bool dbusSuccess = false;
310 DBusMessage *reply = nullptr;
311 if (!sPendingCall)
312 goto dbus_done;
314 dbus_pending_call_block(sPendingCall);
315 reply = dbus_pending_call_steal_reply(sPendingCall);
316 dbus_pending_call_unref(sPendingCall);
317 sPendingCall = nullptr;
318 if (!reply ||
319 dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
320 strcmp(dbus_message_get_signature (reply), DBUS_TYPE_VARIANT_AS_STRING))
321 goto dbus_done;
323 DBusMessageIter iter, iter_variant, iter_struct;
324 dbus_bool_t dResult;
325 dbus_message_iter_init(reply, &iter);
326 dbus_message_iter_recurse (&iter, &iter_variant);
327 switch (dbus_message_iter_get_arg_type(&iter_variant)) {
328 case DBUS_TYPE_STRUCT:
329 // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
330 dbus_message_iter_recurse(&iter_variant, &iter_struct);
331 if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
332 dbus_message_iter_get_basic(&iter_struct, &dResult);
333 sShouldEnable = dResult;
334 dbusSuccess = true;
337 break;
338 case DBUS_TYPE_BOOLEAN:
339 dbus_message_iter_get_basic(&iter_variant, &dResult);
340 sShouldEnable = dResult;
341 dbusSuccess = true;
342 break;
343 default:
344 break;
347 dbus_done:
348 if (reply)
349 dbus_message_unref(reply);
351 if (dbusSuccess)
352 return sShouldEnable;
353 #endif
355 //check gconf-2 setting
356 static const char sGconfAccessibilityKey[] =
357 "/desktop/gnome/interface/accessibility";
358 nsresult rv = NS_OK;
359 nsCOMPtr<nsIGConfService> gconf =
360 do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv);
361 if (NS_SUCCEEDED(rv) && gconf)
362 gconf->GetBool(NS_LITERAL_CSTRING(sGconfAccessibilityKey), &sShouldEnable);
364 return sShouldEnable;