atspi: add elm_atspi_ewk_wrapper class 22/95222/4
authorLukasz Stanislawski <l.stanislaws@samsung.com>
Sun, 16 Oct 2016 16:19:10 +0000 (18:19 +0200)
committerLukasz Stanislawski <l.stanislaws@samsung.com>
Wed, 9 Nov 2016 12:18:54 +0000 (13:18 +0100)
Create wrapper widget around Ewk_View Evas_Object in order
to integrate a11y frameworks from elementary and Chromium.
Such wrapper should be created on Chromium EFL port side, however
currently will be added on elementary side with hope to move it
into chromium library.

Change-Id: I02b15376cf387b9c88f416c7c96a9ec41e89ec79

src/lib/Elementary.h.in
src/lib/Makefile.am
src/lib/elm_atspi_ewk_wrapper.c [new file with mode: 0644]
src/lib/elm_atspi_ewk_wrapper.eo [new file with mode: 0644]
src/lib/elm_atspi_ewk_wrapper.h [new file with mode: 0644]
src/lib/elm_widget.c

index f01e284..05b0e69 100644 (file)
@@ -194,6 +194,7 @@ EAPI extern Elm_Version *elm_version;
 #include <elm_app_client_view.h>
 #include <elm_app.h>
 #include <elm_atspi_app_object.h>
+#include <elm_atspi_ewk_wrapper.h>
 #include <elm_atspi_bridge.h>
 #include <elm_atspi_proxy.h>
 #include <elm_bg.h>
index 21b5eb3..4341d0c 100644 (file)
@@ -39,6 +39,7 @@ includesdir = $(includedir)/elementary-@VMAJ@
 includesunstable_HEADERS = \
 elm_gen_common.h \
 elm_atspi_bridge.h \
+elm_atspi_ewk_wrapper.h \
 elm_interface_atspi_accessible.h \
 elm_interface_atspi_text.h \
 elm_interface_atspi_widget_action.h \
@@ -401,6 +402,7 @@ elm_app_client_view.c \
 elm_atspi_app_object.c \
 elm_atspi_proxy.c \
 elm_atspi_bridge.c \
+elm_atspi_ewk_wrapper.c \
 elm_bg.c \
 elm_box.c \
 elm_bubble.c \
@@ -511,6 +513,7 @@ elm_eolian_files = \
 elm_access.eo \
 elm_actionslider.eo \
 elm_atspi_bridge.eo \
+elm_atspi_ewk_wrapper.eo \
 elm_app_client.eo \
 elm_app_client_view.eo \
 elm_app_server.eo \
