LCOV - code coverage report
Current view: directory - accessible/src/atk - nsApplicationAccessibleWrap.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 360 0 0.0 %
Date: 2012-06-02 Functions: 32 0 0.0 %

       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                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Sun Microsystems, Inc.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2002
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Bolian Yin (bolian.yin@sun.com)
      25                 :  *   Ginn Chen (ginn.chen@sun.com)
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "nsApplicationAccessibleWrap.h"
      42                 : 
      43                 : #include "nsCOMPtr.h"
      44                 : #include "nsMai.h"
      45                 : #include "prlink.h"
      46                 : #include "prenv.h"
      47                 : #include "nsIGConfService.h"
      48                 : #include "nsIServiceManager.h"
      49                 : #include "nsAutoPtr.h"
      50                 : #include "nsAccessibilityService.h"
      51                 : #include "AtkSocketAccessible.h"
      52                 : 
      53                 : #include <gtk/gtk.h>
      54                 : #include <atk/atk.h>
      55                 : #ifdef MOZ_ENABLE_DBUS
      56                 : #include <dbus/dbus.h>
      57                 : #endif
      58                 : 
      59                 : using namespace mozilla;
      60                 : using namespace mozilla::a11y;
      61                 : 
      62                 : typedef GType (* AtkGetTypeType) (void);
      63                 : GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
      64                 : static bool sATKChecked = false;
      65                 : static PRLibrary *sATKLib = nsnull;
      66                 : static const char sATKLibName[] = "libatk-1.0.so.0";
      67                 : static const char sATKHyperlinkImplGetTypeSymbol[] =
      68                 :   "atk_hyperlink_impl_get_type";
      69                 : 
      70                 : /* gail function pointer */
      71                 : static guint (* gail_add_global_event_listener) (GSignalEmissionHook listener,
      72                 :                                                  const gchar *event_type);
      73                 : static void (* gail_remove_global_event_listener) (guint remove_listener);
      74                 : static void (* gail_remove_key_event_listener) (guint remove_listener);
      75                 : static AtkObject * (*gail_get_root) (void);
      76                 : 
      77                 : /* maiutil */
      78                 : 
      79                 : static guint mai_util_add_global_event_listener(GSignalEmissionHook listener,
      80                 :                                                 const gchar *event_type);
      81                 : static void mai_util_remove_global_event_listener(guint remove_listener);
      82                 : static guint mai_util_add_key_event_listener(AtkKeySnoopFunc listener,
      83                 :                                              gpointer data);
      84                 : static void mai_util_remove_key_event_listener(guint remove_listener);
      85                 : static AtkObject *mai_util_get_root(void);
      86                 : static G_CONST_RETURN gchar *mai_util_get_toolkit_name(void);
      87                 : static G_CONST_RETURN gchar *mai_util_get_toolkit_version(void);
      88                 : 
      89                 : 
      90                 : /* Misc */
      91                 : 
      92                 : static void _listener_info_destroy(gpointer data);
      93                 : static guint add_listener (GSignalEmissionHook listener,
      94                 :                            const gchar *object_type,
      95                 :                            const gchar *signal,
      96                 :                            const gchar *hook_data,
      97                 :                            guint gail_listenerid = 0);
      98                 : static AtkKeyEventStruct *atk_key_event_from_gdk_event_key(GdkEventKey *key);
      99                 : static gboolean notify_hf(gpointer key, gpointer value, gpointer data);
     100                 : static void insert_hf(gpointer key, gpointer value, gpointer data);
     101                 : static gint mai_key_snooper(GtkWidget *the_widget, GdkEventKey *event,
     102                 :                             gpointer func_data);
     103                 : 
     104                 : static GHashTable* sListener_list = NULL;
     105                 : static gint sListener_idx = 1;
     106                 : 
     107                 : #define MAI_TYPE_UTIL              (mai_util_get_type ())
     108                 : #define MAI_UTIL(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
     109                 :                                     MAI_TYPE_UTIL, MaiUtil))
     110                 : #define MAI_UTIL_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), \
     111                 :                                     MAI_TYPE_UTIL, MaiUtilClass))
     112                 : #define MAI_IS_UTIL(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
     113                 :                                     MAI_TYPE_UTIL))
     114                 : #define MAI_IS_UTIL_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), \
     115                 :                                     MAI_TYPE_UTIL))
     116                 : #define MAI_UTIL_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), \
     117                 :                                     MAI_TYPE_UTIL, MaiUtilClass))
     118                 : 
     119                 : static GHashTable* sKey_listener_list = NULL;
     120                 : static guint sKey_snooper_id = 0;
     121                 : static bool sToplevel_event_hook_added = false;
     122                 : static gulong sToplevel_show_hook = 0;
     123                 : static gulong sToplevel_hide_hook = 0;
     124                 : 
     125                 : G_BEGIN_DECLS
     126                 : typedef void (*GnomeAccessibilityInit) (void);
     127                 : typedef void (*GnomeAccessibilityShutdown) (void);
     128                 : G_END_DECLS
     129                 : 
     130                 : struct MaiUtil
     131                 : {
     132                 :     AtkUtil parent;
     133                 :     GList *listener_list;
     134                 : };
     135                 : 
     136                 : struct MaiKeyEventInfo
     137                 : {
     138                 :     AtkKeyEventStruct *key_event;
     139                 :     gpointer func_data;
     140                 : };
     141                 : 
     142                 : union AtkKeySnoopFuncPointer
     143                 : {
     144                 :     AtkKeySnoopFunc func_ptr;
     145                 :     gpointer data;
     146                 : };
     147                 : 
     148                 : struct GnomeAccessibilityModule
     149                 : {
     150                 :     const char *libName;
     151                 :     PRLibrary *lib;
     152                 :     const char *initName;
     153                 :     GnomeAccessibilityInit init;
     154                 :     const char *shutdownName;
     155                 :     GnomeAccessibilityShutdown shutdown;
     156                 : };
     157                 : 
     158                 : struct MaiUtilClass
     159                 : {
     160                 :     AtkUtilClass parent_class;
     161                 : };
     162                 : 
     163                 : GType mai_util_get_type (void);
     164                 : static void mai_util_class_init(MaiUtilClass *klass);
     165                 : 
     166                 : /* supporting */
     167                 : PRLogModuleInfo *gMaiLog = NULL;
     168                 : 
     169                 : #define MAI_VERSION MOZILLA_VERSION
     170                 : #define MAI_NAME "Gecko"
     171                 : 
     172                 : struct MaiUtilListenerInfo
     173                 : {
     174                 :     gint key;
     175                 :     guint signal_id;
     176                 :     gulong hook_id;
     177                 :     // For window create/destory/minimize/maximize/restore/activate/deactivate
     178                 :     // events, we'll chain gail_util's add/remove_global_event_listener.
     179                 :     // So we store the listenerid returned by gail's add_global_event_listener
     180                 :     // in this structure to call gail's remove_global_event_listener later.
     181                 :     guint gail_listenerid;
     182                 : };
     183                 : 
     184                 : static GnomeAccessibilityModule sAtkBridge = {
     185                 : #ifdef AIX
     186                 :     "libatk-bridge.a(libatk-bridge.so.0)", NULL,
     187                 : #else
     188                 :     "libatk-bridge.so", NULL,
     189                 : #endif
     190                 :     "gnome_accessibility_module_init", NULL,
     191                 :     "gnome_accessibility_module_shutdown", NULL
     192                 : };
     193                 : 
     194                 : static GnomeAccessibilityModule sGail = {
     195                 :     "libgail.so", NULL,
     196                 :     "gnome_accessibility_module_init", NULL,
     197                 :     "gnome_accessibility_module_shutdown", NULL
     198                 : };
     199                 : 
     200                 : GType
     201               0 : mai_util_get_type(void)
     202                 : {
     203                 :     static GType type = 0;
     204                 : 
     205               0 :     if (!type) {
     206                 :         static const GTypeInfo tinfo = {
     207                 :             sizeof(MaiUtilClass),
     208                 :             (GBaseInitFunc) NULL, /* base init */
     209                 :             (GBaseFinalizeFunc) NULL, /* base finalize */
     210                 :             (GClassInitFunc) mai_util_class_init, /* class init */
     211                 :             (GClassFinalizeFunc) NULL, /* class finalize */
     212                 :             NULL, /* class data */
     213                 :             sizeof(MaiUtil), /* instance size */
     214                 :             0, /* nb preallocs */
     215                 :             (GInstanceInitFunc) NULL, /* instance init */
     216                 :             NULL /* value table */
     217                 :         };
     218                 : 
     219                 :         type = g_type_register_static(ATK_TYPE_UTIL,
     220               0 :                                       "MaiUtil", &tinfo, GTypeFlags(0));
     221                 :     }
     222               0 :     return type;
     223                 : }
     224                 : 
     225                 : static void
     226               0 : window_added (AtkObject *atk_obj,
     227                 :               guint     index,
     228                 :               AtkObject *child)
     229                 : {
     230               0 :   if (!IS_MAI_OBJECT(child))
     231               0 :       return;
     232                 : 
     233               0 :   static guint id =  g_signal_lookup ("create", MAI_TYPE_ATK_OBJECT);
     234               0 :   g_signal_emit (child, id, 0);
     235                 : }
     236                 : 
     237                 : static void
     238               0 : window_removed (AtkObject *atk_obj,
     239                 :                 guint     index,
     240                 :                 AtkObject *child)
     241                 : {
     242               0 :   if (!IS_MAI_OBJECT(child))
     243               0 :       return;
     244                 : 
     245               0 :   static guint id =  g_signal_lookup ("destroy", MAI_TYPE_ATK_OBJECT);
     246               0 :   g_signal_emit (child, id, 0);
     247                 : }
     248                 : 
     249                 : /* intialize the the atk interface (function pointers) with MAI implementation.
     250                 :  * When atk bridge get loaded, these interface can be used.
     251                 :  */
     252                 : static void
     253               0 : mai_util_class_init(MaiUtilClass *klass)
     254                 : {
     255                 :     AtkUtilClass *atk_class;
     256                 :     gpointer data;
     257                 : 
     258               0 :     data = g_type_class_peek(ATK_TYPE_UTIL);
     259               0 :     atk_class = ATK_UTIL_CLASS(data);
     260                 : 
     261                 :     // save gail function pointer
     262               0 :     gail_add_global_event_listener = atk_class->add_global_event_listener;
     263               0 :     gail_remove_global_event_listener = atk_class->remove_global_event_listener;
     264               0 :     gail_remove_key_event_listener = atk_class->remove_key_event_listener;
     265               0 :     gail_get_root = atk_class->get_root;
     266                 : 
     267                 :     atk_class->add_global_event_listener =
     268               0 :         mai_util_add_global_event_listener;
     269                 :     atk_class->remove_global_event_listener =
     270               0 :         mai_util_remove_global_event_listener;
     271               0 :     atk_class->add_key_event_listener = mai_util_add_key_event_listener;
     272               0 :     atk_class->remove_key_event_listener = mai_util_remove_key_event_listener;
     273               0 :     atk_class->get_root = mai_util_get_root;
     274               0 :     atk_class->get_toolkit_name = mai_util_get_toolkit_name;
     275               0 :     atk_class->get_toolkit_version = mai_util_get_toolkit_version;
     276                 : 
     277                 :     sListener_list = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
     278               0 :                                            _listener_info_destroy);
     279                 :     // Keep track of added/removed windows.
     280               0 :     AtkObject *root = atk_get_root ();
     281               0 :     g_signal_connect (root, "children-changed::add", (GCallback) window_added, NULL);
     282               0 :     g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, NULL);
     283               0 : }
     284                 : 
     285                 : static guint
     286               0 : mai_util_add_global_event_listener(GSignalEmissionHook listener,
     287                 :                                    const gchar *event_type)
     288                 : {
     289               0 :     guint rc = 0;
     290                 :     gchar **split_string;
     291                 : 
     292               0 :     split_string = g_strsplit (event_type, ":", 3);
     293                 : 
     294               0 :     if (split_string) {
     295               0 :         if (!strcmp ("window", split_string[0])) {
     296               0 :             guint gail_listenerid = 0;
     297               0 :             if (gail_add_global_event_listener) {
     298                 :                 // call gail's function to track gtk native window events
     299                 :                 gail_listenerid =
     300               0 :                     gail_add_global_event_listener(listener, event_type);
     301                 :             }
     302                 : 
     303               0 :             rc = add_listener (listener, "MaiAtkObject", split_string[1],
     304               0 :                                event_type, gail_listenerid);
     305                 :         }
     306                 :         else {
     307               0 :             rc = add_listener (listener, split_string[1], split_string[2],
     308               0 :                                event_type);
     309                 :         }
     310               0 :         g_strfreev(split_string);
     311                 :     }
     312               0 :     return rc;
     313                 : }
     314                 : 
     315                 : static void
     316               0 : mai_util_remove_global_event_listener(guint remove_listener)
     317                 : {
     318               0 :     if (remove_listener > 0) {
     319                 :         MaiUtilListenerInfo *listener_info;
     320               0 :         gint tmp_idx = remove_listener;
     321                 : 
     322                 :         listener_info = (MaiUtilListenerInfo *)
     323               0 :             g_hash_table_lookup(sListener_list, &tmp_idx);
     324                 : 
     325               0 :         if (listener_info != NULL) {
     326               0 :             if (gail_remove_global_event_listener &&
     327                 :                 listener_info->gail_listenerid) {
     328               0 :               gail_remove_global_event_listener(listener_info->gail_listenerid);
     329                 :             }
     330                 : 
     331                 :             /* Hook id of 0 and signal id of 0 are invalid */
     332               0 :             if (listener_info->hook_id != 0 && listener_info->signal_id != 0) {
     333                 :                 /* Remove the emission hook */
     334                 :                 g_signal_remove_emission_hook(listener_info->signal_id,
     335               0 :                                               listener_info->hook_id);
     336                 : 
     337                 :                 /* Remove the element from the hash */
     338               0 :                 g_hash_table_remove(sListener_list, &tmp_idx);
     339                 :             }
     340                 :             else {
     341               0 :                 g_warning("Invalid listener hook_id %ld or signal_id %d\n",
     342               0 :                           listener_info->hook_id, listener_info->signal_id);
     343                 :             }
     344                 :         }
     345                 :         else {
     346                 :             // atk-bridge is initialized with gail (e.g. yelp)
     347                 :             // try gail_remove_global_event_listener
     348               0 :             if (gail_remove_global_event_listener) {
     349               0 :                 return gail_remove_global_event_listener(remove_listener);
     350                 :             }
     351                 : 
     352                 :             g_warning("No listener with the specified listener id %d",
     353               0 :                       remove_listener);
     354                 :         }
     355                 :     }
     356                 :     else {
     357               0 :         g_warning("Invalid listener_id %d", remove_listener);
     358                 :     }
     359                 : }
     360                 : 
     361                 : static AtkKeyEventStruct *
     362               0 : atk_key_event_from_gdk_event_key (GdkEventKey *key)
     363                 : {
     364               0 :     AtkKeyEventStruct *event = g_new0(AtkKeyEventStruct, 1);
     365               0 :     switch (key->type) {
     366                 :     case GDK_KEY_PRESS:
     367               0 :         event->type = ATK_KEY_EVENT_PRESS;
     368               0 :         break;
     369                 :     case GDK_KEY_RELEASE:
     370               0 :         event->type = ATK_KEY_EVENT_RELEASE;
     371               0 :         break;
     372                 :     default:
     373               0 :         g_assert_not_reached ();
     374                 :         return NULL;
     375                 :     }
     376               0 :     event->state = key->state;
     377               0 :     event->keyval = key->keyval;
     378               0 :     event->length = key->length;
     379               0 :     if (key->string && key->string [0] &&
     380                 :         (key->state & GDK_CONTROL_MASK ||
     381               0 :          g_unichar_isgraph (g_utf8_get_char (key->string)))) {
     382               0 :         event->string = key->string;
     383                 :     }
     384               0 :     else if (key->type == GDK_KEY_PRESS ||
     385                 :              key->type == GDK_KEY_RELEASE) {
     386               0 :         event->string = gdk_keyval_name (key->keyval);
     387                 :     }
     388               0 :     event->keycode = key->hardware_keycode;
     389               0 :     event->timestamp = key->time;
     390                 : 
     391               0 :     MAI_LOG_DEBUG(("MaiKey:\tsym %u\n\tmods %x\n\tcode %u\n\ttime %lx\n",
     392                 :                    (unsigned int) event->keyval,
     393                 :                    (unsigned int) event->state,
     394                 :                    (unsigned int) event->keycode,
     395                 :                    (unsigned long int) event->timestamp));
     396               0 :     return event;
     397                 : }
     398                 : 
     399                 : static gboolean
     400               0 : notify_hf(gpointer key, gpointer value, gpointer data)
     401                 : {
     402               0 :     MaiKeyEventInfo *info = (MaiKeyEventInfo *)data;
     403                 :     AtkKeySnoopFuncPointer atkKeySnoop;
     404               0 :     atkKeySnoop.data = value;
     405               0 :     return (atkKeySnoop.func_ptr)(info->key_event, info->func_data) ? TRUE : FALSE;
     406                 : }
     407                 : 
     408                 : static void
     409               0 : insert_hf(gpointer key, gpointer value, gpointer data)
     410                 : {
     411               0 :     GHashTable *new_table = (GHashTable *) data;
     412               0 :     g_hash_table_insert (new_table, key, value);
     413               0 : }
     414                 : 
     415                 : static gint
     416               0 : mai_key_snooper(GtkWidget *the_widget, GdkEventKey *event, gpointer func_data)
     417                 : {
     418                 :     /* notify each AtkKeySnoopFunc in turn... */
     419                 : 
     420               0 :     MaiKeyEventInfo *info = g_new0(MaiKeyEventInfo, 1);
     421               0 :     gint consumed = 0;
     422               0 :     if (sKey_listener_list) {
     423               0 :         GHashTable *new_hash = g_hash_table_new(NULL, NULL);
     424               0 :         g_hash_table_foreach (sKey_listener_list, insert_hf, new_hash);
     425               0 :         info->key_event = atk_key_event_from_gdk_event_key (event);
     426               0 :         info->func_data = func_data;
     427               0 :         consumed = g_hash_table_foreach_steal (new_hash, notify_hf, info);
     428               0 :         g_hash_table_destroy (new_hash);
     429               0 :         g_free(info->key_event);
     430                 :     }
     431               0 :     g_free(info);
     432               0 :     return (consumed ? 1 : 0);
     433                 : }
     434                 : 
     435                 : static guint
     436               0 : mai_util_add_key_event_listener (AtkKeySnoopFunc listener,
     437                 :                                  gpointer data)
     438                 : {
     439               0 :     NS_ENSURE_TRUE(listener, 0);
     440                 : 
     441                 :     static guint key=0;
     442                 : 
     443               0 :     if (!sKey_listener_list) {
     444               0 :         sKey_listener_list = g_hash_table_new(NULL, NULL);
     445               0 :         sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data);
     446                 :     }
     447                 :     AtkKeySnoopFuncPointer atkKeySnoop;
     448               0 :     atkKeySnoop.func_ptr = listener;
     449                 :     g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER (key++),
     450               0 :                         atkKeySnoop.data);
     451               0 :     return key;
     452                 : }
     453                 : 
     454                 : static void
     455               0 : mai_util_remove_key_event_listener (guint remove_listener)
     456                 : {
     457               0 :     if (!sKey_listener_list) {
     458                 :         // atk-bridge is initialized with gail (e.g. yelp)
     459                 :         // try gail_remove_key_event_listener
     460               0 :         return gail_remove_key_event_listener(remove_listener);
     461                 :     }
     462                 : 
     463               0 :     g_hash_table_remove(sKey_listener_list, GUINT_TO_POINTER (remove_listener));
     464               0 :     if (g_hash_table_size(sKey_listener_list) == 0) {
     465               0 :         gtk_key_snooper_remove(sKey_snooper_id);
     466                 :     }
     467                 : }
     468                 : 
     469                 : AtkObject *
     470               0 : mai_util_get_root(void)
     471                 : {
     472               0 :     if (nsAccessibilityService::IsShutdown()) {
     473                 :         // We've shutdown, try to use gail instead
     474                 :         // (to avoid assert in spi_atk_tidy_windows())
     475               0 :         if (gail_get_root)
     476               0 :             return gail_get_root();
     477                 : 
     478               0 :         return nsnull;
     479                 :     }
     480                 : 
     481                 :     nsApplicationAccessible *applicationAcc =
     482               0 :         nsAccessNode::GetApplicationAccessible();
     483                 : 
     484               0 :     if (applicationAcc)
     485               0 :         return applicationAcc->GetAtkObject();
     486                 : 
     487               0 :     return nsnull;
     488                 : }
     489                 : 
     490                 : G_CONST_RETURN gchar *
     491               0 : mai_util_get_toolkit_name(void)
     492                 : {
     493               0 :     return MAI_NAME;
     494                 : }
     495                 : 
     496                 : G_CONST_RETURN gchar *
     497               0 : mai_util_get_toolkit_version(void)
     498                 : {
     499               0 :     return MAI_VERSION;
     500                 : }
     501                 : 
     502                 : void
     503               0 : _listener_info_destroy(gpointer data)
     504                 : {
     505               0 :     g_free(data);
     506               0 : }
     507                 : 
     508                 : guint
     509               0 : add_listener (GSignalEmissionHook listener,
     510                 :               const gchar *object_type,
     511                 :               const gchar *signal,
     512                 :               const gchar *hook_data,
     513                 :               guint gail_listenerid)
     514                 : {
     515                 :     GType type;
     516                 :     guint signal_id;
     517               0 :     gint rc = 0;
     518                 : 
     519               0 :     type = g_type_from_name(object_type);
     520               0 :     if (type) {
     521               0 :         signal_id = g_signal_lookup(signal, type);
     522               0 :         if (signal_id > 0) {
     523                 :             MaiUtilListenerInfo *listener_info;
     524                 : 
     525               0 :             rc = sListener_idx;
     526                 : 
     527                 :             listener_info =  (MaiUtilListenerInfo *)
     528               0 :                 g_malloc(sizeof(MaiUtilListenerInfo));
     529               0 :             listener_info->key = sListener_idx;
     530                 :             listener_info->hook_id =
     531                 :                 g_signal_add_emission_hook(signal_id, 0, listener,
     532               0 :                                            g_strdup(hook_data),
     533               0 :                                            (GDestroyNotify)g_free);
     534               0 :             listener_info->signal_id = signal_id;
     535               0 :             listener_info->gail_listenerid = gail_listenerid;
     536                 : 
     537                 :             g_hash_table_insert(sListener_list, &(listener_info->key),
     538               0 :                                 listener_info);
     539               0 :             sListener_idx++;
     540                 :         }
     541                 :         else {
     542               0 :             g_warning("Invalid signal type %s\n", signal);
     543                 :         }
     544                 :     }
     545                 :     else {
     546               0 :         g_warning("Invalid object type %s\n", object_type);
     547                 :     }
     548               0 :     return rc;
     549                 : }
     550                 : 
     551                 : static nsresult LoadGtkModule(GnomeAccessibilityModule& aModule);
     552                 : 
     553                 : // nsApplicationAccessibleWrap
     554                 : 
     555               0 : nsApplicationAccessibleWrap::nsApplicationAccessibleWrap():
     556               0 :     nsApplicationAccessible()
     557                 : {
     558               0 :     MAI_LOG_DEBUG(("======Create AppRootAcc=%p\n", (void*)this));
     559               0 : }
     560                 : 
     561               0 : nsApplicationAccessibleWrap::~nsApplicationAccessibleWrap()
     562                 : {
     563               0 :     MAI_LOG_DEBUG(("======Destory AppRootAcc=%p\n", (void*)this));
     564               0 :     nsAccessibleWrap::ShutdownAtkObject();
     565               0 : }
     566                 : 
     567                 : static gboolean
     568               0 : toplevel_event_watcher(GSignalInvocationHint* ihint,
     569                 :                        guint                  n_param_values,
     570                 :                        const GValue*          param_values,
     571                 :                        gpointer               data)
     572                 : {
     573                 :   static GQuark sQuark_gecko_acc_obj = 0;
     574                 : 
     575               0 :   if (!sQuark_gecko_acc_obj)
     576               0 :     sQuark_gecko_acc_obj = g_quark_from_static_string("GeckoAccObj");
     577                 : 
     578               0 :   if (nsAccessibilityService::IsShutdown())
     579               0 :     return TRUE;
     580                 : 
     581               0 :   GObject* object = reinterpret_cast<GObject*>(g_value_get_object(param_values));
     582               0 :   if (!GTK_IS_WINDOW(object))
     583               0 :     return TRUE;
     584                 : 
     585               0 :   AtkObject* child = gtk_widget_get_accessible(GTK_WIDGET(object));
     586                 : 
     587                 :   // GTK native dialog
     588               0 :   if (!IS_MAI_OBJECT(child) &&
     589               0 :       (atk_object_get_role(child) == ATK_ROLE_DIALOG)) {
     590                 : 
     591               0 :     if (data == reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW)) {
     592                 : 
     593                 :       // Attach the dialog accessible to app accessible tree
     594               0 :       nsAccessible* windowAcc = GetAccService()->AddNativeRootAccessible(child);
     595               0 :       g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj,
     596               0 :                          reinterpret_cast<gpointer>(windowAcc));
     597                 : 
     598                 :     } else {
     599                 : 
     600                 :       // Deattach the dialog accessible
     601                 :       nsAccessible* windowAcc =
     602                 :         reinterpret_cast<nsAccessible*>
     603               0 :                         (g_object_get_qdata(G_OBJECT(child), sQuark_gecko_acc_obj));
     604               0 :       if (windowAcc) {
     605               0 :         GetAccService()->RemoveNativeRootAccessible(windowAcc);
     606               0 :         g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj, NULL);
     607                 :       }
     608                 : 
     609                 :     }
     610                 :   }
     611                 : 
     612               0 :   return TRUE;
     613                 : }
     614                 : 
     615                 : bool
     616               0 : nsApplicationAccessibleWrap::Init()
     617                 : {
     618               0 :     if (ShouldA11yBeEnabled()) {
     619                 :         // load and initialize gail library
     620               0 :         nsresult rv = LoadGtkModule(sGail);
     621               0 :         if (NS_SUCCEEDED(rv)) {
     622               0 :             (*sGail.init)();
     623                 :         }
     624                 :         else {
     625               0 :             MAI_LOG_DEBUG(("Fail to load lib: %s\n", sGail.libName));
     626                 :         }
     627                 : 
     628               0 :         MAI_LOG_DEBUG(("Mozilla Atk Implementation initializing\n"));
     629                 :         // Initialize the MAI Utility class
     630                 :         // it will overwrite gail_util
     631               0 :         g_type_class_unref(g_type_class_ref(MAI_TYPE_UTIL));
     632                 : 
     633                 :         // Init atk-bridge now
     634               0 :         PR_SetEnv("NO_AT_BRIDGE=0");
     635                 : 
     636                 :         // load and initialize atk-bridge library
     637               0 :         rv = LoadGtkModule(sAtkBridge);
     638               0 :         if (NS_SUCCEEDED(rv)) {
     639                 :             // init atk-bridge
     640               0 :             (*sAtkBridge.init)();
     641                 :         }
     642                 :         else
     643               0 :             MAI_LOG_DEBUG(("Fail to load lib: %s\n", sAtkBridge.libName));
     644                 : 
     645               0 :         if (!sToplevel_event_hook_added) {
     646               0 :           sToplevel_event_hook_added = true;
     647                 :           sToplevel_show_hook =
     648                 :             g_signal_add_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
     649                 :               0, toplevel_event_watcher,
     650               0 :               reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW), NULL);
     651                 :           sToplevel_hide_hook =
     652                 :             g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
     653                 :               0, toplevel_event_watcher,
     654               0 :               reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE), NULL);
     655                 :         }
     656                 :     }
     657                 : 
     658               0 :     return nsApplicationAccessible::Init();
     659                 : }
     660                 : 
     661                 : void
     662               0 : nsApplicationAccessibleWrap::Unload()
     663                 : {
     664               0 :     if (sToplevel_event_hook_added) {
     665               0 :       sToplevel_event_hook_added = false;
     666                 :       g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
     667               0 :                                     sToplevel_show_hook);
     668                 :       g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
     669               0 :                                     sToplevel_hide_hook);
     670                 :     }
     671                 : 
     672               0 :     if (sAtkBridge.lib) {
     673                 :         // Do not shutdown/unload atk-bridge,
     674                 :         // an exit function registered will take care of it
     675                 :         // if (sAtkBridge.shutdown)
     676                 :         //     (*sAtkBridge.shutdown)();
     677                 :         // PR_UnloadLibrary(sAtkBridge.lib);
     678               0 :         sAtkBridge.lib = NULL;
     679               0 :         sAtkBridge.init = NULL;
     680               0 :         sAtkBridge.shutdown = NULL;
     681                 :     }
     682               0 :     if (sGail.lib) {
     683                 :         // Do not shutdown gail because
     684                 :         // 1) Maybe it's not init-ed by us. e.g. GtkEmbed
     685                 :         // 2) We need it to avoid assert in spi_atk_tidy_windows
     686                 :         // if (sGail.shutdown)
     687                 :         //   (*sGail.shutdown)();
     688                 :         // PR_UnloadLibrary(sGail.lib);
     689               0 :         sGail.lib = NULL;
     690               0 :         sGail.init = NULL;
     691               0 :         sGail.shutdown = NULL;
     692                 :     }
     693                 :     // if (sATKLib) {
     694                 :     //     PR_UnloadLibrary(sATKLib);
     695                 :     //     sATKLib = nsnull;
     696                 :     // }
     697               0 : }
     698                 : 
     699                 : NS_IMETHODIMP
     700               0 : nsApplicationAccessibleWrap::GetName(nsAString& aName)
     701                 : {
     702                 :   // ATK doesn't provide a way to obtain an application name (for example,
     703                 :   // Firefox or Thunderbird) like IA2 does. Thus let's return an application
     704                 :   // name as accessible name that was used to get a branding name (for example,
     705                 :   // Minefield aka nightly Firefox or Daily aka nightly Thunderbird).
     706               0 :   return GetAppName(aName);
     707                 : }
     708                 : 
     709                 : NS_IMETHODIMP
     710               0 : nsApplicationAccessibleWrap::GetNativeInterface(void **aOutAccessible)
     711                 : {
     712               0 :     *aOutAccessible = nsnull;
     713                 : 
     714               0 :     if (!mAtkObject) {
     715                 :         mAtkObject =
     716                 :             reinterpret_cast<AtkObject *>
     717               0 :                             (g_object_new(MAI_TYPE_ATK_OBJECT, NULL));
     718               0 :         NS_ENSURE_TRUE(mAtkObject, NS_ERROR_OUT_OF_MEMORY);
     719                 : 
     720               0 :         atk_object_initialize(mAtkObject, this);
     721               0 :         mAtkObject->role = ATK_ROLE_INVALID;
     722               0 :         mAtkObject->layer = ATK_LAYER_INVALID;
     723                 :     }
     724                 : 
     725               0 :     *aOutAccessible = mAtkObject;
     726               0 :     return NS_OK;
     727                 : }
     728                 : 
     729                 : struct AtkRootAccessibleAddedEvent {
     730                 :   AtkObject *app_accessible;
     731                 :   AtkObject *root_accessible;
     732                 :   PRUint32 index;
     733                 : };
     734                 : 
     735               0 : gboolean fireRootAccessibleAddedCB(gpointer data)
     736                 : {
     737               0 :     AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)data;
     738                 :     g_signal_emit_by_name(eventData->app_accessible, "children_changed::add",
     739               0 :                           eventData->index, eventData->root_accessible, NULL);
     740               0 :     g_object_unref(eventData->app_accessible);
     741               0 :     g_object_unref(eventData->root_accessible);
     742               0 :     free(data);
     743                 : 
     744               0 :     return FALSE;
     745                 : }
     746                 : 
     747                 : bool
     748               0 : nsApplicationAccessibleWrap::AppendChild(nsAccessible *aChild)
     749                 : {
     750               0 :     if (!nsApplicationAccessible::AppendChild(aChild))
     751               0 :       return false;
     752                 : 
     753               0 :     AtkObject *atkAccessible = nsAccessibleWrap::GetAtkObject(aChild);
     754               0 :     atk_object_set_parent(atkAccessible, mAtkObject);
     755                 : 
     756               0 :     PRUint32 count = mChildren.Length();
     757                 : 
     758                 :     // Emit children_changed::add in a timeout
     759                 :     // to make sure aRootAccWrap is fully initialized.
     760                 :     AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)
     761               0 :       malloc(sizeof(AtkRootAccessibleAddedEvent));
     762               0 :     if (eventData) {
     763               0 :       eventData->app_accessible = mAtkObject;
     764               0 :       eventData->root_accessible = atkAccessible;
     765               0 :       eventData->index = count -1;
     766               0 :       g_object_ref(mAtkObject);
     767               0 :       g_object_ref(atkAccessible);
     768               0 :       g_timeout_add(0, fireRootAccessibleAddedCB, eventData);
     769                 :     }
     770                 : 
     771               0 :     return true;
     772                 : }
     773                 : 
     774                 : bool
     775               0 : nsApplicationAccessibleWrap::RemoveChild(nsAccessible* aChild)
     776                 : {
     777               0 :     PRInt32 index = aChild->IndexInParent();
     778                 : 
     779               0 :     AtkObject *atkAccessible = nsAccessibleWrap::GetAtkObject(aChild);
     780               0 :     atk_object_set_parent(atkAccessible, NULL);
     781                 :     g_signal_emit_by_name(mAtkObject, "children_changed::remove", index,
     782               0 :                           atkAccessible, NULL);
     783                 : 
     784               0 :     return nsApplicationAccessible::RemoveChild(aChild);
     785                 : }
     786                 : 
     787                 : void
     788               0 : nsApplicationAccessibleWrap::PreCreate()
     789                 : {
     790               0 :     if (!sATKChecked) {
     791               0 :         sATKLib = PR_LoadLibrary(sATKLibName);
     792               0 :         if (sATKLib) {
     793               0 :             AtkGetTypeType pfn_atk_hyperlink_impl_get_type = (AtkGetTypeType) PR_FindFunctionSymbol(sATKLib, sATKHyperlinkImplGetTypeSymbol);
     794               0 :             if (pfn_atk_hyperlink_impl_get_type)
     795               0 :                 g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
     796                 : 
     797                 :             AtkGetTypeType pfn_atk_socket_get_type;
     798                 :             pfn_atk_socket_get_type = (AtkGetTypeType)
     799                 :                                       PR_FindFunctionSymbol(sATKLib,
     800               0 :                                                             AtkSocketAccessible::sATKSocketGetTypeSymbol);
     801               0 :             if (pfn_atk_socket_get_type) {
     802                 :                 AtkSocketAccessible::g_atk_socket_type =
     803               0 :                   pfn_atk_socket_get_type();
     804                 :                 AtkSocketAccessible::g_atk_socket_embed = (AtkSocketEmbedType)
     805                 :                   PR_FindFunctionSymbol(sATKLib,
     806                 :                                         AtkSocketAccessible
     807               0 :                                           ::sATKSocketEmbedSymbol);
     808                 :             AtkSocketAccessible::gCanEmbed =
     809                 :               AtkSocketAccessible::g_atk_socket_type != G_TYPE_INVALID &&
     810               0 :               AtkSocketAccessible::g_atk_socket_embed;
     811                 :             }
     812                 :         }
     813               0 :         sATKChecked = true;
     814                 :     }
     815               0 : }
     816                 : 
     817                 : static nsresult
     818               0 : LoadGtkModule(GnomeAccessibilityModule& aModule)
     819                 : {
     820               0 :     NS_ENSURE_ARG(aModule.libName);
     821                 : 
     822               0 :     if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
     823                 : 
     824               0 :         MAI_LOG_DEBUG(("Fail to load lib: %s in default path\n", aModule.libName));
     825                 : 
     826                 :         //try to load the module with "gtk-2.0/modules" appended
     827               0 :         char *curLibPath = PR_GetLibraryPath();
     828               0 :         nsCAutoString libPath(curLibPath);
     829                 : #if defined(LINUX) && defined(__x86_64__)
     830                 :         libPath.Append(":/usr/lib64:/usr/lib");
     831                 : #else
     832               0 :         libPath.Append(":/usr/lib");
     833                 : #endif
     834               0 :         MAI_LOG_DEBUG(("Current Lib path=%s\n", libPath.get()));
     835               0 :         PR_FreeLibraryName(curLibPath);
     836                 : 
     837               0 :         PRInt16 loc1 = 0, loc2 = 0;
     838               0 :         PRInt16 subLen = 0;
     839               0 :         while (loc2 >= 0) {
     840               0 :             loc2 = libPath.FindChar(':', loc1);
     841               0 :             if (loc2 < 0)
     842               0 :                 subLen = libPath.Length() - loc1;
     843                 :             else
     844               0 :                 subLen = loc2 - loc1;
     845               0 :             nsCAutoString sub(Substring(libPath, loc1, subLen));
     846               0 :             sub.Append("/gtk-2.0/modules/");
     847               0 :             sub.Append(aModule.libName);
     848               0 :             aModule.lib = PR_LoadLibrary(sub.get());
     849               0 :             if (aModule.lib) {
     850               0 :                 MAI_LOG_DEBUG(("Ok, load %s from %s\n", aModule.libName, sub.get()));
     851                 :                 break;
     852                 :             }
     853               0 :             loc1 = loc2+1;
     854                 :         }
     855               0 :         if (!aModule.lib) {
     856               0 :             MAI_LOG_DEBUG(("Fail to load %s\n", aModule.libName));
     857               0 :             return NS_ERROR_FAILURE;
     858                 :         }
     859                 :     }
     860                 : 
     861                 :     //we have loaded the library, try to get the function ptrs
     862               0 :     if (!(aModule.init = PR_FindFunctionSymbol(aModule.lib,
     863               0 :                                                aModule.initName)) ||
     864                 :         !(aModule.shutdown = PR_FindFunctionSymbol(aModule.lib,
     865               0 :                                                    aModule.shutdownName))) {
     866                 : 
     867                 :         //fail, :(
     868               0 :         MAI_LOG_DEBUG(("Fail to find symbol %s in %s",
     869                 :                        aModule.init ? aModule.shutdownName : aModule.initName,
     870                 :                        aModule.libName));
     871               0 :         PR_UnloadLibrary(aModule.lib);
     872               0 :         aModule.lib = NULL;
     873               0 :         return NS_ERROR_FAILURE;
     874                 :     }
     875               0 :     return NS_OK;
     876                 : }
     877                 : 
     878                 : namespace mozilla {
     879                 : namespace a11y {
     880                 : 
     881                 :   static const char sAccEnv [] = "GNOME_ACCESSIBILITY";
     882                 : #ifdef MOZ_ENABLE_DBUS
     883                 : static DBusPendingCall *sPendingCall = nsnull;
     884                 : #endif
     885                 : 
     886                 : void
     887               0 : PreInit()
     888                 : {
     889                 : #ifdef MOZ_ENABLE_DBUS
     890                 :   static bool sChecked = FALSE;
     891               0 :   if (sChecked)
     892               0 :     return;
     893                 : 
     894               0 :   sChecked = TRUE;
     895                 : 
     896                 :   // dbus is only checked if GNOME_ACCESSIBILITY is unset
     897                 :   // also make sure that a session bus address is available to prevent dbus from
     898                 :   // starting a new one.  Dbus confuses the test harness when it creates a new
     899                 :   // process (see bug 693343)
     900               0 :   if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS"))
     901               0 :     return;
     902                 : 
     903               0 :   DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nsnull);
     904               0 :   if (!bus)
     905               0 :     return;
     906                 : 
     907               0 :   dbus_connection_set_exit_on_disconnect(bus, FALSE);
     908                 : 
     909                 :   static const char* iface = "org.a11y.Status";
     910                 :   static const char* member = "IsEnabled";
     911                 :   DBusMessage *message;
     912                 :   message = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
     913                 :                                          "org.freedesktop.DBus.Properties",
     914               0 :                                          "Get");
     915               0 :   if (!message)
     916               0 :     goto dbus_done;
     917                 : 
     918                 :   dbus_message_append_args(message, DBUS_TYPE_STRING, &iface,
     919               0 :                            DBUS_TYPE_STRING, &member, DBUS_TYPE_INVALID);
     920               0 :   dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
     921               0 :   dbus_message_unref(message);
     922                 : 
     923                 : dbus_done:
     924               0 :   dbus_connection_unref(bus);
     925                 : #endif
     926                 : }
     927                 : 
     928                 : bool
     929               0 : ShouldA11yBeEnabled()
     930                 : {
     931                 :   static bool sChecked = false, sShouldEnable = false;
     932               0 :   if (sChecked)
     933               0 :     return sShouldEnable;
     934                 : 
     935               0 :   sChecked = true;
     936                 : 
     937                 :   // check if accessibility enabled/disabled by environment variable
     938               0 :   const char* envValue = PR_GetEnv(sAccEnv);
     939               0 :   if (envValue)
     940               0 :     return sShouldEnable = !!atoi(envValue);
     941                 : 
     942                 : #ifdef MOZ_ENABLE_DBUS
     943               0 :   PreInit();
     944               0 :   bool dbusSuccess = false;
     945               0 :   DBusMessage *reply = nsnull;
     946               0 :   if (!sPendingCall)
     947               0 :     goto dbus_done;
     948                 : 
     949               0 :   dbus_pending_call_block(sPendingCall);
     950               0 :   reply = dbus_pending_call_steal_reply(sPendingCall);
     951               0 :   dbus_pending_call_unref(sPendingCall);
     952               0 :   sPendingCall = nsnull;
     953               0 :   if (!reply ||
     954               0 :       dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
     955               0 :       strcmp(dbus_message_get_signature (reply), DBUS_TYPE_VARIANT_AS_STRING))
     956               0 :     goto dbus_done;
     957                 : 
     958                 :   DBusMessageIter iter, iter_variant, iter_struct;
     959                 :   dbus_bool_t dResult;
     960               0 :   dbus_message_iter_init(reply, &iter);
     961               0 :   dbus_message_iter_recurse (&iter, &iter_variant);
     962               0 :   switch (dbus_message_iter_get_arg_type(&iter_variant)) {
     963                 :     case DBUS_TYPE_STRUCT:
     964                 :       // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
     965               0 :       dbus_message_iter_recurse(&iter_variant, &iter_struct);
     966               0 :       if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
     967               0 :         dbus_message_iter_get_basic(&iter_struct, &dResult);
     968               0 :         sShouldEnable = dResult;
     969               0 :         dbusSuccess = true;
     970                 :       }
     971                 : 
     972               0 :       break;
     973                 :     case DBUS_TYPE_BOOLEAN:
     974               0 :       dbus_message_iter_get_basic(&iter_variant, &dResult);
     975               0 :       sShouldEnable = dResult;
     976               0 :       dbusSuccess = true;
     977               0 :       break;
     978                 :     default:
     979               0 :       break;
     980                 :   }
     981                 : 
     982                 : dbus_done:
     983               0 :   if (reply)
     984               0 :     dbus_message_unref(reply);
     985                 : 
     986               0 :   if (dbusSuccess)
     987               0 :     return sShouldEnable;
     988                 : #endif
     989                 : 
     990                 :   //check gconf-2 setting
     991                 : static const char sGconfAccessibilityKey[] =
     992                 :     "/desktop/gnome/interface/accessibility";
     993               0 :   nsresult rv = NS_OK;
     994                 :   nsCOMPtr<nsIGConfService> gconf =
     995               0 :     do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv);
     996               0 :   if (NS_SUCCEEDED(rv) && gconf)
     997               0 :     gconf->GetBool(NS_LITERAL_CSTRING(sGconfAccessibilityKey), &sShouldEnable);
     998                 : 
     999               0 :   return sShouldEnable;
    1000                 : }
    1001                 : } // namespace a11y
    1002                 : } // namespace mozilla
    1003                 : 

Generated by: LCOV version 1.7