Fix memory leak in atspi_accessible_get_role_name
[platform/upstream/at-spi2-core.git] / atspi / atspi-event-listener.c
index d4761ee..e545bda 100644 (file)
@@ -31,6 +31,7 @@ typedef struct
   AtspiEventListenerCB callback;
   void *user_data;
   GDestroyNotify callback_destroyed;
+  char *event_type;
   char *category;
   char *name;
   char *detail;
@@ -116,7 +117,7 @@ callback_unref (gpointer callback)
 
 /**
  * atspi_event_listener_new:
- * @callback: (scope notified): An #AtspiEventListenerSimpleCB to be called
+ * @callback: (scope notified): An #AtspiEventListenerCB to be called
  * when an event is fired.
  * @user_data: (closure): data to pass to the callback.
  * @callback_destroyed: A #GDestroyNotify called when the listener is freed
@@ -209,9 +210,9 @@ cache_process_children_changed (AtspiEvent *event)
   {
     if (g_list_find (event->source->children, child))
       return;
-    GList *new_list = g_list_insert (event->source->children, g_object_ref (child), event->detail1);
-    if (new_list)
-      event->source->children = new_list;
+    event->source->children = g_list_insert (event->source->children,
+                                             g_object_ref (child),
+                                             event->detail1);
   }
   else if (g_list_find (event->source->children, child))
   {
@@ -270,6 +271,18 @@ cache_process_property_change (AtspiEvent *event)
       event->source->cached_properties &= ~ATSPI_CACHE_DESCRIPTION;
     }
   }
+  else if (!strcmp (event->type, "object:property-change:accessible-role"))
+  {
+    if (G_VALUE_HOLDS_INT (&event->any_data))
+    {
+      event->source->role = g_value_get_int (&event->any_data);
+      _atspi_accessible_add_cache (event->source, ATSPI_CACHE_ROLE);
+    }
+    else
+    {
+      event->source->cached_properties &= ~ATSPI_CACHE_ROLE;
+    }
+  }
 }
 
 static void
@@ -345,12 +358,10 @@ convert_event_type_to_dbus (const char *eventType, char **categoryp, char **name
   if (tmp == NULL) return FALSE;
   category = strtok_r (tmp, ":", &saveptr);
   if (category) category = g_strdup (category);
-  if (!category) goto oom;
   name = strtok_r (NULL, ":", &saveptr);
   if (name)
   {
     name = g_strdup (name);
-    if (!name) goto oom;
     detail = strtok_r (NULL, ":", &saveptr);
     if (detail) detail = g_strdup (detail);
   }
@@ -384,18 +395,13 @@ convert_event_type_to_dbus (const char *eventType, char **categoryp, char **name
   else if (detail) g_free (detail);
   g_free (tmp);
   return TRUE;
-oom:
-  if (tmp) g_free (tmp);
-  if (category) g_free (category);
-  if (name) g_free (name);
-  if (detail) g_free (detail);
-  return FALSE;
 }
 
 static void
 listener_entry_free (EventListenerEntry *e)
 {
   gpointer callback = (e->callback == remove_datum ? (gpointer)e->user_data : (gpointer)e->callback);
+  g_free (e->event_type);
   g_free (e->category);
   g_free (e->name);
   if (e->detail) g_free (e->detail);
@@ -507,6 +513,25 @@ atspi_event_listener_register (AtspiEventListener *listener,
                                                       event_type, error);
 }
 
+static gboolean
+notify_event_registered (EventListenerEntry *e)
+{
+  DBusMessage *message, *reply;
+
+  message = dbus_message_new_method_call (atspi_bus_registry,
+       atspi_path_registry,
+       atspi_interface_registry,
+       "RegisterEvent");
+  if (!message)
+    return FALSE;
+  dbus_message_append_args (message, DBUS_TYPE_STRING, &e->event_type, DBUS_TYPE_INVALID);
+  reply = _atspi_dbus_send_with_reply_and_block (message, NULL);
+
+  if (reply)
+    dbus_message_unref (reply);
+  return TRUE;
+}
+
 /**
  * atspi_event_listener_register_from_callback:
  * @callback: (scope notified): the #AtspiEventListenerCB to be registered 
@@ -531,8 +556,6 @@ atspi_event_listener_register_from_callback (AtspiEventListenerCB callback,
 {
   EventListenerEntry *e;
   DBusError d_error;
-  GList *new_list;
-  DBusMessage *message, *reply;
   GPtrArray *matchrule_array;
   gint i;
 
@@ -548,6 +571,7 @@ atspi_event_listener_register_from_callback (AtspiEventListenerCB callback,
   }
 
   e = g_new (EventListenerEntry, 1);
+  e->event_type = g_strdup (event_type);
   e->callback = callback;
   e->user_data = user_data;
   e->callback_destroyed = callback_destroyed;
@@ -558,42 +582,40 @@ atspi_event_listener_register_from_callback (AtspiEventListenerCB callback,
     g_free (e);
     return FALSE;
   }
-  new_list = g_list_prepend (event_listeners, e);
-  if (!new_list)
-  {
-    listener_entry_free (e);
-    return FALSE;
-  }
-  event_listeners = new_list;
-  dbus_error_init (&d_error);
+  event_listeners = g_list_prepend (event_listeners, e);
   for (i = 0; i < matchrule_array->len; i++)
   {
     char *matchrule = g_ptr_array_index (matchrule_array, i);
+    dbus_error_init (&d_error);
     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);
+        dbus_error_free (&d_error);
+        /* TODO: Set error */
+      }
+
     g_free (matchrule);
   }
   g_ptr_array_free (matchrule_array, TRUE);
