Bug 1647875 [wpt PR 24320] - [AspectRatio] Add an in-flow test for computing a block...
[gecko.git] / accessible / atk / Platform.cpp
blob64c8ae844068fe4583c32daf9b1bd3a478058c60
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 "nsIGSettingsService.h"
11 #include "nsMai.h"
12 #include "AtkSocketAccessible.h"
13 #include "prenv.h"
14 #include "prlink.h"
16 #ifdef MOZ_ENABLE_DBUS
17 # include <dbus/dbus.h>
18 #endif
19 #include <gtk/gtk.h>
21 #ifdef MOZ_WIDGET_GTK
22 extern "C" __attribute__((weak, visibility("default"))) int
23 atk_bridge_adaptor_init(int*, char**[]);
24 #endif
26 using namespace mozilla;
27 using namespace mozilla::a11y;
29 int atkMajorVersion = 1, atkMinorVersion = 12, atkMicroVersion = 0;
31 GType (*gAtkTableCellGetTypeFunc)();
33 extern "C" {
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*,
45 gpointer);
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 {
53 const char* libName;
54 PRLibrary* lib;
55 const char* initName;
56 GnomeAccessibilityInit init;
57 const char* shutdownName;
58 GnomeAccessibilityShutdown shutdown;
61 static GnomeAccessibilityModule sAtkBridge = {
62 #ifdef AIX
63 "libatk-bridge.a(libatk-bridge.so.0)", nullptr,
64 #else
65 "libatk-bridge.so", nullptr,
66 #endif
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");
79 #else
80 libPath.AppendLiteral(":/usr/lib");
81 #endif
82 PR_FreeLibraryName(curLibPath);
84 int16_t loc1 = 0, loc2 = 0;
85 int16_t subLen = 0;
86 while (loc2 >= 0) {
87 loc2 = libPath.FindChar(':', loc1);
88 if (loc2 < 0)
89 subLen = libPath.Length() - loc1;
90 else
91 subLen = loc2 - 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;
98 loc1 = loc2 + 1;
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)) ||
105 !(aModule.shutdown =
106 PR_FindFunctionSymbol(aModule.lib, aModule.shutdownName))) {
107 // fail, :(
108 PR_UnloadLibrary(aModule.lib);
109 aModule.lib = nullptr;
110 return NS_ERROR_FAILURE;
112 return NS_OK;
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");
145 if (atkGetVersion) {
146 const char* version = atkGetVersion();
147 if (version) {
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);
166 } else
167 #endif
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;
205 // if (sATKLib) {
206 // PR_UnloadLibrary(sATKLib);
207 // sATKLib = nullptr;
208 // }
211 static const char sAccEnv[] = "GNOME_ACCESSIBILITY";
212 #ifdef MOZ_ENABLE_DBUS
213 static DBusPendingCall* sPendingCall = nullptr;
214 #endif
216 void a11y::PreInit() {
217 #ifdef MOZ_ENABLE_DBUS
218 static bool sChecked = FALSE;
219 if (sChecked) return;
221 sChecked = TRUE;
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);
230 if (!bus) return;
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;
237 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);
247 dbus_done:
248 dbus_connection_unref(bus);
249 #endif
252 bool a11y::ShouldA11yBeEnabled() {
253 static bool sChecked = false, sShouldEnable = false;
254 if (sChecked) return sShouldEnable;
256 sChecked = true;
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
266 PreInit();
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;
275 if (!reply ||
276 dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
277 strcmp(dbus_message_get_signature(reply), DBUS_TYPE_VARIANT_AS_STRING))
278 goto dbus_done;
280 DBusMessageIter iter, iter_variant, iter_struct;
281 dbus_bool_t dResult;
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;
291 dbusSuccess = true;
294 break;
295 case DBUS_TYPE_BOOLEAN:
296 dbus_message_iter_get_basic(&iter_variant, &dResult);
297 sShouldEnable = dResult;
298 dbusSuccess = true;
299 break;
300 default:
301 break;
304 dbus_done:
305 if (reply) dbus_message_unref(reply);
307 if (dbusSuccess) return sShouldEnable;
308 #endif
310 // check GSettings
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;
317 if (gsettings) {
318 gsettings->GetCollectionForSchema(nsLiteralCString(GSETINGS_A11Y_INTERFACE),
319 getter_AddRefs(a11y_settings));
320 if (a11y_settings) {
321 a11y_settings->GetBoolean(nsLiteralCString(GSETINGS_A11Y_KEY),
322 &sShouldEnable);
326 return sShouldEnable;