atspi: "GetNeighbor" is using deputy object 57/134257/2
authorShinwoo Kim <cinoo.kim@samsung.com>
Thu, 15 Jun 2017 12:16:35 +0000 (21:16 +0900)
committerShinwoo Kim <cinoo.kim@samsung.com>
Wed, 28 Jun 2017 23:34:34 +0000 (23:34 +0000)
A01 object is deputy object, and exists in embedding process.
A02, A03 object exist in embedding process.
B01, B02 objects exist in embedded process.

The “GetNeighbor” method should return A02 object as “next” object of B02,
and return B02 object as “prev” object of A02.
The “next” object of A01 is B01.
The “prev” object of B01 is A01.
This is default implementation.
The following is default order: A01 - B01 - B02 - A02 - A03

But more important thing is that the embedding process could use
ELM_ATSPI_RELATION_FLOWS_TO/FROM for each objects A01, A02, and A03.
So there are some cases should be considered.

(1) If A01 is ELM_ATSPI_RELATION_FLOWS_FROM object of A03, then the
   “GetNeighbor” should find B02 first when it navigates backward from A03.

(2) If A03 is ELM_ATSPI_RELATION_FLOWS_TO object of A01, then the
   “GetNeighbor” should find A03 first when it navigates backward from B02.

(3) Even though the A01 has ELM_ATSPI_RELATION_FLOWS_TO relation information,
   the “GetNeighbor” should find B01 first. Because the
   ELM_ATSPI_RELATION_FLOWS_TO object is used when “GetNeighbor” finds
   next object of B02.

Change-Id: Id9ec5762fb5abe94f942bf3823c69729e6389702

src/lib/elm_atspi_bridge.c
src/lib/elm_widget.c

