X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=atk-adaptor%2Faccessible-register.c;h=793e0eaf69e25309130ca04a533f34be7846f041;hb=f35ba2e4b28768ab3f6a07403a3bd360162094ed;hp=8b8e34a26919711c6e5a71d1ce83552a2e20ec0f;hpb=e09291e0c5ea8f7ca59f88868657b49a9e17b57b;p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git diff --git a/atk-adaptor/accessible-register.c b/atk-adaptor/accessible-register.c index 8b8e34a..793e0ea 100644 --- a/atk-adaptor/accessible-register.c +++ b/atk-adaptor/accessible-register.c @@ -26,12 +26,9 @@ #include #include "bridge.h" +#include "tree-adaptor.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 +36,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'. @@ -59,22 +56,29 @@ /* * 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. + * 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. * - * Things could also be changed that do not cause signal emission, - * but do cause a failure. Not sure what these would be. + * This is the same system as Qt has with the QAccessibleBridge + * and QAccessibleBridgePlugin. It entails some rather + * large structural changes to ATK though: * - * 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. + * Removing infinite spaces (Child access no longer references child). + * Removing lazy creation of accessible objects. */ +#define SPI_ATK_OBJECT_PATH_PREFIX "/org/freedesktop/atspi/accessible" +#define SPI_ATK_OBJECT_PATH_DESKTOP "/root" + +#define SPI_ATK_PATH_PREFIX_LENGTH 33 +#define SPI_ATK_OBJECT_REFERENCE_TEMPLATE SPI_ATK_OBJECT_PATH_PREFIX "/%d" + + static GHashTable *ref2ptr = NULL; /* Used for converting a D-Bus path (Reference) to the object pointer */ -static guint counter = 1; +static guint reference_counter = 0; static GStaticRecMutex registration_mutex = G_STATIC_REC_MUTEX_INIT; @@ -113,10 +117,11 @@ recursion_check_unset () static guint assign_reference(void) { - counter++; + reference_counter++; /* Reference of 0 not allowed as used as direct key in hash table */ - if (counter == 0) - counter++; + if (reference_counter == 0) + reference_counter++; + return reference_counter; } /* @@ -131,10 +136,10 @@ object_to_ref (AtkObject *accessible) /* * Converts the Accessible object reference to its D-Bus object path */ -static gchar * -ref_to_path (guint ref) +gchar * +atk_dbus_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); } /*---------------------------------------------------------------------------*/ @@ -149,11 +154,12 @@ static void deregister_accessible (gpointer data, GObject *accessible) { guint ref; - g_assert (ATK_IS_OBJECT (accessible)); + g_return_if_fail (ATK_IS_OBJECT (accessible)); ref = object_to_ref (ATK_OBJECT(accessible)); if (ref != 0) { + spi_emit_cache_removal (ref, atk_adaptor_app_data->bus); g_hash_table_remove(ref2ptr, GINT_TO_POINTER(ref)); } } @@ -165,7 +171,7 @@ static void register_accessible (AtkObject *accessible) { guint ref; - g_assert(ATK_IS_OBJECT(accessible)); + g_return_if_fail (ATK_IS_OBJECT(accessible)); ref = assign_reference(); @@ -176,6 +182,7 @@ register_accessible (AtkObject *accessible) /*---------------------------------------------------------------------------*/ +#ifdef SPI_ATK_DEBUG /* * This function checks that the ref-count of an accessible * is greater than 1. @@ -199,6 +206,7 @@ non_owned_accessible (AtkObject *accessible) return FALSE; } } +#endif /* SPI_ATK_DEBUG */ /*---------------------------------------------------------------------------*/ @@ -215,7 +223,9 @@ has_manages_descendants (AtkObject *accessible) 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); @@ -223,6 +233,30 @@ has_manages_descendants (AtkObject *accessible) 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); +#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. @@ -235,87 +269,43 @@ has_manages_descendants (AtkObject *accessible) void register_subtree (AtkObject *accessible) { - AtkObject *current, *tmp; - GQueue *stack; - guint i; - gboolean recurse; + AtkObject *current; + GQueue *traversal; + GQueue *emit_update; + g_return_if_fail (ATK_IS_OBJECT (accessible)); - current = g_object_ref (accessible); - if (has_manages_descendants (current)) - { - g_object_unref (current); - return; - } + traversal = g_queue_new (); + emit_update = g_queue_new (); - stack = g_queue_new (); + g_object_ref (accessible); + g_queue_push_tail (traversal, accessible); - 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)) + while (!g_queue_is_empty (traversal)) { - /* Find the next child node that needs processing */ - - i = GPOINTER_TO_INT(g_queue_peek_head (stack)); - recurse = FALSE; - - while (i < atk_object_get_n_accessible_children (current) && - recurse == FALSE) + current = g_queue_pop_head (traversal); + g_queue_push_tail (emit_update, current); + if (!object_to_ref (current)) { - tmp = atk_object_ref_accessible_child (current, i); - - /* TODO Add debug wrapper */ - non_owned_accessible (tmp); - - 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); +#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)); +#endif + append_children (current, traversal); } } - g_queue_free (stack); + + 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); } /*---------------------------------------------------------------------------*/ @@ -328,7 +318,7 @@ static void update_accessible (AtkObject *accessible) { guint ref = 0; - g_assert(ATK_IS_OBJECT(accessible)); + g_return_if_fail (ATK_IS_OBJECT(accessible)); ref = object_to_ref (accessible); if (ref) @@ -354,14 +344,14 @@ atk_dbus_path_to_object (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') + if (!g_strcmp0 (SPI_ATK_OBJECT_PATH_DESKTOP, path)) return atk_get_root(); if (path[0] != '/') return NULL; @@ -376,6 +366,63 @@ atk_dbus_path_to_object (const char *path) } /* + * 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) +{ + 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); + } +} + + +/* * Used to lookup a D-Bus path from the AtkObject. */ gchar * @@ -387,11 +434,23 @@ atk_dbus_object_to_path (AtkObject *accessible) if (!ref) return NULL; else - return ref_to_path (ref); + return atk_dbus_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. * @@ -401,38 +460,33 @@ atk_dbus_object_to_path (AtkObject *accessible) * 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_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +tree_update_wrapper (GSignalInvocationHint *signal_hint, + guint n_param_values, + const GValue *param_values, + gpointer data, + TreeUpdateAction action) { AtkObject *accessible; - AtkPropertyValues *values; - const gchar *pname = NULL; 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)); + 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)) { - /* TODO Add debug wrapper */ +#ifdef SPI_ATK_DEBUG if (recursion_check_and_set ()) g_warning ("AT-SPI: Recursive use of registration module"); - 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 */ + g_debug ("AT-SPI: Tree update listener"); +#endif + action (signal_hint, n_param_values, param_values, data, accessible); recursion_check_unset (); } @@ -442,36 +496,70 @@ tree_update_listener (GSignalInvocationHint *signal_hint, return TRUE; } -/* - * 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, +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) { - 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)); + tree_update_wrapper (signal_hint, n_param_values, param_values, data, tree_update_property_action); + return TRUE; +} - if (object_to_ref (accessible)) - { - /* TODO Add debug wrapper */ - if (recursion_check_and_set ()) - g_warning ("AT-SPI: Recursive use of registration module"); +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 TRUE; if (signal_hint->detail) detail = g_quark_to_string (signal_hint->detail); @@ -484,18 +572,83 @@ tree_update_children_listener (GSignalInvocationHint *signal_hint, if (!ATK_IS_OBJECT (child)) { child = atk_object_ref_accessible_child (accessible, index); - /* TODO Add debug wrapper */ +#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); + + 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 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 (); } g_static_rec_mutex_unlock (®istration_mutex); +} - return TRUE; +static void +spi_atk_register_toplevel_removed (AtkObject *accessible, + guint index, + AtkObject *child) +{ + 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); } /* @@ -510,14 +663,27 @@ atk_dbus_initialize (AtkObject *root) 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_listener, "Gtk:AtkObject:property-change"); + 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); } /*END------------------------------------------------------------------------*/ -