Bumping manifests a=b2g-bump
[gecko.git] / accessible / atk / Platform.cpp
blob42f52becaddac3511f4c44b1815f74143db09620
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>
22 using namespace mozilla;
23 using namespace mozilla::a11y;
25 int atkMajorVersion = 1, atkMinorVersion = 12;
27 extern "C" {
28 typedef GType (* AtkGetTypeType) (void);
29 typedef void (*GnomeAccessibilityInit) (void);
30 typedef void (*GnomeAccessibilityShutdown) (void);
33 static PRLibrary* sATKLib = nullptr;
34 static const char sATKLibName[] = "libatk-1.0.so.0";
35 static const char sATKHyperlinkImplGetTypeSymbol[] =
36 "atk_hyperlink_impl_get_type";
38 gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
39 gpointer);
40 static bool sToplevel_event_hook_added = false;
41 static gulong sToplevel_show_hook = 0;
42 static gulong sToplevel_hide_hook = 0;
44 GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
46 struct GnomeAccessibilityModule
48 const char *libName;
49 PRLibrary *lib;
50 const char *initName;
51 GnomeAccessibilityInit init;
52 const char *shutdownName;
53 GnomeAccessibilityShutdown shutdown;
56 static GnomeAccessibilityModule sAtkBridge = {
57 #ifdef AIX
58 "libatk-bridge.a(libatk-bridge.so.0)", nullptr,
59 #else
60 "libatk-bridge.so", nullptr,
61 #endif
62 "gnome_accessibility_module_init", nullptr,
63 "gnome_accessibility_module_shutdown", nullptr
66 static GnomeAccessibilityModule sGail = {
67 "libgail.so", nullptr,
68 "gnome_accessibility_module_init", nullptr,
69 "gnome_accessibility_module_shutdown", nullptr
72 static nsresult
73 LoadGtkModule(GnomeAccessibilityModule& aModule)
75 NS_ENSURE_ARG(aModule.libName);
77 if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
78 //try to load the module with "gtk-2.0/modules" appended
79 char *curLibPath = PR_GetLibraryPath();
80 nsAutoCString libPath(curLibPath);
81 #if defined(LINUX) && defined(__x86_64__)
82 libPath.AppendLiteral(":/usr/lib64:/usr/lib");
83 #else
84 libPath.AppendLiteral(":/usr/lib");
85 #endif
86 PR_FreeLibraryName(curLibPath);
88 int16_t loc1 = 0, loc2 = 0;
89 int16_t subLen = 0;
90 while (loc2 >= 0) {
91 loc2 = libPath.FindChar(':', loc1);
92 if (loc2 < 0)
93 subLen = libPath.Length() - loc1;
94 else
95 subLen = loc2 - loc1;
96 nsAutoCString sub(Substring(libPath, loc1, subLen));
97 sub.AppendLiteral("/gtk-2.0/modules/");
98 sub.Append(aModule.libName);
99 aModule.lib = PR_LoadLibrary(sub.get());
100 if (aModule.lib)
101 break;
103 loc1 = loc2+1;
105 if (!aModule.lib)
106 return NS_ERROR_FAILURE;
109 //we have loaded the library, try to get the function ptrs
110 if (!(aModule.init = PR_FindFunctionSymbol(aModule.lib,
111 aModule.initName)) ||
112 !(aModule.shutdown = PR_FindFunctionSymbol(aModule.lib,
113 aModule.shutdownName))) {
115 //fail, :(
116 PR_UnloadLibrary(aModule.lib);
117 aModule.lib = nullptr;
118 return NS_ERROR_FAILURE;
120 return NS_OK;
123 void
124 a11y::PlatformInit()
126 if (!ShouldA11yBeEnabled())
127 return;
129 sATKLib = PR_LoadLibrary(sATKLibName);
130 if (!sATKLib)
131 return;
133 AtkGetTypeType pfn_atk_hyperlink_impl_get_type =
134 (AtkGetTypeType) PR_FindFunctionSymbol(sATKLib, sATKHyperlinkImplGetTypeSymbol);
135 if (pfn_atk_hyperlink_impl_get_type)
136 g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
138 AtkGetTypeType pfn_atk_socket_get_type = (AtkGetTypeType)
139 PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible::sATKSocketGetTypeSymbol);
140 if (pfn_atk_socket_get_type) {
141 AtkSocketAccessible::g_atk_socket_type = pfn_atk_socket_get_type();
142 AtkSocketAccessible::g_atk_socket_embed = (AtkSocketEmbedType)
143 PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible ::sATKSocketEmbedSymbol);
144 AtkSocketAccessible::gCanEmbed =
145 AtkSocketAccessible::g_atk_socket_type != G_TYPE_INVALID &&
146 AtkSocketAccessible::g_atk_socket_embed;
149 const char* (*atkGetVersion)() =
150 (const char* (*)()) PR_FindFunctionSymbol(sATKLib, "atk_get_version");
151 if (atkGetVersion) {
152 const char* version = atkGetVersion();
153 if (version) {
154 char* endPtr = nullptr;
155 atkMajorVersion = strtol(version, &endPtr, 10);
156 if (*endPtr == '.')
157 atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
161 // Load and initialize gail library.
162 nsresult rv = LoadGtkModule(sGail);
163 if (NS_SUCCEEDED(rv))
164 (*sGail.init)();
166 // Initialize the MAI Utility class, it will overwrite gail_util.
167 g_type_class_unref(g_type_class_ref(mai_util_get_type()));
169 // Init atk-bridge now
170 PR_SetEnv("NO_AT_BRIDGE=0");
171 rv = LoadGtkModule(sAtkBridge);
172 if (NS_SUCCEEDED(rv)) {
173 (*sAtkBridge.init)();
176 if (!sToplevel_event_hook_added) {
177 sToplevel_event_hook_added = true;
178 sToplevel_show_hook =
179 g_signal_add_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
180 0, toplevel_event_watcher,
181 reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW),
182 nullptr);
183 sToplevel_hide_hook =
184 g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW), 0,
185 toplevel_event_watcher,
186 reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE),
187 nullptr);
191 void
192 a11y::PlatformShutdown()
194 if (sToplevel_event_hook_added) {
195 sToplevel_event_hook_added = false;
196 g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
197 sToplevel_show_hook);
198 g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
199 sToplevel_hide_hook);
202 if (sAtkBridge.lib) {
203 // Do not shutdown/unload atk-bridge,
204 // an exit function registered will take care of it
205 // if (sAtkBridge.shutdown)
206 // (*sAtkBridge.shutdown)();
207 // PR_UnloadLibrary(sAtkBridge.lib);
208 sAtkBridge.lib = nullptr;
209 sAtkBridge.init = nullptr;
210 sAtkBridge.shutdown = nullptr;
212 if (sGail.lib) {
213 // Do not shutdown gail because
214 // 1) Maybe it's not init-ed by us. e.g. GtkEmbed
215 // 2) We need it to avoid assert in spi_atk_tidy_windows
216 // if (sGail.shutdown)
217 // (*sGail.shutdown)();
218 // PR_UnloadLibrary(sGail.lib);
219 sGail.lib = nullptr;
220 sGail.init = nullptr;
221 sGail.shutdown = nullptr;
223 // if (sATKLib) {
224 // PR_UnloadLibrary(sATKLib);
225 // sATKLib = nullptr;
226 // }
229 static const char sAccEnv [] = "GNOME_ACCESSIBILITY";
230 #ifdef MOZ_ENABLE_DBUS
231 static DBusPendingCall *sPendingCall = nullptr;
232 #endif
234 void
235 a11y::PreInit()
237 #ifdef MOZ_ENABLE_DBUS
238 static bool sChecked = FALSE;
239 if (sChecked)
240 return;
242 sChecked = TRUE;
244 // dbus is only checked if GNOME_ACCESSIBILITY is unset
245 // also make sure that a session bus address is available to prevent dbus from
246 // starting a new one. Dbus confuses the test harness when it creates a new
247 // process (see bug 693343)
248 if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS"))
249 return;
251 DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nullptr);
252 if (!bus)
253 return;
255 dbus_connection_set_exit_on_disconnect(bus, FALSE);
257 static const char* iface = "org.a11y.Status";
258 static const char* member = "IsEnabled";
259 DBusMessage *message;
260 message = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
261 "org.freedesktop.DBus.Properties",
262 "Get");
263 if (!message)
264 goto dbus_done;
266 dbus_message_append_args(message, DBUS_TYPE_STRING, &iface,
267 DBUS_TYPE_STRING, &member, DBUS_TYPE_INVALID);
268 dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
269 dbus_message_unref(message);
271 dbus_done:
272 dbus_connection_unref(bus);
273 #endif
276 bool
277 a11y::ShouldA11yBeEnabled()
279 static bool sChecked = false, sShouldEnable = false;
280 if (sChecked)
281 return sShouldEnable;
283 sChecked = true;
285 EPlatformDisabledState disabledState = PlatformDisabledState();
286 if (disabledState == ePlatformIsDisabled)
287 return sShouldEnable = false;
289 // check if accessibility enabled/disabled by environment variable
290 const char* envValue = PR_GetEnv(sAccEnv);
291 if (envValue)
292 return sShouldEnable = !!atoi(envValue);
294 #ifdef MOZ_ENABLE_DBUS
295 PreInit();
296 bool dbusSuccess = false;
297 DBusMessage *reply = nullptr;
298 if (!sPendingCall)
299 goto dbus_done;
301 dbus_pending_call_block(sPendingCall);
302 reply = dbus_pending_call_steal_reply(sPendingCall);
303 dbus_pending_call_unref(sPendingCall);
304 sPendingCall = nullptr;
305 if (!reply ||
306 dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
307 strcmp(dbus_message_get_signature (reply), DBUS_TYPE_VARIANT_AS_STRING))
308 goto dbus_done;
310 DBusMessageIter iter, iter_variant, iter_struct;
311 dbus_bool_t dResult;
312 dbus_message_iter_init(reply, &iter);
313 dbus_message_iter_recurse (&iter, &iter_variant);
314 switch (dbus_message_iter_get_arg_type(&iter_variant)) {
315 case DBUS_TYPE_STRUCT:
316 // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
317 dbus_message_iter_recurse(&iter_variant, &iter_struct);
318 if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
319 dbus_message_iter_get_basic(&iter_struct, &dResult);
320 sShouldEnable = dResult;
321 dbusSuccess = true;
324 break;
325 case DBUS_TYPE_BOOLEAN:
326 dbus_message_iter_get_basic(&iter_variant, &dResult);
327 sShouldEnable = dResult;
328 dbusSuccess = true;
329 break;
330 default:
331 break;
334 dbus_done:
335 if (reply)
336 dbus_message_unref(reply);
338 if (dbusSuccess)
339 return sShouldEnable;
340 #endif
342 //check gconf-2 setting
343 static const char sGconfAccessibilityKey[] =
344 "/desktop/gnome/interface/accessibility";
345 nsresult rv = NS_OK;
346 nsCOMPtr<nsIGConfService> gconf =
347 do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv);
348 if (NS_SUCCEEDED(rv) && gconf)
349 gconf->GetBool(NS_LITERAL_CSTRING(sGconfAccessibilityKey), &sShouldEnable);
351 return sShouldEnable;