diff --git a/src/lib/elm_atspi_ewk_wrapper.c b/src/lib/elm_atspi_ewk_wrapper.c
new file mode 100644 (file)
index 0000000..f87ceef
--- /dev/null
@@ -0,0 +1,215 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#define ELM_INTERFACE_ATSPI_ACCESSIBLE_PROTECTED
+#define ELM_INTERFACE_ATSPI_COMPONENT_PROTECTED
+
+#include <Elementary.h>
+#include "elm_widget.h"
+#include "elm_priv.h"
+
+#define EWK_A11Y_DATA_KEY "__a11y_wrapper"
+
+typedef struct _Elm_Atspi_Ewk_Wrapper_Data Elm_Atspi_Ewk_Wrapper_Data;
+
+struct _Elm_Atspi_Ewk_Wrapper_Data
+{
+   Elm_Atspi_Proxy *proxy;
+   Evas_Object *ewk_view;
+   char *plug_id;
+};
+
+static Eina_Bool
+_ewk_view_geometry_changed(void *data, Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED);
+
+EO_CALLBACKS_ARRAY_DEFINE(resize_watch,
+                          { EVAS_OBJECT_EVENT_MOVE, _ewk_view_geometry_changed },
+                          { EVAS_OBJECT_EVENT_RESIZE, _ewk_view_geometry_changed });
+
+static void
+_elm_atspi_ewk_wrapper_disconnect(Elm_Atspi_Ewk_Wrapper_Data *_pd)
+{
+   if (_pd->proxy) eo_del(_pd->proxy);
+   free(_pd->plug_id);
+   _pd->proxy = NULL;
+   _pd->plug_id = NULL;
+}
+
+static Eina_Bool
+_ewk_view_geometry_changed(void *data, Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Evas_Coord x, y, w, h;
+   Elm_Atspi_Ewk_Wrapper *wrapper = data;
+
+   evas_object_geometry_get(obj, &x, &y, &w, &h);
+   evas_object_move(wrapper, x, y);
+   evas_object_resize(wrapper, w, h);
+   return EINA_TRUE;
+}
+
+EOLIAN static void
+_elm_atspi_ewk_wrapper_constructor(Elm_Atspi_Ewk_Wrapper *obj, Elm_Atspi_Ewk_Wrapper_Data *_pd, Evas_Object *ewk_view)
+{
+   Evas_Coord x, y, w, h;
+   _pd->ewk_view = ewk_view;
+
+   elm_widget_sub_object_parent_add(obj);
+   elm_atspi_accessible_role_set(obj, ELM_ATSPI_ROLE_EMBEDDED);
+
+   evas_object_geometry_get(ewk_view, &x, &y, &w, &h);
+   evas_object_move(obj, x, y);
+   evas_object_resize(obj, w, h);
+   evas_object_show(obj);
+   eo_do(ewk_view, eo_event_callback_array_add(resize_watch(), obj));
+
+   eo_do(obj, elm_obj_atspi_ewk_wrapper_connection_init());
+}
+
+static Eo*
+_elm_atspi_ewk_wrapper_proxy_create(const char *plugid, Eo *parent)
+{
+   Elm_Atspi_Proxy *proxy;
+   char *bus = NULL, *path = NULL;
+
+   if (!_elm_atspi_bridge_plug_id_split(plugid, &bus, &path)) {
+        ERR("_elm_atspi_bridge_plug_id_split failed");
+        return NULL;
+   }
+
+   proxy = eo_add(ELM_ATSPI_PROXY_CLASS, parent,
+                 elm_obj_atspi_proxy_constructor(ELM_ATSPI_PROXY_TYPE_PLUG));
+   if (!proxy) {
+        ERR("Unable to create Elm_Atspi_Proxy object");
+        free(bus); free(path);
+        return NULL;
+   }
+
+   eo_do(proxy, elm_obj_atspi_proxy_address_set(bus, path));
+
+   free(bus);
+   free(path);
+
+   elm_atspi_bridge_utils_proxy_connect(proxy);
+   return proxy;
+}
+
+EOLIAN static void
+_elm_atspi_ewk_wrapper_connection_init(Elm_Atspi_Ewk_Wrapper *obj, Elm_Atspi_Ewk_Wrapper_Data *_pd)
+{
+   // PlugID is set by chromium
+   const char *plug_id = evas_object_data_get(_pd->ewk_view, "__PlugID");
+
+   // when plug_id is NULL, disconnect
+   if (!plug_id)
+     {
+        _elm_atspi_ewk_wrapper_disconnect(_pd);
+        return;
+     }
+
+   // check if already connected to atspi object referenced by plug_id
+   if (_pd->plug_id && !strcmp(_pd->plug_id, plug_id))
+     return;
+
+   // destroy current connection
+   _elm_atspi_ewk_wrapper_disconnect(_pd);
+
+   // make new connection using proxy object
+   _pd->proxy = _elm_atspi_ewk_wrapper_proxy_create(plug_id, obj);
+   if (!_pd->proxy) {
+        ERR("Failed connect to Ewk_View root accessible object");
+        return;
+   }
+   _pd->plug_id = strdup(plug_id);
+}
+
+EOLIAN static void
+_elm_atspi_ewk_wrapper_eo_base_destructor(Elm_Atspi_Ewk_Wrapper *obj EINA_UNUSED, Elm_Atspi_Ewk_Wrapper_Data *_pd)
+{
+   free(_pd->plug_id);
+   if (_pd->ewk_view)
+     eo_do(_pd->ewk_view, eo_event_callback_array_del(resize_watch(), obj));
+}
+
+EOLIAN static Eina_List*
+_elm_atspi_ewk_wrapper_elm_interface_atspi_accessible_children_get(Elm_Atspi_Ewk_Wrapper *obj EINA_UNUSED, Elm_Atspi_Ewk_Wrapper_Data *_pd)
+{
+   if (_pd->proxy)
+     return eina_list_append(NULL, _pd->proxy);
+   else
+     return NULL;
+}
+
+static Eina_Bool
+_wrapper_widget_del(void *data, Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Evas_Object *ewk_view = data;
+   evas_object_data_set(ewk_view, EWK_A11Y_DATA_KEY, NULL);
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_ewk_view_widget_del(void *data, Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Elm_Atspi_Ewk_Wrapper *wrapper = data;
+   eo_del(wrapper);
+   return EINA_TRUE;
+}
+
+EOLIAN void
+_elm_atspi_ewk_wrapper_a11y_init(Eo *class EINA_UNUSED, void *pd EINA_UNUSED,
+                                 Evas_Object *parent, Evas_Object *ewk_view)
+{
+   Evas_Object *wrapper = evas_object_data_get(ewk_view, EWK_A11Y_DATA_KEY);
+   if (!wrapper) {
+        wrapper = eo_add(ELM_ATSPI_EWK_WRAPPER_CLASS, parent, elm_obj_atspi_ewk_wrapper_constructor(ewk_view));
+        evas_object_data_set(ewk_view, EWK_A11Y_DATA_KEY, wrapper);
+        // make sure that wrapper will not outlive ewk_view
+        eo_do(wrapper, eo_event_callback_del(EO_EV_DEL, _wrapper_widget_del, ewk_view));
+        eo_do(ewk_view, eo_event_callback_del(EO_EV_DEL, _ewk_view_widget_del, wrapper));
+   } else {
+        // Check if reparenting occured on Ewk_View
+        // This may happen in case when ewk_view is taken out from layout and put into another one.
+        // In order to avoid situation when ewk a11y may be accessed from different
+        // a11y node we check if reparenting occured and reset wrapper parent
+        if (parent != elm_widget_parent_get(wrapper)) {
+            eo_do(parent, elm_obj_widget_sub_object_add(wrapper));
+        }
+   }
+   elm_atspi_ewk_wrapper_connection_init(wrapper);
+}
+
+EOLIAN static Eo *
+_elm_atspi_ewk_wrapper_elm_interface_atspi_component_accessible_at_point_get(Elm_Atspi_Ewk_Wrapper *obj EINA_UNUSED, Elm_Atspi_Ewk_Wrapper_Data *_pd, Eina_Bool screen_coords, int x, int y)
+{
+   Eina_Rectangle rect;
+   int ee_x, ee_y;
+
+   if (!_pd->ewk_view)
+     return NULL;
+
+   if (screen_coords)
+     {
+        Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(_pd->ewk_view));
+        if (!ee) return NULL;
+        ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
+        x -= ee_x;
+        y -= ee_y;
+     }
+
+   evas_object_geometry_get(_pd->ewk_view, &rect.x, &rect.y, &rect.w, &rect.h);
+
+   if (eina_rectangle_coords_inside(&rect, x, y))
+      return _pd->proxy;
+   else
+      return NULL;
+}
+
+EOLIAN static Evas_Object *
+_elm_atspi_ewk_wrapper_ewk_view_get(Elm_Atspi_Ewk_Wrapper *obj EINA_UNUSED, Elm_Atspi_Ewk_Wrapper_Data *_pd)
+{
+   return _pd->ewk_view;
+}
+
+
+#include "elm_atspi_ewk_wrapper.eo.c"
diff --git a/src/lib/elm_atspi_ewk_wrapper.eo b/src/lib/elm_atspi_ewk_wrapper.eo
new file mode 100644 (file)
index 0000000..2e1e775
--- /dev/null
@@ -0,0 +1,35 @@
+class Elm_Atspi_Ewk_Wrapper (Elm.Widget)
+{
+   data: Elm_Atspi_Ewk_Wrapper_Data;
+   eo_prefix: elm_obj_atspi_ewk_wrapper;
+   methods {
+      constructor {
+         params {
+             @in ewk: Evas.Object*;
+         }
+      }
+      connection_init {
+      }
+      @property ewk_view {
+           get {
+           }
+           values {
+                ewk: Evas.Object*;
+           }
+      }
+      a11y_init @class {
+            params {
+                @in parent: Evas.Object*;
+                @in ewk: Evas.Object*;
+            }
+      }
+   }
+   constructors {
+      .constructor;
+   }
+   implements {
+      Eo.Base.destructor;
+      Elm_Interface_Atspi_Accessible.children.get;
+      Elm_Interface_Atspi_Component.accessible_at_point_get;
+   }
+}
diff --git a/src/lib/elm_atspi_ewk_wrapper.h b/src/lib/elm_atspi_ewk_wrapper.h
new file mode 100644 (file)
index 0000000..03ffe2a
--- /dev/null
@@ -0,0 +1,8 @@
+#ifdef EFL_BETA_API_SUPPORT
+#ifdef EFL_EO_API_SUPPORT
+#include "elm_atspi_ewk_wrapper.eo.h"
+#endif
+#ifndef EFL_NOLEGACY_API_SUPPORT
+#include "elm_atspi_ewk_wrapper.eo.legacy.h"
+#endif
+#endif
index 7b82d32..69efc5c 100644 (file)
@@ -6329,27 +6329,6 @@ static Eina_List *_lines_split(Eina_List *children)
 }
 //
 
