Add navigation helper functions for screen-reader and friends (part 2)
[platform/upstream/at-spi2-atk.git] / atk-adaptor / adaptors / accessible-adaptor.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  * Copyright 2001, 2002 Sun Microsystems Inc.,
7  * Copyright 2001, 2002 Ximian, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 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  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <atk/atk.h>
26 #include <droute/droute.h>
27 #include "bridge.h"
28
29 #include "atspi/atspi.h"
30 #include "spi-dbus.h"
31 #include "accessible-stateset.h"
32 #include "accessible-register.h"
33 #include "object.h"
34 #include "introspection.h"
35 #include <string.h>
36 #include <stdint.h>
37 #include <stdlib.h>
38
39 // TIZEN_ONLY(20170310) - implementation of get object under coordinates for accessibility
40 typedef struct {
41    void **objects;
42    unsigned int capacity;
43    unsigned int size;
44 } vector;
45
46 typedef enum {
47   NEIGHBOR_SEARCH_MODE_NORMAL = 0,
48   NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1,
49   NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING = 2,
50 } GetNeighborSearchMode;
51
52 typedef struct accessibility_navigation_pointer_table {
53    AtspiRole (*object_get_role)(struct accessibility_navigation_pointer_table *t, void *ptr);
54    uint64_t (*object_get_state_set)(struct accessibility_navigation_pointer_table *t, void *ptr);
55    void *(*get_object_in_relation_by_type)(struct accessibility_navigation_pointer_table *t, void *ptr, AtspiRelationType type);
56    unsigned char (*object_is_zero_size)(struct accessibility_navigation_pointer_table *t, void *ptr);
57    void *(*get_parent)(struct accessibility_navigation_pointer_table *t, void *ptr);
58    unsigned char (*object_is_scrollable)(struct accessibility_navigation_pointer_table *t, void *ptr);
59    void *(*get_object_at_point)(struct accessibility_navigation_pointer_table *t, void *ptr, int x, int y, unsigned char coordinates_are_screen_based);
60    unsigned char (*object_contains)(struct accessibility_navigation_pointer_table *t, void *ptr, int x, int y, unsigned char coordinates_are_screen_based);
61    unsigned char (*object_is_proxy)(struct accessibility_navigation_pointer_table *t, void *ptr);
62    void (*get_children)(struct accessibility_navigation_pointer_table *t, void *ptr, vector *v);
63 } accessibility_navigation_pointer_table;
64
65 static vector vector_init(void)
66 {
67    vector v;
68    v.objects = NULL;
69    v.capacity = 0;
70    v.size = 0;
71    return v;
72 }
73
74 static void vector_free(vector *v)
75 {
76    free(v->objects);
77    v->objects = NULL;
78    v->capacity = 0;
79    v->size = 0;
80 }
81 static void vector_reserve(vector *v, unsigned int s)
82 {
83    if (s > v->capacity)
84      {
85        v->objects = (void**)realloc(v->objects, sizeof(v->objects[0]) * s);
86        v->capacity = s;
87      }
88 }
89
90 static void vector_resize(vector *v, unsigned int s)
91 {
92    vector_reserve(v, s);
93    v->size = s;
94 }
95
96 static void *vector_get(vector *v, unsigned int index)
97 {
98    return v->objects[index];
99 }
100
101 static void vector_set(vector *v, unsigned int index, void *data)
102 {
103    v->objects[index] = data;
104 }
105
106 static unsigned int vector_size(vector *v)
107 {
108    return v->size;
109 }
110
111 #define CALL(fncname, ...) table->fncname(table, __VA_ARGS__)
112 static unsigned char _accept_object_check_role(accessibility_navigation_pointer_table *table, void *obj)
113 {
114    AtspiRole role = CALL(object_get_role, obj);
115    switch (role)
116      {
117        case ATSPI_ROLE_APPLICATION:
118        case ATSPI_ROLE_FILLER:
119        case ATSPI_ROLE_SCROLL_PANE:
120        case ATSPI_ROLE_SPLIT_PANE:
121        case ATSPI_ROLE_WINDOW:
122        case ATSPI_ROLE_IMAGE:
123        case ATSPI_ROLE_LIST:
124        case ATSPI_ROLE_ICON:
125        case ATSPI_ROLE_TOOL_BAR:
126        case ATSPI_ROLE_REDUNDANT_OBJECT:
127        case ATSPI_ROLE_COLOR_CHOOSER:
128        case ATSPI_ROLE_PANEL:
129        case ATSPI_ROLE_TREE_TABLE:
130        case ATSPI_ROLE_PAGE_TAB_LIST:
131        case ATSPI_ROLE_PAGE_TAB:
132        case ATSPI_ROLE_SPIN_BUTTON:
133        case ATSPI_ROLE_INPUT_METHOD_WINDOW:
134        case ATSPI_ROLE_EMBEDDED:
135        case ATSPI_ROLE_INVALID:
136        case ATSPI_ROLE_NOTIFICATION:
137          return 0;
138        default:
139          break;
140      }
141    return 1;
142 }
143
144 static unsigned char _state_set_is_set(uint64_t state_set, AtspiStateType state)
145 {
146    return (state_set & ((uint64_t)1 << (unsigned int)state)) != 0;
147 }
148
149 static unsigned char _object_is_defunct(accessibility_navigation_pointer_table *table, void *ptr)
150 {
151    uint64_t states = CALL(object_get_state_set, ptr);
152    return _state_set_is_set(states, ATSPI_STATE_DEFUNCT);
153 }
154
155 static unsigned char _object_role_is_acceptable_when_navigating_next_prev(accessibility_navigation_pointer_table *table, void *obj)
156 {
157    AtspiRole role = CALL(object_get_role, obj);
158    return role != ATSPI_ROLE_POPUP_MENU && role != ATSPI_ROLE_DIALOG;
159 }
160
161 static void *_get_object_in_relation_flow(accessibility_navigation_pointer_table *table, void *source, unsigned char forward)
162 {
163     return CALL(get_object_in_relation_by_type, source, forward ? ATSPI_RELATION_FLOWS_TO : ATSPI_RELATION_FLOWS_FROM);
164 }
165
166 static unsigned char _object_is_item(accessibility_navigation_pointer_table *table, void *obj)
167 {
168    AtspiRole role = CALL(object_get_role, obj);
169    return role == ATSPI_ROLE_LIST_ITEM || role == ATSPI_ROLE_MENU_ITEM;
170 }
171
172 static unsigned char _object_is_highlightable(accessibility_navigation_pointer_table *table, void *obj)
173 {
174    uint64_t state_set = CALL(object_get_state_set, obj);
175    return _state_set_is_set(state_set, ATSPI_STATE_HIGHLIGHTABLE);
176 }
177
178 static unsigned char _object_is_showing(accessibility_navigation_pointer_table *table, void *obj)
179 {
180    uint64_t state_set = CALL(object_get_state_set, obj);
181    return _state_set_is_set(state_set, ATSPI_STATE_SHOWING);
182 }
183
184 static unsigned char _object_is_collapsed(accessibility_navigation_pointer_table *table, void *obj)
185 {
186    uint64_t state_set = CALL(object_get_state_set, obj);
187    return
188       _state_set_is_set(state_set, ATSPI_STATE_EXPANDABLE) &&
189       !_state_set_is_set(state_set, ATSPI_STATE_EXPANDED);
190 }
191
192 static unsigned char _object_has_modal_state(accessibility_navigation_pointer_table *table, void *obj)
193 {
194    uint64_t state_set = CALL(object_get_state_set, obj);
195    return _state_set_is_set(state_set, ATSPI_STATE_MODAL);
196 }
197
198 static unsigned char _object_is_zero_size(accessibility_navigation_pointer_table *table, void *obj)
199 {
200    return CALL(object_is_zero_size, obj);
201 }
202
203 static void *_get_scrollable_parent(accessibility_navigation_pointer_table *table, void *obj)
204 {
205    while(obj)
206      {
207        obj = CALL(get_parent, obj);
208        if (obj && CALL(object_is_scrollable, obj)) return obj;
209      }
210    return NULL;
211 }
212 static unsigned char _accept_object(accessibility_navigation_pointer_table *table, void *obj)
213 {
214    if (!obj) return 0;
215    if (!_accept_object_check_role(table, obj)) return 0;
216    if (CALL(get_object_in_relation_by_type, obj, ATSPI_RELATION_CONTROLLED_BY) != NULL) return 0;
217    if (!_object_is_highlightable(table, obj)) return 0;
218
219    if (_get_scrollable_parent(table, obj) != NULL)
220      {
221        void *parent = CALL(get_parent, obj);
222
223        if (parent)
224          {
225            return !_object_is_item(table, obj) || !_object_is_collapsed(table, parent);
226          }
227      }
228    else
229      {
230        if (_object_is_zero_size(table, obj)) return 0;
231        if (!_object_is_showing(table, obj)) return 0;
232      }
233    return 1;
234 }
235
236 static void *_calculate_navigable_accessible_at_point_impl(accessibility_navigation_pointer_table *table,
237           void *root, int x, int y, unsigned char coordinates_are_screen_based)
238 {
239    if (!root) return NULL;
240
241    void *return_value = NULL;
242    while (1)
243      {
244        void *target = CALL(get_object_at_point, root, x, y, coordinates_are_screen_based);
245        if (!target) break;
246
247        // always return proxy, so atspi lib can call on it again
248        if (CALL(object_is_proxy, target)) return target;
249
250        root = target;
251        void *relation_obj = CALL(get_object_in_relation_by_type, root, ATSPI_RELATION_CONTROLLED_BY);
252        unsigned char contains = 0;
253        if (relation_obj)
254          {
255            contains = CALL(object_contains, relation_obj, x, y, coordinates_are_screen_based);
256            if (contains) root = relation_obj;
257          }
258
259        if (_accept_object(table, root))
260          {
261            return_value = root;
262            if (contains) break;
263          }
264      }
265
266    if (return_value && _object_has_modal_state(table, return_value)) return_value = NULL;
267    return return_value;
268 }
269
270
271
272
273
274
275
276 static void *_find_non_defunct_child(accessibility_navigation_pointer_table *table,
277             vector *objects, unsigned int current_index, unsigned char forward)
278 {
279    for(; current_index < vector_size(objects); forward ? ++current_index : --current_index)
280      {
281        void *n = vector_get(objects, current_index);
282        if (n && !_object_is_defunct(table, n)) return n;
283      }
284    return NULL;
285 }
286
287 static void *_directional_depth_first_search_try_non_defunct_child(accessibility_navigation_pointer_table *table,
288             void *node, vector *children, unsigned char forward)
289 {
290    if (vector_size(children) > 0)
291      {
292        unsigned char is_showing = _get_scrollable_parent(table, node) == NULL ? _object_is_showing(table, node) : 1;
293        if (is_showing)
294          {
295            return _find_non_defunct_child(table, children, forward ? 0 : vector_size(children) - 1, forward);
296          }
297      }
298    return NULL;
299 }
300
301 static void *_get_next_non_defunct_sibling(accessibility_navigation_pointer_table *table,
302             void *obj, unsigned char forward)
303 {
304    if (!obj) return NULL;
305    void *parent = CALL(get_parent, obj);
306    if (!parent) return NULL;
307
308    vector children = vector_init();
309    CALL(get_children, parent, &children);
310    if (vector_size(&children) == 0)
311      {
312        vector_free(&children);
313        return NULL;
314      }
315    unsigned int current = 0;
316    for(; current < vector_size(&children) && vector_get(&children, current) != obj; ++current) ;
317    if (current >= vector_size(&children))
318      {
319        vector_free(&children);
320        return NULL;
321      }
322    forward ? ++current : --current;
323    void *ret = _find_non_defunct_child(table, &children, current, forward);
324    vector_free(&children);
325    return ret;
326 }
327
328 static void *_directional_depth_first_search_try_non_defunct_sibling(accessibility_navigation_pointer_table *table,
329             unsigned char *all_children_visited_ptr, void *node, void *root, unsigned char forward)
330 {
331    while(1)
332      {
333        void *sibling = _get_next_non_defunct_sibling(table, node, forward);
334        if (sibling != NULL)
335          {
336            node = sibling;
337            *all_children_visited_ptr = 0;
338            break;
339          }
340
341        // walk up...
342        node = CALL(get_parent, node);
343        if (node == NULL || node == root) return NULL;
344
345        // in backward traversing stop the walk up on parent
346        if (!forward) break;
347      }
348    return node;
349 }
350
351 typedef struct {
352     const void *key;
353     unsigned int current_search_size;
354     unsigned int counter;
355 } cycle_detection_data;
356
357 void cycle_detection_initialize(cycle_detection_data *data, const void *key)
358 {
359    if (!data) return;
360    data->key = key;
361    data->current_search_size = 1;
362    data->counter = 1;
363 }
364
365 unsigned char cycle_detection_check_if_in_cycle(cycle_detection_data *data, const void *key)
366 {
367    if (!data) return 1;
368    if (data->key == key) return 1;
369    if (--data->counter == 0)
370      {
371        data->current_search_size <<= 1;
372        if (data->current_search_size == 0) return 1;
373        data->counter = data->current_search_size;
374        data->key = key;
375      }
376    return 0;
377 }
378
379 static void *_calculate_neighbor_impl(accessibility_navigation_pointer_table *table, void *root, void *start, unsigned char forward, GetNeighborSearchMode search_mode)
380 {
381    if (root && _object_is_defunct(table, root)) return NULL;
382    if (start && _object_is_defunct(table, start))
383      {
384        start = root;
385        forward = 1;
386      }
387    void *node = start;
388    if (!node) return NULL;
389
390    // initialization of all-children-visiten flag for start node - we assume
391    // that when we begin at start node and we navigate backward, then all children
392    // are visited, so navigation will ignore start's children and go to
393    // previous sibling available.
394    unsigned char all_children_visited = search_mode != NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT && !forward;
395    unsigned char force_next = search_mode == NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
396
397    cycle_detection_data cycle_detection;
398    cycle_detection_initialize(&cycle_detection, node);
399    while (node)
400      {
401        if (_object_is_defunct(table, node)) return NULL;
402
403        // always accept proxy object from different world
404        if (!force_next && CALL(object_is_proxy, node)) return node;
405
406        vector children = vector_init();
407        CALL(get_children, node, &children);
408
409        // do accept:
410        // 1. not start node
411        // 2. parent after all children in backward traversing
412        // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element.
413        //    Objects with those roles shouldnt be reachable, when navigating next / prev.
414        unsigned char all_children_visited_or_moving_forward = (vector_size(&children) == 0 || forward || all_children_visited);
415        if (!force_next && node != start && all_children_visited_or_moving_forward && _accept_object(table, node))
416          {
417            if (start == NULL || _object_role_is_acceptable_when_navigating_next_prev(table, node))
418              {
419                vector_free(&children);
420                return node;
421              }
422          }
423
424        void *next_related_in_direction = !force_next ? _get_object_in_relation_flow(table, node, forward) : NULL;
425
426        if (next_related_in_direction && _object_is_defunct(table, next_related_in_direction))
427            next_related_in_direction = NULL;
428        unsigned char want_cycle_detection = 0;
429        if (next_related_in_direction)
430          {
431            node = next_related_in_direction;
432            want_cycle_detection = 1;
433          }
434        else {
435            void *child = !force_next && !all_children_visited ?
436                           _directional_depth_first_search_try_non_defunct_child(table, node, &children, forward) : NULL;
437            if (child != NULL) want_cycle_detection = 1;
438            else
439              {
440                if (!force_next && node == root)
441                  {
442                    vector_free(&children);
443                    return NULL;
444                  }
445                all_children_visited = 1;
446                child = _directional_depth_first_search_try_non_defunct_sibling(table, &all_children_visited, node, root, forward);
447              }
448            node = child;
449        }
450
451        force_next = 0;
452        if (want_cycle_detection && cycle_detection_check_if_in_cycle(&cycle_detection, node))
453          {
454            vector_free(&children);
455            return NULL;
456          }
457        vector_free(&children);
458      }
459    return NULL;
460 }
461
462
463
464
465 static AtspiRelationType spi_relation_type_from_atk_relation_type (AtkRelationType type);
466 #define MARK_AS_UNUSED(v) do { (void)(v); } while(0)
467
468 static AtspiRole _object_get_role_impl(accessibility_navigation_pointer_table *table, void *ptr)
469 {
470   MARK_AS_UNUSED(table);
471   AtkObject *obj = (AtkObject*)ptr;
472   AtkRole role = atk_object_get_role (obj);
473   AtspiRole rv = spi_accessible_role_from_atk_role (role);
474   return rv;
475 }
476
477 static uint64_t _object_get_state_set_impl(accessibility_navigation_pointer_table *table, void *ptr)
478 {
479   MARK_AS_UNUSED(table);
480   dbus_uint32_t array[2];
481   spi_atk_state_to_dbus_array((AtkObject*)ptr, array);
482   uint64_t res = array[0] | ((uint64_t)array[1] << 32);
483   return res;
484 }
485
486 static void *_get_object_in_relation_by_type_impl(accessibility_navigation_pointer_table *table, void *ptr, AtspiRelationType expected_relation_type)
487 {
488   MARK_AS_UNUSED(table);
489   if (ptr) {
490     g_return_val_if_fail (ATK_IS_OBJECT (ptr), NULL);
491     AtkRelationSet *set = atk_object_ref_relation_set (ptr);
492     if (!set)
493       return NULL;
494     gint count = atk_relation_set_get_n_relations (set), i;
495     for (i = 0; i < count; i++)
496     {
497       AtkRelation *r = atk_relation_set_get_relation (set, i);
498       if (!r)
499         continue;
500       AtkRelationType rt = atk_relation_get_relation_type (r);
501       AtspiRelationType type = spi_relation_type_from_atk_relation_type (rt);
502       if (expected_relation_type == type)
503       {
504         GPtrArray *target = atk_relation_get_target (r);
505         if (target && target->len > 0)
506           return target->pdata[0];
507       }
508     }
509     g_object_unref (set);
510   }
511   return NULL;
512 }
513
514 static unsigned char _object_is_zero_size_impl(accessibility_navigation_pointer_table *table, void *ptr)
515 {
516   MARK_AS_UNUSED(table);
517   AtkObject *obj = (AtkObject*)ptr;
518   g_return_val_if_fail(ATK_IS_COMPONENT (obj), 0);
519   gint x, y, w, h;
520   atk_component_get_extents (ATK_COMPONENT(obj), &x, &y, &w, &h, ATSPI_COORD_TYPE_SCREEN);
521   return w == 0 || h == 0;
522 }
523
524 unsigned char _object_is_scrollable_impl(accessibility_navigation_pointer_table *table, void *ptr)
525 {
526   AtspiRole role = _object_get_role_impl(table, ptr);
527   switch (role) {
528   case ATSPI_ROLE_SCROLL_PANE:    //scroller
529   case ATSPI_ROLE_LIST:           //genlist, list
530   case ATSPI_ROLE_TREE_TABLE:     //gengrid
531   case ATSPI_ROLE_TOOL_BAR:       //toolbar
532       return 1;
533   default:
534       break;
535   }
536   return 0;
537 }
538
539 void *_get_parent_impl(accessibility_navigation_pointer_table *table, void *ptr)
540 {
541   MARK_AS_UNUSED(table);
542   return atk_object_get_parent((AtkObject*)ptr);
543 }
544
545 void *_get_object_at_point_impl(accessibility_navigation_pointer_table *table, void *ptr, int x, int y, unsigned char coordinates_are_screen_based)
546 {
547   MARK_AS_UNUSED(table);
548   AtkObject *obj = (AtkObject*)ptr;
549   g_return_val_if_fail(ATK_IS_COMPONENT (obj), 0);
550   return atk_component_ref_accessible_at_point (ATK_COMPONENT(obj), x, y,
551           coordinates_are_screen_based ? ATSPI_COORD_TYPE_SCREEN : ATSPI_COORD_TYPE_WINDOW);
552 }
553
554 unsigned char _object_contains_impl(accessibility_navigation_pointer_table *table, void *ptr, int x, int y, unsigned char coordinates_are_screen_based)
555 {
556   MARK_AS_UNUSED(table);
557   AtkObject *obj = (AtkObject*)ptr;
558   g_return_val_if_fail(ATK_IS_COMPONENT (obj), 0);
559
560   return atk_component_contains (ATK_COMPONENT(obj), x, y,
561         coordinates_are_screen_based ? ATSPI_COORD_TYPE_SCREEN : ATSPI_COORD_TYPE_WINDOW);
562 }
563
564 unsigned char _object_is_proxy_impl(accessibility_navigation_pointer_table *table, void *ptr)
565 {
566   MARK_AS_UNUSED(table);
567   return 0;
568 }
569
570 void _get_children_impl(accessibility_navigation_pointer_table *table, void *ptr, vector *v)
571 {
572   MARK_AS_UNUSED(table);
573     AtkObject *obj = (AtkObject*)ptr;
574     gint count = atk_object_get_n_accessible_children (obj);
575     vector_resize(v, (unsigned int)count);
576
577     gint i, index = 0;
578     for (i = 0; i < count; i++) {
579         AtkObject *child = atk_object_ref_accessible_child (obj, i);
580         if (child) {
581             vector_set(v, index, child);
582             ++index;
583             g_object_unref (child);
584         }
585     }
586     vector_resize(v, index);
587 }
588
589
590
591
592
593 accessibility_navigation_pointer_table construct_accessibility_navigation_pointer_table(void)
594 {
595    accessibility_navigation_pointer_table table;
596 #define INIT(n) table.n = _## n ## _impl
597    INIT(object_get_role);
598    INIT(object_get_state_set);
599    INIT(get_object_in_relation_by_type);
600    INIT(object_is_zero_size);
601    INIT(get_parent);
602    INIT(object_is_scrollable);
603    INIT(get_object_at_point);
604    INIT(object_contains);
605    INIT(object_is_proxy);
606    INIT(get_children);
607 #undef INIT
608    return table;
609 }
610
611
612
613 static AtkObject *_calculate_navigable_accessible_at_point(AtkObject *root, unsigned char coord_type, int x, int y)
614 {
615    accessibility_navigation_pointer_table table = construct_accessibility_navigation_pointer_table();
616    AtkObject *result = (AtkObject*)_calculate_navigable_accessible_at_point_impl(&table, root, x, y, coord_type ? 1 : 0);
617    return result;
618 }
619
620 static AtkObject *_calculate_neighbor(AtkObject *root, AtkObject *start, unsigned char forward, GetNeighborSearchMode search_mode)
621 {
622    accessibility_navigation_pointer_table table = construct_accessibility_navigation_pointer_table();
623    AtkObject *result = (AtkObject*)_calculate_neighbor_impl(&table, root, start, forward ? 1 : 0, search_mode);
624    return result;
625 }
626
627 static DBusMessage *
628 impl_GetNeighbor (DBusConnection * bus, DBusMessage * message, void *user_data)
629 {
630   AtkObject *start = (AtkObject *) user_data;
631   dbus_uint32_t direction, search_mode;
632   const char *root_path = NULL;
633   DBusMessage *reply;
634   AtkObject *child;
635
636   if (!dbus_message_get_args
637       (message, NULL, DBUS_TYPE_STRING, &root_path, DBUS_TYPE_INT32, &direction, DBUS_TYPE_INT32, &search_mode, DBUS_TYPE_INVALID))
638     {
639       return droute_invalid_arguments_error (message);
640     }
641
642   AtkObject *root_object = (AtkObject*)spi_global_register_path_to_object(root_path);
643   child = _calculate_neighbor (root_object, start, direction == 1, search_mode);
644   reply = spi_object_return_reference_and_recurse_flag (message, child, 0);
645
646   return reply;
647 }
648
649 static DBusMessage *
650 impl_GetNavigableAtPoint (DBusConnection * bus, DBusMessage * message,
651                            void *user_data)
652 {
653   AtkObject *object = (AtkObject *) user_data;
654   dbus_int32_t x, y;
655   dbus_uint32_t coord_type;
656   DBusMessage *reply;
657   AtkObject *child;
658
659   if (!dbus_message_get_args
660       (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
661        DBUS_TYPE_UINT32, &coord_type, DBUS_TYPE_INVALID))
662     {
663       return droute_invalid_arguments_error (message);
664     }
665   child = _calculate_navigable_accessible_at_point (object, coord_type == ATSPI_COORD_TYPE_SCREEN, x, y);
666   reply = spi_object_return_reference_and_recurse_flag (message, child, 0);
667
668   return reply;
669 }
670 //
671
672 static dbus_bool_t
673 impl_get_Name (DBusMessageIter * iter, void *user_data)
674 {
675   AtkObject *object = (AtkObject *) user_data;
676
677   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
678
679   return droute_return_v_string (iter, atk_object_get_name (object));
680 }
681
682 static dbus_bool_t
683 impl_get_Description (DBusMessageIter * iter, void *user_data)
684 {
685   AtkObject *object = (AtkObject *) user_data;
686
687   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
688
689   return droute_return_v_string (iter, atk_object_get_description (object));
690 }
691
692 static dbus_bool_t
693 impl_get_Locale (DBusMessageIter * iter, void *user_data)
694 {
695   AtkObject *object = (AtkObject *) user_data;
696
697   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
698
699   return droute_return_v_string (iter, atk_object_get_object_locale (object));
700 }
701
702 static dbus_bool_t
703 impl_get_Parent (DBusMessageIter * iter, void *user_data)
704 {
705   AtkObject *obj = (AtkObject *) user_data;
706   AtkObject *parent;
707   DBusMessageIter iter_variant;
708   dbus_uint32_t role;
709
710   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
711
712   role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
713
714   dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "(so)",
715                                     &iter_variant);
716
717   parent = atk_object_get_parent (obj);
718   if (parent == NULL)
719     {
720       /* TODO, move in to a 'Plug' wrapper. */
721       if (ATK_IS_PLUG (obj))
722         {
723           char *id = g_object_get_data (G_OBJECT (obj), "dbus-plug-parent");
724           char *bus_parent;
725           char *path_parent;
726
727           if (id)
728             {
729               bus_parent = g_strdup (id);
730               if (bus_parent && (path_parent = g_utf8_strchr (bus_parent + 1, -1, ':')))
731                 {
732                   DBusMessageIter iter_parent;
733                   *(path_parent++) = '\0';
734                   dbus_message_iter_open_container (&iter_variant, DBUS_TYPE_STRUCT, NULL,
735                                                     &iter_parent);
736                   dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_STRING, &bus_parent);
737                   dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_OBJECT_PATH, &path_parent);
738                   dbus_message_iter_close_container (&iter_variant, &iter_parent);
739                 }
740               else
741                 {
742                   spi_object_append_null_reference (&iter_variant);
743                 }
744             }
745           else
746             {
747               spi_object_append_null_reference (&iter_variant);
748             }
749         }
750       else if (role != ATSPI_ROLE_APPLICATION)
751          spi_object_append_null_reference (&iter_variant);
752       else
753          spi_object_append_desktop_reference (&iter_variant);
754       }
755   else
756     {
757       spi_object_append_reference (&iter_variant, parent);
758     }
759
760
761   dbus_message_iter_close_container (iter, &iter_variant);
762   return TRUE;
763 }
764
765 static dbus_bool_t
766 impl_get_ChildCount (DBusMessageIter * iter, void *user_data)
767 {
768   AtkObject *object = (AtkObject *) user_data;
769   int childCount;
770
771   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
772
773   childCount = (ATK_IS_SOCKET (object) && atk_socket_is_occupied (ATK_SOCKET (object)))
774                ? 1
775                : atk_object_get_n_accessible_children (object);
776   return droute_return_v_int32 (iter, childCount);
777 }
778
779 static DBusMessage *
780 impl_GetChildAtIndex (DBusConnection * bus,
781                       DBusMessage * message, void *user_data)
782 {
783   AtkObject *object = (AtkObject *) user_data;
784   DBusMessage *reply;
785   dbus_int32_t i;
786   AtkObject *child;
787
788   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
789                         droute_not_yet_handled_error (message));
790   if (!dbus_message_get_args
791        (message, NULL, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID))
792     {
793       return droute_invalid_arguments_error (message);
794     }
795
796   if (ATK_IS_SOCKET (object) && atk_socket_is_occupied (ATK_SOCKET (object)) && i == 0)
797     {
798       AtkSocket *socket = ATK_SOCKET (object);
799       gchar *child_name, *child_path;
800       child_name = g_strdup (socket->embedded_plug_id);
801       child_path = g_utf8_strchr (child_name + 1, -1, ':');
802       if (child_path)
803         {
804           DBusMessageIter iter, iter_socket;
805           *(child_path++) = '\0';
806           reply = dbus_message_new_method_return (message);
807           if (!reply)
808             return NULL;
809           dbus_message_iter_init_append (reply, &iter);
810           dbus_message_iter_open_container (&iter, DBUS_TYPE_STRUCT, NULL,
811                                             &iter_socket);
812           dbus_message_iter_append_basic (&iter_socket, DBUS_TYPE_STRING, &child_name);
813           dbus_message_iter_append_basic (&iter_socket, DBUS_TYPE_OBJECT_PATH, &child_path);
814           dbus_message_iter_close_container (&iter, &iter_socket);
815           return reply;
816         }
817       g_free (child_name);
818     }
819   child = atk_object_ref_accessible_child (object, i);
820   reply = spi_object_return_reference (message, child);
821   g_object_unref (child);
822
823   return reply;
824 }
825
826 static DBusMessage *
827 impl_GetChildren (DBusConnection * bus,
828                   DBusMessage * message, void *user_data)
829 {
830   AtkObject *object = (AtkObject *) user_data;
831   gint i;
832   gint count;
833   DBusMessage *reply;
834   DBusMessageIter iter, iter_array;
835
836   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
837                         droute_not_yet_handled_error (message));
838   count = atk_object_get_n_accessible_children (object);
839   reply = dbus_message_new_method_return (message);
840   if (!reply)
841     goto oom;
842   dbus_message_iter_init_append (reply, &iter);
843   if (!dbus_message_iter_open_container
844       (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array))
845     goto oom;
846   for (i = 0; i < count; i++)
847     {
848       AtkObject *child = atk_object_ref_accessible_child (object, i);
849       spi_object_append_reference (&iter_array, child);
850       if (child)
851         g_object_unref (child);
852     }
853   if (!dbus_message_iter_close_container (&iter, &iter_array))
854     goto oom;
855   return reply;
856 oom:
857   // TODO: handle out-of-memory
858   return reply;
859 }
860
861 static DBusMessage *
862 impl_GetIndexInParent (DBusConnection * bus,
863                        DBusMessage * message, void *user_data)
864 {
865   AtkObject *object = (AtkObject *) user_data;
866   dbus_int32_t rv;
867   DBusMessage *reply;
868
869   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
870                         droute_not_yet_handled_error (message));
871
872   rv = atk_object_get_index_in_parent (object);
873   reply = dbus_message_new_method_return (message);
874   dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv, DBUS_TYPE_INVALID);
875   return reply;
876 }
877
878 static gboolean
879 spi_init_relation_type_table (AtspiRelationType * types)
880 {
881   gint i;
882
883   for (i = 0; i < ATK_RELATION_LAST_DEFINED; i++)
884     types[i] = ATSPI_RELATION_NULL;
885
886   types[ATK_RELATION_CONTROLLED_BY] = ATSPI_RELATION_CONTROLLED_BY;
887   types[ATK_RELATION_CONTROLLER_FOR] = ATSPI_RELATION_CONTROLLER_FOR;
888   types[ATK_RELATION_LABEL_FOR] = ATSPI_RELATION_LABEL_FOR;
889   types[ATK_RELATION_LABELLED_BY] = ATSPI_RELATION_LABELLED_BY;
890   types[ATK_RELATION_MEMBER_OF] = ATSPI_RELATION_MEMBER_OF;
891   types[ATK_RELATION_NODE_CHILD_OF] = ATSPI_RELATION_NODE_CHILD_OF;
892   types[ATK_RELATION_FLOWS_TO] = ATSPI_RELATION_FLOWS_TO;
893   types[ATK_RELATION_FLOWS_FROM] = ATSPI_RELATION_FLOWS_FROM;
894   types[ATK_RELATION_SUBWINDOW_OF] = ATSPI_RELATION_SUBWINDOW_OF;
895   types[ATK_RELATION_EMBEDS] = ATSPI_RELATION_EMBEDS;
896   types[ATK_RELATION_EMBEDDED_BY] = ATSPI_RELATION_EMBEDDED_BY;
897   types[ATK_RELATION_POPUP_FOR] = ATSPI_RELATION_POPUP_FOR;
898   types[ATK_RELATION_PARENT_WINDOW_OF] =
899     ATSPI_RELATION_PARENT_WINDOW_OF;
900   types[ATK_RELATION_DESCRIPTION_FOR] =
901     ATSPI_RELATION_DESCRIPTION_FOR;
902   types[ATK_RELATION_DESCRIBED_BY] = ATSPI_RELATION_DESCRIBED_BY;
903   types[ATK_RELATION_NODE_PARENT_OF] = ATSPI_RELATION_NODE_PARENT_OF;
904
905   return TRUE;
906 }
907
908 static AtspiRelationType
909 spi_relation_type_from_atk_relation_type (AtkRelationType type)
910 {
911   static gboolean is_initialized = FALSE;
912   static AtspiRelationType
913     spi_relation_type_table[ATK_RELATION_LAST_DEFINED];
914   AtspiRelationType spi_type;
915
916   if (!is_initialized)
917     is_initialized = spi_init_relation_type_table (spi_relation_type_table);
918
919   if (type > ATK_RELATION_NULL && type < ATK_RELATION_LAST_DEFINED)
920     spi_type = spi_relation_type_table[type];
921   else
922     spi_type = ATSPI_RELATION_EXTENDED;
923   return spi_type;
924 }
925
926 static DBusMessage *
927 impl_GetRelationSet (DBusConnection * bus,
928                      DBusMessage * message, void *user_data)
929 {
930   AtkObject *object = (AtkObject *) user_data;
931   DBusMessage *reply;
932   AtkRelationSet *set;
933   DBusMessageIter iter, iter_array, iter_struct, iter_targets;
934   gint count;
935   gint i, j;
936
937   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
938                         droute_not_yet_handled_error (message));
939   reply = dbus_message_new_method_return (message);
940   if (!reply)
941     return NULL;
942   set = atk_object_ref_relation_set (object);
943   dbus_message_iter_init_append (reply, &iter);
944   if (!dbus_message_iter_open_container
945       (&iter, DBUS_TYPE_ARRAY, "(ua(so))", &iter_array))
946     {
947       goto oom;
948     }
949   count = 0;
950   if (set)
951     count = atk_relation_set_get_n_relations (set);
952   for (i = 0; i < count; i++)
953     {
954       AtkRelation *r = atk_relation_set_get_relation (set, i);
955       AtkRelationType rt;
956       GPtrArray *target;
957       dbus_uint32_t type;
958       if (!r)
959         continue;
960       rt = atk_relation_get_relation_type (r);
961       type = spi_relation_type_from_atk_relation_type (rt);
962       target = atk_relation_get_target (r);
963       if (!dbus_message_iter_open_container
964           (&iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct))
965         {
966           goto oom;
967         }
968       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &type);
969       if (!dbus_message_iter_open_container
970           (&iter_struct, DBUS_TYPE_ARRAY, "(so)", &iter_targets))
971         {
972           goto oom;
973         }
974       for (j = 0; j < target->len; j++)
975         {
976           AtkObject *obj = target->pdata[j];
977           if (!obj)
978             continue;
979           spi_object_append_reference (&iter_targets, obj);
980         }
981       dbus_message_iter_close_container (&iter_struct, &iter_targets);
982       dbus_message_iter_close_container (&iter_array, &iter_struct);
983     }
984   dbus_message_iter_close_container (&iter, &iter_array);
985 oom:
986   if (set)
987     g_object_unref (set);
988   // TODO: handle out of memory */
989   return reply;
990 }
991
992 static DBusMessage *
993 impl_GetRole (DBusConnection * bus, DBusMessage * message, void *user_data)
994 {
995   AtkObject *object = (AtkObject *) user_data;
996   gint role;
997   dbus_uint32_t rv;
998   DBusMessage *reply;
999
1000   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
1001                         droute_not_yet_handled_error (message));
1002   role = atk_object_get_role (object);
1003   rv = spi_accessible_role_from_atk_role (role);
1004   reply = dbus_message_new_method_return (message);
1005   if (reply)
1006     {
1007       dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv,
1008                                 DBUS_TYPE_INVALID);
1009     }
1010   return reply;
1011 }
1012
1013 static DBusMessage *
1014 impl_GetRoleName (DBusConnection * bus,
1015                   DBusMessage * message, void *user_data)
1016 {
1017   AtkObject *object = (AtkObject *) user_data;
1018   gint role;
1019   const char *role_name;
1020   DBusMessage *reply;
1021
1022   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
1023                         droute_not_yet_handled_error (message));
1024   role = atk_object_get_role (object);
1025   role_name = atk_role_get_name (role);
1026   if (!role_name)
1027     role_name = "";
1028   reply = dbus_message_new_method_return (message);
1029   if (reply)
1030     {
1031       dbus_message_append_args (reply, DBUS_TYPE_STRING, &role_name,
1032                                 DBUS_TYPE_INVALID);
1033     }
1034   return reply;
1035 }
1036
1037 static DBusMessage *
1038 impl_GetLocalizedRoleName (DBusConnection * bus,
1039                            DBusMessage * message, void *user_data)
1040 {
1041   AtkObject *object = (AtkObject *) user_data;
1042   gint role;
1043   const char *role_name;
1044   DBusMessage *reply;
1045
1046   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
1047                         droute_not_yet_handled_error (message));
1048   role = atk_object_get_role (object);
1049   role_name = atk_role_get_localized_name (role);
1050   if (!role_name)
1051     role_name = "";
1052   reply = dbus_message_new_method_return (message);
1053   if (reply)
1054     {
1055       dbus_message_append_args (reply, DBUS_TYPE_STRING, &role_name,
1056                                 DBUS_TYPE_INVALID);
1057     }
1058   return reply;
1059 }
1060
1061 static DBusMessage *
1062 impl_GetState (DBusConnection * bus, DBusMessage * message, void *user_data)
1063 {
1064   AtkObject *object = (AtkObject *) user_data;
1065
1066   DBusMessage *reply = NULL;
1067   DBusMessageIter iter, iter_array;
1068
1069   dbus_uint32_t states[2];
1070
1071   guint count;
1072
1073   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
1074                         droute_not_yet_handled_error (message));
1075
1076   reply = dbus_message_new_method_return (message);
1077   dbus_message_iter_init_append (reply, &iter);
1078
1079   spi_atk_state_to_dbus_array (object, states);
1080   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "u", &iter_array);
1081   for (count = 0; count < 2; count++)
1082     {
1083       dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_UINT32,
1084                                       &states[count]);
1085     }
1086   dbus_message_iter_close_container (&iter, &iter_array);
1087   return reply;
1088 }
1089
1090 static DBusMessage *
1091 impl_GetAttributes (DBusConnection * bus,
1092                     DBusMessage * message, void *user_data)
1093 {
1094   AtkObject *object = (AtkObject *) user_data;
1095   AtkAttributeSet *attributes;
1096   DBusMessage *reply = NULL;
1097   DBusMessageIter iter;
1098
1099   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
1100                         droute_not_yet_handled_error (message));
1101
1102   attributes = atk_object_get_attributes (object);
1103
1104   reply = dbus_message_new_method_return (message);
1105   dbus_message_iter_init_append (reply, &iter);
1106   spi_object_append_attribute_set (&iter, attributes);
1107
1108   atk_attribute_set_free (attributes);
1109
1110   return reply;
1111 }
1112
1113 static dbus_bool_t
1114 impl_get_Attributes (DBusMessageIter * iter, void *user_data)
1115 {
1116   DBusMessageIter iter_variant;
1117   AtkObject *object = (AtkObject *) user_data;
1118   AtkAttributeSet *attributes;
1119
1120   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
1121
1122   attributes = atk_object_get_attributes (object);
1123
1124   dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "a{ss}", &iter_variant);
1125   spi_object_append_attribute_set (&iter_variant, attributes);
1126   dbus_message_iter_close_container (iter, &iter_variant);
1127
1128   atk_attribute_set_free (attributes);
1129
1130   return TRUE;
1131 }
1132
1133 static DBusMessage *
1134 impl_GetApplication (DBusConnection * bus,
1135                      DBusMessage * message, void *user_data)
1136 {
1137   return spi_object_return_reference (message, atk_get_root ());
1138 }
1139
1140 static DBusMessage *
1141 impl_GetInterfaces (DBusConnection * bus,
1142                     DBusMessage * message, void *user_data)
1143 {
1144   AtkObject *object = (AtkObject *) user_data;
1145   DBusMessage *reply;
1146   DBusMessageIter iter, iter_array;
1147
1148   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
1149                         droute_not_yet_handled_error (message));
1150   reply = dbus_message_new_method_return (message);
1151   if (reply)
1152     {
1153       dbus_message_iter_init_append (reply, &iter);
1154       dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s",
1155                                         &iter_array);
1156       spi_object_append_interfaces (&iter_array, object);
1157       dbus_message_iter_close_container (&iter, &iter_array);
1158     }
1159   return reply;
1160 }
1161
1162 static DRouteMethod methods[] = {
1163 // TIZEN_ONLY(20170310) - implementation of get object under coordinates for accessibility
1164   {impl_GetNavigableAtPoint, "GetNavigableAtPoint"},
1165   {impl_GetNeighbor, "GetNeighbor"},
1166 //
1167   {impl_GetChildAtIndex, "GetChildAtIndex"},
1168   {impl_GetChildren, "GetChildren"},
1169   {impl_GetIndexInParent, "GetIndexInParent"},
1170   {impl_GetRelationSet, "GetRelationSet"},
1171   {impl_GetRole, "GetRole"},
1172   {impl_GetRoleName, "GetRoleName"},
1173   {impl_GetLocalizedRoleName, "GetLocalizedRoleName"},
1174   {impl_GetState, "GetState"},
1175   {impl_GetAttributes, "GetAttributes"},
1176   {impl_GetApplication, "GetApplication"},
1177   {impl_GetInterfaces, "GetInterfaces"},
1178   {NULL, NULL}
1179 };
1180
1181 static DRouteProperty properties[] = {
1182   {impl_get_Name, NULL, "Name"},
1183   {impl_get_Description, NULL, "Description"},
1184   {impl_get_Locale, NULL, "Locale"},
1185   {impl_get_Parent, NULL, "Parent"},
1186   {impl_get_ChildCount, NULL, "ChildCount"},
1187   {impl_get_Attributes, NULL, "Attributes"},
1188   {NULL, NULL, NULL}
1189 };
1190
1191 void
1192 spi_initialize_accessible (DRoutePath * path)
1193 {
1194   spi_atk_add_interface (path,
1195                          ATSPI_DBUS_INTERFACE_ACCESSIBLE,
1196                          spi_org_a11y_atspi_Accessible,
1197                          methods, properties);
1198 };