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/. */
9 #include "nsIAccessibleEvent.h"
10 #include "nsIGConfService.h"
11 #include "nsIServiceManager.h"
13 #include "AtkSocketAccessible.h"
17 #ifdef MOZ_ENABLE_DBUS
18 #include <dbus/dbus.h>
22 using namespace mozilla
;
23 using namespace mozilla::a11y
;
25 int atkMajorVersion
= 1, atkMinorVersion
= 12;
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
*,
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
51 GnomeAccessibilityInit init
;
52 const char *shutdownName
;
53 GnomeAccessibilityShutdown shutdown
;
56 static GnomeAccessibilityModule sAtkBridge
= {
58 "libatk-bridge.a(libatk-bridge.so.0)", nullptr,
60 "libatk-bridge.so", nullptr,
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
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");
84 libPath
.AppendLiteral(":/usr/lib");
86 PR_FreeLibraryName(curLibPath
);
88 int16_t loc1
= 0, loc2
= 0;
91 loc2
= libPath
.FindChar(':', loc1
);
93 subLen
= libPath
.Length() - 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());
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
))) {
116 PR_UnloadLibrary(aModule
.lib
);
117 aModule
.lib
= nullptr;
118 return NS_ERROR_FAILURE
;
126 if (!ShouldA11yBeEnabled())
129 sATKLib
= PR_LoadLibrary(sATKLibName
);
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");
152 const char* version
= atkGetVersion();
154 char* endPtr
= nullptr;
155 atkMajorVersion
= strtol(version
, &endPtr
, 10);
157 atkMinorVersion
= strtol(endPtr
+ 1, &endPtr
, 10);
161 // Load and initialize gail library.
162 nsresult rv
= LoadGtkModule(sGail
);
163 if (NS_SUCCEEDED(rv
))
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
),
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
),
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;
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);
220 sGail
.init
= nullptr;
221 sGail
.shutdown
= nullptr;
224 // PR_UnloadLibrary(sATKLib);
225 // sATKLib = nullptr;
229 static const char sAccEnv
[] = "GNOME_ACCESSIBILITY";
230 #ifdef MOZ_ENABLE_DBUS
231 static DBusPendingCall
*sPendingCall
= nullptr;
237 #ifdef MOZ_ENABLE_DBUS
238 static bool sChecked
= FALSE
;
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"))
251 DBusConnection
* bus
= dbus_bus_get(DBUS_BUS_SESSION
, nullptr);
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",
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
);
272 dbus_connection_unref(bus
);
277 a11y::ShouldA11yBeEnabled()
279 static bool sChecked
= false, sShouldEnable
= false;
281 return sShouldEnable
;
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
);
292 return sShouldEnable
= !!atoi(envValue
);
294 #ifdef MOZ_ENABLE_DBUS
296 bool dbusSuccess
= false;
297 DBusMessage
*reply
= nullptr;
301 dbus_pending_call_block(sPendingCall
);
302 reply
= dbus_pending_call_steal_reply(sPendingCall
);
303 dbus_pending_call_unref(sPendingCall
);
304 sPendingCall
= nullptr;
306 dbus_message_get_type(reply
) != DBUS_MESSAGE_TYPE_METHOD_RETURN
||
307 strcmp(dbus_message_get_signature (reply
), DBUS_TYPE_VARIANT_AS_STRING
))
310 DBusMessageIter iter
, iter_variant
, iter_struct
;
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
;
325 case DBUS_TYPE_BOOLEAN
:
326 dbus_message_iter_get_basic(&iter_variant
, &dResult
);
327 sShouldEnable
= dResult
;
336 dbus_message_unref(reply
);
339 return sShouldEnable
;
342 //check gconf-2 setting
343 static const char sGconfAccessibilityKey
[] =
344 "/desktop/gnome/interface/accessibility";
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
;