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 "nsServiceManagerUtils.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, atkMicroVersion
= 0;
27 GType (*gAtkTableCellGetTypeFunc
)();
30 typedef GType (*AtkGetTypeType
)(void);
31 typedef void (*AtkBridgeAdaptorInit
)(int*, char**[]);
34 static PRLibrary
* sATKLib
= nullptr;
35 static const char sATKLibName
[] = "libatk-1.0.so.0";
36 static const char sATKHyperlinkImplGetTypeSymbol
[] =
37 "atk_hyperlink_impl_get_type";
39 gboolean
toplevel_event_watcher(GSignalInvocationHint
*, guint
, const GValue
*,
41 static bool sToplevel_event_hook_added
= false;
42 static gulong sToplevel_show_hook
= 0;
43 static gulong sToplevel_hide_hook
= 0;
45 GType g_atk_hyperlink_impl_type
= G_TYPE_INVALID
;
47 struct AtkBridgeModule
{
51 AtkBridgeAdaptorInit init
;
54 static AtkBridgeModule sAtkBridge
= {"libatk-bridge-2.0.so.0", nullptr,
55 "atk_bridge_adaptor_init", nullptr};
57 static nsresult
LoadGtkModule(AtkBridgeModule
& aModule
) {
58 NS_ENSURE_ARG(aModule
.libName
);
60 if (!(aModule
.lib
= PR_LoadLibrary(aModule
.libName
))) {
61 return NS_ERROR_FAILURE
;
64 // we have loaded the library, try to get the function ptrs
65 if (!(aModule
.init
= (AtkBridgeAdaptorInit
)PR_FindFunctionSymbol(
66 aModule
.lib
, aModule
.initName
))) {
68 PR_UnloadLibrary(aModule
.lib
);
69 aModule
.lib
= nullptr;
70 return NS_ERROR_FAILURE
;
75 void a11y::PlatformInit() {
76 if (!ShouldA11yBeEnabled()) return;
78 sATKLib
= PR_LoadLibrary(sATKLibName
);
81 AtkGetTypeType pfn_atk_hyperlink_impl_get_type
=
82 (AtkGetTypeType
)PR_FindFunctionSymbol(sATKLib
,
83 sATKHyperlinkImplGetTypeSymbol
);
84 if (pfn_atk_hyperlink_impl_get_type
) {
85 g_atk_hyperlink_impl_type
= pfn_atk_hyperlink_impl_get_type();
88 AtkGetTypeType pfn_atk_socket_get_type
=
89 (AtkGetTypeType
)PR_FindFunctionSymbol(
90 sATKLib
, AtkSocketAccessible::sATKSocketGetTypeSymbol
);
91 if (pfn_atk_socket_get_type
) {
92 AtkSocketAccessible::g_atk_socket_type
= pfn_atk_socket_get_type();
93 AtkSocketAccessible::g_atk_socket_embed
=
94 (AtkSocketEmbedType
)PR_FindFunctionSymbol(
95 sATKLib
, AtkSocketAccessible ::sATKSocketEmbedSymbol
);
96 AtkSocketAccessible::gCanEmbed
=
97 AtkSocketAccessible::g_atk_socket_type
!= G_TYPE_INVALID
&&
98 AtkSocketAccessible::g_atk_socket_embed
;
101 gAtkTableCellGetTypeFunc
=
102 (GType(*)())PR_FindFunctionSymbol(sATKLib
, "atk_table_cell_get_type");
104 const char* (*atkGetVersion
)() =
105 (const char* (*)())PR_FindFunctionSymbol(sATKLib
, "atk_get_version");
107 const char* version
= atkGetVersion();
109 char* endPtr
= nullptr;
110 atkMajorVersion
= strtol(version
, &endPtr
, 10);
111 if (atkMajorVersion
!= 0L) {
112 atkMinorVersion
= strtol(endPtr
+ 1, &endPtr
, 10);
113 if (atkMinorVersion
!= 0L) {
114 atkMicroVersion
= strtol(endPtr
+ 1, &endPtr
, 10);
120 // Initialize the MAI Utility class, it will overwrite gail_util.
121 g_type_class_unref(g_type_class_ref(mai_util_get_type()));
123 // Init atk-bridge now
124 PR_SetEnv("NO_AT_BRIDGE=0");
125 nsresult rv
= LoadGtkModule(sAtkBridge
);
126 if (NS_SUCCEEDED(rv
)) {
127 (*sAtkBridge
.init
)(nullptr, nullptr);
130 if (!sToplevel_event_hook_added
) {
131 sToplevel_event_hook_added
= true;
132 sToplevel_show_hook
= g_signal_add_emission_hook(
133 g_signal_lookup("show", GTK_TYPE_WINDOW
), 0, toplevel_event_watcher
,
134 reinterpret_cast<gpointer
>(nsIAccessibleEvent::EVENT_SHOW
), nullptr);
135 sToplevel_hide_hook
= g_signal_add_emission_hook(
136 g_signal_lookup("hide", GTK_TYPE_WINDOW
), 0, toplevel_event_watcher
,
137 reinterpret_cast<gpointer
>(nsIAccessibleEvent::EVENT_HIDE
), nullptr);
141 void a11y::PlatformShutdown() {
142 if (sToplevel_event_hook_added
) {
143 sToplevel_event_hook_added
= false;
144 g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW
),
145 sToplevel_show_hook
);
146 g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW
),
147 sToplevel_hide_hook
);
150 if (sAtkBridge
.lib
) {
151 // Do not shutdown/unload atk-bridge,
152 // an exit function registered will take care of it
153 // PR_UnloadLibrary(sAtkBridge.lib);
154 sAtkBridge
.lib
= nullptr;
155 sAtkBridge
.init
= nullptr;
158 // PR_UnloadLibrary(sATKLib);
159 // sATKLib = nullptr;
163 static const char sAccEnv
[] = "GNOME_ACCESSIBILITY";
164 #ifdef MOZ_ENABLE_DBUS
165 static DBusPendingCall
* sPendingCall
= nullptr;
168 void a11y::PreInit() {
169 #ifdef MOZ_ENABLE_DBUS
170 static bool sChecked
= FALSE
;
171 if (sChecked
) return;
175 // dbus is only checked if GNOME_ACCESSIBILITY is unset
176 // also make sure that a session bus address is available to prevent dbus from
177 // starting a new one. Dbus confuses the test harness when it creates a new
178 // process (see bug 693343)
179 if (PR_GetEnv(sAccEnv
) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS")) return;
181 DBusConnection
* bus
= dbus_bus_get(DBUS_BUS_SESSION
, nullptr);
184 dbus_connection_set_exit_on_disconnect(bus
, FALSE
);
186 static const char* iface
= "org.a11y.Status";
187 static const char* member
= "IsEnabled";
188 DBusMessage
* message
;
190 dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
191 "org.freedesktop.DBus.Properties", "Get");
192 if (!message
) goto dbus_done
;
194 dbus_message_append_args(message
, DBUS_TYPE_STRING
, &iface
, DBUS_TYPE_STRING
,
195 &member
, DBUS_TYPE_INVALID
);
196 dbus_connection_send_with_reply(bus
, message
, &sPendingCall
, 1000);
197 dbus_message_unref(message
);
200 dbus_connection_unref(bus
);
204 bool a11y::ShouldA11yBeEnabled() {
205 static bool sChecked
= false, sShouldEnable
= false;
206 if (sChecked
) return sShouldEnable
;
210 EPlatformDisabledState disabledState
= PlatformDisabledState();
211 if (disabledState
== ePlatformIsDisabled
) return sShouldEnable
= false;
213 // check if accessibility enabled/disabled by environment variable
214 const char* envValue
= PR_GetEnv(sAccEnv
);
215 if (envValue
) return sShouldEnable
= !!atoi(envValue
);
217 #ifdef MOZ_ENABLE_DBUS
219 bool dbusSuccess
= false;
220 DBusMessage
* reply
= nullptr;
221 if (!sPendingCall
) goto dbus_done
;
223 dbus_pending_call_block(sPendingCall
);
224 reply
= dbus_pending_call_steal_reply(sPendingCall
);
225 dbus_pending_call_unref(sPendingCall
);
226 sPendingCall
= nullptr;
228 dbus_message_get_type(reply
) != DBUS_MESSAGE_TYPE_METHOD_RETURN
||
229 strcmp(dbus_message_get_signature(reply
), DBUS_TYPE_VARIANT_AS_STRING
)) {
233 DBusMessageIter iter
, iter_variant
, iter_struct
;
235 dbus_message_iter_init(reply
, &iter
);
236 dbus_message_iter_recurse(&iter
, &iter_variant
);
237 switch (dbus_message_iter_get_arg_type(&iter_variant
)) {
238 case DBUS_TYPE_STRUCT
:
239 // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
240 dbus_message_iter_recurse(&iter_variant
, &iter_struct
);
241 if (dbus_message_iter_get_arg_type(&iter_struct
) == DBUS_TYPE_BOOLEAN
) {
242 dbus_message_iter_get_basic(&iter_struct
, &dResult
);
243 sShouldEnable
= dResult
;
248 case DBUS_TYPE_BOOLEAN
:
249 dbus_message_iter_get_basic(&iter_variant
, &dResult
);
250 sShouldEnable
= dResult
;
258 if (reply
) dbus_message_unref(reply
);
260 if (dbusSuccess
) return sShouldEnable
;
264 #define GSETINGS_A11Y_INTERFACE "org.gnome.desktop.interface"
265 #define GSETINGS_A11Y_KEY "toolkit-accessibility"
266 nsCOMPtr
<nsIGSettingsService
> gsettings
=
267 do_GetService(NS_GSETTINGSSERVICE_CONTRACTID
);
268 nsCOMPtr
<nsIGSettingsCollection
> a11y_settings
;
271 gsettings
->GetCollectionForSchema(nsLiteralCString(GSETINGS_A11Y_INTERFACE
),
272 getter_AddRefs(a11y_settings
));
274 a11y_settings
->GetBoolean(nsLiteralCString(GSETINGS_A11Y_KEY
),
279 return sShouldEnable
;