Platform.cpp (8637B)
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/. */ 6 7 #include "Platform.h" 8 9 #include "mozilla/ClearOnShutdown.h" 10 #include "mozilla/GRefPtr.h" 11 #include "mozilla/GUniquePtr.h" 12 #include "mozilla/widget/GSettings.h" 13 #include "nsIAccessibleEvent.h" 14 #include "nsMai.h" 15 #include "nsServiceManagerUtils.h" 16 #include "nsWindow.h" 17 #include "prenv.h" 18 #include "prlink.h" 19 20 #ifdef MOZ_ENABLE_DBUS 21 # include "mozilla/widget/AsyncDBus.h" 22 #endif 23 #include <gtk/gtk.h> 24 25 using namespace mozilla; 26 using namespace mozilla::a11y; 27 28 int atkMajorVersion = 1, atkMinorVersion = 12, atkMicroVersion = 0; 29 30 GType (*gAtkTableCellGetTypeFunc)(); 31 32 extern "C" { 33 typedef GType (*AtkGetTypeType)(void); 34 typedef void (*AtkBridgeAdaptorInit)(int*, char**[]); 35 } 36 37 static PRLibrary* sATKLib = nullptr; 38 static const char sATKLibName[] = "libatk-1.0.so.0"; 39 static const char sATKHyperlinkImplGetTypeSymbol[] = 40 "atk_hyperlink_impl_get_type"; 41 42 gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*, 43 gpointer); 44 static bool sToplevel_event_hook_added = false; 45 static gulong sToplevel_show_hook = 0; 46 static gulong sToplevel_hide_hook = 0; 47 48 GType g_atk_hyperlink_impl_type = G_TYPE_INVALID; 49 50 struct AtkBridgeModule { 51 const char* libName; 52 PRLibrary* lib; 53 const char* initName; 54 AtkBridgeAdaptorInit init; 55 }; 56 57 static AtkBridgeModule sAtkBridge = {"libatk-bridge-2.0.so.0", nullptr, 58 "atk_bridge_adaptor_init", nullptr}; 59 60 static nsresult LoadGtkModule(AtkBridgeModule& aModule) { 61 NS_ENSURE_ARG(aModule.libName); 62 63 if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) { 64 return NS_ERROR_FAILURE; 65 } 66 67 // we have loaded the library, try to get the function ptrs 68 if (!(aModule.init = (AtkBridgeAdaptorInit)PR_FindFunctionSymbol( 69 aModule.lib, aModule.initName))) { 70 // fail, :( 71 PR_UnloadLibrary(aModule.lib); 72 aModule.lib = nullptr; 73 return NS_ERROR_FAILURE; 74 } 75 return NS_OK; 76 } 77 78 void a11y::PlatformInit() { 79 if (!ShouldA11yBeEnabled()) return; 80 81 sATKLib = PR_LoadLibrary(sATKLibName); 82 if (!sATKLib) return; 83 84 AtkGetTypeType pfn_atk_hyperlink_impl_get_type = 85 (AtkGetTypeType)PR_FindFunctionSymbol(sATKLib, 86 sATKHyperlinkImplGetTypeSymbol); 87 if (pfn_atk_hyperlink_impl_get_type) { 88 g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type(); 89 } 90 91 gAtkTableCellGetTypeFunc = 92 (GType(*)())PR_FindFunctionSymbol(sATKLib, "atk_table_cell_get_type"); 93 94 const char* (*atkGetVersion)() = 95 (const char* (*)())PR_FindFunctionSymbol(sATKLib, "atk_get_version"); 96 if (atkGetVersion) { 97 const char* version = atkGetVersion(); 98 if (version) { 99 char* endPtr = nullptr; 100 atkMajorVersion = strtol(version, &endPtr, 10); 101 if (atkMajorVersion != 0L) { 102 atkMinorVersion = strtol(endPtr + 1, &endPtr, 10); 103 if (atkMinorVersion != 0L) { 104 atkMicroVersion = strtol(endPtr + 1, &endPtr, 10); 105 } 106 } 107 } 108 } 109 110 // Initialize the MAI Utility class, it will overwrite gail_util. 111 g_type_class_unref(g_type_class_ref(mai_util_get_type())); 112 113 // Init atk-bridge now 114 PR_SetEnv("NO_AT_BRIDGE=0"); 115 nsresult rv = LoadGtkModule(sAtkBridge); 116 if (NS_SUCCEEDED(rv)) { 117 (*sAtkBridge.init)(nullptr, nullptr); 118 } 119 120 if (!sToplevel_event_hook_added) { 121 sToplevel_event_hook_added = true; 122 sToplevel_show_hook = g_signal_add_emission_hook( 123 g_signal_lookup("show", GTK_TYPE_WINDOW), 0, toplevel_event_watcher, 124 reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW), nullptr); 125 sToplevel_hide_hook = g_signal_add_emission_hook( 126 g_signal_lookup("hide", GTK_TYPE_WINDOW), 0, toplevel_event_watcher, 127 reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE), nullptr); 128 } 129 } 130 131 void a11y::PlatformShutdown() { 132 if (sToplevel_event_hook_added) { 133 sToplevel_event_hook_added = false; 134 g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW), 135 sToplevel_show_hook); 136 g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW), 137 sToplevel_hide_hook); 138 } 139 140 if (sAtkBridge.lib) { 141 // Do not shutdown/unload atk-bridge, 142 // an exit function registered will take care of it 143 // PR_UnloadLibrary(sAtkBridge.lib); 144 sAtkBridge.lib = nullptr; 145 sAtkBridge.init = nullptr; 146 } 147 // if (sATKLib) { 148 // PR_UnloadLibrary(sATKLib); 149 // sATKLib = nullptr; 150 // } 151 } 152 153 static const char sAccEnv[] = "GNOME_ACCESSIBILITY"; 154 #ifdef MOZ_ENABLE_DBUS 155 StaticRefPtr<GDBusProxy> sA11yBusProxy; 156 StaticRefPtr<GCancellable> sCancellable; 157 #endif 158 159 static void StartAccessibility() { 160 if (nsWindow* window = nsWindow::GetFocusedWindow()) { 161 window->DispatchActivateEventAccessible(); 162 } 163 } 164 165 static void A11yBusProxyPropertyChanged(GDBusProxy* aProxy, 166 GVariant* aChangedProperties, 167 char** aInvalidatedProperties, 168 gpointer aUserData) { 169 gboolean isEnabled; 170 g_variant_lookup(aChangedProperties, "IsEnabled", "b", &isEnabled); 171 if (isEnabled) { 172 StartAccessibility(); 173 } 174 } 175 176 // Called from `nsWindow::Create()` before the window is shown. 177 void a11y::PreInit() { 178 #ifdef MOZ_ENABLE_DBUS 179 static bool sInited = false; 180 if (sInited) { 181 return; 182 } 183 sInited = true; 184 sCancellable = dont_AddRef(g_cancellable_new()); 185 186 widget::CreateDBusProxyForBus(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, 187 /* aInterfaceInfo = */ nullptr, "org.a11y.Bus", 188 "/org/a11y/bus", "org.a11y.Status", 189 sCancellable) 190 ->Then( 191 GetCurrentSerialEventTarget(), __func__, 192 193 [](RefPtr<GDBusProxy>&& aProxy) { 194 sA11yBusProxy = std::move(aProxy); 195 sCancellable = nullptr; 196 g_signal_connect(sA11yBusProxy, "g-properties-changed", 197 G_CALLBACK(A11yBusProxyPropertyChanged), nullptr); 198 RefPtr<GVariant> isEnabled = dont_AddRef( 199 g_dbus_proxy_get_cached_property(sA11yBusProxy, "IsEnabled")); 200 if (isEnabled && g_variant_get_boolean(isEnabled)) { 201 // If a window is already focused, `StartAccessibility()` will 202 // initialize a11y by sending an activate event. If the window has 203 // not yet been shown/focused, nothing will happen. We now have 204 // the `IsEnabled` property cached so when `ShouldA11yBeEnabled()` 205 // is called from `nsWindow::Show()`, a root accessible will be 206 // created and events will be dispatched henceforth. 207 StartAccessibility(); 208 } 209 }, 210 [](GUniquePtr<GError>&& aError) { 211 sCancellable = nullptr; 212 if (!g_error_matches(aError.get(), G_IO_ERROR, 213 G_IO_ERROR_CANCELLED)) { 214 g_warning( 215 "Failed to create DBus proxy for org.a11y.Bus: " 216 "%s\n", 217 aError->message); 218 } 219 }); 220 221 RunOnShutdown([] { 222 if (sCancellable) { 223 g_cancellable_cancel(sCancellable); 224 sCancellable = nullptr; 225 } 226 227 if (sA11yBusProxy) { 228 sA11yBusProxy = nullptr; 229 } 230 }); 231 #endif 232 } 233 234 bool a11y::ShouldA11yBeEnabled() { 235 EPlatformDisabledState disabledState = PlatformDisabledState(); 236 if (disabledState == ePlatformIsDisabled) { 237 return false; 238 } 239 if (disabledState == ePlatformIsForceEnabled) { 240 return true; 241 } 242 243 // check if accessibility enabled/disabled by environment variable 244 if (const char* envValue = PR_GetEnv(sAccEnv)) { 245 return !!atoi(envValue); 246 } 247 248 #ifdef MOZ_ENABLE_DBUS 249 if (sA11yBusProxy) { 250 RefPtr<GVariant> isEnabled = dont_AddRef( 251 g_dbus_proxy_get_cached_property(sA11yBusProxy, "IsEnabled")); 252 // result can be null if proxy isn't actually working. 253 if (isEnabled) { 254 return g_variant_get_boolean(isEnabled); 255 } 256 } 257 #endif 258 259 // check GSettings 260 return widget::GSettings::GetBoolean("org.gnome.desktop.interface"_ns, 261 "toolkit-accessibility"_ns) 262 .valueOr(false); 263 } 264 265 uint64_t a11y::GetCacheDomainsForKnownClients(uint64_t aCacheDomains) { 266 return aCacheDomains; 267 }