1
/*
2
 * AT-SPI - Assistive Technology Service Provider Interface
3
 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4
 *
5
 * Copyright 2007 IBM Corp.
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 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
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, write to the
19
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22

            
23
/* collection.c: implements the Collection interface */
24

            
25
#include <string.h>
26
#include <strings.h>
27

            
28
#include "bridge.h"
29
#include <atk/atk.h>
30
#include <droute/droute.h>
31

            
32
#include "accessible-stateset.h"
33
#include "bitarray.h"
34
#include "spi-dbus.h"
35

            
36
#include "accessible-register.h"
37
#include "introspection.h"
38
#include "object.h"
39

            
40
typedef struct _MatchRulePrivate MatchRulePrivate;
41
struct _MatchRulePrivate
42
{
43
  gint *states;
44
  AtspiCollectionMatchType statematchtype;
45
  AtkAttributeSet *attributes;
46
  AtspiCollectionMatchType attributematchtype;
47
  gint *roles;
48
  AtspiCollectionMatchType rolematchtype;
49
  gchar **ifaces;
50
  AtspiCollectionMatchType interfacematchtype;
51
  gboolean invert;
52
};
53

            
54
static gboolean
55
15
child_interface_p (AtkObject *child, gchar *repo_id)
56
{
57
15
  if (!strncasecmp (repo_id, "action", 6))
58
    {
59
      AtkAction *iface;
60
      gint i, count;
61
      char *p;
62
      char name[64];
63
15
      if (!ATK_IS_ACTION (child))
64
9
        return FALSE;
65
6
      iface = ATK_ACTION (child);
66
6
      count = atk_action_get_n_actions (iface);
67
6
      if (count <= 0)
68
3
        return FALSE;
69
3
      if (repo_id[6] == '\0')
70
1
        return TRUE;
71
2
      p = strchr (repo_id, '(');
72
2
      if (!p)
73
        return FALSE;
74
2
      strncpy (name, p + 1, sizeof (name) - 1);
75
2
      name[sizeof (name) - 1] = '\0';
76
2
      p = strchr (name, ')');
77
2
      if (p)
78
2
        *p = '\0';
79
3
      for (i = 0; i < count; i++)
80
        {
81
2
          const char *action = atk_action_get_name (iface, i);
82
2
          if (!strcasecmp (name, action))
83
1
            return TRUE;
84
        }
85
1
      return FALSE;
86
    }
87
  if (!strcasecmp (repo_id, "accessible") || !strcasecmp (repo_id, "collection"))
88
    return ATK_IS_OBJECT (child);
89
  if (!strcasecmp (repo_id, "component"))
90
    return ATK_IS_COMPONENT (child);
91
  if (!strcasecmp (repo_id, "editabletext"))
92
    return ATK_IS_EDITABLE_TEXT (child);
93
  if (!strcasecmp (repo_id, "text"))
94
    return ATK_IS_TEXT (child);
95
  if (!strcasecmp (repo_id, "hypertext"))
96
    return ATK_IS_HYPERTEXT (child);
97
  if (!strcasecmp (repo_id, "image"))
98
    return ATK_IS_IMAGE (child);
99
  if (!strcasecmp (repo_id, "selection"))
100
    return ATK_IS_SELECTION (child);
101
  if (!strcasecmp (repo_id, "table"))
102
    return ATK_IS_TABLE (child);
103
  if (!strcasecmp (repo_id, "value"))
104
    return ATK_IS_VALUE (child);
105
  if (!strcasecmp (repo_id, "streamablecontent"))
106
    return ATK_IS_STREAMABLE_CONTENT (child);
107
  if (!strcasecmp (repo_id, "document"))
108
    return ATK_IS_DOCUMENT (child);
109
  return FALSE;
110
}
111

            
112
static gboolean
113
21
match_states_all_p (AtkObject *child, gint *set)
114
{
115
  AtkStateSet *chs;
116
  gint i;
117
21
  gboolean ret = TRUE;
118

            
119
21
  if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
120
12
    return TRUE;
121

            
122
9
  chs = atk_object_ref_state_set (child);
123

            
124
  // TODO: use atk-state_set_contains_states; would be more efficient
125
35
  for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
126
    {
127
29
      if (!atk_state_set_contains_state (chs, set[i]))
128
        {
129
3
          ret = FALSE;
130
3
          break;
131
        }
132
    }
133

            
134
9
  g_object_unref (chs);
135
9
  return ret;
136
}
137

            
138
static gboolean
139
match_states_any_p (AtkObject *child, gint *set)
140
{
141
  AtkStateSet *chs;
142
  gint i;
143
  gboolean ret = FALSE;
144

            
145
  if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
146
    return TRUE;
147

            
148
  chs = atk_object_ref_state_set (child);
149

            
150
  for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
151
    {
152
      if (atk_state_set_contains_state (chs, set[i]))
153
        {
154
          ret = TRUE;
155
          break;
156
        }
157
    }
158

            
159
  g_object_unref (chs);
160
  return ret;
161
}
162

            
163
static gboolean
164
match_states_none_p (AtkObject *child, gint *set)
165
{
166
  AtkStateSet *chs;
167
  gint i;
168
  gboolean ret = TRUE;
169

            
170
  if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
171
    return TRUE;
172

            
173
  chs = atk_object_ref_state_set (child);
174

            
175
  for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
176
    {
177
      if (atk_state_set_contains_state (chs, set[i]))
178
        {
179
          ret = FALSE;
180
          break;
181
        }
182
    }
183

            
184
  g_object_unref (chs);
185
  return ret;
186
}
187

            
188
// TODO: need to convert at-spi roles/states to atk roles/states */
189
static gboolean
190
21
match_states_lookup (AtkObject *child, MatchRulePrivate *mrp)
191
{
192
21
  switch (mrp->statematchtype)
193
    {
194
21
    case ATSPI_Collection_MATCH_ALL:
195
21
      if (match_states_all_p (child, mrp->states))
196
18
        return TRUE;
197
3
      break;
198

            
199
    case ATSPI_Collection_MATCH_ANY:
200
      if (match_states_any_p (child, mrp->states))
201
        return TRUE;
202
      break;
203

            
204
    case ATSPI_Collection_MATCH_NONE:
205
      if (match_states_none_p (child, mrp->states))
206
        return TRUE;
207
      break;
208

            
209
    default:
210
      break;
211
    }
212

            
213
3
  return FALSE;
214
}
215

            
216
// TODO: Map at-spi -> atk roles at mrp creation instead of doing this;
217
// would be more efficient
218
#define spi_get_role(obj) spi_accessible_role_from_atk_role (atk_object_get_role (obj))
219

            
220
static gboolean
221
13
match_roles_all_p (AtkObject *child, gint *roles)
222
{
223
13
  if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
224
13
    return TRUE;
225
  else if (roles[1] != BITARRAY_SEQ_TERM)
226
    return FALSE;
227

            
228
  return (spi_get_role (child) == roles[0]);
229
}
230

            
231
static gboolean
232
match_roles_any_p (AtkObject *child, gint *roles)
233
{
234
  AtspiRole role;
235
  int i;
236

            
237
  if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
238
    return TRUE;
239

            
240
  role = spi_accessible_role_from_atk_role (atk_object_get_role (child));
241

            
242
  for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
243
    if (role == roles[i])
244
      return TRUE;
245

            
246
  return FALSE;
247
}
248

            
249
static gboolean
250
5
match_roles_none_p (AtkObject *child, gint *roles)
251
{
252
  AtspiRole role;
253
  int i;
254

            
255
5
  if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
256
    return TRUE;
257

            
258
5
  role = spi_get_role (child);
259

            
260
9
  for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
261
5
    if (role == roles[i])
262
1
      return FALSE;
263

            
264
4
  return TRUE;
265
}
266

            
267
static gboolean
268
18
match_roles_lookup (AtkObject *child, MatchRulePrivate *mrp)
269
{
270
18
  switch (mrp->rolematchtype)
271
    {
272
13
    case ATSPI_Collection_MATCH_ALL:
273
13
      if (match_roles_all_p (child, mrp->roles))
274
13
        return TRUE;
275
      break;
276

            
277
    case ATSPI_Collection_MATCH_ANY:
278
      if (match_roles_any_p (child, mrp->roles))
279
        return TRUE;
280
      break;
281

            
282
5
    case ATSPI_Collection_MATCH_NONE:
283
5
      if (match_roles_none_p (child, mrp->roles))
284
4
        return TRUE;
285
1
      break;
286

            
287
    default:
288
      break;
289
    }
290
1
  return FALSE;
291
}
292

            
293
static gboolean
294
34
match_interfaces_all_p (AtkObject *obj, gchar **ifaces)
295
{
296
  gint i;
297

            
298
34
  if (ifaces == NULL)
299
    return TRUE;
300

            
301
36
  for (i = 0; ifaces[i]; i++)
302
15
    if (!child_interface_p (obj, ifaces[i]))
303
      {
304
13
        return FALSE;
305
      }
306
21
  return TRUE;
307
}
308

            
309
static gboolean
310
match_interfaces_any_p (AtkObject *obj, gchar **ifaces)
311
{
312
  gint i;
313

            
314
  if (ifaces == NULL)
315
    return TRUE;
316

            
317
  for (i = 0; ifaces[i]; i++)
318
    if (child_interface_p (obj, ifaces[i]))
319
      {
320
        return TRUE;
321
      }
322
  return FALSE;
323
}
324

            
325
static gboolean
326
match_interfaces_none_p (AtkObject *obj, gchar **ifaces)
327
{
328
  gint i;
329

            
330
  for (i = 0; ifaces[i]; i++)
331
    if (child_interface_p (obj, ifaces[i]))
332
      return FALSE;
333

            
334
  return TRUE;
335
}
336

            
337
static gboolean
338
34
match_interfaces_lookup (AtkObject *child, MatchRulePrivate *mrp)
339
{
340
34
  switch (mrp->interfacematchtype)
341
    {
342

            
343
34
    case ATSPI_Collection_MATCH_ALL:
344
34
      if (match_interfaces_all_p (child, mrp->ifaces))
345
21
        return TRUE;
346
13
      break;
347

            
348
    case ATSPI_Collection_MATCH_ANY:
349
      if (match_interfaces_any_p (child, mrp->ifaces))
350
        return TRUE;
351
      break;
352

            
353
    case ATSPI_Collection_MATCH_NONE:
354
      if (match_interfaces_none_p (child, mrp->ifaces))
355
        return TRUE;
356
      break;
357

            
358
    default:
359
      break;
360
    }
361

            
362
13
  return FALSE;
363
}
364

            
365
#define split_attributes(attributes) (g_strsplit (attributes, ";", 0))
366

            
367
static gboolean
368
12
match_attributes_all_p (AtkObject *child, AtkAttributeSet *attributes)
369
{
370
  int i, k;
371
  AtkAttributeSet *oa;
372
  gint length, oa_length;
373
12
  gboolean flag = FALSE;
374

            
375
12
  if (attributes == NULL || g_slist_length (attributes) == 0)
376
12
    return TRUE;
377

            
378
  oa = atk_object_get_attributes (child);
379
  length = g_slist_length (attributes);
380
  oa_length = g_slist_length (oa);
381

            
382
  for (i = 0; i < length; i++)
383
    {
384
      AtkAttribute *attr = g_slist_nth_data (attributes, i);
385
      for (k = 0; k < oa_length; k++)
386
        {
387
          AtkAttribute *oa_attr = g_slist_nth_data (oa, k);
388
          if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
389
              !g_ascii_strcasecmp (oa_attr->value, attr->value))
390
            {
391
              flag = TRUE;
392
              break;
393
            }
394
          else
395
            flag = FALSE;
396
        }
397
      if (!flag)
398
        {
399
          atk_attribute_set_free (oa);
400
          return FALSE;
401
        }
402
    }