-  if (d_error.message)
-  {
-    g_warning ("Atspi: Adding match: %s", d_error.message);
-    /* TODO: Set error */
-  }
-
-  dbus_error_init (&d_error);
-  message = dbus_message_new_method_call (atspi_bus_registry,
-       atspi_path_registry,
-       atspi_interface_registry,
-       "RegisterEvent");
-  if (!message)
-    return FALSE;
-  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)
-    dbus_message_unref (reply);
 
+  notify_event_registered (e);
   return TRUE;
 }
 
+void
+_atspi_reregister_event_listeners ()
+{
+  GList *l;
+  EventListenerEntry *e;
+
+  for (l = event_listeners; l; l = l->next)
+    {
+      e = l->data;
+      notify_event_registered (e);
+    }
+}
+
 /**
  * atspi_event_listener_register_no_data:
  * @callback: (scope notified): the #AtspiEventListenerSimpleCB to be
@@ -695,21 +717,16 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
         is_superset (detail, e->detail))
     {
       gboolean need_replace;
-      DBusError d_error;
       DBusMessage *message, *reply;
       need_replace = (l == event_listeners);
       l = g_list_remove (l, e);
       if (need_replace)
         event_listeners = l;
-      dbus_error_init (&d_error);
-  for (i = 0; i < matchrule_array->len; i++)
-  {
-    char *matchrule = g_ptr_array_index (matchrule_array, i);
-    dbus_bus_remove_match (_atspi_bus(), matchrule, &d_error);
-    g_free (matchrule);
-  }
-  g_ptr_array_free (matchrule_array, TRUE);
-      dbus_error_init (&d_error);
+      for (i = 0; i < matchrule_array->len; i++)
+      {
+       char *matchrule = g_ptr_array_index (matchrule_array, i);
+       dbus_bus_remove_match (_atspi_bus(), matchrule, NULL);
+      }
       message = dbus_message_new_method_call (atspi_bus_registry,
            atspi_path_registry,
            atspi_interface_registry,
@@ -728,6 +745,9 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
   g_free (category);
   g_free (name);
   if (detail) g_free (detail);
+  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;
 }
 
@@ -781,6 +801,9 @@ detail_matches_listener (const char *event_detail, const char *listener_detail)
   if (!listener_detail)
     return TRUE;
 
+  if (!event_detail)
+    return (listener_detail ? FALSE : TRUE);
+
   return !(listener_detail [strcspn (listener_detail, ":")] == '\0'
                ? strncmp (listener_detail, event_detail,
                           strcspn (event_detail, ":"))
@@ -792,6 +815,7 @@ _atspi_send_event (AtspiEvent *e)
 {
   char *category, *name, *detail;
   GList *l;
+  GList *called_listeners = NULL;
 
   /* Ensure that the value is set to avoid a Python exception */
   /* TODO: Figure out how to do this without using a private field */
@@ -813,12 +837,24 @@ _atspi_send_event (AtspiEvent *e)
         (entry->name == NULL || !strcmp (name, entry->name)) &&
         detail_matches_listener (detail, entry->detail))
     {
+      GList *l2;
+      for (l2 = called_listeners; l2; l2 = l2->next)
+      {
+        EventListenerEntry *e2 = l2->data;
+        if (entry->callback == e2->callback && entry->user_data == e2->user_data)
+          break;
+      }
+      if (!l2)
+      {
         entry->callback (atspi_event_copy (e), entry->user_data);
+        called_listeners = g_list_prepend (called_listeners, entry);
+      }
     }
   }
   if (detail) g_free (detail);
   g_free (name);
   g_free (category);
+  g_list_free (called_listeners);
 }
 
 DBusHandlerResult
@@ -906,7 +942,8 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
        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);
-       g_object_unref (accessible);    /* value now owns it */
+       if (accessible)
+         g_object_unref (accessible);  /* value now owns it */
       }
       break;
     }
@@ -933,6 +970,11 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
   {
     cache_process_state_changed (&e);
   }
+  else if (!strncmp (e.type, "focus", 5))
+  {
+    /* BGO#663992 - TODO: figure out the real problem */
+    e.source->cached_properties &= ~(ATSPI_CACHE_STATES);
+  }
 
   _atspi_send_event (&e);