accessible/src/atk/UtilInterface.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "ApplicationAccessibleWrap.h"
michael@0 8 #include "mozilla/Likely.h"
michael@0 9 #include "nsAccessibilityService.h"
michael@0 10 #include "nsMai.h"
michael@0 11
michael@0 12 #include <atk/atk.h>
michael@0 13 #include <gtk/gtk.h>
michael@0 14 #include <string.h>
michael@0 15
michael@0 16 using namespace mozilla;
michael@0 17 using namespace mozilla::a11y;
michael@0 18
michael@0 19 typedef AtkUtil MaiUtil;
michael@0 20 typedef AtkUtilClass MaiUtilClass;
michael@0 21
michael@0 22 #define MAI_VERSION MOZILLA_VERSION
michael@0 23 #define MAI_NAME "Gecko"
michael@0 24
michael@0 25 extern "C" {
michael@0 26 static guint (*gail_add_global_event_listener)(GSignalEmissionHook listener,
michael@0 27 const gchar* event_type);
michael@0 28 static void (*gail_remove_global_event_listener) (guint remove_listener);
michael@0 29 static void (*gail_remove_key_event_listener) (guint remove_listener);
michael@0 30 static AtkObject* (*gail_get_root)();
michael@0 31 }
michael@0 32
michael@0 33 struct MaiUtilListenerInfo
michael@0 34 {
michael@0 35 gint key;
michael@0 36 guint signal_id;
michael@0 37 gulong hook_id;
michael@0 38 // For window create/destory/minimize/maximize/restore/activate/deactivate
michael@0 39 // events, we'll chain gail_util's add/remove_global_event_listener.
michael@0 40 // So we store the listenerid returned by gail's add_global_event_listener
michael@0 41 // in this structure to call gail's remove_global_event_listener later.
michael@0 42 guint gail_listenerid;
michael@0 43 };
michael@0 44
michael@0 45 static GHashTable* sListener_list = nullptr;
michael@0 46 static gint sListener_idx = 1;
michael@0 47
michael@0 48 extern "C" {
michael@0 49 static guint
michael@0 50 add_listener (GSignalEmissionHook listener,
michael@0 51 const gchar *object_type,
michael@0 52 const gchar *signal,
michael@0 53 const gchar *hook_data,
michael@0 54 guint gail_listenerid = 0)
michael@0 55 {
michael@0 56 GType type;
michael@0 57 guint signal_id;
michael@0 58 gint rc = 0;
michael@0 59
michael@0 60 type = g_type_from_name(object_type);
michael@0 61 if (type) {
michael@0 62 signal_id = g_signal_lookup(signal, type);
michael@0 63 if (signal_id > 0) {
michael@0 64 MaiUtilListenerInfo *listener_info;
michael@0 65
michael@0 66 rc = sListener_idx;
michael@0 67
michael@0 68 listener_info = (MaiUtilListenerInfo *)
michael@0 69 g_malloc(sizeof(MaiUtilListenerInfo));
michael@0 70 listener_info->key = sListener_idx;
michael@0 71 listener_info->hook_id =
michael@0 72 g_signal_add_emission_hook(signal_id, 0, listener,
michael@0 73 g_strdup(hook_data),
michael@0 74 (GDestroyNotify)g_free);
michael@0 75 listener_info->signal_id = signal_id;
michael@0 76 listener_info->gail_listenerid = gail_listenerid;
michael@0 77
michael@0 78 g_hash_table_insert(sListener_list, &(listener_info->key),
michael@0 79 listener_info);
michael@0 80 sListener_idx++;
michael@0 81 }
michael@0 82 else {
michael@0 83 g_warning("Invalid signal type %s\n", signal);
michael@0 84 }
michael@0 85 }
michael@0 86 else {
michael@0 87 g_warning("Invalid object type %s\n", object_type);
michael@0 88 }
michael@0 89 return rc;
michael@0 90 }
michael@0 91
michael@0 92 static guint
michael@0 93 mai_util_add_global_event_listener(GSignalEmissionHook listener,
michael@0 94 const gchar *event_type)
michael@0 95 {
michael@0 96 guint rc = 0;
michael@0 97 gchar **split_string;
michael@0 98
michael@0 99 split_string = g_strsplit (event_type, ":", 3);
michael@0 100
michael@0 101 if (split_string) {
michael@0 102 if (!strcmp ("window", split_string[0])) {
michael@0 103 guint gail_listenerid = 0;
michael@0 104 if (gail_add_global_event_listener) {
michael@0 105 // call gail's function to track gtk native window events
michael@0 106 gail_listenerid =
michael@0 107 gail_add_global_event_listener(listener, event_type);
michael@0 108 }
michael@0 109
michael@0 110 rc = add_listener (listener, "MaiAtkObject", split_string[1],
michael@0 111 event_type, gail_listenerid);
michael@0 112 }
michael@0 113 else {
michael@0 114 rc = add_listener (listener, split_string[1], split_string[2],
michael@0 115 event_type);
michael@0 116 }
michael@0 117 g_strfreev(split_string);
michael@0 118 }
michael@0 119 return rc;
michael@0 120 }
michael@0 121
michael@0 122 static void
michael@0 123 mai_util_remove_global_event_listener(guint remove_listener)
michael@0 124 {
michael@0 125 if (remove_listener > 0) {
michael@0 126 MaiUtilListenerInfo *listener_info;
michael@0 127 gint tmp_idx = remove_listener;
michael@0 128
michael@0 129 listener_info = (MaiUtilListenerInfo *)
michael@0 130 g_hash_table_lookup(sListener_list, &tmp_idx);
michael@0 131
michael@0 132 if (listener_info != nullptr) {
michael@0 133 if (gail_remove_global_event_listener &&
michael@0 134 listener_info->gail_listenerid) {
michael@0 135 gail_remove_global_event_listener(listener_info->gail_listenerid);
michael@0 136 }
michael@0 137
michael@0 138 /* Hook id of 0 and signal id of 0 are invalid */
michael@0 139 if (listener_info->hook_id != 0 && listener_info->signal_id != 0) {
michael@0 140 /* Remove the emission hook */
michael@0 141 g_signal_remove_emission_hook(listener_info->signal_id,
michael@0 142 listener_info->hook_id);
michael@0 143
michael@0 144 /* Remove the element from the hash */
michael@0 145 g_hash_table_remove(sListener_list, &tmp_idx);
michael@0 146 }
michael@0 147 else {
michael@0 148 g_warning("Invalid listener hook_id %ld or signal_id %d\n",
michael@0 149 listener_info->hook_id, listener_info->signal_id);
michael@0 150 }
michael@0 151 }
michael@0 152 else {
michael@0 153 // atk-bridge is initialized with gail (e.g. yelp)
michael@0 154 // try gail_remove_global_event_listener
michael@0 155 if (gail_remove_global_event_listener) {
michael@0 156 return gail_remove_global_event_listener(remove_listener);
michael@0 157 }
michael@0 158
michael@0 159 g_warning("No listener with the specified listener id %d",
michael@0 160 remove_listener);
michael@0 161 }
michael@0 162 }
michael@0 163 else {
michael@0 164 g_warning("Invalid listener_id %d", remove_listener);
michael@0 165 }
michael@0 166 }
michael@0 167
michael@0 168 static AtkKeyEventStruct *
michael@0 169 atk_key_event_from_gdk_event_key (GdkEventKey *key)
michael@0 170 {
michael@0 171 AtkKeyEventStruct *event = g_new0(AtkKeyEventStruct, 1);
michael@0 172 switch (key->type) {
michael@0 173 case GDK_KEY_PRESS:
michael@0 174 event->type = ATK_KEY_EVENT_PRESS;
michael@0 175 break;
michael@0 176 case GDK_KEY_RELEASE:
michael@0 177 event->type = ATK_KEY_EVENT_RELEASE;
michael@0 178 break;
michael@0 179 default:
michael@0 180 g_assert_not_reached ();
michael@0 181 return nullptr;
michael@0 182 }
michael@0 183 event->state = key->state;
michael@0 184 event->keyval = key->keyval;
michael@0 185 event->length = key->length;
michael@0 186 if (key->string && key->string [0] &&
michael@0 187 (key->state & GDK_CONTROL_MASK ||
michael@0 188 g_unichar_isgraph (g_utf8_get_char (key->string)))) {
michael@0 189 event->string = key->string;
michael@0 190 }
michael@0 191 else if (key->type == GDK_KEY_PRESS ||
michael@0 192 key->type == GDK_KEY_RELEASE) {
michael@0 193 event->string = gdk_keyval_name (key->keyval);
michael@0 194 }
michael@0 195 event->keycode = key->hardware_keycode;
michael@0 196 event->timestamp = key->time;
michael@0 197
michael@0 198 return event;
michael@0 199 }
michael@0 200
michael@0 201 struct MaiKeyEventInfo
michael@0 202 {
michael@0 203 AtkKeyEventStruct *key_event;
michael@0 204 gpointer func_data;
michael@0 205 };
michael@0 206
michael@0 207 union AtkKeySnoopFuncPointer
michael@0 208 {
michael@0 209 AtkKeySnoopFunc func_ptr;
michael@0 210 gpointer data;
michael@0 211 };
michael@0 212
michael@0 213 static gboolean
michael@0 214 notify_hf(gpointer key, gpointer value, gpointer data)
michael@0 215 {
michael@0 216 MaiKeyEventInfo *info = (MaiKeyEventInfo *)data;
michael@0 217 AtkKeySnoopFuncPointer atkKeySnoop;
michael@0 218 atkKeySnoop.data = value;
michael@0 219 return (atkKeySnoop.func_ptr)(info->key_event, info->func_data) ? TRUE : FALSE;
michael@0 220 }
michael@0 221
michael@0 222 static void
michael@0 223 insert_hf(gpointer key, gpointer value, gpointer data)
michael@0 224 {
michael@0 225 GHashTable *new_table = (GHashTable *) data;
michael@0 226 g_hash_table_insert (new_table, key, value);
michael@0 227 }
michael@0 228
michael@0 229 static GHashTable* sKey_listener_list = nullptr;
michael@0 230
michael@0 231 static gint
michael@0 232 mai_key_snooper(GtkWidget *the_widget, GdkEventKey *event, gpointer func_data)
michael@0 233 {
michael@0 234 /* notify each AtkKeySnoopFunc in turn... */
michael@0 235
michael@0 236 MaiKeyEventInfo *info = g_new0(MaiKeyEventInfo, 1);
michael@0 237 gint consumed = 0;
michael@0 238 if (sKey_listener_list) {
michael@0 239 GHashTable *new_hash = g_hash_table_new(nullptr, nullptr);
michael@0 240 g_hash_table_foreach (sKey_listener_list, insert_hf, new_hash);
michael@0 241 info->key_event = atk_key_event_from_gdk_event_key (event);
michael@0 242 info->func_data = func_data;
michael@0 243 consumed = g_hash_table_foreach_steal (new_hash, notify_hf, info);
michael@0 244 g_hash_table_destroy (new_hash);
michael@0 245 g_free(info->key_event);
michael@0 246 }
michael@0 247 g_free(info);
michael@0 248 return (consumed ? 1 : 0);
michael@0 249 }
michael@0 250
michael@0 251 static guint sKey_snooper_id = 0;
michael@0 252
michael@0 253 static guint
michael@0 254 mai_util_add_key_event_listener (AtkKeySnoopFunc listener,
michael@0 255 gpointer data)
michael@0 256 {
michael@0 257 if (MOZ_UNLIKELY(!listener))
michael@0 258 return 0;
michael@0 259
michael@0 260 static guint key=0;
michael@0 261
michael@0 262 if (!sKey_listener_list) {
michael@0 263 sKey_listener_list = g_hash_table_new(nullptr, nullptr);
michael@0 264 sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data);
michael@0 265 }
michael@0 266 AtkKeySnoopFuncPointer atkKeySnoop;
michael@0 267 atkKeySnoop.func_ptr = listener;
michael@0 268 g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER (key++),
michael@0 269 atkKeySnoop.data);
michael@0 270 return key;
michael@0 271 }
michael@0 272
michael@0 273 static void
michael@0 274 mai_util_remove_key_event_listener (guint remove_listener)
michael@0 275 {
michael@0 276 if (!sKey_listener_list) {
michael@0 277 // atk-bridge is initialized with gail (e.g. yelp)
michael@0 278 // try gail_remove_key_event_listener
michael@0 279 return gail_remove_key_event_listener(remove_listener);
michael@0 280 }
michael@0 281
michael@0 282 g_hash_table_remove(sKey_listener_list, GUINT_TO_POINTER (remove_listener));
michael@0 283 if (g_hash_table_size(sKey_listener_list) == 0) {
michael@0 284 gtk_key_snooper_remove(sKey_snooper_id);
michael@0 285 }
michael@0 286 }
michael@0 287
michael@0 288 static AtkObject*
michael@0 289 mai_util_get_root()
michael@0 290 {
michael@0 291 ApplicationAccessible* app = ApplicationAcc();
michael@0 292 if (app)
michael@0 293 return app->GetAtkObject();
michael@0 294
michael@0 295 // We've shutdown, try to use gail instead
michael@0 296 // (to avoid assert in spi_atk_tidy_windows())
michael@0 297 // XXX tbsaunde then why didn't we replace the gail atk_util impl?
michael@0 298 if (gail_get_root)
michael@0 299 return gail_get_root();
michael@0 300
michael@0 301 return nullptr;
michael@0 302 }
michael@0 303
michael@0 304 static const gchar*
michael@0 305 mai_util_get_toolkit_name()
michael@0 306 {
michael@0 307 return MAI_NAME;
michael@0 308 }
michael@0 309
michael@0 310 static const gchar*
michael@0 311 mai_util_get_toolkit_version()
michael@0 312 {
michael@0 313 return MAI_VERSION;
michael@0 314 }
michael@0 315
michael@0 316 static void
michael@0 317 _listener_info_destroy(gpointer data)
michael@0 318 {
michael@0 319 g_free(data);
michael@0 320 }
michael@0 321
michael@0 322 static void
michael@0 323 window_added (AtkObject *atk_obj,
michael@0 324 guint index,
michael@0 325 AtkObject *child)
michael@0 326 {
michael@0 327 if (!IS_MAI_OBJECT(child))
michael@0 328 return;
michael@0 329
michael@0 330 static guint id = g_signal_lookup ("create", MAI_TYPE_ATK_OBJECT);
michael@0 331 g_signal_emit (child, id, 0);
michael@0 332 }
michael@0 333
michael@0 334 static void
michael@0 335 window_removed (AtkObject *atk_obj,
michael@0 336 guint index,
michael@0 337 AtkObject *child)
michael@0 338 {
michael@0 339 if (!IS_MAI_OBJECT(child))
michael@0 340 return;
michael@0 341
michael@0 342 static guint id = g_signal_lookup ("destroy", MAI_TYPE_ATK_OBJECT);
michael@0 343 g_signal_emit (child, id, 0);
michael@0 344 }
michael@0 345
michael@0 346 static void
michael@0 347 UtilInterfaceInit(MaiUtilClass* klass)
michael@0 348 {
michael@0 349 AtkUtilClass *atk_class;
michael@0 350 gpointer data;
michael@0 351
michael@0 352 data = g_type_class_peek(ATK_TYPE_UTIL);
michael@0 353 atk_class = ATK_UTIL_CLASS(data);
michael@0 354
michael@0 355 // save gail function pointer
michael@0 356 gail_add_global_event_listener = atk_class->add_global_event_listener;
michael@0 357 gail_remove_global_event_listener = atk_class->remove_global_event_listener;
michael@0 358 gail_remove_key_event_listener = atk_class->remove_key_event_listener;
michael@0 359 gail_get_root = atk_class->get_root;
michael@0 360
michael@0 361 atk_class->add_global_event_listener =
michael@0 362 mai_util_add_global_event_listener;
michael@0 363 atk_class->remove_global_event_listener =
michael@0 364 mai_util_remove_global_event_listener;
michael@0 365 atk_class->add_key_event_listener = mai_util_add_key_event_listener;
michael@0 366 atk_class->remove_key_event_listener = mai_util_remove_key_event_listener;
michael@0 367 atk_class->get_root = mai_util_get_root;
michael@0 368 atk_class->get_toolkit_name = mai_util_get_toolkit_name;
michael@0 369 atk_class->get_toolkit_version = mai_util_get_toolkit_version;
michael@0 370
michael@0 371 sListener_list = g_hash_table_new_full(g_int_hash, g_int_equal, nullptr,
michael@0 372 _listener_info_destroy);
michael@0 373 // Keep track of added/removed windows.
michael@0 374 AtkObject *root = atk_get_root ();
michael@0 375 g_signal_connect (root, "children-changed::add", (GCallback) window_added, nullptr);
michael@0 376 g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, nullptr);
michael@0 377 }
michael@0 378 }
michael@0 379
michael@0 380 GType
michael@0 381 mai_util_get_type()
michael@0 382 {
michael@0 383 static GType type = 0;
michael@0 384
michael@0 385 if (!type) {
michael@0 386 static const GTypeInfo tinfo = {
michael@0 387 sizeof(MaiUtilClass),
michael@0 388 (GBaseInitFunc) nullptr, /* base init */
michael@0 389 (GBaseFinalizeFunc) nullptr, /* base finalize */
michael@0 390 (GClassInitFunc) UtilInterfaceInit, /* class init */
michael@0 391 (GClassFinalizeFunc) nullptr, /* class finalize */
michael@0 392 nullptr, /* class data */
michael@0 393 sizeof(MaiUtil), /* instance size */
michael@0 394 0, /* nb preallocs */
michael@0 395 (GInstanceInitFunc) nullptr, /* instance init */
michael@0 396 nullptr /* value table */
michael@0 397 };
michael@0 398
michael@0 399 type = g_type_register_static(ATK_TYPE_UTIL,
michael@0 400 "MaiUtil", &tinfo, GTypeFlags(0));
michael@0 401 }
michael@0 402 return type;
michael@0 403 }
michael@0 404

mercurial