403
  atk_attribute_set_free (oa);
404
  return TRUE;
405
}
406

            
407
static gboolean
408
match_attributes_any_p (AtkObject *child, AtkAttributeSet *attributes)
409
{
410
  int i, k;
411

            
412
  AtkAttributeSet *oa;
413
  gint length, oa_length;
414

            
415
  length = g_slist_length (attributes);
416
  if (length == 0)
417
    return TRUE;
418

            
419
  oa = atk_object_get_attributes (child);
420
  oa_length = g_slist_length (oa);
421

            
422
  for (i = 0; i < length; i++)
423
    {
424
      AtkAttribute *attr = g_slist_nth_data (attributes, i);
425
      for (k = 0; k < oa_length; k++)
426
        {
427
          AtkAttribute *oa_attr = g_slist_nth_data (oa, k);
428
          if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
429
              !g_ascii_strcasecmp (oa_attr->value, attr->value))
430
            {
431
              atk_attribute_set_free (oa);
432
              return TRUE;
433
            }
434
        }
435
    }
436
  atk_attribute_set_free (oa);
437
  return FALSE;
438
}
439

            
440
static gboolean
441
5
match_attributes_none_p (AtkObject *child, AtkAttributeSet *attributes)
442
{
443
  int i, k;
444

            
445
  AtkAttributeSet *oa;
446
  gint length, oa_length;
447

            
448
5
  length = g_slist_length (attributes);
449
5
  if (length == 0)
450
    return TRUE;
451

            
452
5
  oa = atk_object_get_attributes (child);
453
5
  oa_length = g_slist_length (oa);
454

            
455
10
  for (i = 0; i < length; i++)
456
    {
457
5
      AtkAttribute *attr = g_slist_nth_data (attributes, i);
458
15
      for (k = 0; k < oa_length; k++)
459
        {
460
10
          AtkAttribute *oa_attr = g_slist_nth_data (oa, k);
461
10
          if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
462
              !g_ascii_strcasecmp (oa_attr->value, attr->value))
463
            {
464
              atk_attribute_set_free (oa);
465
              return FALSE;
466
            }
467
        }
468
    }
