X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git;a=blobdiff_plain;f=atk-adaptor%2Faccessible-register.c;h=7ca416fd2aef8494006e3873e3ee2d57d6157e88;hp=9a095f600d0594745527c57cf07433a7534fe8fc;hb=17b84f5bd418999040720f012f56abad66055f8a;hpb=5d30ed4afc98e7f19f35dfcfc1fbd534966e8344 diff --git a/atk-adaptor/accessible-register.c b/atk-adaptor/accessible-register.c index 9a095f6..7ca416f 100644 --- a/atk-adaptor/accessible-register.c +++ b/atk-adaptor/accessible-register.c @@ -3,7 +3,7 @@ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * * Copyright 2008 Novell, Inc. - * Copyright 2008, 2009 Codethink Ltd. + * Copyright 2008, 2009, 2010 Codethink Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -28,10 +28,6 @@ #include "bridge.h" #include "accessible-register.h" -#define ATK_BRIDGE_OBJECT_PATH_PREFIX "/org/freedesktop/atspi/accessible" -#define ATK_BRIDGE_OBJECT_REFERENCE_TEMPLATE ATK_BRIDGE_OBJECT_PATH_PREFIX "/%d" -#define ATK_BRIDGE_PATH_PREFIX_LENGTH 33 - /* * This module is responsible for keeping track of all the AtkObjects in * the application, so that they can be accessed remotely and placed in @@ -39,7 +35,7 @@ * * To access an AtkObject remotely we need to provide a D-Bus object * path for it. The D-Bus object paths used have a standard prefix - * (ATK_BRIDGE_OBJECT_PATH_PREFIX). Appended to this prefix is a string + * (SPI_ATK_OBJECT_PATH_PREFIX). Appended to this prefix is a string * representation of an integer reference. So to access an AtkObject * remotely we keep a Hashtable that maps the given reference to * the AtkObject pointer. An object in this hash table is said to be 'registered'. @@ -49,57 +45,100 @@ * object destruction. When an object is destroyed it must be 'deregistered' * To do this lookup we keep a dbus-id attribute on each AtkObject. * - * In addition to accessing the objects remotely we must be able to update - * the client side cache. This is done using the 'update' signal of the - * org.freedesktop.atspi.Tree interface. The update signal should send out - * all of the cacheable data for an Accessible object. - * */ -/* - * FIXME - * - * While traversing the ATK tree we may modify it unintentionally. - * This is either a bug in the Gail implementation or this module. - * If a change is caused that recurses, via a signal into this module - * we should catch it. - * - * Things could also be changed that do not cause signal emission, - * but do cause a failure. Not sure what these would be. - * - * The other option is that there are threads that modify the GUI. - * This IS A BUG IN THE PROGRAM. But it may happen. If seeing very - * odd bugs change this to take the GDK lock. Just to make sure. - */ +#define SPI_ATK_PATH_PREFIX_LENGTH 27 +#define SPI_ATK_OBJECT_PATH_PREFIX "/org/a11y/atspi/accessible/" +#define SPI_ATK_OBJECT_PATH_ROOT "root" + +#define SPI_ATK_OBJECT_REFERENCE_TEMPLATE SPI_ATK_OBJECT_PATH_PREFIX "%d" + +#define SPI_DBUS_ID "spi-dbus-id" -static GHashTable *ref2ptr = NULL; /* Used for converting a D-Bus path (Reference) to the object pointer */ +SpiRegister *spi_global_register = NULL; -static guint counter = 1; +static const gchar * spi_register_root_path = SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT; -static GStaticRecMutex registration_mutex = G_STATIC_REC_MUTEX_INIT; +enum +{ + OBJECT_REGISTERED, + OBJECT_DEREGISTERED, + LAST_SIGNAL +}; +static guint register_signals[LAST_SIGNAL] = { 0 }; + +/*---------------------------------------------------------------------------*/ + +static void +spi_register_finalize (GObject * object); /*---------------------------------------------------------------------------*/ -static GStaticMutex recursion_check_guard = G_STATIC_MUTEX_INIT; -static gboolean recursion_check = FALSE; +G_DEFINE_TYPE (SpiRegister, spi_register, G_TYPE_OBJECT) + +static void spi_register_class_init (SpiRegisterClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + spi_register_parent_class = g_type_class_ref (G_TYPE_OBJECT); + + object_class->finalize = spi_register_finalize; + + register_signals [OBJECT_REGISTERED] = + g_signal_new ("object-registered", + SPI_REGISTER_TYPE, + G_SIGNAL_ACTION, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + register_signals [OBJECT_DEREGISTERED] = + g_signal_new ("object-deregistered", + SPI_REGISTER_TYPE, + G_SIGNAL_ACTION, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} + +static void +spi_register_init (SpiRegister * reg) +{ + reg->ref2ptr = g_hash_table_new (g_direct_hash, g_direct_equal); + reg->reference_counter = 0; +} -static gboolean -recursion_check_and_set () +static void +deregister_object (gpointer data, GObject * gobj) +{ + SpiRegister *reg = SPI_REGISTER (data); + + spi_register_deregister_object (reg, gobj, FALSE); +} + +static void +spi_register_remove_weak_ref (gpointer key, gpointer val, gpointer reg) { - gboolean ret; - g_static_mutex_lock (&recursion_check_guard); - ret = recursion_check; - recursion_check = TRUE; - g_static_mutex_unlock (&recursion_check_guard); - return ret; + g_object_weak_unref (val, deregister_object, reg); } static void -recursion_check_unset () +spi_register_finalize (GObject * object) { - g_static_mutex_lock (&recursion_check_guard); - recursion_check = FALSE; - g_static_mutex_unlock (&recursion_check_guard); + SpiRegister *reg = SPI_REGISTER (object); + + g_hash_table_foreach (reg->ref2ptr, spi_register_remove_weak_ref, reg); + g_hash_table_unref (reg->ref2ptr); + + G_OBJECT_CLASS (spi_register_parent_class)->finalize (object); } /*---------------------------------------------------------------------------*/ @@ -109,23 +148,28 @@ recursion_check_unset () * * This function provides an integer reference for a new * AtkObject. + * + * TODO: Make this reference a little more unique, this is shoddy. */ static guint -assign_reference(void) +assign_reference (SpiRegister * reg) { - counter++; + reg->reference_counter++; /* Reference of 0 not allowed as used as direct key in hash table */ - if (counter == 0) - counter++; + if (reg->reference_counter == 0) + reg->reference_counter++; + return reg->reference_counter; } +/*---------------------------------------------------------------------------*/ + /* * Returns the reference of the object, or 0 if it is not registered. */ static guint -object_to_ref (AtkObject *accessible) +object_to_ref (GObject * gobj) { - return GPOINTER_TO_INT(g_object_get_data (G_OBJECT (accessible), "dbus-id")); + return GPOINTER_TO_INT (g_object_get_data (gobj, SPI_DBUS_ID)); } /* @@ -134,7 +178,7 @@ object_to_ref (AtkObject *accessible) static gchar * ref_to_path (guint ref) { - return g_strdup_printf(ATK_BRIDGE_OBJECT_REFERENCE_TEMPLATE, ref); + return g_strdup_printf (SPI_ATK_OBJECT_REFERENCE_TEMPLATE, ref); } /*---------------------------------------------------------------------------*/ @@ -145,389 +189,132 @@ ref_to_path (guint ref) * Removes the AtkObject from the reference lookup tables, meaning * it is no longer exposed over D-Bus. */ -static void -deregister_accessible (gpointer data, GObject *accessible) +void +spi_register_deregister_object (SpiRegister *reg, GObject *gobj, gboolean unref) { guint ref; - g_assert (ATK_IS_OBJECT (accessible)); - ref = object_to_ref (ATK_OBJECT(accessible)); + ref = object_to_ref (gobj); if (ref != 0) { - g_hash_table_remove(ref2ptr, GINT_TO_POINTER(ref)); - } -} - -/* - * Called to register an AtkObject with AT-SPI and expose it over D-Bus. - */ -static void -register_accessible (AtkObject *accessible) -{ - guint ref; - g_assert(ATK_IS_OBJECT(accessible)); - - ref = assign_reference(); - - g_hash_table_insert (ref2ptr, GINT_TO_POINTER(ref), accessible); - g_object_set_data (G_OBJECT(accessible), "dbus-id", GINT_TO_POINTER(ref)); - g_object_weak_ref(G_OBJECT(accessible), deregister_accessible, NULL); -} - -/*---------------------------------------------------------------------------*/ + g_signal_emit (reg, + register_signals [OBJECT_DEREGISTERED], + 0, + gobj); + if (unref) + g_object_weak_unref (gobj, deregister_object, reg); + g_hash_table_remove (reg->ref2ptr, GINT_TO_POINTER (ref)); #ifdef SPI_ATK_DEBUG -/* - * This function checks that the ref-count of an accessible - * is greater than 1. - * - * There is not currently any remote reference counting - * in AT-SPI D-Bus so objects that are remotely owned are not - * allowed. - * - * TODO Add debug wrapper - */ -static gboolean -non_owned_accessible (AtkObject *accessible) -{ - if ((G_OBJECT (accessible))->ref_count <= 1) - { - g_warning ("AT-SPI: Child referenced that is not owned by its parent"); - return TRUE; - } - else - { - return FALSE; - } -} -#endif /* SPI_ATK_DEBUG */ - -/*---------------------------------------------------------------------------*/ - -static gboolean -has_manages_descendants (AtkObject *accessible) -{ - AtkStateSet *state; - gboolean result = FALSE; - - /* This is dangerous, refing the state set - * seems to do wierd things to the tree & cause recursion - * by modifying the tree alot. - */ - state = atk_object_ref_state_set (accessible); - if (atk_state_set_contains_state (state, ATK_STATE_MANAGES_DESCENDANTS)) - { -#ifdef SPI_ATK_DEBUG - g_warning ("AT-SPI: Object with 'Manages descendants' states not currently handled by AT-SPI"); + g_debug ("DEREG - %d", ref); #endif - result = TRUE; - } - g_object_unref (state); - - return result; + } } -/* - * Registers a subtree of accessible objects - * rooted at the accessible object provided. - * - * The leaf nodes do not have their children - * registered. A node is considered a leaf - * if it has the state "manages-descendants" - * or if it has already been registered. - */ -void -register_subtree (AtkObject *accessible) +static void +register_object (SpiRegister * reg, GObject * gobj) { - AtkObject *current, *tmp; - GQueue *stack; - guint i; - gboolean recurse; - - - current = g_object_ref (accessible); - if (has_manages_descendants (current)) - { - g_object_unref (current); - return; - } - - stack = g_queue_new (); - - register_accessible (current); - g_queue_push_head (stack, GINT_TO_POINTER (0)); - - /* - * The index held on the stack is the next child node - * that needs processing at the corresponding level in the tree. - */ - while (!g_queue_is_empty (stack)) - { - /* Find the next child node that needs processing */ + guint ref; + g_return_if_fail (G_IS_OBJECT (gobj)); - i = GPOINTER_TO_INT(g_queue_peek_head (stack)); - recurse = FALSE; + ref = assign_reference (reg); - while (i < atk_object_get_n_accessible_children (current) && - recurse == FALSE) - { - tmp = atk_object_ref_accessible_child (current, i); + g_hash_table_insert (reg->ref2ptr, GINT_TO_POINTER (ref), gobj); + g_object_set_data (G_OBJECT (gobj), SPI_DBUS_ID, GINT_TO_POINTER (ref)); + g_object_weak_ref (G_OBJECT (gobj), deregister_object, reg); #ifdef SPI_ATK_DEBUG - non_owned_accessible (tmp); + g_debug ("REG - %d", ref); #endif - if (object_to_ref (tmp)) - { - /* If its already registered, just update */ - spi_emit_cache_update (tmp, atk_adaptor_app_data->bus); - } - else if (has_manages_descendants (tmp)) - { - /* If it has manages descendants, just register and update */ - register_accessible (tmp); - spi_emit_cache_update (tmp, atk_adaptor_app_data->bus); - } - else - { - recurse = TRUE; - } - - if (!recurse) - { - g_object_unref (G_OBJECT (tmp)); - } - - i++; - } - - if (recurse) - { - /* Push onto stack */ - current = tmp; - register_accessible (current); - - g_queue_peek_head_link (stack)->data = GINT_TO_POINTER (i); - g_queue_push_head (stack, GINT_TO_POINTER (0)); - } - else - { - /* Pop from stack */ - spi_emit_cache_update (current, atk_adaptor_app_data->bus); - tmp = current; - current = atk_object_get_parent (current); - g_object_unref (G_OBJECT (tmp)); - g_queue_pop_head (stack); - } - } - g_queue_free (stack); + g_signal_emit (reg, register_signals [OBJECT_REGISTERED], 0, gobj); } /*---------------------------------------------------------------------------*/ /* - * Called when an already registered object is updated in such a - * way that client side cache needs to be updated. + * Used to lookup an GObject from its D-Bus path. + * + * If the D-Bus path is not found this function returns NULL. */ -static void -update_accessible (AtkObject *accessible) -{ - guint ref = 0; - g_assert(ATK_IS_OBJECT(accessible)); - - ref = object_to_ref (accessible); - if (ref) - { - spi_emit_cache_update (accessible, atk_adaptor_app_data->bus); - } -} - -/*---------------------------------------------------------------------------*/ - -void -atk_dbus_foreach_registered(GHFunc func, gpointer data) -{ - g_hash_table_foreach(ref2ptr, func, data); -} - -/* - * Used to lookup an AtkObject from its D-Bus path. - */ -AtkObject * -atk_dbus_path_to_object (const char *path) +GObject * +spi_register_path_to_object (SpiRegister * reg, const char *path) { guint index; void *data; - g_assert (path); + g_return_val_if_fail (path, NULL); - if (strncmp(path, ATK_BRIDGE_OBJECT_PATH_PREFIX, ATK_BRIDGE_PATH_PREFIX_LENGTH) != 0) + if (strncmp (path, SPI_ATK_OBJECT_PATH_PREFIX, SPI_ATK_PATH_PREFIX_LENGTH) + != 0) return NULL; - path += ATK_BRIDGE_PATH_PREFIX_LENGTH; /* Skip over the prefix */ + path += SPI_ATK_PATH_PREFIX_LENGTH; /* Skip over the prefix */ - if (path[0] == '\0') - return atk_get_root(); - if (path[0] != '/') - return NULL; + /* Map the root path to the root object. */ + if (!g_strcmp0 (SPI_ATK_OBJECT_PATH_ROOT, path)) + return G_OBJECT (spi_global_app_data->root); - path++; index = atoi (path); - data = g_hash_table_lookup (ref2ptr, GINT_TO_POINTER(index)); + data = g_hash_table_lookup (reg->ref2ptr, GINT_TO_POINTER (index)); if (data) - return ATK_OBJECT (data); + return G_OBJECT(data); else return NULL; } -/* - * Used to lookup a D-Bus path from the AtkObject. - */ -gchar * -atk_dbus_object_to_path (AtkObject *accessible) +GObject * +spi_global_register_path_to_object (const char * path) { - guint ref; - - ref = object_to_ref (accessible); - if (!ref) - return NULL; - else - return ref_to_path (ref); + return spi_register_path_to_object (spi_global_register, path); } -/*---------------------------------------------------------------------------*/ - /* - * Events are not evaluated for non-registered accessibles. - * - * When a property change is made on a registered accessible - * the client side cache should be updated. - * - * When a parent is changed the subtree is de-registered - * if the parent is not attached to the root accessible. + * Used to lookup a D-Bus path from the GObject. + * + * If the objects is not already registered, + * this function will register it. */ -static gboolean -tree_update_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +gchar * +spi_register_object_to_path (SpiRegister * reg, GObject * gobj) { - AtkObject *accessible; - AtkPropertyValues *values; - const gchar *pname = NULL; + guint ref; - g_static_rec_mutex_lock (®istration_mutex); + if (gobj == NULL) + return NULL; - /* Ensure that only registered accessibles - * have their signals processed. - */ - accessible = g_value_get_object (¶m_values[0]); - g_assert (ATK_IS_OBJECT (accessible)); + /* Map the root object to the root path. */ + if ((void *)gobj == (void *)spi_global_app_data->root) + return g_strdup (spi_register_root_path); - if (object_to_ref (accessible)) + ref = object_to_ref (gobj); + if (!ref) { -#ifdef SPI_ATK_DEBUG - if (recursion_check_and_set ()) - g_warning ("AT-SPI: Recursive use of registration module"); -#endif - - values = (AtkPropertyValues*) g_value_get_pointer (¶m_values[1]); - pname = values[0].property_name; - if (strcmp (pname, "accessible-name") == 0 || - strcmp (pname, "accessible-description") == 0) - { - update_accessible (accessible); - } - /* Parent value us updated by child-add signal of parent object */ - - recursion_check_unset (); + register_object (reg, gobj); + ref = object_to_ref (gobj); } - g_static_rec_mutex_unlock (®istration_mutex); - - return TRUE; + if (!ref) + return NULL; + else + return ref_to_path (ref); } -/* - * Events are not evaluated for non registered accessibles. - * - * When the children of a registered accessible are changed - * the subtree, rooted at the child is registered. - */ -static gboolean -tree_update_children_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +guint +spi_register_object_to_ref (GObject * gobj) { - AtkObject *accessible; - const gchar *detail = NULL; - AtkObject *child; - - g_static_rec_mutex_lock (®istration_mutex); - - /* Ensure that only registered accessibles - * have their signals processed. - */ - accessible = g_value_get_object (¶m_values[0]); - g_assert (ATK_IS_OBJECT (accessible)); - - if (object_to_ref (accessible)) - { -#ifdef SPI_ATK_DEBUG - if (recursion_check_and_set ()) - g_warning ("AT-SPI: Recursive use of registration module"); -#endif - - if (signal_hint->detail) - detail = g_quark_to_string (signal_hint->detail); - - if (!strcmp (detail, "add")) - { - gpointer child; - int index = g_value_get_uint (param_values + 1); - child = g_value_get_pointer (param_values + 2); - - if (!ATK_IS_OBJECT (child)) - { - child = atk_object_ref_accessible_child (accessible, index); -#ifdef SPI_ATK_DEBUG - non_owned_accessible (child); -#endif - } - register_subtree (child); - } - - recursion_check_unset (); - } - - g_static_rec_mutex_unlock (®istration_mutex); - - return TRUE; + return object_to_ref (gobj); } - + /* - * Initializes required global data. The update and removal lists - * and the reference lookup tables. - * - * Initializes all of the required D-Bus interfaces. + * Gets the path that indicates the accessible desktop object. + * This object is logically located on the registry daemon and not + * within any particular application. */ -void -atk_dbus_initialize (AtkObject *root) +gchar * +spi_register_root_object_path () { - if (!ref2ptr) - ref2ptr = g_hash_table_new(g_direct_hash, g_direct_equal); - -#ifdef SPI_ATK_DEBUG - if (g_thread_supported ()) - g_message ("AT-SPI: Threads enabled"); -#endif - - register_subtree (root); - - atk_add_global_event_listener (tree_update_listener, "Gtk:AtkObject:property-change"); - atk_add_global_event_listener (tree_update_children_listener, "Gtk:AtkObject:children-changed"); + return g_strdup (SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT); } /*END------------------------------------------------------------------------*/ -