index 402d2ecef20b3d2e8ce9556125922282b4eaf1a1..6cd662688858710a600e9472f8187ae8dd1287e5 100644 (file)
@@ -4125,6 +4125,7 @@ typedef enum {
   NEIGHBOR_SEARCH_MODE_NORMAL = 0,
   NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1,
   NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING = 2,
+  NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE = 3,
 } GetNeighborSearchMode;
 
 typedef struct accessibility_navigation_pointer_table {
@@ -4454,6 +4455,81 @@ unsigned char cycle_detection_check_if_in_cycle(cycle_detection_data *data, cons
    return 0;
 }
 
+static Eina_Bool
+_deputy_is(Eo *obj)
+{
+   if (eo_isa(obj, ELM_ACCESS_CLASS))
+     {
+        Elm_Access_Info *info;
+
+        info = _elm_access_info_get(obj);
+        if (info && eo_isa(info->part_object, ELM_LAYOUT_CLASS))
+          {
+             Eina_List *attrs, *l;
+             Elm_Atspi_Attribute *attr;
+
+             eo_do(info->part_object,
+               attrs = elm_interface_atspi_accessible_attributes_get());
+             EINA_LIST_FOREACH(attrs, l, attr)
+               {
+                  if (!strcmp(attr->key, "___PlugID"))
+                    {
+                       elm_atspi_attributes_list_free(attrs);
+                       return EINA_TRUE;
+                    }
+               }
+             elm_atspi_attributes_list_free(attrs);
+          }
+     }
+   return EINA_FALSE;
+}
+
+static Eo *
+_proxy_in_parent_get(Eo *obj)
+{
+   Eina_List *l;
+   Eo *proxy = NULL;
+   Eina_List *children_list = NULL;
+   eo_do(obj, children_list = elm_interface_atspi_accessible_children_get());
+
+   Evas_Object *child;
+   EINA_LIST_FOREACH(children_list, l, child)
+     {
+        if (eo_isa(child, ELM_ATSPI_PROXY_CLASS))
+          {
+             proxy = child;
+             break;
+          }
+     }
+   eina_list_free(children_list);
+
+   return proxy;
+}
+
+static Eo *
+_deputy_of_proxy_in_parent_get(Eo *obj)
+{
+   Eina_List *l;
+   Eo *deputy = NULL;
+   Eina_List *children_list = NULL;
+   eo_do(obj, children_list = elm_interface_atspi_accessible_children_get());
+
+   unsigned int index = 0;
+   Evas_Object *child;
+   EINA_LIST_FOREACH(children_list, l, child)
+     {
+        if (eo_isa(child, ELM_ATSPI_PROXY_CLASS))
+          {
+             deputy = eina_list_nth(children_list, index - 1);
+             break;
+          }
+        index++;
+     }
+   eina_list_free(children_list);
+
+   return deputy;
+}
+
 static void *_calculate_neighbor_impl(accessibility_navigation_pointer_table *table, void *root, void *start, unsigned char forward, GetNeighborSearchMode search_mode)
 {
    if (root && _object_is_defunct(table, root)) return NULL;
@@ -4462,6 +4538,14 @@ static void *_calculate_neighbor_impl(accessibility_navigation_pointer_table *ta
        start = NULL;
        forward = 1;
      }
+
+   if (search_mode == NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE)
+     {
+        /* This only works if we navigate backward, and it is not possible to
+           find in embedded process. In this case the deputy should be used */
+        return _deputy_of_proxy_in_parent_get(start);
+     }
+
    void *node = start ? start : root;
    if (!node) return NULL;
 
@@ -4510,11 +4594,48 @@ static void *_calculate_neighbor_impl(accessibility_navigation_pointer_table *ta
 
        void *next_related_in_direction = !force_next ? _get_object_in_relation_flow(table, node, forward) : NULL;
 
+       /* force_next means that the search_mode is NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING
+          in this case the node is elm_layout which is parent of proxy object.
+          There is an access object working for the proxy object, and the access
+          object could have relation information. This relation information should
+          be checked first before using the elm_layout as a node. */
+       if (force_next && forward)
+         {
+            Eo *deputy;
+            deputy = _deputy_of_proxy_in_parent_get(node);
+            next_related_in_direction =
+              _get_object_in_relation_flow(table, deputy, forward);
+         }
+
        if (next_related_in_direction && _object_is_defunct(table, next_related_in_direction))
            next_related_in_direction = NULL;
        unsigned char want_cycle_detection = 0;
        if (next_related_in_direction)
          {
+           /* Check next_related_in_direction is deputy object */
+           Eo *parent;
+           if (!forward)
+             {
+                /* If the prev object is deputy, then go to inside of its proxy first */
+                if (_deputy_is(next_related_in_direction))
+                  {
+                     parent = elm_widget_parent_get(next_related_in_direction);
+                     next_related_in_direction =
+                       _proxy_in_parent_get(parent);
+                  }
+             }
+           else
+             {
+                /* If current object is deputy, and it has relation next object,
+                   then do not use the relation next object, and use proxy first */
+                if (_deputy_is(node))
+                  {
+                     parent = elm_widget_parent_get(node);
+                     next_related_in_direction =
+                       _proxy_in_parent_get(parent);
+                  }
+             }
+
            node = next_related_in_direction;
            want_cycle_detection = 1;
          }
index 4fe75259a2b79d8ad0cfbb15ef9fe8f82394a896..436fe37a9511338d19ad8fb7a61d3f427ffeb36f 100644 (file)
@@ -6763,6 +6763,7 @@ _elm_widget_elm_interface_atspi_accessible_children_get(Eo *obj, Elm_Widget_Smar
         }
      }
 
+   Eo *proxy = NULL;
    EINA_LIST_FOREACH(wd->subobjs, l, widget)
      {
         // TIZEN_ONLY(20160824): Do not append a child, if its accessible parent is different with widget parent
@@ -6778,7 +6779,6 @@ _elm_widget_elm_interface_atspi_accessible_children_get(Eo *obj, Elm_Widget_Smar
             // TIZEN_ONLY(20160930) : endless recursion fix
             eo_do_super(obj, MY_CLASS, elm_interface_atspi_accessible_attribute_append("___PlugID", plug_id_2));
 
-             Eo *proxy;
              char *svcname, *svcnum;
 
              proxy = evas_object_data_get(widget, "__widget_proxy");
@@ -6815,6 +6815,30 @@ _elm_widget_elm_interface_atspi_accessible_children_get(Eo *obj, Elm_Widget_Smar
    EINA_LIST_FREE(lines, line)
      accs = eina_list_merge(accs, eina_list_sort(line, -1, _sort_horizontally));
    //
+
+   if (proxy)
+     {
+        Eo *deputy = NULL;
+        accs = eina_list_remove(accs, proxy);
+        EINA_LIST_FOREACH(accs, l, widget)
+          {
+              if (eo_isa(widget, ELM_ACCESS_CLASS))
+                {
+                   Elm_Access_Info *info = _elm_access_info_get(widget);
+                   if (!info) continue;
+                   if (obj == info->part_object)
+                     {
+                        deputy = widget;
+                        break;
+                     }
+                }
+          }
+
+        if (deputy)
+          {
+             accs = eina_list_append_relative(accs, proxy, deputy);
+          }
+     }
    return accs;
 }