469
5
  atk_attribute_set_free (oa);
470
5
  return TRUE;
471
}
472

            
473
static gboolean
474
17
match_attributes_lookup (AtkObject *child, MatchRulePrivate *mrp)
475
{
476
17
  switch (mrp->attributematchtype)
477
    {
478

            
479
12
    case ATSPI_Collection_MATCH_ALL:
480
12
      if (match_attributes_all_p (child, mrp->attributes))
481
12
        return TRUE;
482
      break;
483

            
484
    case ATSPI_Collection_MATCH_ANY:
485
      if (match_attributes_any_p (child, mrp->attributes))
486
        return TRUE;
487
      break;
488

            
489
5
    case ATSPI_Collection_MATCH_NONE:
490
5
      if (match_attributes_none_p (child, mrp->attributes))
491
5
        return TRUE;
492
      break;
493

            
494
    default:
495
      break;
496
    }
497
  return FALSE;
498
}
499

            
500
static int
501
43
sort_order_canonical (MatchRulePrivate *mrp, GPtrArray *arr, gint kount, gint max, AtkObject *obj, glong index, gboolean flag, AtkObject *pobj, gboolean traverse)
502
{
503
43
  gint i = index;
504
43
  glong acount = atk_object_get_n_accessible_children (obj);
505
43
  gboolean prev = pobj ? TRUE : FALSE;
506

            
507
43
  if (acount > ATSPI_MAX_CHILDREN)
508
    acount = ATSPI_MAX_CHILDREN;
509
76
  for (; i < acount && (max == 0 || kount < max); i++)
510
    {
511
33
      AtkObject *child = atk_object_ref_accessible_child (obj, i);
512

            
513
33
      if (!child)
514
        continue;
515

            
516
33
      if (prev && child == pobj)
517
        {
518
          g_object_unref (child);
519
          return kount;
520
        }
521

            
522
33
      if (flag && match_interfaces_lookup (child, mrp) && match_states_lookup (child, mrp) && match_roles_lookup (child, mrp) && match_attributes_lookup (child, mrp))
523
        {
524

            
525
16
          g_ptr_array_add (arr, g_object_ref (child));
526
16
          kount++;
527
        }
528

            
529
33
      if (!flag)
530
        flag = TRUE;
531

            
532
33
      if (traverse)
533
30
        kount = sort_order_canonical (mrp, arr, kount,
534
                                      max, child, 0, TRUE,
535
                                      pobj, traverse);
536
33
      g_object_unref (child);
537
    }
538
43
  return kount;
539
}
540

            
541
static int
542
3
sort_order_rev_canonical (MatchRulePrivate *mrp, GPtrArray *arr, gint kount, gint max, AtkObject *obj, gboolean flag, AtkObject *pobj)
543
{
544
  AtkObject *nextobj;
545
  AtkObject *parent;
546
  glong indexinparent;
547

            
548
  /* This breaks us out of the recursion. */
549
3
  if (!obj || obj == pobj)
550
    {
551
1
      return kount;
552
    }
553

            
554
  /* Add to the list if it matches */
555
2
  if (flag && match_interfaces_lookup (obj, mrp) && match_states_lookup (obj, mrp) && match_roles_lookup (obj, mrp) && match_attributes_lookup (obj, mrp) && (max == 0 || kount < max))
556
    {
557
1
      g_ptr_array_add (arr, g_object_ref (obj));
558
1
      kount++;
559
    }
560

            
561
  /* Get the current nodes index in it's parent and the parent object. */
562
2
  indexinparent = atk_object_get_index_in_parent (obj);
563
2
  parent = atk_object_get_parent (obj);
564

            
565
2
  if (indexinparent > 0 && (max == 0 || kount < max))
566
    {
567
      /* there are still some siblings to visit so get the previous sibling
568
         and get it's last descendant.
569
         First, get the previous sibling */
570
1
      nextobj = atk_object_ref_accessible_child (parent, indexinparent - 1);
571

            
572
      /* Now, drill down the right side to the last descendant */
573
1
      while (nextobj && atk_object_get_n_accessible_children (nextobj) > 0)
574
        {
575
          AtkObject *follow;
576
          gint count = atk_object_get_n_accessible_children (nextobj);
577
          if (count > ATSPI_MAX_CHILDREN)
578
            count = ATSPI_MAX_CHILDREN;
579

            
580
          follow = atk_object_ref_accessible_child (nextobj, count - 1);
581
          g_object_unref (nextobj);
582
          nextobj = follow;
583
        }
584
      /* recurse with the last descendant */
585
1
      kount = sort_order_rev_canonical (mrp, arr, kount, max,
586
                                        nextobj, TRUE, pobj);
587
1
      if (nextobj)
588
1
        g_object_unref (nextobj);
589
    }
590
1
  else if (max == 0 || kount < max)
591
    {
592
      /* no more siblings so next node must be the parent */
593
1
      kount = sort_order_rev_canonical (mrp, arr, kount, max,
594
                                        parent, TRUE, pobj);
595
    }
596
2
  return kount;
597
}
598

            
599
static int
600
1
query_exec (MatchRulePrivate *mrp, AtspiCollectionSortOrder sortby, GPtrArray *arr, gint kount, gint max, AtkObject *obj, glong index, gboolean flag, AtkObject *pobj, gboolean traverse)
601
{
602
1
  switch (sortby)
603
    {
604
1
    case ATSPI_Collection_SORT_ORDER_CANONICAL:
605
1
      kount = sort_order_canonical (mrp, arr, 0, max, obj, index, flag,
606
                                    pobj, traverse);
607
1
      break;
608
    case ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL:
609
      kount = sort_order_canonical (mrp, arr, 0, max, obj, index, flag,
610
                                    pobj, traverse);
611
      break;
612
    default:
613
      kount = 0;
614
      g_warning ("Sort method not implemented yet");
615
      break;
616
    }
617

            
618
1
  return kount;
619
}
620

            
621
static gboolean
622
16
bitarray_to_seq (dbus_uint32_t *array, int array_count, int **ret)
623
{
624
16
  int out_size = 4;
625
16
  int out_count = 0;
626
  int i, j;
627
16
  int *out = (int *) g_malloc (out_size * sizeof (int));
628

            
629
64
  for (i = 0; i < array_count; i++)
630
    {
631
1584
      for (j = 0; j < 32; j++)
632
        {
633
1536
          if (array[i] & (1 << j))
634
            {
635
15
              if (out_count == out_size - 2)
636
                {
637
3
                  out_size <<= 1;
638
3
                  out = (int *) g_realloc (out, out_size * sizeof (int));
639
                }
640
15
              out[out_count++] = i * 32 + j;
641
            }
642
        }
643
    }
644
16
  out[out_count] = BITARRAY_SEQ_TERM;
645
16
  *ret = out;
646
16
  return TRUE;
647
}
648

            
649
static dbus_bool_t
650
8
read_mr (DBusMessageIter *iter, MatchRulePrivate *mrp)
651
{
652
  DBusMessageIter iter_struct, iter_array, iter_dict, iter_dict_entry;
653
  dbus_uint32_t *array;
654
  dbus_int32_t matchType;
655
  int array_count;
656
  AtkAttribute *attr;
657
  int i;
658

            
659
8
  dbus_message_iter_recurse (iter, &iter_struct);
660

            
661
  /* states */
662
8
  dbus_message_iter_recurse (&iter_struct, &iter_array);
663
8
  dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count);
