atspi: fix infinite loop issue 34/153834/1
authorShinwoo Kim <cinoo.kim@samsung.com>
Wed, 20 Sep 2017 04:14:27 +0000 (13:14 +0900)
committerShinwoo Kim <cinoo.kim@samsung.com>
Sat, 30 Sep 2017 08:35:14 +0000 (17:35 +0900)
It is possilbe to get a target object as a return value of get_object_at_point.
In this case _elm_widget_elm_interface_atspi_component_accessible_at_point_get
works again with the target which is same with root object.

[Enhanced]
- We do not have to go parent if the bottom up logic meets root object.
- Visiblity of panel is always true. So we are checking its SHOWING state.
  If the panel is not SHOWING, remove it from valid children list.

Change-Id: I2edeefdc9a0c5955f1c28926321b74424c71bc40

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

index 56710b6..9625629 100644 (file)
@@ -4301,16 +4301,34 @@ static unsigned char _accept_object(accessibility_navigation_pointer_table *tabl
    return 1;
 }
 
+/* The target cannot be a parent of root */
+static Eina_Bool _target_validation_check(Eo *target, Eo *root)
+{
+   Eo *parent;
+   eo_do(root, parent = elm_interface_atspi_accessible_parent_get());
+
+   while (parent)
+     {
+        if (parent == target) return EINA_FALSE;
+        eo_do(parent, parent = elm_interface_atspi_accessible_parent_get());
+     }
+
+   return EINA_TRUE;
+}
+
 static void *_calculate_navigable_accessible_at_point_impl(accessibility_navigation_pointer_table *table,
           void *root, int x, int y, unsigned char coordinates_are_screen_based)
 {
    if (!root) return NULL;
+   void *prev_root = root;
 
    void *return_value = NULL;
    while (1)
      {
        void *target = CALL(get_object_at_point, root, x, y, coordinates_are_screen_based);
        if (!target) break;
+       if (target == root || target == prev_root) break;
+       if (!_target_validation_check(target, root)) break;
 
        // always return proxy, so atspi lib can call on it again
        if (CALL(object_is_proxy, target)) return target;
index 7502188..a5402c1 100644 (file)
@@ -7330,20 +7330,28 @@ _child_object_at_point_get(Eo *obj, int x, int y)
 }
 
 static Eina_Bool
-_is_acceptable_leaf(Eo *obj)
+_acceptable_child_is(Eo *obj)
 {
    Elm_Atspi_Role role;
    Eina_List *children;
+   Elm_Atspi_State_Set ss;
 
    eo_do(obj, role = elm_interface_atspi_accessible_role_get());
    switch (role)
      {
        case ELM_ATSPI_ROLE_IMAGE:
        case ELM_ATSPI_ROLE_ICON:
+         /* remove unacceptable leaf node */
          eo_do(obj, children = elm_interface_atspi_accessible_children_get());
          if (!children) return EINA_FALSE;
          break;
 
+       case ELM_ATSPI_ROLE_PANEL:
+         /* remove closed panel fron children list */
+         eo_do(obj, ss = elm_interface_atspi_accessible_state_set_get());
+         if (!STATE_TYPE_GET(ss, ELM_ATSPI_STATE_SHOWING)) return EINA_FALSE;
+         break;
+
        default:
          break;
      }
@@ -7369,7 +7377,7 @@ _accessible_at_point_top_down_get(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSE
 
    EINA_LIST_FOREACH(children, l2, child)
      {
-        if (_is_inside(child, x, y) && _is_acceptable_leaf(child))
+        if (_is_inside(child, x, y) && _acceptable_child_is(child))
           valid_children = eina_list_append(valid_children, child);
      }
 
@@ -7537,6 +7545,11 @@ _elm_widget_elm_interface_atspi_component_accessible_at_point_get(Eo *obj, Elm_W
         Evas_Object *smart_parent = stack_item;
         while (smart_parent)
           {
+             /* If parent equals to obj, it is not necessary to go upper.
+                So the top down logic would be better than NULL return. */
+             if (smart_parent == obj)
+               return _accessible_at_point_top_down_get(obj, _pd, screen_coords, x, y);
+
              Evas_Object *ao = elm_access_object_get(smart_parent);
              if (ao) return ao;