Protecting against potential NULL pointer dereference
[platform/upstream/at-spi2-core.git] / atspi / atspi-event-listener.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2002 Ximian Inc.
6  * Copyright 2002 Sun Microsystems, Inc.
7  * Copyright 2010, 2011 Novell, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24
25 #include "atspi-private.h"
26 #include "atspi-accessible-private.h"
27 #include <string.h>
28 #include <strings.h>
29 #include <ctype.h>
30
31 typedef struct
32 {
33   AtspiEventListenerCB callback;
34   void *user_data;
35   GDestroyNotify callback_destroyed;
36   char *event_type;
37   char *category;
38   char *name;
39   char *detail;
40   GArray *properties;
41 } EventListenerEntry;
42
43 G_DEFINE_TYPE (AtspiEventListener, atspi_event_listener, G_TYPE_OBJECT)
44
45 void
46 atspi_event_listener_init (AtspiEventListener *listener)
47 {
48 }
49
50 void
51 atspi_event_listener_class_init (AtspiEventListenerClass *klass)
52 {
53 }
54
55 static void
56 remove_datum (AtspiEvent *event, void *user_data)
57 {
58   AtspiEventListenerSimpleCB cb = user_data;
59   cb (event);
60 }
61
62 typedef struct
63 {
64   gpointer callback;
65   GDestroyNotify callback_destroyed;
66   gint ref_count;
67 } CallbackInfo;
68 static GHashTable *callbacks;
69
70 void
71 callback_ref (void *callback, GDestroyNotify callback_destroyed)
72 {
73   CallbackInfo *info;
74
75   if (!callbacks)
76   {
77     callbacks = g_hash_table_new (g_direct_hash, g_direct_equal);
78     if (!callbacks)
79       return;
80   }
81
82   info = g_hash_table_lookup (callbacks, callback);
83   if (!info)
84   {
85     info = g_new (CallbackInfo, 1);
86     info->callback = callback;
87     info->callback_destroyed = callback_destroyed;
88     info->ref_count = 1;
89     g_hash_table_insert (callbacks, callback, info);
90   }
91   else
92     info->ref_count++;
93 }
94
95 void
96 callback_unref (gpointer callback)
97 {
98   CallbackInfo *info;
99
100   if (!callbacks)
101     return;
102   info = g_hash_table_lookup (callbacks, callback);
103   if (!info)
104   {
105     g_warning ("AT-SPI: Dereferencing invalid callback %p\n", callback);
106     return;
107   }
108   info->ref_count--;
109   if (info->ref_count == 0)
110   {
111 #if 0
112     /* TODO: Figure out why this seg faults from Python */
113     if (info->callback_destroyed)
114       (*info->callback_destroyed) (info->callback);
115 #endif
116     g_free (info);
117     g_hash_table_remove (callbacks, callback);
118   }
119 }
120
121 /**
122  * atspi_event_listener_new:
123  * @callback: (scope notified): An #AtspiEventListenerCB to be called
124  * when an event is fired.
125  * @user_data: (closure): data to pass to the callback.
126  * @callback_destroyed: A #GDestroyNotify called when the listener is freed
127  * and data associated with the callback should be freed.  Can be NULL.
128  *
129  * Creates a new #AtspiEventListener associated with a specified @callback.
130  *
131  * Returns: (transfer full): A new #AtspiEventListener.
132  */
133 AtspiEventListener *
134 atspi_event_listener_new (AtspiEventListenerCB callback,
135                                  gpointer user_data,
136                                  GDestroyNotify callback_destroyed)
137 {
138   AtspiEventListener *listener = g_object_new (ATSPI_TYPE_EVENT_LISTENER, NULL);
139   listener->callback = callback;
140   callback_ref (callback, callback_destroyed);
141   listener->user_data = user_data;
142   listener->cb_destroyed = callback_destroyed;
143   return listener;
144 }
145
146 /**
147  * atspi_event_listener_new_simple: (skip)
148  * @callback: (scope notified): An #AtspiEventListenerSimpleCB to be called
149  * when an event is fired.
150  * @callback_destroyed: A #GDestroyNotify called when the listener is freed
151  * and data associated with the callback should be freed.  Can be NULL.
152  *
153  * Creates a new #AtspiEventListener associated with a specified @callback.
154  * Returns: (transfer full): A new #AtspiEventListener.
155  **/
156 AtspiEventListener *
157 atspi_event_listener_new_simple (AtspiEventListenerSimpleCB callback,
158                                  GDestroyNotify callback_destroyed)
159 {
160   AtspiEventListener *listener = g_object_new (ATSPI_TYPE_EVENT_LISTENER, NULL);
161   listener->callback = remove_datum;
162   callback_ref (remove_datum, callback_destroyed);
163   listener->user_data = callback;
164   listener->cb_destroyed = callback_destroyed;
165   return listener;
166 }
167
168 static GList *event_listeners = NULL;
169 static GList *pending_removals = NULL;
170 static int in_send = 0;
171
172
173 static gchar *
174 convert_name_from_dbus (const char *name, gboolean path_hack)
175 {
176   gchar *ret;
177   const char *p = name;
178   gchar *q;
179
180   if (!name)
181     return g_strdup ("");
182
183  ret = g_malloc (g_utf8_strlen (name, -1) * 2 + 1);
184   q = ret;
185
186   while (*p)
187   {
188     if (isupper (*p))
189     {
190       if (q > ret)
191         *q++ = '-';
192       *q++ = tolower (*p++);
193     }
194     else if (path_hack && *p == '/')
195     {
196       *q++ = ':';
197       p++;
198     }
199     else
200       *q++ = *p++;
201   }
202   *q = '\0';
203   return ret;
204 }
205
206 static void
207 cache_process_children_changed (AtspiEvent *event)
208 {
209   AtspiAccessible *child;
210
211   if (!G_VALUE_HOLDS (&event->any_data, ATSPI_TYPE_ACCESSIBLE) ||
212       !(event->source->cached_properties & ATSPI_CACHE_CHILDREN) ||
213       atspi_state_set_contains (event->source->states, ATSPI_STATE_MANAGES_DESCENDANTS))
214     return;
215
216   child = g_value_get_object (&event->any_data);
217   if (child == NULL)
218     return;
219
220   if (!strncmp (event->type, "object:children-changed:add", 27))
221   {
222     g_ptr_array_remove (event->source->children, child); /* just to be safe */
223     if (event->detail1 < 0 || event->detail1 > event->source->children->len)
224     {
225       event->source->cached_properties &= ~ATSPI_CACHE_CHILDREN;
226       return;
227     }
228     /* Unfortunately, there's no g_ptr_array_insert or similar */
229     g_ptr_array_add (event->source->children, NULL);
230     memmove (event->source->children->pdata + event->detail1 + 1,
231              event->source->children->pdata + event->detail1,
232              (event->source->children->len - event->detail1 - 1) * sizeof (gpointer));
233     g_ptr_array_index (event->source->children, event->detail1) = g_object_ref (child);
234   }
235   else
236   {
237     g_ptr_array_remove (event->source->children, child);
238     if (atspi_accessible_is_equal (child, child->parent.app->root))
239       g_object_run_dispose (G_OBJECT (child->parent.app));
240   }
241 }
242
243 static void
244 cache_process_property_change (AtspiEvent *event)
245 {
246   if (!strcmp (event->type, "object:property-change:accessible-parent"))
247   {
248     if (event->source->accessible_parent)
249       g_object_unref (event->source->accessible_parent);
250     if (G_VALUE_HOLDS (&event->any_data, ATSPI_TYPE_ACCESSIBLE))
251     {
252       event->source->accessible_parent = g_value_dup_object (&event->any_data);
253       _atspi_accessible_add_cache (event->source, ATSPI_CACHE_PARENT);
254     }
255     else
256     {
257       event->source->accessible_parent = NULL;
258       event->source->cached_properties &= ~ATSPI_CACHE_PARENT;
259     }
260   }
261   else if (!strcmp (event->type, "object:property-change:accessible-name"))
262   {
263     if (event->source->name)
264       g_free (event->source->name);
265     if (G_VALUE_HOLDS_STRING (&event->any_data))
266     {
267       event->source->name = g_value_dup_string (&event->any_data);
268       _atspi_accessible_add_cache (event->source, ATSPI_CACHE_NAME);
269     }
270     else
271     {
272       event->source->name = NULL;
273       event->source->cached_properties &= ~ATSPI_CACHE_NAME;
274     }
275   }
276   else if (!strcmp (event->type, "object:property-change:accessible-description"))
277   {
278     if (event->source->description)
279       g_free (event->source->description);
280     if (G_VALUE_HOLDS_STRING (&event->any_data))
281     {
282       event->source->description = g_value_dup_string (&event->any_data);
283       _atspi_accessible_add_cache (event->source, ATSPI_CACHE_DESCRIPTION);
284     }
285     else
286     {
287       event->source->description = NULL;
288       event->source->cached_properties &= ~ATSPI_CACHE_DESCRIPTION;
289     }
290   }
291   else if (!strcmp (event->type, "object:property-change:accessible-role"))
292   {
293     if (G_VALUE_HOLDS_INT (&event->any_data))
294     {
295       event->source->role = g_value_get_int (&event->any_data);
296       _atspi_accessible_add_cache (event->source, ATSPI_CACHE_ROLE);
297     }
298     else
299     {
300       event->source->cached_properties &= ~ATSPI_CACHE_ROLE;
301     }
302   }
303 }
304
305 static void
306 cache_process_state_changed (AtspiEvent *event)
307 {
308   if (event->source->states)
309     atspi_state_set_set_by_name (event->source->states, event->type + 21,
310                                  event->detail1);
311 }
312
313 static dbus_bool_t
314 demarshal_rect (DBusMessageIter *iter, AtspiRect *rect)
315 {
316   dbus_int32_t x, y, width, height;
317   DBusMessageIter iter_struct;
318
319   dbus_message_iter_recurse (iter, &iter_struct);
320   if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
321   dbus_message_iter_get_basic (&iter_struct, &x);
322   dbus_message_iter_next (&iter_struct);
323   if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
324   dbus_message_iter_get_basic (&iter_struct, &y);
325   dbus_message_iter_next (&iter_struct);
326   if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
327   dbus_message_iter_get_basic (&iter_struct, &width);
328   dbus_message_iter_next (&iter_struct);
329   if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
330   dbus_message_iter_get_basic (&iter_struct, &height);
331   rect->x = x;
332   rect->y = y;
333   rect->width = width;
334   rect->height = height;
335   return TRUE;
336 }
337
338 static gboolean
339 convert_event_type_to_dbus (const char *eventType, char **categoryp, char **namep, char **detailp, GPtrArray **matchrule_array)
340 {
341   gchar *tmp = _atspi_strdup_and_adjust_for_dbus (eventType);
342   char *category = NULL, *name = NULL, *detail = NULL;
343   char *saveptr = NULL;
344
345   if (tmp == NULL) return FALSE;
346   category = strtok_r (tmp, ":", &saveptr);
347   if (category) category = g_strdup (category);
348   name = strtok_r (NULL, ":", &saveptr);
349   if (name)
350   {
351     name = g_strdup (name);
352     detail = strtok_r (NULL, ":", &saveptr);
353     if (detail) detail = g_strdup (detail);
354   }
355   if (matchrule_array)
356   {
357     gchar *matchrule;
358     (*matchrule_array) = g_ptr_array_new ();
359     matchrule = g_strdup_printf ("type='signal',interface='org.a11y.atspi.Event.%s'", category);
360     if (name && name [0])
361     {
362              gchar *new_str = g_strconcat (matchrule, ",member='", name, "'", NULL);
363              g_free (matchrule);
364              matchrule = new_str;
365     }
366     if (detail && detail [0])
367     {
368       gchar *new_str = g_strconcat (matchrule, ",arg0='", detail, "'", NULL);
369       g_ptr_array_add (*matchrule_array, new_str);
370       new_str = g_strconcat (matchrule, ",arg0path='", detail, "/'", NULL);
371       g_ptr_array_add (*matchrule_array, new_str);
372       g_free (matchrule);
373     }
374     else
375       g_ptr_array_add (*matchrule_array, matchrule);
376   }
377   if (categoryp) *categoryp = category;
378   else g_free (category);
379   if (namep) *namep = name;
380   else if (name) g_free (name);
381   if (detailp) *detailp = detail;
382   else if (detail) g_free (detail);
383   g_free (tmp);
384   return TRUE;
385 }
386
387 static void
388 listener_entry_free (EventListenerEntry *e)
389 {
390   gpointer callback = (e->callback == remove_datum ? (gpointer)e->user_data : (gpointer)e->callback);
391   g_free (e->event_type);
392   g_free (e->category);
393   g_free (e->name);
394   if (e->detail) g_free (e->detail);
395   callback_unref (callback);
396
397   for (int i=0; i < e->properties->len; i++)
398     g_free (g_array_index (e->properties, char*, i));
399
400   g_array_free (e->properties, TRUE);
401
402   g_free (e);
403 }
404
405 /**
406  * atspi_event_listener_register:
407  * @listener: The #AtspiEventListener to register against an event type.
408  * @event_type: a character string indicating the type of events for which
409  *            notification is requested.  Format is
410  *            EventClass:major_type:minor_type:detail
411  *            where all subfields other than EventClass are optional.
412  *            EventClasses include "object", "window", "mouse",
413  *            and toolkit events (e.g. "Gtk", "AWT").
414  *            Examples: "focus:", "Gtk:GtkWidget:button_press_event".
415  *
416  * Adds an in-process callback function to an existing #AtspiEventListener.
417  *
418  * Legal object event types:
419  *
420  *    (property change events)
421  *
422  *            object:property-change
423  *            object:property-change:accessible-name
424  *            object:property-change:accessible-description
425  *            object:property-change:accessible-parent
426  *            object:property-change:accessible-value
427  *            object:property-change:accessible-role
428  *            object:property-change:accessible-table-caption
429  *            object:property-change:accessible-table-column-description
430  *            object:property-change:accessible-table-column-header
431  *            object:property-change:accessible-table-row-description
432  *            object:property-change:accessible-table-row-header
433  *            object:property-change:accessible-table-summary
434  *
435  *    (other object events)
436  *
437  *            object:state-changed
438  *            object:children-changed
439  *            object:visible-data-changed
440  *            object:selection-changed
441  *            object:text-selection-changed
442  *            object:text-changed
443  *            object:text-caret-moved
444  *            object:row-inserted
445  *            object:row-reordered
446  *            object:row-deleted
447  *            object:column-inserted
448  *            object:column-reordered
449  *            object:column-deleted
450  *            object:model-changed
451  *            object:active-descendant-changed
452  *
453  *  (screen reader events)
454 *             screen-reader:region-changed
455  *
456  *  (window events)
457  *
458  *            window:minimize
459  *            window:maximize
460  *            window:restore
461  *            window:close
462  *            window:create
463  *            window:reparent
464  *            window:desktop-create
465  *            window:desktop-destroy
466  *            window:activate
467  *            window:deactivate
468  *            window:raise
469  *            window:lower
470  *            window:move
471  *            window:resize
472  *            window:shade
473  *            window:unshade
474  *            window:restyle
475  *
476  *  (other events)
477  *
478  *            focus:
479  *            mouse:abs
480  *            mouse:rel
481  *            mouse:b1p
482  *            mouse:b1r
483  *            mouse:b2p
484  *            mouse:b2r
485  *            mouse:b3p
486  *            mouse:b3r
487  *
488  * NOTE: this character string may be UTF-8, but should not contain byte
489  * value 56
490  *            (ascii ':'), except as a delimiter, since non-UTF-8 string
491  *            delimiting functions are used internally.
492  *            In general, listening to
493  *            toolkit-specific events is not recommended.
494  *
495  * Currently, object:text-reading-position needs to be specified explicitly
496  * (it is not implied by object:text), since it is generated by the screen
497  * reader and is thus a special case internally.
498  *
499  * Returns: #TRUE if successful, otherwise #FALSE.
500  **/
501 gboolean
502 atspi_event_listener_register (AtspiEventListener *listener,
503                                              const gchar              *event_type,
504                                              GError **error)
505 {
506   /* TODO: Keep track of which events have been registered, so that we
507  * deregister all of them when the event listener is destroyed */
508
509   return atspi_event_listener_register_from_callback (listener->callback,
510                                                       listener->user_data,
511                                                       listener->cb_destroyed,
512                                                       event_type, error);
513 }
514
515 /**
516  * atspi_event_listener_register_full:
517  * @listener: The #AtspiEventListener to register against an event type.
518  * @event_type: a character string indicating the type of events for which
519  *            notification is requested.  See #atspi_event_listener_register
520  * for a description of the format and legal event types.
521 * @properties: (element-type gchar*) (transfer none) (allow-none): a list of
522  *             properties that should be sent along with the event. The
523  *             properties are valued for the duration of the event callback.k
524  *             TODO: Document.
525  *
526  * Adds an in-process callback function to an existing #AtspiEventListener.
527  *
528  * Returns: #TRUE if successful, otherwise #FALSE.
529  **/
530 gboolean
531 atspi_event_listener_register_full (AtspiEventListener *listener,
532                                              const gchar              *event_type,
533                                              GArray *properties,
534                                              GError **error)
535 {
536   /* TODO: Keep track of which events have been registered, so that we
537  * deregister all of them when the event listener is destroyed */
538
539   return atspi_event_listener_register_from_callback_full (listener->callback,
540                                                            listener->user_data,
541                                                            listener->cb_destroyed,
542                                                            event_type,
543                                                            properties,
544                                                            error);
545 }
546
547 static gboolean
548 notify_event_registered (EventListenerEntry *e)
549 {
550
551   if (e->properties)
552     dbind_method_call_reentrant (_atspi_bus (), atspi_bus_registry,
553                                  atspi_path_registry,
554                                  atspi_interface_registry,
555                                  "RegisterEvent",
556                                  NULL, "sas", e->event_type,
557                                  e->properties);
558   else
559     dbind_method_call_reentrant (_atspi_bus (), atspi_bus_registry,
560                                  atspi_path_registry,
561                                  atspi_interface_registry,
562                                  "RegisterEvent",
563                                  NULL, "s", e->event_type);
564
565   return TRUE;
566 }
567
568 /**
569  * atspi_event_listener_register_from_callback:
570  * @callback: (scope notified): the #AtspiEventListenerCB to be registered
571  * against an event type.
572  * @user_data: (closure): User data to be passed to the callback.
573  * @callback_destroyed: A #GDestroyNotify called when the callback is destroyed.
574  * @event_type: a character string indicating the type of events for which
575  *            notification is requested.  See #atspi_event_listener_register
576  * for a description of the format.
577  *
578  * Registers an #AtspiEventListenerCB against an @event_type.
579  *
580  * Returns: #TRUE if successfull, otherwise #FALSE.
581  *
582  **/
583 gboolean
584 atspi_event_listener_register_from_callback (AtspiEventListenerCB callback,
585                                              void *user_data,
586                                              GDestroyNotify callback_destroyed,
587                                              const gchar              *event_type,
588                                              GError **error)
589 {
590   return atspi_event_listener_register_from_callback_full (callback,
591                                                            user_data,
592                                                            callback_destroyed,
593                                                            event_type, NULL,
594                                                            error);
595 }
596
597 static GArray *
598 copy_event_properties (GArray *src)
599 {
600   gint i;
601
602   GArray *dst = g_array_new (FALSE, FALSE, sizeof (char *));
603
604   if (!src)
605     return dst;
606   for (i = 0; i < src->len; i++)
607     {
608       gchar *dup = g_strdup (g_array_index (src, char *, i));
609       g_array_append_val (dst, dup);
610     }
611   return dst;
612 }
613
614 /**
615  * atspi_event_listener_register_from_callback_full:
616  * @callback: (scope async): an #AtspiEventListenerCB function pointer.
617  * @user_data: (closure callback)
618  * @callback_destroyed: (destroy callback)
619  * @event_type:
620  * @properties: (element-type utf8)
621  * @error:
622  *
623  * Returns: #TRUE if successful, otherwise #FALSE.
624  *
625  **/
626 gboolean
627 atspi_event_listener_register_from_callback_full (AtspiEventListenerCB callback,
628                                                   void *user_data,
629                                                   GDestroyNotify callback_destroyed,
630                                                   const gchar              *event_type,
631                                                   GArray *properties,
632                                                   GError **error)
633 {
634   EventListenerEntry *e;
635   DBusError d_error;
636   GPtrArray *matchrule_array;
637   gint i;
638
639   if (!callback)
640     {
641       return FALSE;
642     }
643
644   if (!event_type)
645   {
646     g_warning ("called atspi_event_listener_register_from_callback with a NULL event_type");
647     return FALSE;
648   }
649
650   e = g_new (EventListenerEntry, 1);
651   e->event_type = g_strdup (event_type);
652   e->callback = callback;
653   e->user_data = user_data;
654   e->callback_destroyed = callback_destroyed;
655   callback_ref (callback == remove_datum ? (gpointer)user_data : (gpointer)callback,
656                 callback_destroyed);
657   if (!convert_event_type_to_dbus (event_type, &e->category, &e->name, &e->detail, &matchrule_array))
658   {
659     g_free (e->event_type);
660     g_free (e);
661     return FALSE;
662   }
663   e->properties = copy_event_properties (properties);
664   event_listeners = g_list_prepend (event_listeners, e);
665   for (i = 0; i < matchrule_array->len; i++)
666   {
667     char *matchrule = g_ptr_array_index (matchrule_array, i);
668     dbus_error_init (&d_error);
669     dbus_bus_add_match (_atspi_bus(), matchrule, &d_error);
670     if (dbus_error_is_set (&d_error))
671       {
672         g_warning ("AT-SPI: Adding match: %s", d_error.message);
673         dbus_error_free (&d_error);
674         /* TODO: Set error */
675       }
676
677     g_free (matchrule);
678   }
679   g_ptr_array_free (matchrule_array, TRUE);
680
681   notify_event_registered (e);
682   return TRUE;
683 }
684
685 void
686 _atspi_reregister_event_listeners ()
687 {
688   GList *l;
689   EventListenerEntry *e;
690
691   for (l = event_listeners; l; l = l->next)
692     {
693       e = l->data;
694       notify_event_registered (e);
695     }
696 }
697
698 /**
699  * atspi_event_listener_register_no_data: (skip)
700  * @callback: (scope notified): the #AtspiEventListenerSimpleCB to be
701  *            registered against an event type.
702  * @callback_destroyed: A #GDestroyNotify called when the callback is destroyed.
703  * @event_type: a character string indicating the type of events for which
704  *            notification is requested.  Format is
705  *            EventClass:major_type:minor_type:detail
706  *            where all subfields other than EventClass are optional.
707  *            EventClasses include "object", "window", "mouse",
708  *            and toolkit events (e.g. "Gtk", "AWT").
709  *            Examples: "focus:", "Gtk:GtkWidget:button_press_event".
710  *
711  * Registers an #AtspiEventListenetSimpleCB. The method is similar to
712  * #atspi_event_listener_register, but @callback takes no user_data.
713  *
714  * Returns: #TRUE if successfull, otherwise #FALSE.
715  **/
716 gboolean
717 atspi_event_listener_register_no_data (AtspiEventListenerSimpleCB callback,
718                                  GDestroyNotify callback_destroyed,
719                                  const gchar              *event_type,
720                                  GError **error)
721 {
722   return atspi_event_listener_register_from_callback (remove_datum, callback,
723                                                       callback_destroyed,
724                                                       event_type, error);
725 }
726
727 static gboolean
728 is_superset (const gchar *super, const gchar *sub)
729 {
730   if (!super || !super [0])
731     return TRUE;
732   return (strcmp (super, sub) == 0);
733 }
734
735 /**
736  * atspi_event_listener_deregister:
737  * @listener: The #AtspiEventListener to deregister.
738  * @event_type: a string specifying the event type for which this
739  *             listener is to be deregistered.
740  *
741  * Deregisters an #AtspiEventListener from the registry, for a specific
742  *             event type.
743  *
744  * Returns: #TRUE if successful, otherwise #FALSE.
745  **/
746 gboolean
747 atspi_event_listener_deregister (AtspiEventListener *listener,
748                                                const gchar              *event_type,
749                                                GError **error)
750 {
751   return atspi_event_listener_deregister_from_callback (listener->callback,
752                                                         listener->user_data,
753                                                         event_type, error);
754 }
755
756 /**
757  * atspi_event_listener_deregister_from_callback:
758  * @callback: (scope call): the #AtspiEventListenerCB registered against an
759  *            event type.
760  * @user_data: (closure): User data that was passed in for this callback.
761  * @event_type: a string specifying the event type for which this
762  *             listener is to be deregistered.
763  *
764  * Deregisters an #AtspiEventListenerCB from the registry, for a specific
765  *             event type.
766  *
767  * Returns: #TRUE if successful, otherwise #FALSE.
768  **/
769 gboolean
770 atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
771                                                void *user_data,
772                                                const gchar              *event_type,
773                                                GError **error)
774 {
775   char *category, *name, *detail;
776   GPtrArray *matchrule_array;
777   gint i;
778   GList *l;
779   gboolean result = TRUE;
780
781   if (!callback)
782   {
783     return FALSE;
784   }
785   if (!convert_event_type_to_dbus (event_type, &category, &name, &detail, &matchrule_array))
786   {
787     return FALSE;
788   }
789
790   for (l = event_listeners; l;)
791   {
792     EventListenerEntry *e = l->data;
793     if (e->callback == callback &&
794         e->user_data == user_data &&
795         is_superset (category, e->category) &&
796         is_superset (name, e->name) &&
797         is_superset (detail, e->detail))
798     {
799       DBusMessage *message, *reply;
800       l = g_list_next (l);
801       if (in_send)
802       {
803         pending_removals = g_list_remove (pending_removals, e);
804         pending_removals = g_list_append (pending_removals, e);
805       }
806       else
807         event_listeners = g_list_remove (event_listeners, e);
808       for (i = 0; i < matchrule_array->len; i++)
809       {
810         char *matchrule = g_ptr_array_index (matchrule_array, i);
811         dbus_bus_remove_match (_atspi_bus(), matchrule, NULL);
812       }
813       message = dbus_message_new_method_call (atspi_bus_registry,
814             atspi_path_registry,
815             atspi_interface_registry,
816             "DeregisterEvent");
817       if (!message) {
818         result = FALSE;
819         break;
820       }
821       dbus_message_append_args (message, DBUS_TYPE_STRING, &event_type, DBUS_TYPE_INVALID);
822       reply = _atspi_dbus_send_with_reply_and_block (message, error);
823       if (reply)
824         dbus_message_unref (reply);
825
826       if (!in_send)
827         listener_entry_free (e);
828     }
829     else
830       l = g_list_next (l);
831   }
832   g_free (category);
833   g_free (name);
834   if (detail) g_free (detail);
835   for (i = 0; i < matchrule_array->len; i++)
836     g_free (g_ptr_array_index (matchrule_array, i));
837   g_ptr_array_free (matchrule_array, TRUE);
838   return result;
839 }
840
841 /**
842  * atspi_event_listener_deregister_no_data: (skip)
843  * @callback: (scope call): the #AtspiEventListenerSimpleCB registered against
844  *            an event type.
845  * @event_type: a string specifying the event type for which this
846  *             listener is to be deregistered.
847  *
848  * deregisters an #AtspiEventListenerSimpleCB from the registry, for a specific
849  *             event type.
850  *
851  * Returns: #TRUE if successful, otherwise #FALSE.
852  **/
853 gboolean
854 atspi_event_listener_deregister_no_data (AtspiEventListenerSimpleCB callback,
855                                    const gchar              *event_type,
856                                    GError **error)
857 {
858   return atspi_event_listener_deregister_from_callback (remove_datum, callback,
859                                                         event_type,
860                                                         error);
861 }
862
863 static AtspiEvent *
864 atspi_event_copy (AtspiEvent *src)
865 {
866   AtspiEvent *dst = g_new0 (AtspiEvent, 1);
867   dst->type = g_strdup (src->type);
868   dst->source = g_object_ref (src->source);
869   dst->detail1 = src->detail1;
870   dst->detail2 = src->detail2;
871   g_value_init (&dst->any_data, G_VALUE_TYPE (&src->any_data));
872   g_value_copy (&src->any_data, &dst->any_data);
873   if (src->sender)
874     dst->sender = g_object_ref (src->sender);
875   return dst;
876 }
877
878 static void
879 atspi_event_free (AtspiEvent *event)
880 {
881   g_object_unref (event->source);
882   g_free (event->type);
883   g_value_unset (&event->any_data);
884   g_clear_object (&event->sender);
885   g_free (event);
886 }
887
888 static gboolean
889 detail_matches_listener (const char *event_detail, const char *listener_detail)
890 {
891   if (!listener_detail)
892     return TRUE;
893
894   if (!event_detail)
895     return FALSE;
896
897   return !(listener_detail [strcspn (listener_detail, ":")] == '\0'
898                ? strncmp (listener_detail, event_detail,
899                           strcspn (event_detail, ":"))
900                : strcmp (listener_detail, event_detail));
901 }
902
903 static void
904 resolve_pending_removal (gpointer data)
905 {
906   event_listeners = g_list_remove (event_listeners, data);
907   listener_entry_free (data);
908 }
909
910 void
911 _atspi_send_event (AtspiEvent *e)
912 {
913   char *category, *name, *detail;
914   GList *l;
915   GList *called_listeners = NULL;
916
917   /* Ensure that the value is set to avoid a Python exception */
918   /* TODO: Figure out how to do this without using a private field */
919   if (e->any_data.g_type == 0)
920   {
921     g_value_init (&e->any_data, G_TYPE_INT);
922     g_value_set_int (&e->any_data, 0);
923   }
924
925   if (!convert_event_type_to_dbus (e->type, &category, &name, &detail, NULL))
926   {
927     g_warning ("AT-SPI: Couldn't parse event: %s\n", e->type);
928     return;
929   }
930   in_send++;
931   for (l = event_listeners; l; l = g_list_next (l))
932   {
933     EventListenerEntry *entry = l->data;
934     if (!strcmp (category, entry->category) &&
935         (entry->name == NULL || !strcmp (name, entry->name)) &&
936         detail_matches_listener (detail, entry->detail))
937     {
938       GList *l2;
939       for (l2 = called_listeners; l2; l2 = l2->next)
940       {
941         EventListenerEntry *e2 = l2->data;
942         if (entry->callback == e2->callback && entry->user_data == e2->user_data)
943           break;
944       }
945       if (!l2)
946       {
947         for (l2 = pending_removals; l2; l2 = l2->next)
948         {
949         if (l2->data == entry)
950           break;
951         }
952       }
953       if (!l2)
954       {
955         entry->callback (atspi_event_copy (e), entry->user_data);
956         called_listeners = g_list_prepend (called_listeners, entry);
957       }
958     }
959   }
960   in_send--;
961   if (detail) g_free (detail);
962   g_free (name);
963   g_free (category);
964   g_list_free (called_listeners);
965
966   g_list_free_full (pending_removals, resolve_pending_removal);
967   pending_removals = NULL;
968 }
969
970 DBusHandlerResult
971 _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
972 {
973   char *detail = NULL;
974   const char *category = dbus_message_get_interface (message);
975   const char *sender = dbus_message_get_sender (message);
976   const char *member = dbus_message_get_member (message);
977   const char *signature = dbus_message_get_signature (message);
978   gchar *name;
979   gchar *converted_type;
980   DBusMessageIter iter, iter_variant;
981   dbus_message_iter_init (message, &iter);
982   AtspiEvent e;
983   dbus_int32_t detail1, detail2;
984   char *p;
985   GHashTable *cache = NULL;
986
987   if (strcmp (signature, "siiv(so)") != 0 &&
988       strcmp (signature, "siiva{sv}") != 0)
989   {
990    g_warning ("Got invalid signature %s for signal %s from interface %s\n", signature, member, category);
991     return DBUS_HANDLER_RESULT_HANDLED;
992   }
993
994   memset (&e, 0, sizeof (e));
995
996   if (category)
997   {
998     category = g_utf8_strrchr (category, -1, '.');
999     if (category == NULL)
1000     {
1001       // TODO: Error
1002       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1003     }
1004     category++;
1005   }
1006   else
1007   {
1008     // TODO: Error
1009     // Note that the single caller of this function, process_deferred_message(), ignores the return value.
1010     // We should probably free the message if we aren't going to process it after all.
1011     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1012   }
1013   dbus_message_iter_get_basic (&iter, &detail);
1014   dbus_message_iter_next (&iter);
1015   dbus_message_iter_get_basic (&iter, &detail1);
1016   e.detail1 = detail1;
1017   dbus_message_iter_next (&iter);
1018   dbus_message_iter_get_basic (&iter, &detail2);
1019   e.detail2 = detail2;
1020   dbus_message_iter_next (&iter);
1021
1022   converted_type = convert_name_from_dbus (category, FALSE);
1023   name = convert_name_from_dbus (member, FALSE);
1024   detail = convert_name_from_dbus (detail, TRUE);
1025
1026   if (strcasecmp  (category, name) != 0)
1027   {
1028     p = g_strconcat (converted_type, ":", name, NULL);
1029     g_free (converted_type);
1030     converted_type = p;
1031   }
1032   else if (detail [0] == '\0')
1033   {
1034     p = g_strconcat (converted_type, ":",  NULL);
1035     g_free (converted_type);
1036     converted_type = p;
1037   }
1038
1039   if (detail[0] != '\0')
1040   {
1041     p = g_strconcat (converted_type, ":", detail, NULL);
1042     g_free (converted_type);
1043     converted_type = p;
1044   }
1045   e.type = converted_type;
1046   if (strcmp (category, "ScreenReader") != 0)
1047   {
1048     e.source = _atspi_ref_accessible (sender, dbus_message_get_path (message));
1049     if (e.source == NULL)
1050     {
1051       g_warning ("Got no valid source accessible for signal %s from interface %s\n", member, category);
1052       g_free (converted_type);
1053       g_free (name);
1054       g_free (detail);
1055       return DBUS_HANDLER_RESULT_HANDLED;
1056     }
1057   }
1058
1059   dbus_message_iter_recurse (&iter, &iter_variant);
1060   switch (dbus_message_iter_get_arg_type (&iter_variant))
1061   {
1062     case DBUS_TYPE_STRUCT:
1063     {
1064       AtspiRect rect;
1065       if (demarshal_rect (&iter_variant, &rect))
1066       {
1067         g_value_init (&e.any_data, ATSPI_TYPE_RECT);
1068         g_value_set_boxed (&e.any_data, &rect);
1069       }
1070       else
1071       {
1072         AtspiAccessible *accessible;
1073         accessible = _atspi_dbus_return_accessible_from_iter (&iter_variant);
1074         if (!strcmp (category, "ScreenReader"))
1075         {
1076           g_object_unref (e.source);
1077           e.source = accessible;
1078           if (e.source == NULL)
1079           {
1080             g_warning ("Got no valid source accessible for signal %s from interface %s\n", member, category);
1081             g_free (converted_type);
1082             g_free (name);
1083             g_free (detail);
1084             return DBUS_HANDLER_RESULT_HANDLED;
1085           }
1086         }
1087         else
1088         {
1089           g_value_init (&e.any_data, ATSPI_TYPE_ACCESSIBLE);
1090           g_value_set_instance (&e.any_data, accessible);
1091           if (accessible)
1092             g_object_unref (accessible);        /* value now owns it */
1093         }
1094       }
1095       break;
1096     }
1097     case DBUS_TYPE_STRING:
1098     {
1099       dbus_message_iter_get_basic (&iter_variant, &p);
1100       g_value_init (&e.any_data, G_TYPE_STRING);
1101       g_value_set_string (&e.any_data, p);
1102       break;
1103     }
1104   default:
1105     break;
1106   }
1107
1108   g_assert (e.source != NULL);
1109
1110   dbus_message_iter_next (&iter);
1111   if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY)
1112   {
1113     /* new form -- parse properties sent with event */
1114     cache = _atspi_dbus_update_cache_from_dict (e.source, &iter);
1115   }
1116
1117   e.sender = _atspi_ref_accessible (sender, ATSPI_DBUS_PATH_ROOT);
1118
1119   if (!strncmp (e.type, "object:children-changed", 23))
1120   {
1121     cache_process_children_changed (&e);
1122   }
1123   else if (!strncmp (e.type, "object:property-change", 22))
1124   {
1125     cache_process_property_change (&e);
1126   }
1127   else if (!strncmp (e.type, "object:state-changed", 20))
1128   {
1129     cache_process_state_changed (&e);
1130     if (detail && !strncmp(detail, "defunct", 7))
1131     {
1132       remove_accessible_from_app_hash(sender, e.source);
1133       remove_accessible_from_app_hash(sender, e.sender);
1134     }
1135   }
1136   else if (!strncmp (e.type, "focus", 5))
1137   {
1138     /* BGO#663992 - TODO: figure out the real problem */
1139     e.source->cached_properties &= ~(ATSPI_CACHE_STATES);
1140   }
1141
1142   _atspi_send_event (&e);
1143
1144   if (cache)
1145     _atspi_accessible_unref_cache (e.source);
1146
1147   g_free (converted_type);
1148   g_free (name);
1149   g_free (detail);
1150   g_object_unref (e.source);
1151   g_object_unref (e.sender);
1152   g_value_unset (&e.any_data);
1153   return DBUS_HANDLER_RESULT_HANDLED;
1154 }
1155
1156 G_DEFINE_BOXED_TYPE (AtspiEvent, atspi_event, atspi_event_copy, atspi_event_free)