664
8
  bitarray_to_seq (array, array_count, &mrp->states);
665
22
  for (i = 0; mrp->states[i] != BITARRAY_SEQ_TERM; i++)
666
    {
667
14
      mrp->states[i] = spi_atk_state_from_spi_state (mrp->states[i]);
668
    }
669
8
  dbus_message_iter_next (&iter_struct);
670
8
  dbus_message_iter_get_basic (&iter_struct, &matchType);
671
8
  dbus_message_iter_next (&iter_struct);
672
8
  mrp->statematchtype = matchType;
673
  ;
674

            
675
  /* attributes */
676
8
  mrp->attributes = NULL;
677
8
  dbus_message_iter_recurse (&iter_struct, &iter_dict);
678
9
  while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID)
679
    {
680
      const char *key, *val;
681
      const char *p, *q;
682
1
      dbus_message_iter_recurse (&iter_dict, &iter_dict_entry);
683
1
      dbus_message_iter_get_basic (&iter_dict_entry, &key);
684
1
      dbus_message_iter_next (&iter_dict_entry);
685
1
      dbus_message_iter_get_basic (&iter_dict_entry, &val);
686
1
      p = q = val;
687
      for (;;)
688
        {
689
5
          if (*q == '\0' || (*q == ':' && (q == val || q[-1] != '\\')))
690
            {
691
              char *tmp;
692
1
              attr = g_new (AtkAttribute, 1);
693
2
              attr->name = g_strdup (key);
694
1
              attr->value = g_strdup (p);
695
1
              attr->value[q - p] = '\0';
696
1
              tmp = attr->value;
697
5
              while (*tmp != '\0')
698
                {
699
4
                  if (*tmp == '\\')
700
                    memmove (tmp, tmp + 1, strlen (tmp));
701
                  else
702
4
                    tmp++;
703
                }
704
1
              mrp->attributes = g_slist_prepend (mrp->attributes, attr);
705
1
              if (*q == '\0')
706
1
                break;
707
              else
708
                p = ++q;
709
            }
710
          else
711
4
            q++;
712
        }
713
1
      dbus_message_iter_next (&iter_dict);
714
    }
