LCOV - code coverage report
Current view: top level - accessible/atk - UtilInterface.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 159 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 17 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13