Coverage Report

Created: 2018-09-25 14:53

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