accessible/src/atk/UtilInterface.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial