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=4a177efabdc616b5f8cdd20de523fcc345c0a22d;hb=abc815aa731b80392c676559c34815d463b0e51c;hpb=1f14708c906c6cf008fbc6cf89b52684882098a5 diff --git a/atk-adaptor/accessible-register.c b/atk-adaptor/accessible-register.c index 4a177ef..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 @@ -45,64 +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 - * - * This code seems very brittle. - * I would prefer changes to be made to - * gail and the ATK interface so that all Accessible - * objects are registered with an exporting module. - * - * This is the same system as Qt has with the QAccessibleBridge - * and QAccessibleBridgePlugin. It entails some rather - * large structural changes to ATK though: - * - * Removing infinite spaces (Child access no longer references child). - * Removing lazy creation of accessible objects. - */ +#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_PATH_PREFIX "/org/freedesktop/atspi/accessible" -#define SPI_ATK_OBJECT_PATH_DESKTOP "/root" +#define SPI_ATK_OBJECT_REFERENCE_TEMPLATE SPI_ATK_OBJECT_PATH_PREFIX "%d" -#define SPI_ATK_PATH_PREFIX_LENGTH 33 -#define SPI_ATK_OBJECT_REFERENCE_TEMPLATE SPI_ATK_OBJECT_PATH_PREFIX "/%d" +#define SPI_DBUS_ID "spi-dbus-id" +SpiRegister *spi_global_register = NULL; -static GHashTable *ref2ptr = NULL; /* Used for converting a D-Bus path (Reference) to the object pointer */ +static const gchar * spi_register_root_path = SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT; -static guint reference_counter = 0; +enum +{ + OBJECT_REGISTERED, + OBJECT_DEREGISTERED, + LAST_SIGNAL +}; +static guint register_signals[LAST_SIGNAL] = { 0 }; + +/*---------------------------------------------------------------------------*/ -static GStaticRecMutex registration_mutex = G_STATIC_REC_MUTEX_INIT; +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 void +deregister_object (gpointer data, GObject * gobj) +{ + SpiRegister *reg = SPI_REGISTER (data); + + spi_register_deregister_object (reg, gobj, FALSE); +} -static gboolean -recursion_check_and_set () +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); } /*---------------------------------------------------------------------------*/ @@ -112,33 +148,37 @@ 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) { - reference_counter++; + reg->reference_counter++; /* Reference of 0 not allowed as used as direct key in hash table */ - if (reference_counter == 0) - reference_counter++; - return reference_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)); } /* * Converts the Accessible object reference to its D-Bus object path */ -gchar * -atk_dbus_ref_to_path (guint ref) +static gchar * +ref_to_path (guint ref) { - return g_strdup_printf(SPI_ATK_OBJECT_REFERENCE_TEMPLATE, ref); + return g_strdup_printf (SPI_ATK_OBJECT_REFERENCE_TEMPLATE, ref); } /*---------------------------------------------------------------------------*/ @@ -149,539 +189,132 @@ atk_dbus_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_return_if_fail (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_return_if_fail (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"); -#endif - result = TRUE; - } - g_object_unref (state); - - return result; -} - -static void -append_children (AtkObject *accessible, GQueue *traversal) -{ - AtkObject *current; - guint i; - gint count = atk_object_get_n_accessible_children (accessible); - - if (count < 0) count = 0; - for (i =0; i < count; i++) - { - current = atk_object_ref_accessible_child (accessible, i); - if (current) - { -#ifdef SPI_ATK_DEBUG - non_owned_accessible (current); + g_debug ("DEREG - %d", ref); #endif - if (!has_manages_descendants (current)) - g_queue_push_tail (traversal, current); - else - g_object_unref (G_OBJECT (current)); - } } } -/* - * 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; - GQueue *traversal; - GQueue *emit_update; - - g_return_if_fail (ATK_IS_OBJECT (accessible)); + guint ref; + g_return_if_fail (G_IS_OBJECT (gobj)); - traversal = g_queue_new (); - emit_update = g_queue_new (); + ref = assign_reference (reg); - g_object_ref (accessible); - g_queue_push_tail (traversal, accessible); + 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); - while (!g_queue_is_empty (traversal)) - { - current = g_queue_pop_head (traversal); - g_queue_push_tail (emit_update, current); - if (!object_to_ref (current)) - { - register_accessible (current); #ifdef SPI_ATK_DEBUG - g_debug ("REG - %s - %d - %s", atk_object_get_name (current), - atk_object_get_role (current), - atk_dbus_object_to_path (current)); + g_debug ("REG - %d", ref); #endif - append_children (current, traversal); - } - } - - while (!g_queue_is_empty (emit_update)) - { - current = g_queue_pop_head (emit_update); - spi_emit_cache_update (current, atk_adaptor_app_data->bus); - g_object_unref (G_OBJECT (current)); - } - g_queue_free (traversal); - g_queue_free (emit_update); + 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_return_if_fail (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_return_val_if_fail (path, NULL); - if (strncmp(path, SPI_ATK_OBJECT_PATH_PREFIX, SPI_ATK_PATH_PREFIX_LENGTH) != 0) + if (strncmp (path, SPI_ATK_OBJECT_PATH_PREFIX, SPI_ATK_PATH_PREFIX_LENGTH) + != 0) return NULL; path += SPI_ATK_PATH_PREFIX_LENGTH; /* Skip over the prefix */ - if (!g_strcmp0 (SPI_ATK_OBJECT_PATH_DESKTOP, path)) - 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; } -/* - * TODO WARNING HACK This function is dangerous. - * It should only be called before sending an event on an - * object that has not already been registered. - */ -gchar * -atk_dbus_object_attempt_registration (AtkObject *accessible) +GObject * +spi_global_register_path_to_object (const char * path) { - guint ref; - - ref = object_to_ref (accessible); - if (!ref) - { - /* See if the object is attached to the main tree */ - AtkObject *current, *prev = NULL; - guint cref = 0; - - /* This should iterate until it hits a NULL or registered parent */ - prev = accessible; - current = atk_object_get_parent (accessible); - if (current) - cref = object_to_ref (current); - while (current && !cref) - { - prev = current; - current = atk_object_get_parent (current); - if (current) - cref = object_to_ref (current); - } - - /* A registered parent, with non-registered child, has been found */ - if (current) - { - register_subtree (prev); - } - - /* The object SHOULD be registered now. If it isn't - I give up */ - ref = object_to_ref (accessible); - if (ref) - { - return atk_dbus_ref_to_path (ref); - } - else - { -#ifdef SPI_ATK_DEBUG - g_debug ("AT-SPI: Could not register a non-attached accessible object"); -#endif - return NULL; - } - } - else - { - return atk_dbus_ref_to_path (ref); - } + return spi_register_path_to_object (spi_global_register, path); } - /* - * Used to lookup a D-Bus path from the AtkObject. + * Used to lookup a D-Bus path from the GObject. + * + * If the objects is not already registered, + * this function will register it. */ gchar * -atk_dbus_object_to_path (AtkObject *accessible) +spi_register_object_to_path (SpiRegister * reg, GObject * gobj) { guint ref; - ref = object_to_ref (accessible); - if (!ref) - return NULL; - else - return ref_to_path (ref); -} - -gchar * -atk_dbus_desktop_object_path () -{ - return g_strdup (SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_DESKTOP); -} - -/*---------------------------------------------------------------------------*/ - -typedef gboolean (*TreeUpdateAction) (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data, - AtkObject *accessible); - -/* - * 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. - */ -/* TODO Turn this function into a macro? */ -static gboolean -tree_update_wrapper (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data, - TreeUpdateAction action) -{ - AtkObject *accessible; - - g_static_rec_mutex_lock (®istration_mutex); - - /* Ensure that only registered accessibles - * have their signals processed. - */ - accessible = ATK_OBJECT(g_value_get_object (¶m_values[0])); - g_return_val_if_fail (ATK_IS_OBJECT (accessible), TRUE); - - if (object_to_ref (accessible)) - { -#ifdef SPI_ATK_DEBUG - if (recursion_check_and_set ()) - g_warning ("AT-SPI: Recursive use of registration module"); - - g_debug ("AT-SPI: Tree update listener"); -#endif - action (signal_hint, n_param_values, param_values, data, accessible); - - recursion_check_unset (); - } - - g_static_rec_mutex_unlock (®istration_mutex); - - return TRUE; -} - -static gboolean -tree_update_state_action (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data, - AtkObject *accessible) -{ - update_accessible (accessible); - return TRUE; -} - -static gboolean -tree_update_state_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) -{ - tree_update_wrapper (signal_hint, n_param_values, param_values, data, tree_update_state_action); - return TRUE; -} - -static gboolean -tree_update_property_action (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data, - AtkObject *accessible) -{ - AtkPropertyValues *values; - const gchar *pname = NULL; - - 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 || - strcmp (pname, "accessible-parent") == 0) - { - update_accessible (accessible); - } - /* Parent value us updated by child-add signal of parent object */ - return TRUE; -} - -static gboolean -tree_update_property_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) -{ - tree_update_wrapper (signal_hint, n_param_values, param_values, data, tree_update_property_action); - return TRUE; -} - -static gboolean -tree_update_children_action (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data, - AtkObject *accessible) -{ - const gchar *detail = NULL; - AtkObject *child; - - if (has_manages_descendants (accessible)) return; - 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); - update_accessible (accessible); - } - return TRUE; -} - -static gboolean -tree_update_children_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) -{ - tree_update_wrapper (signal_hint, n_param_values, param_values, data, tree_update_children_action); - return TRUE; -} - -/*---------------------------------------------------------------------------*/ - -static void -spi_atk_register_toplevel_added (AtkObject *accessible, - guint index, - AtkObject *child) -{ - g_static_rec_mutex_lock (®istration_mutex); + if (gobj == NULL) + return NULL; - g_return_if_fail (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"); - - g_debug ("AT-SPI: Toplevel added listener"); -#endif - 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); - update_accessible (accessible); - - recursion_check_unset (); + register_object (reg, gobj); + ref = object_to_ref (gobj); } - g_static_rec_mutex_unlock (®istration_mutex); + if (!ref) + return NULL; + else + return ref_to_path (ref); } -static void -spi_atk_register_toplevel_removed (AtkObject *accessible, - guint index, - AtkObject *child) +guint +spi_register_object_to_ref (GObject * gobj) { - g_static_rec_mutex_lock (®istration_mutex); - - g_return_if_fail (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"); - - g_debug ("AT-SPI: Toplevel removed listener"); -#endif - update_accessible (accessible); - recursion_check_unset (); - } - - g_static_rec_mutex_unlock (®istration_mutex); + 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"); - - g_debug ("AT-SPI: Initial Atk tree regisration"); -#endif - - register_subtree (root); - - atk_add_global_event_listener (tree_update_property_listener, "Gtk:AtkObject:property-change"); - atk_add_global_event_listener (tree_update_children_listener, "Gtk:AtkObject:children-changed"); - atk_add_global_event_listener (tree_update_state_listener, "Gtk:AtkObject:state-change"); - - g_signal_connect (root, - "children-changed::add", - (GCallback) spi_atk_register_toplevel_added, - NULL); - g_signal_connect (root, - "children-changed::remove", - (GCallback) spi_atk_register_toplevel_removed, - NULL); + return g_strdup (SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT); } /*END------------------------------------------------------------------------*/