Platform.cpp 11 KB

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