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 "nsIGSettingsService.h"
12 #include "AtkSocketAccessible.h"
16 #ifdef MOZ_ENABLE_DBUS
17 # include <dbus/dbus.h>
22 extern "C" __attribute__((weak
, visibility("default"))) int
23 atk_bridge_adaptor_init(int*, char**[]);
26 using namespace mozilla
;
27 using namespace mozilla::a11y
;
29 int atkMajorVersion
= 1, atkMinorVersion
= 12, atkMicroVersion
= 0;
31 GType (*gAtkTableCellGetTypeFunc
)();
34 typedef GType (*AtkGetTypeType
)(void);
35 typedef void (*GnomeAccessibilityInit
)(void);
36 typedef void (*GnomeAccessibilityShutdown
)(void);
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";
44 gboolean
toplevel_event_watcher(GSignalInvocationHint
*, guint
, const GValue
*,
46 static bool sToplevel_event_hook_added
= false;
47 static gulong sToplevel_show_hook
= 0;
48 static gulong sToplevel_hide_hook
= 0;
50 GType g_atk_hyperlink_impl_type
= G_TYPE_INVALID
;
52 struct GnomeAccessibilityModule
{
56 GnomeAccessibilityInit init
;
57 const char* shutdownName
;
58 GnomeAccessibilityShutdown shutdown
;
61 static GnomeAccessibilityModule sAtkBridge
= {
63 "libatk-bridge.a(libatk-bridge.so.0)", nullptr,
65 "libatk-bridge.so", nullptr,
67 "gnome_accessibility_module_init", nullptr,
68 "gnome_accessibility_module_shutdown", nullptr};
70 static nsresult
LoadGtkModule(GnomeAccessibilityModule
& aModule
) {
71 NS_ENSURE_ARG(aModule
.libName
);
73 if (!(aModule
.lib
= PR_LoadLibrary(aModule
.libName
))) {
74 // try to load the module with "gtk-2.0/modules" appended
75 char* curLibPath
= PR_GetLibraryPath();
76 nsAutoCString
libPath(curLibPath
);
77 #if defined(LINUX) && defined(__x86_64__)
78 libPath
.AppendLiteral(":/usr/lib64:/usr/lib");
80 libPath
.AppendLiteral(":/usr/lib");
82 PR_FreeLibraryName(curLibPath
);
84 int16_t loc1
= 0, loc2
= 0;
87 loc2
= libPath
.FindChar(':', loc1
);
89 subLen
= libPath
.Length() - loc1
;
92 nsAutoCString
sub(Substring(libPath
, loc1
, subLen
));
93 sub
.AppendLiteral("/gtk-3.0/modules/");
94 sub
.Append(aModule
.libName
);
95 aModule
.lib
= PR_LoadLibrary(sub
.get());
96 if (aModule
.lib
) break;
100 if (!aModule
.lib
) return NS_ERROR_FAILURE
;
103 // we have loaded the library, try to get the function ptrs
104 if (!(aModule
.init
= PR_FindFunctionSymbol(aModule
.lib
, aModule
.initName
)) ||
106 PR_FindFunctionSymbol(aModule
.lib
, aModule
.shutdownName
))) {
108 PR_UnloadLibrary(aModule
.lib
);
109 aModule
.lib
= nullptr;
110 return NS_ERROR_FAILURE
;
115 void a11y::PlatformInit() {
116 if (!ShouldA11yBeEnabled()) return;
118 sATKLib
= PR_LoadLibrary(sATKLibName
);
119 if (!sATKLib
) return;
121 AtkGetTypeType pfn_atk_hyperlink_impl_get_type
=
122 (AtkGetTypeType
)PR_FindFunctionSymbol(sATKLib
,
123 sATKHyperlinkImplGetTypeSymbol
);
124 if (pfn_atk_hyperlink_impl_get_type
)
125 g_atk_hyperlink_impl_type
= pfn_atk_hyperlink_impl_get_type();
127 AtkGetTypeType pfn_atk_socket_get_type
=
128 (AtkGetTypeType
)PR_FindFunctionSymbol(
129 sATKLib
, AtkSocketAccessible::sATKSocketGetTypeSymbol
);
130 if (pfn_atk_socket_get_type
) {
131 AtkSocketAccessible::g_atk_socket_type
= pfn_atk_socket_get_type();
132 AtkSocketAccessible::g_atk_socket_embed
=
133 (AtkSocketEmbedType
)PR_FindFunctionSymbol(
134 sATKLib
, AtkSocketAccessible ::sATKSocketEmbedSymbol
);
135 AtkSocketAccessible::gCanEmbed
=
136 AtkSocketAccessible::g_atk_socket_type
!= G_TYPE_INVALID
&&
137 AtkSocketAccessible::g_atk_socket_embed
;
140 gAtkTableCellGetTypeFunc
=
141 (GType(*)())PR_FindFunctionSymbol(sATKLib
, "atk_table_cell_get_type");
143 const char* (*atkGetVersion
)() =
144 (const char* (*)())PR_FindFunctionSymbol(sATKLib
, "atk_get_version");
146 const char* version
= atkGetVersion();
148 char* endPtr
= nullptr;
149 atkMajorVersion
= strtol(version
, &endPtr
, 10);
150 if (atkMajorVersion
!= 0L) {
151 atkMinorVersion
= strtol(endPtr
+ 1, &endPtr
, 10);
152 if (atkMinorVersion
!= 0L)
153 atkMicroVersion
= strtol(endPtr
+ 1, &endPtr
, 10);
158 // Initialize the MAI Utility class, it will overwrite gail_util.
159 g_type_class_unref(g_type_class_ref(mai_util_get_type()));
161 // Init atk-bridge now
162 PR_SetEnv("NO_AT_BRIDGE=0");
163 #ifdef MOZ_WIDGET_GTK
164 if (atk_bridge_adaptor_init
) {
165 atk_bridge_adaptor_init(nullptr, nullptr);
169 nsresult rv
= LoadGtkModule(sAtkBridge
);
170 if (NS_SUCCEEDED(rv
)) {
171 (*sAtkBridge
.init
)();
175 if (!sToplevel_event_hook_added
) {
176 sToplevel_event_hook_added
= true;
177 sToplevel_show_hook
= g_signal_add_emission_hook(
178 g_signal_lookup("show", GTK_TYPE_WINDOW
), 0, toplevel_event_watcher
,
179 reinterpret_cast<gpointer
>(nsIAccessibleEvent::EVENT_SHOW
), nullptr);
180 sToplevel_hide_hook
= g_signal_add_emission_hook(
181 g_signal_lookup("hide", GTK_TYPE_WINDOW
), 0, toplevel_event_watcher
,
182 reinterpret_cast<gpointer
>(nsIAccessibleEvent::EVENT_HIDE
), nullptr);
186 void a11y::PlatformShutdown() {
187 if (sToplevel_event_hook_added
) {
188 sToplevel_event_hook_added
= false;
189 g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW
),
190 sToplevel_show_hook
);
191 g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW
),
192 sToplevel_hide_hook
);
195 if (sAtkBridge
.lib
) {
196 // Do not shutdown/unload atk-bridge,
197 // an exit function registered will take care of it
198 // if (sAtkBridge.shutdown)
199 // (*sAtkBridge.shutdown)();
200 // PR_UnloadLibrary(sAtkBridge.lib);
201 sAtkBridge
.lib
= nullptr;
202 sAtkBridge
.init
= nullptr;
203 sAtkBridge
.shutdown
= nullptr;
206 // PR_UnloadLibrary(sATKLib);
207 // sATKLib = nullptr;
211 static const char sAccEnv
[] = "GNOME_ACCESSIBILITY";
212 #ifdef MOZ_ENABLE_DBUS
213 static DBusPendingCall
* sPendingCall
= nullptr;
216 void a11y::PreInit() {
217 #ifdef MOZ_ENABLE_DBUS
218 static bool sChecked
= FALSE
;
219 if (sChecked
) return;
223 // dbus is only checked if GNOME_ACCESSIBILITY is unset
224 // also make sure that a session bus address is available to prevent dbus from
225 // starting a new one. Dbus confuses the test harness when it creates a new
226 // process (see bug 693343)
227 if (PR_GetEnv(sAccEnv
) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS")) return;
229 DBusConnection
* bus
= dbus_bus_get(DBUS_BUS_SESSION
, nullptr);
232 dbus_connection_set_exit_on_disconnect(bus
, FALSE
);
234 static const char* iface
= "org.a11y.Status";
235 static const char* member
= "IsEnabled";
236 DBusMessage
* message
;
238 dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
239 "org.freedesktop.DBus.Properties", "Get");
240 if (!message
) goto dbus_done
;
242 dbus_message_append_args(message
, DBUS_TYPE_STRING
, &iface
, DBUS_TYPE_STRING
,
243 &member
, DBUS_TYPE_INVALID
);
244 dbus_connection_send_with_reply(bus
, message
, &sPendingCall
, 1000);
245 dbus_message_unref(message
);
248 dbus_connection_unref(bus
);
252 bool a11y::ShouldA11yBeEnabled() {
253 static bool sChecked
= false, sShouldEnable
= false;
254 if (sChecked
) return sShouldEnable
;
258 EPlatformDisabledState disabledState
= PlatformDisabledState();
259 if (disabledState
== ePlatformIsDisabled
) return sShouldEnable
= false;
261 // check if accessibility enabled/disabled by environment variable
262 const char* envValue
= PR_GetEnv(sAccEnv
);
263 if (envValue
) return sShouldEnable
= !!atoi(envValue
);
265 #ifdef MOZ_ENABLE_DBUS
267 bool dbusSuccess
= false;
268 DBusMessage
* reply
= nullptr;
269 if (!sPendingCall
) goto dbus_done
;
271 dbus_pending_call_block(sPendingCall
);
272 reply
= dbus_pending_call_steal_reply(sPendingCall
);
273 dbus_pending_call_unref(sPendingCall
);
274 sPendingCall
= nullptr;
276 dbus_message_get_type(reply
) != DBUS_MESSAGE_TYPE_METHOD_RETURN
||
277 strcmp(dbus_message_get_signature(reply
), DBUS_TYPE_VARIANT_AS_STRING
))
280 DBusMessageIter iter
, iter_variant
, iter_struct
;
282 dbus_message_iter_init(reply
, &iter
);
283 dbus_message_iter_recurse(&iter
, &iter_variant
);
284 switch (dbus_message_iter_get_arg_type(&iter_variant
)) {
285 case DBUS_TYPE_STRUCT
:
286 // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
287 dbus_message_iter_recurse(&iter_variant
, &iter_struct
);
288 if (dbus_message_iter_get_arg_type(&iter_struct
) == DBUS_TYPE_BOOLEAN
) {
289 dbus_message_iter_get_basic(&iter_struct
, &dResult
);
290 sShouldEnable
= dResult
;
295 case DBUS_TYPE_BOOLEAN
:
296 dbus_message_iter_get_basic(&iter_variant
, &dResult
);
297 sShouldEnable
= dResult
;
305 if (reply
) dbus_message_unref(reply
);
307 if (dbusSuccess
) return sShouldEnable
;
311 #define GSETINGS_A11Y_INTERFACE "org.gnome.desktop.interface"
312 #define GSETINGS_A11Y_KEY "toolkit-accessibility"
313 nsCOMPtr
<nsIGSettingsService
> gsettings
=
314 do_GetService(NS_GSETTINGSSERVICE_CONTRACTID
);
315 nsCOMPtr
<nsIGSettingsCollection
> a11y_settings
;
318 gsettings
->GetCollectionForSchema(nsLiteralCString(GSETINGS_A11Y_INTERFACE
),
319 getter_AddRefs(a11y_settings
));
321 a11y_settings
->GetBoolean(nsLiteralCString(GSETINGS_A11Y_KEY
),
326 return sShouldEnable
;