atspi: "GetNeighbor" is using deputy object 88/167188/4
authorJunsuChoi <jsuya.choi@samsung.com>
Tue, 16 Jan 2018 05:50:52 +0000 (14:50 +0900)
committerGerrit Code Review <gerrit@review.ap-northeast-2.compute.internal>
Tue, 16 Jan 2018 06:14:28 +0000 (06:14 +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: I36dde39af3e99efcd7168c146b065fc6dfa5d104

src/lib/elementary/efl_ui_widget.c
src/lib/elementary/elm_atspi_bridge.c

index 436a894..9a32013 100644 (file)
@@ -5797,6 +5797,7 @@ _efl_ui_widget_efl_access_children_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Dat
    // TIZEN_ONLY(20160824): Do not append a child, if its accessible parent is different with widget parent
    Eo *parent;
    //
+   Eo *proxy = NULL;
 
    EINA_LIST_FOREACH(pd->subobjs, l, widget)
      {
@@ -5809,7 +5810,6 @@ _efl_ui_widget_efl_access_children_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Dat
              elm_atspi_ewk_wrapper_a11y_init(obj, widget);
           }
      }
-   Eo *proxy = NULL;
    EINA_LIST_FOREACH(pd->subobjs, l, widget)
      {
         // TIZEN_ONLY(20160824): Do not append a child, if its accessible parent is different with widget parent
@@ -5852,6 +5852,31 @@ _efl_ui_widget_efl_access_children_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Dat
    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 (efl_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;
 }
 
index 0ff3019..c19afeb 100644 (file)
@@ -4226,6 +4226,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 {
@@ -4618,6 +4619,86 @@ unsigned char cycle_detection_check_if_in_cycle(cycle_detection_data *data, cons
    return 0;
 }
 
+static Eina_Bool
+_deputy_is(Eo *obj)
+{
+   if (efl_isa(obj, ELM_ACCESS_CLASS))
+     {
+        Elm_Access_Info *info;
+
+        info = _elm_access_info_get(obj);
+        if (info && efl_isa(info->part_object, EFL_UI_LAYOUT_CLASS))
+          {
+             Eina_List *attrs, *l;
+             Efl_Access_Attribute *attr;
+
+             attrs = efl_access_attributes_get(info->part_object);
+             EINA_LIST_FOREACH(attrs, l, attr)
+               {
+                  if (!strcmp(attr->key, "___PlugID"))
+                    {
+                       efl_access_attributes_list_free(attrs);
+                       return EINA_TRUE;
+                    }
+               }
+             efl_access_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;
+   children_list = efl_access_children_get(obj);
+
+   Evas_Object *child;
+   EINA_LIST_FOREACH(children_list, l, child)
+     {
+        if (efl_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;
+   children_list = efl_access_children_get(obj);
+
+   unsigned int index = 0;
+   Evas_Object *child;
+   EINA_LIST_FOREACH(children_list, l, child)
+     {
+        if (efl_isa(child, ELM_ATSPI_PROXY_CLASS))
+          {
+             if (index == 0)
+               {
+                  WRN("Proxy does not have deputy object");
+                  break;
+               }
+
+             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;
@@ -4626,6 +4707,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;
 
@@ -4675,11 +4764,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 = efl_ui_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 = efl_ui_widget_parent_get(node);
+                     next_related_in_direction =
+                       _proxy_in_parent_get(parent);
+                  }
+             }
+
            node = next_related_in_direction;
            want_cycle_detection = 1;
          }