Fix memory management issues
[platform/upstream/at-spi2-core.git] / atspi / atspi-event-listener.c
index 718c0d2..e7e7116 100644 (file)
@@ -7,24 +7,25 @@
  * Copyright 2010, 2011 Novell, Inc.
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 #include "atspi-private.h"
 #include "atspi-accessible-private.h"
 #include <string.h>
+#include <strings.h>
 #include <ctype.h>
 
 typedef struct
@@ -101,7 +102,7 @@ callback_unref (gpointer callback)
   info = g_hash_table_lookup (callbacks, callback);
   if (!info)
   {
-    g_warning ("Atspi: Dereferencing invalid callback %p\n", callback);
+    g_warning ("AT-SPI: Dereferencing invalid callback %p\n", callback);
     return;
   }
   info->ref_count--;
@@ -169,12 +170,15 @@ static GList *event_listeners = NULL;
 static gchar *
 convert_name_from_dbus (const char *name, gboolean path_hack)
 {
-  gchar *ret = g_malloc (g_utf8_strlen (name, -1) * 2 + 1);
+  gchar *ret;
   const char *p = name;
-  gchar *q = ret;
+  gchar *q;
 
-  if (!ret)
-    return NULL;
+  if (!name)
+    return g_strdup ("");
+
+ ret = g_malloc (g_utf8_strlen (name, -1) * 2 + 1);
+  q = ret;
 
   while (*p)
   {
@@ -378,14 +382,14 @@ convert_event_type_to_dbus (const char *eventType, char **categoryp, char **name
   if (matchrule_array)
   {
     gchar *matchrule;
+    (*matchrule_array) = g_ptr_array_new ();
     matchrule = g_strdup_printf ("type='signal',interface='org.a11y.atspi.Event.%s'", category);
     if (name && name [0])
     {
-      gchar *new_str = g_strconcat (matchrule, ",member='", name, "'", NULL);
-      g_free (matchrule);
-      matchrule = new_str;
+             gchar *new_str = g_strconcat (matchrule, ",member='", name, "'", NULL);
+             g_free (matchrule);
+             matchrule = new_str;
     }
-    (*matchrule_array) = g_ptr_array_new ();
     if (detail && detail [0])
     {
       gchar *new_str = g_strconcat (matchrule, ",arg0='", detail, "'", NULL);
@@ -467,6 +471,9 @@ listener_entry_free (EventListenerEntry *e)
  *            object:model-changed
  *            object:active-descendant-changed
  *
+ *  (screen reader events)
+*             screen-reader:region-changed
+ *
  *  (window events)
  *
  *            window:minimize
@@ -506,6 +513,9 @@ listener_entry_free (EventListenerEntry *e)
  *            In general, listening to
  *            toolkit-specific events is not recommended.
  *
+ * Currently, object:text-reading-position needs to be specified explicitly
+ * (it is not implied by object:text), since it is generated by the screen
+ * reader and is thus a special case internally.
  *
  * Returns: #TRUE if successful, otherwise #FALSE.
  **/
@@ -622,6 +632,18 @@ copy_event_properties (GArray *src)
   return dst;
 }
 
+/**
+ * atspi_event_listener_register_from_callback_full:
+ * @callback: (scope async): an #AtspiEventListenerCB function pointer.
+ * @user_data: (closure callback)
+ * @callback_destroyed: (destroy callback)
+ * @event_type:
+ * @properties: (element-type utf8)
+ * @error:
+ *
+ * Returns: #TRUE if successful, otherwise #FALSE.
+ *
+ **/
 gboolean
 atspi_event_listener_register_from_callback_full (AtspiEventListenerCB callback,
                                                  void *user_data,
@@ -667,7 +689,7 @@ atspi_event_listener_register_from_callback_full (AtspiEventListenerCB callback,
     dbus_bus_add_match (_atspi_bus(), matchrule, &d_error);
     if (dbus_error_is_set (&d_error))
       {
-        g_warning ("Atspi: Adding match: %s", d_error.message);
+        g_warning ("AT-SPI: Adding match: %s", d_error.message);
         dbus_error_free (&d_error);
         /* TODO: Set error */
       }
@@ -774,15 +796,16 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
   GPtrArray *matchrule_array;
   gint i;
   GList *l;
+  gboolean result = TRUE;
 
+  if (!callback)
+  {
+    return FALSE;
+  }
   if (!convert_event_type_to_dbus (event_type, &category, &name, &detail, &matchrule_array))
   {
     return FALSE;
   }
-  if (!callback)
-    {
-      return FALSE;
-    }
 
   for (l = event_listeners; l;)
   {
@@ -808,8 +831,10 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
            atspi_path_registry,
            atspi_interface_registry,
            "DeregisterEvent");
-      if (!message)
-      return FALSE;
+      if (!message) {
+        result = FALSE;
+        break;
+      }
       dbus_message_append_args (message, DBUS_TYPE_STRING, &event_type, DBUS_TYPE_INVALID);
       reply = _atspi_dbus_send_with_reply_and_block (message, error);
       if (reply)
@@ -825,7 +850,7 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
   for (i = 0; i < matchrule_array->len; i++)
     g_free (g_ptr_array_index (matchrule_array, i));
   g_ptr_array_free (matchrule_array, TRUE);
-  return TRUE;
+  return result;
 }
 
 /**
@@ -860,6 +885,7 @@ atspi_event_copy (AtspiEvent *src)
   dst->detail2 = src->detail2;
   g_value_init (&dst->any_data, G_VALUE_TYPE (&src->any_data));
   g_value_copy (&src->any_data, &dst->any_data);
+  dst->sender = g_object_ref (src->sender);
   return dst;
 }
 
@@ -869,6 +895,7 @@ atspi_event_free (AtspiEvent *event)
   g_object_unref (event->source);
   g_free (event->type);
   g_value_unset (&event->any_data);
+  g_object_unref (event->sender);
   g_free (event);
 }
 
@@ -879,7 +906,7 @@ detail_matches_listener (const char *event_detail, const char *listener_detail)
     return TRUE;
 
   if (!event_detail)
-    return (listener_detail ? FALSE : TRUE);
+    return FALSE;
 
   return !(listener_detail [strcspn (listener_detail, ":")] == '\0'
                ? strncmp (listener_detail, event_detail,
@@ -904,7 +931,7 @@ _atspi_send_event (AtspiEvent *e)
 
   if (!convert_event_type_to_dbus (e->type, &category, &name, &detail, NULL))
   {
-    g_warning ("Atspi: Couldn't parse event: %s\n", e->type);
+    g_warning ("AT-SPI: Couldn't parse event: %s\n", e->type);
     return;
   }
   for (l = event_listeners; l; l = g_list_next (l))
@@ -939,6 +966,7 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
 {
   char *detail = NULL;
   const char *category = dbus_message_get_interface (message);
+  const char *sender = dbus_message_get_sender (message);
   const char *member = dbus_message_get_member (message);
   const char *signature = dbus_message_get_signature (message);
   gchar *name;
@@ -953,22 +981,26 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
   if (strcmp (signature, "siiv(so)") != 0 &&
       strcmp (signature, "siiva{sv}") != 0)
   {
-    g_warning ("Got invalid signature %s for signal %s from interface %s\n", signature, member, category);
+   g_warning ("Got invalid signature %s for signal %s from interface %s\n", signature, member, category);
     return DBUS_HANDLER_RESULT_HANDLED;
   }
 
   memset (&e, 0, sizeof (e));
 
-  if (category)
+  if (!category)
   {
-    category = g_utf8_strrchr (category, -1, '.');
-    if (category == NULL)
-    {
-      // TODO: Error
-      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-    }
-    category++;
+    // TODO: Error
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
   }
+
+  category = g_utf8_strrchr (category, -1, '.');
+  if (category == NULL)
+  {
+    // TODO: Error
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+  category++;
+
   dbus_message_iter_get_basic (&iter, &detail);
   dbus_message_iter_next (&iter);
   dbus_message_iter_get_basic (&iter, &detail1);
@@ -1002,14 +1034,17 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
     converted_type = p;
   }
   e.type = converted_type;
-  e.source = _atspi_ref_accessible (dbus_message_get_sender(message), dbus_message_get_path(message));
-  if (e.source == NULL)
+  if (strcmp (category, "ScreenReader") != 0)
   {
-    g_warning ("Got no valid source accessible for signal for signal %s from interface %s\n", member, category);
-    g_free (converted_type);
-    g_free (name);
-    g_free (detail);
-    return DBUS_HANDLER_RESULT_HANDLED;
+    e.source = _atspi_ref_accessible (sender, dbus_message_get_path (message));
+    if (e.source == NULL)
+    {
+      g_warning ("Got no valid source accessible for signal %s from interface %s\n", member, category);
+      g_free (converted_type);
+      g_free (name);
+      g_free (detail);
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
   }
 
   dbus_message_iter_recurse (&iter, &iter_variant);
@@ -1020,17 +1055,32 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
       AtspiRect rect;
       if (demarshal_rect (&iter_variant, &rect))
       {
-       g_value_init (&e.any_data, ATSPI_TYPE_RECT);
-       g_value_set_boxed (&e.any_data, &rect);
+        g_value_init (&e.any_data, ATSPI_TYPE_RECT);
+        g_value_set_boxed (&e.any_data, &rect);
       }
       else
       {
         AtspiAccessible *accessible;
-       accessible = _atspi_dbus_return_accessible_from_iter (&iter_variant);
-       g_value_init (&e.any_data, ATSPI_TYPE_ACCESSIBLE);
-       g_value_set_instance (&e.any_data, accessible);
-       if (accessible)
-         g_object_unref (accessible);  /* value now owns it */
+       accessible = _atspi_dbus_return_accessible_from_iter (&iter_variant);
+        if (!strcmp (category, "ScreenReader"))
+        {
+          e.source = accessible;
+          if (e.source == NULL)
+          {
+            g_warning ("Got no valid source accessible for signal %s from interface %s\n", member, category);
+            g_free (converted_type);
+            g_free (name);
+            g_free (detail);
+            return DBUS_HANDLER_RESULT_HANDLED;
+          }
+        }
+        else
+        {
+          g_value_init (&e.any_data, ATSPI_TYPE_ACCESSIBLE);
+          g_value_set_instance (&e.any_data, accessible);
+          if (accessible)
+            g_object_unref (accessible);       /* value now owns it */
+        }
       }
       break;
     }
@@ -1052,6 +1102,8 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
     cache = _atspi_dbus_update_cache_from_dict (e.source, &iter);
   }
 
+  e.sender = _atspi_ref_accessible (sender, ATSPI_DBUS_PATH_ROOT);
+
   if (!strncmp (e.type, "object:children-changed", 23))
   {
     cache_process_children_changed (&e);