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