-// TIZEN ONLY
-static void
-_ewk_view_load_finished(Eo *plug,
-                        Evas_Object *obj,
-                        const char *addr)
-{
-   char *bus, *path;
-
-   if (addr && !evas_object_data_get(obj, "__plug_connected"))
-     {
-       if (_elm_atspi_bridge_plug_id_split(addr, &bus, &path))
-         {
-            eo_do(plug, elm_obj_atspi_proxy_address_set(bus, path));
-            elm_atspi_bridge_utils_proxy_connect(plug);
-            evas_object_data_set(obj, "__plug_connected", (void*)1);
-            free(bus);
-            free(path);
-        }
-     }
-}
-
 // TIZEN_ONLY(20160705) - enable atspi_proxy to work
 static void
 _proxy_widget_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
@@ -6405,42 +6384,29 @@ _elm_widget_elm_interface_atspi_accessible_children_get(Eo *obj, Elm_Widget_Smar
 
    EINA_LIST_FOREACH(wd->subobjs, l, widget)
      {
-        // TIZEN_ONLY(20160824): Do not append a child, if its accessible parent is different with widget parent
-        eo_do(widget, parent = elm_interface_atspi_accessible_parent_get());
-        if (parent && (parent != obj)) continue;
-        //
-
+        const char *type = evas_object_type_get(widget);
         // TIZEN ONLY
-        // Ugly Tizen hack to integrate AT-SPI2 accessibility provided by WebKit with
-        // elementary one. Due to problematic cross dependencies between Webkit
-        // and elementary, instead of directly using ewk API to integrate accessibility
-        // we use evas_object_data_set with pre defined key to share data
-        // between webkit and elemetary libraries.
-        const char *plug_id;
-        if ((plug_id = evas_object_data_get(widget, "__PlugID")) != NULL)
-          {
-            // TIZEN_ONLY(20160930) : endless recursion fix
-            eo_do_super(obj, MY_CLASS, elm_interface_atspi_accessible_attribute_append("__PlugID", plug_id));
-            //
-             Eo *plug = evas_object_data_get(widget, "__ewk_proxy");
-             if (!plug)
-               {
-                  plug = eo_add(ELM_ATSPI_PROXY_CLASS, obj, elm_obj_atspi_proxy_constructor(ELM_ATSPI_PROXY_TYPE_PLUG));
-                  evas_object_data_set(widget, "__ewk_proxy", plug);
-                  eo_do(widget, eo_event_callback_add(EO_EV_DEL, _on_widget_del, plug));
-                  _ewk_view_load_finished(plug, widget, plug_id);
-               }
-             if (plug && evas_object_data_get(widget, "__plug_connected"))
-                accs = eina_list_append(accs, plug);
-             continue;
-          }
+        // Ugly Tizen hack to integrate AT-SPI2 accessibility provided by WebKit/Chromium with elementary one.
+        // This wrapper class should be implemented in Webkit/Chromium EFL ports
+        if (type && (!strcmp(type, "EWebView") || !strcmp(type, "WebView"))) {
+           elm_atspi_ewk_wrapper_a11y_init(obj, widget);
+        }
+     }
+
+   EINA_LIST_FOREACH(wd->subobjs, l, widget)
+     {
+        // TIZEN_ONLY(20160824): Do not append a child, if its accessible parent is different with widget parent
+        if (eo_isa(widget, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN)) {
+           eo_do(widget, parent = elm_interface_atspi_accessible_parent_get());
+           if (parent && (parent != obj)) continue;
+        }
 
         // TIZEN_ONLY(20160705) - enable atspi_proxy to work
         const char *plug_id_2;
         if ((plug_id_2 = evas_object_data_get(widget, "___PLUGID")) != NULL)
           {
             // TIZEN_ONLY(20160930) : endless recursion fix
-            eo_do_super(obj, MY_CLASS, elm_interface_atspi_accessible_attribute_append("___PlugID", plug_id));
+            eo_do_super(obj, MY_CLASS, elm_interface_atspi_accessible_attribute_append("___PlugID", plug_id_2));
 
              Eo *proxy;
              char *svcname, *svcnum;
@@ -6741,6 +6707,13 @@ _elm_widget_elm_interface_atspi_component_accessible_at_point_get(Eo *obj, Elm_W
                   Elm_Access_Info *info = _elm_access_info_get(child);
                   compare_obj = info->part_object;
                }
+
+             /* In case of ewk wrapper object compare with internal ewk_view evas_object */
+             if (eo_isa(child, ELM_ATSPI_EWK_WRAPPER_CLASS))
+               {
+                  compare_obj = elm_atspi_ewk_wrapper_ewk_view_get(child);
+               }
+
              /* If spacial eo children do not have backing evas_object continue with search */
              if (!compare_obj)
                continue;