Add a class for taking leased references of GObjects.
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / adaptors / accessible-marshaller.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2008 Novell, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <droute/droute.h>
24
25 #include "common/spi-dbus.h"
26 #include "common/spi-stateset.h"
27
28 #include "accessible-register.h"
29 #include "accessible-marshaller.h"
30 #include "bridge.h"
31
32 #include "adaptors.h"
33
34 /*---------------------------------------------------------------------------*/
35
36 void
37 spi_dbus_append_name_and_path_inner (DBusMessageIter * iter,
38                                      const char *bus_name, const char *path)
39 {
40   DBusMessageIter iter_struct;
41
42   if (!bus_name)
43     bus_name = "";
44   if (!path)
45     path = SPI_DBUS_PATH_NULL;
46
47   dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL,
48                                     &iter_struct);
49   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &bus_name);
50   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
51   dbus_message_iter_close_container (iter, &iter_struct);
52 }
53
54 extern gchar *atspi_dbus_name;
55
56 void
57 spi_dbus_append_name_and_path (DBusMessage * message, DBusMessageIter * iter,
58                                AtkObject * obj, gboolean do_register,
59                                gboolean unref)
60 {
61   gchar *path;
62   DBusMessageIter iter_struct;
63
64   path = atk_dbus_object_to_path (obj, do_register);
65
66   spi_dbus_append_name_and_path_inner (iter, atspi_dbus_name, path);
67
68   g_free (path);
69   if (obj && unref)
70     g_object_unref (obj);
71 }
72
73 /*
74  * Marshals the D-Bus path of an AtkObject into a D-Bus message.
75  *
76  * Unrefs the AtkObject if unref is true.
77  */
78 DBusMessage *
79 spi_dbus_return_object (DBusMessage * message, AtkObject * obj,
80                         gboolean do_register, gboolean unref)
81 {
82   DBusMessage *reply;
83   reply = dbus_message_new_method_return (message);
84   if (reply)
85     {
86       DBusMessageIter iter;
87       dbus_message_iter_init_append (reply, &iter);
88       spi_dbus_append_name_and_path (message, &iter, obj, do_register, unref);
89     }
90
91   return reply;
92 }
93
94 DBusMessage *
95 spi_dbus_return_hyperlink (DBusMessage * message, AtkHyperlink * link,
96                            AtkObject * container, gboolean unref)
97 {
98   return spi_dbus_return_sub_object (message, G_OBJECT (link),
99                                      G_OBJECT (container), unref);
100 }
101
102 DBusMessage *
103 spi_dbus_return_sub_object (DBusMessage * message, GObject * sub,
104                             GObject * container, gboolean unref)
105 {
106   DBusMessage *reply;
107   gchar *path;
108
109   path = atk_dbus_sub_object_to_path (sub, container);
110
111   if (sub && unref)
112     g_object_unref (sub);
113
114   if (!path)
115     path = g_strdup (SPI_DBUS_PATH_NULL);
116
117   reply = dbus_message_new_method_return (message);
118   if (reply)
119     {
120       dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path,
121                                 DBUS_TYPE_INVALID);
122     }
123
124   g_free (path);
125
126   return reply;
127 }
128
129 /*---------------------------------------------------------------------------*/
130
131 /*
132  * Marshals a variant containing the D-Bus path of an AtkObject into a D-Bus
133  * message.
134  *
135  * Unrefs the object if unref is true.
136  */
137 dbus_bool_t
138 spi_dbus_return_v_object (DBusMessageIter * iter, AtkObject * obj, int unref)
139 {
140   DBusMessageIter iter_variant;
141   char *path;
142
143   path = atk_dbus_object_to_path (obj, FALSE);
144
145   if (!path)
146     path = g_strdup (SPI_DBUS_PATH_NULL);
147
148   if (unref)
149     g_object_unref (obj);
150
151   dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "(so)",
152                                     &iter_variant);
153   spi_dbus_append_name_and_path_inner (&iter_variant, NULL, path);
154   dbus_message_iter_close_container (iter, &iter_variant);
155   return TRUE;
156 }
157
158 /*---------------------------------------------------------------------------*/
159
160 void
161 append_atk_object_interfaces (AtkObject * object, DBusMessageIter * iter)
162 {
163   const gchar *itf;
164
165   itf = SPI_DBUS_INTERFACE_ACCESSIBLE;
166   dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
167
168   if (ATK_IS_ACTION (object))
169     {
170       itf = SPI_DBUS_INTERFACE_ACTION;
171       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
172     }
173
174   if (ATK_IS_COMPONENT (object))
175     {
176       itf = SPI_DBUS_INTERFACE_COMPONENT;
177       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
178     }
179
180   if (ATK_IS_EDITABLE_TEXT (object))
181     {
182       itf = SPI_DBUS_INTERFACE_EDITABLE_TEXT;
183       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
184     }
185
186   if (ATK_IS_TEXT (object))
187     {
188       itf = SPI_DBUS_INTERFACE_TEXT;
189       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
190     }
191
192   if (ATK_IS_HYPERTEXT (object))
193     {
194       itf = SPI_DBUS_INTERFACE_HYPERTEXT;
195       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
196     }
197
198   if (ATK_IS_IMAGE (object))
199     {
200       itf = SPI_DBUS_INTERFACE_IMAGE;
201       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
202     }
203
204   if (ATK_IS_SELECTION (object))
205     {
206       itf = SPI_DBUS_INTERFACE_SELECTION;
207       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
208     }
209
210   if (ATK_IS_TABLE (object))
211     {
212       itf = SPI_DBUS_INTERFACE_TABLE;
213       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
214     }
215
216   if (ATK_IS_VALUE (object))
217     {
218       itf = SPI_DBUS_INTERFACE_VALUE;
219       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
220     }
221
222   if (ATK_IS_STREAMABLE_CONTENT (object))
223     {
224       itf = "org.freedesktop.atspi.StreamableContent";
225       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
226     }
227
228   if (ATK_IS_DOCUMENT (object))
229     {
230       itf = "org.freedesktop.atspi.Collection";
231       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
232       itf = SPI_DBUS_INTERFACE_DOCUMENT;
233       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
234     }
235
236   if (ATK_IS_HYPERLINK_IMPL (object))
237     {
238       itf = SPI_DBUS_INTERFACE_HYPERLINK;
239       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
240     }
241 }
242
243 /*---------------------------------------------------------------------------*/
244
245 /*
246  * Marshals the given AtkObject into the provided D-Bus iterator.
247  *
248  * The object is marshalled including all its client side cache data.
249  * The format of the structure is (o(so)a(so)assusau).
250  * This is used in the updateTree signal and the GetTree method
251  * of the org.freedesktop.atspi.Tree interface.
252  *
253  * To marshal an object its parent, and all its children must already
254  * be registered with D-Bus and have been given a D-Bus object path.
255  */
256 void
257 spi_atk_append_accessible (AtkObject * obj, gpointer data)
258 {
259   DBusMessageIter iter_struct, iter_sub_array;
260   dbus_uint32_t states[2];
261   int count;
262   AtkStateSet *set;
263   DBusMessageIter *iter_array = (DBusMessageIter *) data;
264
265   const char *name, *desc;
266   dbus_uint32_t role;
267
268   set = atk_object_ref_state_set (obj);
269   {
270     AtkObject *parent;
271     gchar *path;
272     gchar *bus_parent = NULL, *path_parent;
273
274     /* Marshall object path */
275     path = atk_dbus_object_to_path (obj, FALSE);
276
277     role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
278
279     /* Marshall parent */
280     parent = atk_object_get_parent (obj);
281     if (parent == NULL)
282       {
283         /* TODO: Support getting parent of an AtkPlug */
284 #ifdef __ATK_PLUG_H__
285           if (ATK_IS_PLUG (obj))
286             {
287               char *id = g_object_get_data (G_OBJECT (obj), "dbus-plug-parent");
288               if (id)
289                 bus_parent = g_strdup (id);
290               if (bus_parent && (path_parent = g_utf8_strchr (bus_parent + 1, -1, ':')))
291                 {
292                   *(path_parent++) = '\0';
293                   /* path_parent is going to be freed, so dup it */
294                   path_parent = g_strdup (path_parent);
295                 }
296             }
297           else if (role != Accessibility_ROLE_APPLICATION)
298 #else
299         if (role != Accessibility_ROLE_APPLICATION)
300 #endif
301           path_parent = g_strdup (SPI_DBUS_PATH_NULL);
302         else
303           path_parent = atk_dbus_desktop_object_path ();
304       }
305     else
306       {
307         path_parent = atk_dbus_object_to_path (parent, FALSE);
308         if (!path_parent)
309           {
310             /* This should only happen if a widget is re-parented to
311              * an AtkObject that has not been registered and is then
312              * updated. Ideally objects would be de-registered when
313              * they are removed from a registered tree object, but
314              * this would invalidate a huge amount of cache when
315              * re-parenting.
316              */
317 #if SPI_ATK_DEBUG
318             g_warning
319               ("AT-SPI: Registered accessible marshalled when parent not registered");
320 #endif
321             path_parent = atk_dbus_desktop_object_path ();
322           }
323       }
324
325     dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
326                                       &iter_struct);
327     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH,
328                                     &path);
329     spi_dbus_append_name_and_path_inner (&iter_struct, bus_parent,
330                                          path_parent);
331     g_free (path_parent);
332     g_free (bus_parent);
333
334     /* Marshall children */
335     dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "(so)",
336                                       &iter_sub_array);
337     if (!atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS))
338       {
339         gint childcount, i;
340
341         childcount = atk_object_get_n_accessible_children (obj);
342         for (i = 0; i < childcount; i++)
343           {
344             AtkObject *child;
345             gchar *child_path;
346
347             child = atk_object_ref_accessible_child (obj, i);
348             child_path = atk_dbus_object_to_path (child, FALSE);
349             if (child_path)
350               {
351                 spi_dbus_append_name_and_path_inner (&iter_sub_array, NULL,
352                                                      child_path);
353                 g_free (child_path);
354               }
355             g_object_unref (G_OBJECT (child));
356           }
357       }
358 #ifdef __ATK_PLUG_H__
359     if (ATK_IS_SOCKET (obj) && atk_socket_is_occupied (ATK_SOCKET (obj)))
360       {
361         AtkSocket *socket = ATK_SOCKET (obj);
362         gchar *child_name, *child_path;
363         child_name = g_strdup (socket->embedded_plug_id);
364         child_path = g_utf8_strchr (child_name + 1, -1, ':');
365         if (child_path)
366           {
367             *(child_path++) = '\0';
368             spi_dbus_append_name_and_path_inner (&iter_sub_array, child_name,
369                                                  child_path);
370           }
371         g_free (child_name);
372       }
373 #endif
374
375     dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
376
377     /* Marshall interfaces */
378     dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
379                                       &iter_sub_array);
380     append_atk_object_interfaces (obj, &iter_sub_array);
381     dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
382
383     /* Marshall name */
384     name = atk_object_get_name (obj);
385     if (!name)
386       name = "";
387     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
388
389     /* Marshall role */
390     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
391
392     /* Marshall description */
393     desc = atk_object_get_description (obj);
394     if (!desc)
395       desc = "";
396     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
397
398     g_free (path);
399
400     /* Marshall state set */
401     spi_atk_state_set_to_dbus_array (set, states);
402     dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "u",
403                                       &iter_sub_array);
404     for (count = 0; count < 2; count++)
405       {
406         dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_UINT32,
407                                         &states[count]);
408       }
409     dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
410   }
411   dbus_message_iter_close_container (iter_array, &iter_struct);
412   g_object_unref (set);
413 }
414
415 void
416 spi_atk_append_attribute_set (DBusMessageIter * iter, AtkAttributeSet * attr)
417 {
418   DBusMessageIter dictIter;
419
420   dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, "{ss}", &dictIter);
421   spi_atk_append_attribute_set_inner (&dictIter, attr);
422   dbus_message_iter_close_container (iter, &dictIter);
423 }
424
425 void
426 spi_atk_append_attribute_set_inner (DBusMessageIter * iter,
427                                     AtkAttributeSet * attr)
428 {
429   DBusMessageIter dictEntryIter;
430
431   while (attr)
432     {
433       AtkAttribute *attribute = (AtkAttribute *) attr->data;
434       dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY, NULL,
435                                         &dictEntryIter);
436       dbus_message_iter_append_basic (&dictEntryIter, DBUS_TYPE_STRING,
437                                       &attribute->name);
438       dbus_message_iter_append_basic (&dictEntryIter, DBUS_TYPE_STRING,
439                                       &attribute->value);
440       dbus_message_iter_close_container (iter, &dictEntryIter);
441       attr = g_slist_next (attr);
442     }
443 }
444
445 /*END------------------------------------------------------------------------*/