715
8
  dbus_message_iter_next (&iter_struct);
716
8
  dbus_message_iter_get_basic (&iter_struct, &matchType);
717
8
  mrp->attributematchtype = matchType;
718
  ;
719
8
  dbus_message_iter_next (&iter_struct);
720

            
721
  /* Get roles and role match */
722
8
  dbus_message_iter_recurse (&iter_struct, &iter_array);
723
8
  dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count);
724
8
  bitarray_to_seq (array, array_count, &mrp->roles);
725
8
  dbus_message_iter_next (&iter_struct);
726
8
  dbus_message_iter_get_basic (&iter_struct, &matchType);
727
8
  mrp->rolematchtype = matchType;
728
  ;
729
8
  dbus_message_iter_next (&iter_struct);
730

            
731
  /* Get interfaces and interface match */
732
8
  dbus_message_iter_recurse (&iter_struct, &iter_array);
733
8
  mrp->ifaces = g_new0 (gchar *, 16);
734
8
  i = 0;
735
11
  while (i < 15 && dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
736
    {
737
      char *iface;
738
3
      dbus_message_iter_get_basic (&iter_array, &iface);
739
3
      mrp->ifaces[i] = g_strdup (iface);
740
3
      i++;
741
3
      dbus_message_iter_next (&iter_array);
742
    }
743
8
  dbus_message_iter_next (&iter_struct);
744
8
  dbus_message_iter_get_basic (&iter_struct, &matchType);
745
8
  mrp->interfacematchtype = matchType;
746
  ;
747
8
  dbus_message_iter_next (&iter_struct);
748
  /* get invert */
749
8
  dbus_message_iter_get_basic (&iter_struct, &mrp->invert);
750
8
  dbus_message_iter_next (iter);
751
8
  return TRUE;
752
}
753

            
754
static DBusMessage *
755
8
return_and_free_array (DBusMessage *message, GPtrArray *arr, gboolean reverse_order)
756
{
757
  DBusMessage *reply;
758
  DBusMessageIter iter, iter_array;
759
  guint ii;
760

            
761
8
  reply = dbus_message_new_method_return (message);
762
8
  if (!reply)
763
    return NULL;
764
8
  dbus_message_iter_init_append (reply, &iter);
765
8
  if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array))
766
    goto oom;
767
25
  for (ii = 0; ii < arr->len; ii++)
768
    {
769
17
      AtkObject *obj = ATK_OBJECT (g_ptr_array_index (arr, reverse_order ? arr->len - ii - 1 : ii));
770
17
      spi_object_append_reference (&iter_array, obj);
771
    }
772
8
  if (!dbus_message_iter_close_container (&iter, &iter_array))
773
    goto oom;
774
8
  g_ptr_array_unref (arr);
775
8
  return reply;
776
oom:
777
  // TODO: Handle out of memory
