From 22a1e551be364c0d65cd6693b40bf9d9ceed67be Mon Sep 17 00:00:00 2001 From: Lukasz Stanislawski Date: Thu, 7 Dec 2017 19:26:57 +0530 Subject: [PATCH] elm: add elm_atspi_ewk_wrapper class 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. @tizen_feature orignal patch: 85d052b2e63dc7e0b5286cea61c0870a2f854dcc Change-Id: I8e99a836e5c47d8e37100b5309c200fbfa20960d --- src/Makefile_Elementary.am | 7 +- src/lib/elementary/Elementary.h | 1 + src/lib/elementary/elm_atspi_ewk_wrapper.c | 211 ++++++++++++++++++++++++++++ src/lib/elementary/elm_atspi_ewk_wrapper.eo | 36 +++++ src/lib/elementary/elm_atspi_ewk_wrapper.h | 8 ++ src/lib/elementary/elm_widget.c | 65 +++------ 6 files changed, 281 insertions(+), 47 deletions(-) create mode 100644 src/lib/elementary/elm_atspi_ewk_wrapper.c create mode 100644 src/lib/elementary/elm_atspi_ewk_wrapper.eo create mode 100644 src/lib/elementary/elm_atspi_ewk_wrapper.h diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index cf4a5b8..470e8dd 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -3,8 +3,9 @@ elm_public_eolian_files = \ lib/elementary/elm_atspi_bridge.eo \ + lib/elementary/elm_atspi_ewk_wrapper.eo \ lib/elementary/elm_atspi_app_object.eo \ - lib/elementary/elm_atspi_proxy.eo \ + lib/elementary/elm_atspi_proxy.eo \ lib/elementary/elm_bg.eo \ lib/elementary/efl_ui_button.eo \ lib/elementary/elm_calendar.eo \ @@ -223,6 +224,7 @@ includesdir = $(includedir)/elementary-@VMAJ@ includesunstable_HEADERS = \ lib/elementary/elm_gen_common.h \ lib/elementary/elm_atspi_bridge.h \ + lib/elementary/elm_atspi_ewk_wrapper.h \ lib/elementary/efl_access.h \ lib/elementary/efl_access_legacy.h \ lib/elementary/efl_access_text.h \ @@ -571,7 +573,8 @@ lib_elementary_libelementary_la_SOURCES = \ lib/elementary/elm_actionslider.c \ lib/elementary/elm_atspi_app_object.c \ lib/elementary/elm_atspi_bridge.c \ - lib/elementary/elm_atspi_proxy.c \ + lib/elementary/elm_atspi_ewk_wrapper.c \ + lib/elementary/elm_atspi_proxy.c \ lib/elementary/elm_bg.c \ lib/elementary/elm_box.c \ lib/elementary/elm_bubble.c \ diff --git a/src/lib/elementary/Elementary.h b/src/lib/elementary/Elementary.h index eabcf66..78eb198 100644 --- a/src/lib/elementary/Elementary.h +++ b/src/lib/elementary/Elementary.h @@ -185,6 +185,7 @@ EAPI extern Elm_Version *elm_version; #include #include #include +#include //TIZEN_ONLY(20171108): make atspi_proxy work #include // diff --git a/src/lib/elementary/elm_atspi_ewk_wrapper.c b/src/lib/elementary/elm_atspi_ewk_wrapper.c new file mode 100644 index 0000000..1f9e665 --- /dev/null +++ b/src/lib/elementary/elm_atspi_ewk_wrapper.c @@ -0,0 +1,211 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#define EFL_ACCESS_PROTECTED +#define EFL_ACCESS_COMPONENT_PROTECTED + +#include +#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 void +_ewk_view_geometry_changed(void *data, const Efl_Event *event); + +EFL_CALLBACKS_ARRAY_DEFINE(resize_watch, + { EFL_GFX_EVENT_MOVE, _ewk_view_geometry_changed }, + { EFL_GFX_EVENT_RESIZE, _ewk_view_geometry_changed }); + +static void +_elm_atspi_ewk_wrapper_disconnect(Elm_Atspi_Ewk_Wrapper_Data *_pd) +{ + if (_pd->proxy) efl_del(_pd->proxy); + free(_pd->plug_id); + _pd->proxy = NULL; + _pd->plug_id = NULL; +} + +static void +_ewk_view_geometry_changed(void *data, const Efl_Event *event) +{ + Evas_Coord x, y, w, h; + Elm_Atspi_Ewk_Wrapper *wrapper = data; + + evas_object_geometry_get(event->object, &x, &y, &w, &h); + evas_object_move(wrapper, x, y); + evas_object_resize(wrapper, w, h); +} + +EOLIAN static void +_elm_atspi_ewk_wrapper_constructor(Eo *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); + efl_access_role_set(obj, EFL_ACCESS_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); + efl_event_callback_array_add(ewk_view, resize_watch(), obj); + + elm_obj_atspi_ewk_wrapper_connection_init(obj); +} + +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 = efl_add(ELM_ATSPI_PROXY_CLASS, parent, elm_obj_atspi_proxy_constructor(parent, ELM_ATSPI_PROXY_TYPE_PLUG)); + if (!proxy) { + ERR("Unable to create Elm_Atspi_Proxy object"); + free(bus); free(path); + return NULL; + } + + elm_obj_atspi_proxy_address_set(proxy, bus, path); + + free(bus); + free(path); + + elm_atspi_bridge_utils_proxy_connect(proxy); + return proxy; +} + +EOLIAN static void +_elm_atspi_ewk_wrapper_connection_init(Eo *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_efl_object_destructor(Eo *obj EINA_UNUSED, Elm_Atspi_Ewk_Wrapper_Data *_pd) +{ + free(_pd->plug_id); + if (_pd->ewk_view) + efl_event_callback_array_del(_pd->ewk_view, resize_watch(), obj); +} + +EOLIAN static Eina_List* +_elm_atspi_ewk_wrapper_efl_access_children_get(Eo *obj EINA_UNUSED, Elm_Atspi_Ewk_Wrapper_Data *_pd) +{ + if (_pd->proxy) + return eina_list_append(NULL, _pd->proxy); + else + return NULL; +} + +static void +_wrapper_widget_del(void *data, const Efl_Event *event EINA_UNUSED) +{ + Evas_Object *ewk_view = data; + evas_object_data_set(ewk_view, EWK_A11Y_DATA_KEY, NULL); +} + +static void +_ewk_view_widget_del(void *data, const Efl_Event *event EINA_UNUSED) +{ + Elm_Atspi_Ewk_Wrapper *wrapper = data; + efl_del(wrapper); +} + +EOLIAN void +_elm_atspi_ewk_wrapper_a11y_init(Eo *obj 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 = efl_add(ELM_ATSPI_EWK_WRAPPER_CLASS, parent, elm_obj_atspi_ewk_wrapper_constructor(obj, ewk_view)); + evas_object_data_set(ewk_view, EWK_A11Y_DATA_KEY, wrapper); + // make sure that wrapper will not outlive ewk_view + efl_event_callback_del(wrapper, EFL_EVENT_DEL, _wrapper_widget_del, ewk_view); + efl_event_callback_del(ewk_view, EFL_EVENT_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)) { + elm_obj_widget_sub_object_add(parent, wrapper); + } + } + elm_atspi_ewk_wrapper_connection_init(wrapper); +} + +EOLIAN static Eo * +_elm_atspi_ewk_wrapper_efl_access_component_accessible_at_point_get(Eo *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(Eo *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/elementary/elm_atspi_ewk_wrapper.eo b/src/lib/elementary/elm_atspi_ewk_wrapper.eo new file mode 100644 index 0000000..01a48ef --- /dev/null +++ b/src/lib/elementary/elm_atspi_ewk_wrapper.eo @@ -0,0 +1,36 @@ +class Elm.Atspi.Ewk_Wrapper (Efl.Object, Efl.Access, Efl.Access.Component) +{ + data: Elm_Atspi_Ewk_Wrapper_Data; + eo_prefix: elm_obj_atspi_ewk_wrapper; + legacy_prefix: elm_atspi_ewk_wrapper; + methods { + constructor { + params { + @in ewk: Efl.Object; + } + } + connection_init { + } + @property ewk_view { + get { + } + values { + ewk: Efl.Object; + } + } + a11y_init @class { + params { + @in parent: Efl.Object; + @in ewk: Efl.Object; + } + } + } + constructors { + .constructor; + } + implements { + Efl.Object.destructor; + Efl.Access.children { get; } + Efl.Access.Component.accessible_at_point_get; + } +} \ No newline at end of file diff --git a/src/lib/elementary/elm_atspi_ewk_wrapper.h b/src/lib/elementary/elm_atspi_ewk_wrapper.h new file mode 100644 index 0000000..03ffe2a --- /dev/null +++ b/src/lib/elementary/elm_atspi_ewk_wrapper.h @@ -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 diff --git a/src/lib/elementary/elm_widget.c b/src/lib/elementary/elm_widget.c index 88fbbc9..a50f726 100644 --- a/src/lib/elementary/elm_widget.c +++ b/src/lib/elementary/elm_widget.c @@ -5619,26 +5619,6 @@ static Eina_List *_lines_split(Eina_List *children) //TIZEN_ONLY(20171114) atspi: integrate ewk_view with elementary accessibility 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)) - { - elm_obj_atspi_proxy_address_set(plug, bus, path); - elm_atspi_bridge_utils_proxy_connect(plug); - evas_object_data_set(obj, "__plug_connected", (void*)1); - free(bus); - free(path); - } - } -} - -static void _on_ewk_del(void *data, const Efl_Event *desc EINA_UNUSED) { Eo *plug = data; @@ -5658,33 +5638,23 @@ _elm_widget_efl_access_children_get(Eo *obj, Elm_Widget_Smart_Data *pd) EINA_LIST_FOREACH(pd->subobjs, l, widget) { - //TIZEN_ONLY(20160824): Do not append a child, if its accessible parent is different with widget parent - parent = efl_access_parent_get(widget); - if (parent && (parent != obj)) continue; - - //TIZEN_ONLY(20171114) atspi: integrate ewk_view with elementary accessibility - // 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) + const char *type = evas_object_type_get(widget); + // TIZEN ONLY + // 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"))) { - Eo *plug = evas_object_data_get(widget, "__ewk_proxy"); - if (!plug) - { - plug = efl_add(ELM_ATSPI_PROXY_CLASS, obj, elm_obj_atspi_proxy_constructor(efl_added, ELM_ATSPI_PROXY_TYPE_PLUG)); - evas_object_data_set(widget, "__ewk_proxy", plug); - efl_event_callback_add(widget, EFL_EVENT_DEL, _on_ewk_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; + elm_atspi_ewk_wrapper_a11y_init(obj, widget); + } + } + EINA_LIST_FOREACH(pd->subobjs, l, widget) + { + // TIZEN_ONLY(20160824): Do not append a child, if its accessible parent is different with widget parent + if (efl_isa(widget, EFL_ACCESS_MIXIN)) + { + parent = efl_access_parent_get(widget); + if (parent && (parent != obj)) continue; } - // - //TIZEN_ONLY(20171108): make atspi_proxy work const char *plug_id_2; if ((plug_id_2 = evas_object_data_get(widget, "___PLUGID")) != NULL) @@ -6592,6 +6562,11 @@ _elm_widget_efl_access_component_accessible_at_point_get(Eo *obj, Elm_Widget_Sma 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 (efl_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; -- 2.7.4