778
  g_ptr_array_unref (arr);
779
  return reply;
780
}
781

            
782
static void
783
8
free_mrp_data (MatchRulePrivate *mrp)
784
{
785
8
  g_free (mrp->states);
786
8
  atk_attribute_set_free (mrp->attributes);
787
8
  g_free (mrp->roles);
788
8
  g_strfreev (mrp->ifaces);
789
8
}
790

            
791
static DBusMessage *
792
GetMatchesFrom (DBusMessage *message,
793
                AtkObject *current_object,
794
                MatchRulePrivate *mrp,
795
                const AtspiCollectionSortOrder sortby,
796
                const dbus_bool_t isrestrict,
797
                dbus_int32_t count,
798
                const dbus_bool_t traverse)
799
{
800
  GPtrArray *arr;
801
  AtkObject *parent;
802
  glong index = atk_object_get_index_in_parent (current_object);
803

            
804
  arr = g_ptr_array_new_with_free_func (g_object_unref);
805

            
806
  if (!isrestrict)
807
    {
808
      parent = atk_object_get_parent (current_object);
809
      query_exec (mrp, sortby, arr, 0, count, parent, index,
810
                  FALSE, NULL, traverse);
811
    }
812
  else
813
    query_exec (mrp, sortby, arr, 0, count,
814
                current_object, 0, FALSE, NULL, traverse);
815

            
816
  free_mrp_data (mrp);
817
  return return_and_free_array (message, arr, sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL);
818
}
819

            
820
/*
821
  inorder traversal from a given object in the hierarchy
822
*/
823

            
824
static int
825
6
inorder (AtkObject *collection, MatchRulePrivate *mrp, GPtrArray *arr, gint kount, gint max, AtkObject *obj, gboolean flag, AtkObject *pobj, dbus_bool_t traverse)
826
{
827
6
  int i = 0;
828

            
829
  /* First, look through the children recursively. */
830
6
  kount = sort_order_canonical (mrp, arr, kount, max, obj, 0, TRUE,
831
                                NULL, TRUE);
832

            
833
  /* Next, we look through the right subtree */
834
12
  while ((max == 0 || kount < max) && obj && obj != collection)
835
    {
836
6
      AtkObject *parent = atk_object_get_parent (obj);
837
6
      i = atk_object_get_index_in_parent (obj);
838
6
      kount = sort_order_canonical (mrp, arr, kount, max, parent,
839
6
                                    i + 1, TRUE, FALSE, TRUE);
840
6
      obj = parent;
841
    }
842

            
843
6
  return kount;
844
}
845

            
846
/*
847
  GetMatchesInOrder: get matches from a given object in an inorder traversal.
848
*/
849

            
850
static DBusMessage *
851
6
GetMatchesInOrder (DBusMessage *message,
852
                   AtkObject *current_object,
853
                   MatchRulePrivate *mrp,
854
                   const AtspiCollectionSortOrder sortby,
855
                   const dbus_bool_t recurse,
856
                   dbus_int32_t count,
857
                   const dbus_bool_t traverse)
858
{
859
  GPtrArray *arr;
860
  AtkObject *obj;
861

            
862
6
  arr = g_ptr_array_new_with_free_func (g_object_unref);
863

            
864
6
  obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
865

            
866
6
  inorder (obj, mrp, arr, 0, count,
867
           current_object, TRUE, NULL, traverse);
868

            
869
6
  free_mrp_data (mrp);
870
6
  return return_and_free_array (message, arr, sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL);
871
}
872

            
873
/*
874
  GetMatchesInBackOrder: get matches from a given object in a
875
  reverse order traversal.
876
*/
877

            
878
static DBusMessage *
879
1
GetMatchesInBackOrder (DBusMessage *message,
880
                       AtkObject *current_object,
881
                       MatchRulePrivate *mrp,
882
                       const AtspiCollectionSortOrder sortby,
883
                       dbus_int32_t count)
884
{
885
  GPtrArray *arr;
886
  AtkObject *collection;
887

            
888
1
  arr = g_ptr_array_new_with_free_func (g_object_unref);
889

            
890
1
  collection = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
891

            
892
1
  sort_order_rev_canonical (mrp, arr, 0, count, current_object,
893
                            FALSE, collection);
894

            
895
1
  free_mrp_data (mrp);
896
1
  return return_and_free_array (message, arr, sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL);
897
}
898

            
899
static DBusMessage *
900
GetMatchesTo (DBusMessage *message,
901
              AtkObject *current_object,
902
              MatchRulePrivate *mrp,
903
              const AtspiCollectionSortOrder sortby,
904
              const dbus_bool_t recurse,
905
              const dbus_bool_t isrestrict,
906
              dbus_int32_t count,
907
              const dbus_bool_t traverse)
908
{
909
  GPtrArray *arr;
910
  AtkObject *obj;
911

            
912
  arr = g_ptr_array_new_with_free_func (g_object_unref);
913

            
914
  if (recurse)
915
    {
916
      obj = ATK_OBJECT (atk_object_get_parent (current_object));
917
      query_exec (mrp, sortby, arr, 0, count,
918
                  obj, 0, TRUE, current_object, traverse);
919
    }
920
  else
921
    {
922
      obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
923
      query_exec (mrp, sortby, arr, 0, count,
924
                  obj, 0, TRUE, current_object, traverse);
925
    }
926

            
927
  free_mrp_data (mrp);
928
  return return_and_free_array (message, arr, sortby != ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL);
929
}
930

            
931
static DBusMessage *
932
6
impl_GetMatchesFrom (DBusConnection *bus, DBusMessage *message, void *user_data)
933
{
934
6
  char *current_object_path = NULL;
935
  AtkObject *current_object;
936
  DBusMessageIter iter;
937
  MatchRulePrivate rule;
938
  dbus_uint32_t sortby;
939
  dbus_uint32_t tree;
940
  dbus_int32_t count;
941
  dbus_bool_t traverse;
942
  const char *signature;
943

            
944
6
  signature = dbus_message_get_signature (message);
945
6
  if (strcmp (signature, "o(aiia{ss}iaiiasib)uuib") != 0)
946
    {
947
      return droute_invalid_arguments_error (message);
948
    }
949

            
950
6
  dbus_message_iter_init (message, &iter);
951
6
  dbus_message_iter_get_basic (&iter, &current_object_path);
952
6
  current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path));
953
6
  if (!current_object)
954
    {
955
      // TODO: object-not-found error
956
      return spi_dbus_general_error (message);
957
    }
958
6
  dbus_message_iter_next (&iter);
959
6
  if (!read_mr (&iter, &rule))
960
    {
961
      return spi_dbus_general_error (message);
962
    }
963
6
  dbus_message_iter_get_basic (&iter, &sortby);
964
6
  dbus_message_iter_next (&iter);
965
6
  dbus_message_iter_get_basic (&iter, &tree);
966
6
  dbus_message_iter_next (&iter);
967
6
  dbus_message_iter_get_basic (&iter, &count);
968
6
  dbus_message_iter_next (&iter);
969
6
  dbus_message_iter_get_basic (&iter, &traverse);
970
6
  dbus_message_iter_next (&iter);
971

            
972
6
  switch (tree)
973
    {
974
    case ATSPI_Collection_TREE_RESTRICT_CHILDREN:
975
      return GetMatchesFrom (message, current_object,
976
                             &rule, sortby, TRUE, count, traverse);
977
      break;
978
    case ATSPI_Collection_TREE_RESTRICT_SIBLING:
979
      return GetMatchesFrom (message, current_object,
980
                             &rule, sortby, FALSE, count, traverse);
981
      break;
982
6
    case ATSPI_Collection_TREE_INORDER:
983
6
      return GetMatchesInOrder (message, current_object,
984
                                &rule, sortby, TRUE, count, traverse);
985
      break;
986
    default:
987
      free_mrp_data (&rule);
988
      return NULL;
989
    }
990
}
991

            
992
static DBusMessage *
993
1
impl_GetMatchesTo (DBusConnection *bus, DBusMessage *message, void *user_data)
994
{
995
1
  char *current_object_path = NULL;
996
  AtkObject *current_object;
997
  DBusMessageIter iter;
998
  MatchRulePrivate rule;
999
  dbus_uint32_t sortby;
  dbus_uint32_t tree;
  dbus_bool_t recurse;
  dbus_int32_t count;
  dbus_bool_t traverse;
  const char *signature;
1
  signature = dbus_message_get_signature (message);
1
  if (strcmp (signature, "o(aiia{ss}iaiiasib)uubib") != 0)
    {
      return droute_invalid_arguments_error (message);
    }
1
  dbus_message_iter_init (message, &iter);
1
  dbus_message_iter_get_basic (&iter, &current_object_path);
1
  current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path));
1
  if (!current_object)
    {
      // TODO: object-not-found error
      return spi_dbus_general_error (message);
    }
1
  dbus_message_iter_next (&iter);
1
  if (!read_mr (&iter, &rule))
    {
      return spi_dbus_general_error (message);
    }
1
  dbus_message_iter_get_basic (&iter, &sortby);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &tree);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &recurse);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &count);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &traverse);
1
  dbus_message_iter_next (&iter);
1
  switch (tree)
    {
    case ATSPI_Collection_TREE_RESTRICT_CHILDREN:
      return GetMatchesTo (message, current_object,
                           &rule, sortby, recurse, TRUE, count, traverse);
      break;
    case ATSPI_Collection_TREE_RESTRICT_SIBLING:
      return GetMatchesTo (message, current_object,
                           &rule, sortby, recurse, FALSE, count, traverse);
      break;
1
    case ATSPI_Collection_TREE_INORDER:
1
      return GetMatchesInBackOrder (message, current_object,
                                    &rule, sortby, count);
      break;
    default:
      free_mrp_data (&rule);
      return NULL;
    }
}
static void
append_accessible_properties (DBusMessageIter *iter, AtkObject *obj, GArray *properties)
{
  DBusMessageIter iter_struct, iter_dict, iter_dict_entry;
  AtkStateSet *set;
  gint i;
  gint count;
  dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &iter_struct);
  spi_object_append_reference (&iter_struct, obj);
  dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_dict);
  if (properties && properties->len)
    {
      gint i;
      for (i = 0; i < properties->len; i++)
        {
          gchar *prop = g_array_index (properties, char *, i);
          DRoutePropertyFunction func;
          GType type;
          func = _atk_bridge_find_property_func (prop, &type);
          if (func && G_TYPE_CHECK_INSTANCE_TYPE (obj, type))
            {
              dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY,
                                                NULL, &iter_dict_entry);
              dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop);
              func (&iter_dict_entry, obj);
              dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
            }
        }
    }
  else
    {
      GHashTableIter hi;
      gpointer key, value;
      g_hash_table_iter_init (&hi, spi_global_app_data->property_hash);
      while (g_hash_table_iter_next (&hi, &key, &value))
        {
          const DRouteProperty *prop = value;
          GType type = _atk_bridge_type_from_iface (key);
          if (!G_TYPE_CHECK_INSTANCE_TYPE (obj, type))
            continue;
          for (; prop->name; prop++)
            {
              const char *p = key + strlen (key);
              gchar *property_name;
              while (p[-1] != '.')
                p--;
              if (!strcmp (p, "Accessible"))
                property_name = g_strdup (prop->name);
              else
                property_name = g_strconcat (p, ".", prop->name, NULL);
              dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY,
                                                NULL, &iter_dict_entry);
              dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &property_name);
              g_free (property_name);
              prop->get (&iter_dict_entry, obj);
              dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
            }
        }
    }
  dbus_message_iter_close_container (&iter_struct, &iter_dict);
  dbus_message_iter_close_container (iter, &iter_struct);
  set = atk_object_ref_state_set (obj);
  if (set)
    {
      gboolean md = atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS);
      g_object_unref (set);
      if (md)
        return;
    }
  count = atk_object_get_n_accessible_children (obj);
  if (count > ATSPI_MAX_CHILDREN)
    count = ATSPI_MAX_CHILDREN;
  for (i = 0; i < count; i++)
    {
      AtkObject *child = atk_object_ref_accessible_child (obj, i);
      if (child)
        {
          append_accessible_properties (iter, child, properties);
          g_object_unref (child);
        }
    }
}
static DBusMessage *
impl_GetTree (DBusConnection *bus,
              DBusMessage *message,
              void *user_data)
{
  AtkObject *object = (AtkObject *) user_data;
  DBusMessage *reply;
  DBusMessageIter iter, iter_array;
  MatchRulePrivate rule;
  GArray *properties;
  g_return_val_if_fail (ATK_IS_OBJECT (user_data),
                        droute_not_yet_handled_error (message));
  if (strcmp (dbus_message_get_signature (message), "(aiia{ss}iaiiasib)as") != 0)
    return droute_invalid_arguments_error (message);
  properties = g_array_new (TRUE, TRUE, sizeof (char *));
  dbus_message_iter_init (message, &iter);
  if (!read_mr (&iter, &rule))
    {
      return spi_dbus_general_error (message);
    }
  dbus_message_iter_recurse (&iter, &iter_array);
  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
    {
      const char *prop;
      dbus_message_iter_get_basic (&iter_array, &prop);
      g_array_append_val (properties, prop);
      dbus_message_iter_next (&iter_array);
    }
  reply = dbus_message_new_method_return (message);
  if (reply)
    {
      dbus_message_iter_init_append (reply, &iter);
      dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "((so)a{sv})",
                                        &iter_array);
      append_accessible_properties (&iter_array, object, properties);
      dbus_message_iter_close_container (&iter, &iter_array);
    }
  free_mrp_data (&rule);
  return reply;
}
static DBusMessage *
1
impl_GetMatches (DBusConnection *bus, DBusMessage *message, void *user_data)
{
1
  AtkObject *obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
  DBusMessageIter iter;
  MatchRulePrivate rule;
  dbus_uint32_t sortby;
  dbus_int32_t count;
  dbus_bool_t traverse;
  GPtrArray *arr;
  const char *signature;
1
  signature = dbus_message_get_signature (message);
1
  if (strcmp (signature, "(aiia{ss}iaiiasib)uib") != 0)
    {
      return droute_invalid_arguments_error (message);
    }
1
  dbus_message_iter_init (message, &iter);
1
  if (!read_mr (&iter, &rule))
    {
      return spi_dbus_general_error (message);
    }
1
  dbus_message_iter_get_basic (&iter, &sortby);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &count);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &traverse);
1
  dbus_message_iter_next (&iter);
1
  arr = g_ptr_array_new_with_free_func (g_object_unref);
1
  count = query_exec (&rule, sortby, arr, 0, count,
                      obj, 0, TRUE, NULL, traverse);
1
  free_mrp_data (&rule);
1
  return return_and_free_array (message, arr, sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL);
}
static dbus_bool_t
impl_get_Version (DBusMessageIter *iter, void *user_data)
{
  return droute_return_v_uint32 (iter, SPI_DBUS_COLLECTION_VERSION);
}
static DRouteMethod methods[] = {
  { impl_GetMatchesFrom, "GetMatchesFrom" },
  { impl_GetMatchesTo, "GetMatchesTo" },
  { impl_GetTree, "GetTree" },
  { impl_GetMatches, "GetMatches" },
  { NULL, NULL }
};
static DRouteProperty properties[] = {
  { impl_get_Version, NULL, "version" },
  { NULL, NULL, NULL }
};
void
161
spi_initialize_collection (DRoutePath *path)
{
161
  spi_atk_add_interface (path,
                         ATSPI_DBUS_INTERFACE_COLLECTION, spi_org_a11y_atspi_Collection,
                         methods, properties);
161
};