From e36a40828ef5e37c11af9ce5f06c113004afea43 Mon Sep 17 00:00:00 2001 From: "ayush.k123" Date: Fri, 3 Feb 2023 15:05:58 +0530 Subject: [PATCH] [M108 Migration][Accessibility] Bringup Accessibility. - Brings up & Refactoring accessibility in accordance with upstream. - Make a AXPlatformNodeEfl inherit from AXPlatformNodeAuraLinux for ATK event/callback. - Migrate from BrowserAccessibility* to AXPlatformNode*. - Remove BrowserAccessibilityManagerEfl / BrowserAccessibilityEfl. - Support value change event. Reference: https://review.tizen.org/gerrit/279337 https://review.tizen.org/gerrit/279612 Change-Id: Ie312e18870a5d20304be4b8c5a6b11aa1b23ae33 Signed-off-by: Ayush Kumar --- build/config/linux/atk/BUILD.gn | 2 +- build/config/ui.gni | 3 +- .../browser/accessibility/browser_accessibility.cc | 32 ++ .../browser/accessibility/browser_accessibility.h | 5 + .../browser_accessibility_auralinux.cc | 20 + .../browser_accessibility_auralinux.h | 9 + .../accessibility/browser_accessibility_manager.cc | 35 ++ .../accessibility/browser_accessibility_manager.h | 13 +- .../web_ax_platform_tree_manager_delegate.h | 5 + .../renderer_host/render_frame_host_impl.cc | 6 + .../browser/renderer_host/render_frame_host_impl.h | 4 + .../renderer_host/render_widget_host_view_aura.cc | 4 + content/browser/web_contents/web_contents_impl.cc | 24 + content/browser/web_contents/web_contents_impl.h | 6 + .../accessibility/render_accessibility_impl.cc | 4 + packaging/chromium-efl.spec | 3 +- .../common/web_preferences/web_preferences.cc | 3 + .../web_preferences_mojom_traits.cc | 3 + .../common/web_preferences/web_preferences.h | 1 + .../web_preferences/web_preferences_mojom_traits.h | 4 + .../mojom/webpreferences/web_preferences.mojom | 3 + third_party/blink/public/web/web_settings.h | 5 + third_party/blink/renderer/core/dom/document.cc | 8 + third_party/blink/renderer/core/dom/element.cc | 14 + .../renderer/core/exported/web_settings_impl.cc | 10 + .../renderer/core/exported/web_settings_impl.h | 5 + .../blink/renderer/core/exported/web_view_impl.cc | 3 + .../blink/renderer/core/frame/local_dom_window.cc | 3 + third_party/blink/renderer/core/frame/settings.h | 10 + .../modules/accessibility/ax_node_object.cc | 38 +- .../renderer/modules/accessibility/ax_object.cc | 16 + tizen_src/build/BUILD.gn | 16 + tizen_src/build/config/BUILD.gn | 8 + tizen_src/build/config/tizen_features.gni | 12 + .../chromium_impl/content/browser/browser_efl.gni | 10 + .../accessibility/platform/ax_platform_node_efl.cc | 501 +++++++++++++++++++++ .../accessibility/platform/ax_platform_node_efl.h | 68 +++ tizen_src/chromium_impl/ui/ui_efl.gni | 5 + tizen_src/ewk/efl_integration/BUILD.gn | 15 + .../ewk/efl_integration/eweb_accessibility.cc | 88 ++++ tizen_src/ewk/efl_integration/eweb_accessibility.h | 54 +++ .../efl_integration/eweb_accessibility_object.cc | 346 ++++++++++++++ .../efl_integration/eweb_accessibility_object.h | 94 ++++ .../ewk/efl_integration/eweb_accessibility_util.cc | 204 +++++++++ .../ewk/efl_integration/eweb_accessibility_util.h | 42 ++ tizen_src/ewk/efl_integration/eweb_view.cc | 73 +++ tizen_src/ewk/efl_integration/eweb_view.h | 26 ++ .../efl_integration/web_contents_observer_efl.cc | 4 + ui/accessibility/BUILD.gn | 8 + .../platform/ax_platform_atk_hyperlink.cc | 4 + .../platform/ax_platform_node_auralinux.cc | 221 ++++++++- .../platform/ax_platform_node_auralinux.h | 19 +- .../platform/ax_platform_node_delegate.h | 10 + .../platform/ax_platform_node_delegate_base.cc | 16 + .../platform/ax_platform_node_delegate_base.h | 10 + ui/base/ui_features.gni | 2 +- ui/gfx/native_widget_types.h | 6 +- 57 files changed, 2151 insertions(+), 12 deletions(-) create mode 100644 tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.cc create mode 100644 tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.h create mode 100644 tizen_src/ewk/efl_integration/eweb_accessibility.cc create mode 100644 tizen_src/ewk/efl_integration/eweb_accessibility.h create mode 100644 tizen_src/ewk/efl_integration/eweb_accessibility_object.cc create mode 100644 tizen_src/ewk/efl_integration/eweb_accessibility_object.h create mode 100644 tizen_src/ewk/efl_integration/eweb_accessibility_util.cc create mode 100644 tizen_src/ewk/efl_integration/eweb_accessibility_util.h diff --git a/build/config/linux/atk/BUILD.gn b/build/config/linux/atk/BUILD.gn index 239c387..fc0926b 100644 --- a/build/config/linux/atk/BUILD.gn +++ b/build/config/linux/atk/BUILD.gn @@ -13,7 +13,7 @@ assert(!is_chromeos) # These packages should _only_ be expected when building for a target. assert(current_toolchain == default_toolchain) -if (use_atk) { +if (use_atk && !use_efl) { assert(use_glib, "use_atk=true requires that use_glib=true") } diff --git a/build/config/ui.gni b/build/config/ui.gni index 84d9e50..40d04cc 100644 --- a/build/config/ui.gni +++ b/build/config/ui.gni @@ -42,7 +42,8 @@ declare_args() { assert(!use_glib || (is_linux && !is_castos)) -use_atk = use_glib && !use_efl && current_toolchain == default_toolchain +use_atk = + is_tizen || (use_glib && !use_efl && current_toolchain == default_toolchain) # Whether using Xvfb to provide a display server for a test might be # necessary. diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index 31726d1..243ffae 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc @@ -1432,6 +1432,11 @@ gfx::NativeViewAccessible BrowserAccessibility::HitTestSync( } gfx::NativeViewAccessible BrowserAccessibility::GetFocus() const { +#if defined(TIZEN_ATK_SUPPORT) + if (!manager_) + return nullptr; +#endif + BrowserAccessibility* focused = manager()->GetFocus(); if (!focused) return nullptr; @@ -1439,6 +1444,33 @@ gfx::NativeViewAccessible BrowserAccessibility::GetFocus() const { return focused->GetNativeViewAccessible(); } +#if defined(TIZEN_ATK_SUPPORT) +gfx::NativeViewAccessible BrowserAccessibility::GetActiveDescendant() { + if (!manager_) + return nullptr; + + auto* active_descendant = + manager()->GetActiveDescendant(manager()->GetFocus()); + if (!active_descendant) + return nullptr; + + return active_descendant->GetNativeViewAccessible(); +} + +void BrowserAccessibility::EvasToBlinkCords(int x, + int y, + int* view_x, + int* view_y) { + if (manager_) + manager_->EvasToBlinkCords(x, y, view_x, view_y); +} + +void BrowserAccessibility::CheckFocusOnEwebview() { + if (manager_) + manager_->CheckFocusOnEwebview(); +} +#endif + ui::AXPlatformNode* BrowserAccessibility::GetFromNodeID(int32_t id) { BrowserAccessibility* node = manager_->GetFromID(id); if (!node) diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h index e2fee78..16609e0 100644 --- a/content/browser/accessibility/browser_accessibility.h +++ b/content/browser/accessibility/browser_accessibility.h @@ -499,6 +499,11 @@ class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate { gfx::NativeViewAccessible HitTestSync(int physical_pixel_x, int physical_pixel_y) const override; gfx::NativeViewAccessible GetFocus() const override; +#if defined(TIZEN_ATK_SUPPORT) + gfx::NativeViewAccessible GetActiveDescendant() override; + void EvasToBlinkCords(int x, int y, int* view_x, int* view_y) override; + void CheckFocusOnEwebview() override; +#endif ui::AXPlatformNode* GetFromNodeID(int32_t id) override; ui::AXPlatformNode* GetFromTreeIDAndNodeID(const ui::AXTreeID& ax_tree_id, int32_t id) override; diff --git a/content/browser/accessibility/browser_accessibility_auralinux.cc b/content/browser/accessibility/browser_accessibility_auralinux.cc index 913575b..58f6053 100644 --- a/content/browser/accessibility/browser_accessibility_auralinux.cc +++ b/content/browser/accessibility/browser_accessibility_auralinux.cc @@ -69,4 +69,24 @@ ui::TextAttributeList BrowserAccessibilityAuraLinux::ComputeTextAttributes() return GetNode()->ComputeTextAttributes(); } +#if defined(TIZEN_ATK_SUPPORT) +ui::AXPlatformNode* BrowserAccessibilityAuraLinux::GetRoot() { + BrowserAccessibility* accessibility = + static_cast(manager_->RootDelegate()); + if (!accessibility) + return nullptr; + + return ToBrowserAccessibilityAuraLinux(accessibility)->GetNode(); +} + +gfx::NativeViewAccessible BrowserAccessibilityAuraLinux::GetRootAtkObject() { + return root_atk_object_; +} + +void BrowserAccessibilityAuraLinux::SetRootAtkObject( + gfx::NativeViewAccessible root_atk_object) { + root_atk_object_ = root_atk_object; +} +#endif + } // namespace content diff --git a/content/browser/accessibility/browser_accessibility_auralinux.h b/content/browser/accessibility/browser_accessibility_auralinux.h index cb876af..2f17216 100644 --- a/content/browser/accessibility/browser_accessibility_auralinux.h +++ b/content/browser/accessibility/browser_accessibility_auralinux.h @@ -42,9 +42,18 @@ class BrowserAccessibilityAuraLinux : public BrowserAccessibility { ui::TextAttributeList ComputeTextAttributes() const override; +#if defined(TIZEN_ATK_SUPPORT) + ui::AXPlatformNode* GetRoot() override; + gfx::NativeViewAccessible GetRootAtkObject() override; + void SetRootAtkObject(gfx::NativeViewAccessible root_atk_object); +#endif + private: // TODO: use a unique_ptr since the node is owned by this class. raw_ptr platform_node_; +#if defined(TIZEN_ATK_SUPPORT) + gfx::NativeViewAccessible root_atk_object_ = nullptr; +#endif }; CONTENT_EXPORT BrowserAccessibilityAuraLinux* ToBrowserAccessibilityAuraLinux( diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc index d225fd0..58f9f36 100644 --- a/content/browser/accessibility/browser_accessibility_manager.cc +++ b/content/browser/accessibility/browser_accessibility_manager.cc @@ -35,6 +35,11 @@ #include "content/public/common/content_switches.h" #endif +#if defined(TIZEN_ATK_SUPPORT) +#include "content/browser/renderer_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_view_aura.h" +#endif + namespace content { namespace { @@ -2066,4 +2071,34 @@ void BrowserAccessibilityManager::UpdateDeviceScaleFactor() { device_scale_factor_ = delegate_->AccessibilityGetDeviceScaleFactor(); } +#if defined(TIZEN_ATK_SUPPORT) +void BrowserAccessibilityManager::EvasToBlinkCords(int x, + int y, + int* view_x, + int* view_y) { + auto view = + static_cast(delegate_->AccessibilityGetView()); + + if (!view) + return; + + view->offscreen_helper()->EvasToBlinkCords(x, y, view_x, view_y); + gfx::Rect rect(*view_x, *view_y, 0, 0); + rect.Offset(GetViewBoundsInScreenCoordinates().OffsetFromOrigin()); + *view_x = rect.x(); + *view_y = rect.y(); +} + +void BrowserAccessibilityManager::CheckFocusOnEwebview() { + auto view = + static_cast(delegate_->AccessibilityGetView()); + + if (!view) + return; + + if (!view->offscreen_helper()->HasFocus()) + view->offscreen_helper()->Focus(true); +} +#endif + } // namespace content diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h index 048ad2f..a2ba368 100644 --- a/content/browser/accessibility/browser_accessibility_manager.h +++ b/content/browser/accessibility/browser_accessibility_manager.h @@ -51,12 +51,17 @@ namespace content { class BrowserAccessibilityManagerAndroid; #elif BUILDFLAG(IS_WIN) class BrowserAccessibilityManagerWin; -#elif BUILDFLAG(USE_ATK) +#elif BUILDFLAG(USE_ATK) || defined(TIZEN_ATK_SUPPORT) class BrowserAccessibilityManagerAuraLinux; #elif BUILDFLAG(IS_MAC) class BrowserAccessibilityManagerMac; #endif +#if defined(TIZEN_ATK_SUPPORT) +class BrowserAccessibilityManagerEfl; +class RenderWidgetHostView; +#endif + // To be called when a BrowserAccessibilityManager fires a generated event. // Provides the host, the event fired, and which node id the event was for. class RenderFrameHostImpl; @@ -305,7 +310,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager BrowserAccessibilityManagerWin* ToBrowserAccessibilityManagerWin(); #endif -#if BUILDFLAG(USE_ATK) +#if BUILDFLAG(USE_ATK) || defined(TIZEN_ATK_SUPPORT) BrowserAccessibilityManagerAuraLinux* ToBrowserAccessibilityManagerAuraLinux(); #endif @@ -465,6 +470,10 @@ class CONTENT_EXPORT BrowserAccessibilityManager // Called in response to a hover event, caches the result for the next // call to CachingAsyncHitTest(). void CacheHitTestResult(BrowserAccessibility* hit_test_result) const; +#if defined(TIZEN_ATK_SUPPORT) + void EvasToBlinkCords(int x, int y, int* view_x, int* view_y); + void CheckFocusOnEwebview(); +#endif // Updates the page scale factor for this frame. void SetPageScaleFactor(float page_scale_factor); diff --git a/content/browser/accessibility/web_ax_platform_tree_manager_delegate.h b/content/browser/accessibility/web_ax_platform_tree_manager_delegate.h index 45862ce..7175bab 100644 --- a/content/browser/accessibility/web_ax_platform_tree_manager_delegate.h +++ b/content/browser/accessibility/web_ax_platform_tree_manager_delegate.h @@ -12,6 +12,7 @@ namespace content { class RenderFrameHostImpl; class WebContentsAccessibility; +class RenderWidgetHostView; // Pure abstract class that is used by `BrowserAccessibilityManager` to gather // information or perform actions that are implemented differently between the @@ -36,6 +37,10 @@ class CONTENT_EXPORT WebAXPlatformTreeManagerDelegate virtual WebContentsAccessibility* AccessibilityGetWebContentsAccessibility() = 0; +#if defined(TIZEN_ATK_SUPPORT) + virtual RenderWidgetHostView* AccessibilityGetView() = 0; +#endif + protected: WebAXPlatformTreeManagerDelegate() = default; }; diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index a614089..1bc379a 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -14623,4 +14623,10 @@ std::ostream& operator<<(std::ostream& o, return o << LifecycleStateImplToString(s); } +#if defined(TIZEN_ATK_SUPPORT) +RenderWidgetHostView* RenderFrameHostImpl::AccessibilityGetView() { + return render_view_host_->GetWidget()->GetView(); +} +#endif + } // namespace content diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h index db82798..6a6b83b 100644 --- a/content/browser/renderer_host/render_frame_host_impl.h +++ b/content/browser/renderer_host/render_frame_host_impl.h @@ -627,6 +627,10 @@ class CONTENT_EXPORT RenderFrameHostImpl RenderFrameHostImpl* AccessibilityRenderFrameHost() override; WebContentsAccessibility* AccessibilityGetWebContentsAccessibility() override; +#if defined(TIZEN_ATK_SUPPORT) + RenderWidgetHostView* AccessibilityGetView() override; +#endif + // SiteInstanceGroup::Observer void RenderProcessGone(SiteInstanceGroup* site_instance_group, const ChildProcessTerminationInfo& info) override; diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 78b93f7..325ae915 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -788,7 +788,11 @@ bool RenderWidgetHostViewAura::ShouldShowStaleContentOnEviction() { } gfx::Rect RenderWidgetHostViewAura::GetViewBounds() { +#if BUILDFLAG(IS_EFL) + return offscreen_helper()->GetViewBounds(); +#else return window_->GetBoundsInScreen(); +#endif } void RenderWidgetHostViewAura::UpdateBackgroundColor() { diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 17895cb..4700997 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -2845,6 +2845,14 @@ const blink::web_pref::WebPreferences WebContentsImpl::ComputeWebPreferences() { prefs.stylus_handwriting_enabled = stylus_handwriting_enabled_; +#if BUILDFLAG(IS_EFL) + if (spatial_navigation_enabled_) + prefs.spatial_navigation_enabled = true; + + if (atk_enabled_) + prefs.atk_enabled = true; +#endif + prefs.disable_reading_from_canvas = command_line.HasSwitch(switches::kDisableReadingFromCanvas); @@ -9647,6 +9655,22 @@ void WebContentsImpl::CreateEflNativeView() { GLSharedContextEfl::Initialize(root_window); } + +void WebContentsImpl::SetSpatialNavigationEnabled(bool enabled) { + if (spatial_navigation_enabled_ == enabled) + return; + + spatial_navigation_enabled_ = enabled; + NotifyPreferencesChanged(); +} + +void WebContentsImpl::SetAtkEnabled(bool enabled) { + if (atk_enabled_ == enabled) + return; + + atk_enabled_ = enabled; + NotifyPreferencesChanged(); +} #endif } // namespace content diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index b8ab515..3d3216f 100755 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -230,6 +230,9 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, Evas_Object* GetEflNativeView() const { return efl_native_view_; } void set_ewk_view(void* ewk_view) { ewk_view_ = ewk_view; } void* ewk_view() const { return ewk_view_; } + + void SetSpatialNavigationEnabled(bool enabled); + void SetAtkEnabled(bool enabled); #endif // Returns the SavePackage which manages the page saving job. May be NULL. @@ -2397,6 +2400,9 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, #if BUILDFLAG(IS_EFL) Evas_Object* efl_native_view_ = nullptr; void* ewk_view_ = nullptr; + + bool atk_enabled_ = false; + bool spatial_navigation_enabled_ = false; #endif base::WeakPtrFactory loading_weak_factory_{this}; diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc index 5ee30b5..ccd850a 100644 --- a/content/renderer/accessibility/render_accessibility_impl.cc +++ b/content/renderer/accessibility/render_accessibility_impl.cc @@ -135,6 +135,10 @@ RenderAccessibilityImpl::RenderAccessibilityImpl( WebView* web_view = render_frame_->GetWebView(); WebSettings* settings = web_view->GetSettings(); +#if defined(TIZEN_ATK_SUPPORT) + settings->SetAccessibilityEnabled(true); +#endif + SetAccessibilityCrashKey(mode); #if BUILDFLAG(IS_ANDROID) // Password values are only passed through on Android. diff --git a/packaging/chromium-efl.spec b/packaging/chromium-efl.spec index 111d603..d16e310 100644 --- a/packaging/chromium-efl.spec +++ b/packaging/chromium-efl.spec @@ -54,8 +54,9 @@ Requires(post): xkeyboard-config Requires(postun): /sbin/ldconfig BuildRequires: binutils-gold -BuildRequires: bison, edje-tools, expat-devel, flex, gettext, gperf, libcap-devel, libcurl, libatomic +BuildRequires: at-spi2-atk-devel, bison, edje-tools, expat-devel, flex, gettext, gperf, libatk-bridge-2_0-0, libcap-devel, libcurl, libatomic BuildRequires: libjpeg-turbo-devel, ninja, perl, python3, python3-xml, which +BuildRequires: pkgconfig(atk) BuildRequires: pkgconfig(capi-appfw-application) BuildRequires: pkgconfig(capi-appfw-app-manager) BuildRequires: pkgconfig(capi-location-manager) diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc index 67b358e..f58b446 100644 --- a/third_party/blink/common/web_preferences/web_preferences.cc +++ b/third_party/blink/common/web_preferences/web_preferences.cc @@ -193,6 +193,9 @@ WebPreferences::WebPreferences() default_minimum_page_scale_factor(1.f), default_maximum_page_scale_factor(4.f), #endif +#if BUILDFLAG(IS_EFL) + atk_enabled(false), +#endif hide_download_ui(false), presentation_receiver(false), media_controls_enabled(true), diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc index 99b78f7..93330bf 100644 --- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc +++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc @@ -162,6 +162,9 @@ bool StructTraitsmax_refresh_rate = data.max_refresh_rate(); #endif +#if BUILDFLAG(IS_EFL) + out->atk_enabled = data.atk_enabled(); +#endif #if BUILDFLAG(IS_ANDROID) out->font_scale_factor = data.font_scale_factor(); out->device_scale_adjustment = data.device_scale_adjustment(); diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h index 21c156f..47e2b5c 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences.h +++ b/third_party/blink/public/common/web_preferences/web_preferences.h @@ -209,6 +209,7 @@ struct BLINK_COMMON_EXPORT WebPreferences { #if BUILDFLAG(IS_EFL) float font_scale_factor; bool link_effect_enabled = false; + bool atk_enabled; #endif bool double_tap_to_zoom_enabled; diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h index 3f2fbaf..31b0f1e 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h +++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h @@ -529,6 +529,10 @@ struct BLINK_COMMON_EXPORT StructTraitsGetFrame(); + if (new_focused_element && + (new_focused_element->IsFocusable() || + (frame && frame->GetSettings() && + frame->GetSettings()->GetAccessibilityEnabled()))) { +#else if (new_focused_element && new_focused_element->IsFocusable()) { +#endif if (IsRootEditableElement(*new_focused_element) && !AcceptsEditingFocus(*new_focused_element)) { // delegate blocks focus change diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index 69013aa..6f4a5a2 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc @@ -5608,7 +5608,13 @@ void Element::Focus(const FocusParams& params) { // https://html.spec.whatwg.org/C/#focusing-steps // // 1. If new focus target is not a focusable area, ... +#if defined(TIZEN_ATK_SUPPORT) && !defined(TIZEN_ATK_FEATURE_VD) + if (!IsFocusable() && + !(GetDocument().GetFrame() && GetDocument().GetFrame()->GetSettings() && + GetDocument().GetFrame()->GetSettings()->GetAccessibilityEnabled())) { +#else if (!IsFocusable()) { +#endif if (Element* new_focus_target = GetFocusableArea()) { // Unlike the specification, we re-run focus() for new_focus_target // because we can't change |this| in a member function. @@ -5801,11 +5807,19 @@ bool Element::SupportsSpatialNavigationFocus() const { // are. Here we make Hand-trees' tip, the first (biggest) node with {cursor: // pointer}, navigable because users shouldn't need to navigate through every // sub element that inherit this CSS. +#if defined(TIZEN_ATK_SUPPORT) + if (GetComputedStyle() && GetComputedStyle()->Cursor() == ECursor::kPointer && + ParentComputedStyle() && + ParentComputedStyle()->Cursor() != ECursor::kPointer) { + return true; + } +#else if (GetComputedStyle()->Cursor() == ECursor::kPointer && (!ParentComputedStyle() || (ParentComputedStyle()->Cursor() != ECursor::kPointer))) { return true; } +#endif if (!IsSVGElement()) return false; diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc index d9c8252..1f5a363 100644 --- a/third_party/blink/renderer/core/exported/web_settings_impl.cc +++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc @@ -797,4 +797,14 @@ bool WebSettingsImpl::TizenCompatibilityModeEnabled() const { } #endif +#if defined(TIZEN_ATK_SUPPORT) +void WebSettingsImpl::SetAccessibilityEnabled(bool enabled) { + settings_->SetAccessibilityEnabled(enabled); +} + +bool WebSettingsImpl::GetAccessibilityEnabled() { + return settings_->GetAccessibilityEnabled(); +} +#endif + } // namespace blink diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h index f5dd42e..dc092d8 100644 --- a/third_party/blink/renderer/core/exported/web_settings_impl.h +++ b/third_party/blink/renderer/core/exported/web_settings_impl.h @@ -232,6 +232,11 @@ class CORE_EXPORT WebSettingsImpl final : public WebSettings { bool TizenCompatibilityModeEnabled() const override; #endif +#if defined(TIZEN_ATK_SUPPORT) + void SetAccessibilityEnabled(bool) override; + bool GetAccessibilityEnabled() override; +#endif + bool RenderVSyncNotificationEnabled() const { return render_v_sync_notification_enabled_; } diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc index 79aac67..f4f53be 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc @@ -1662,6 +1662,9 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, settings->SetDontSendKeyEventsToJavascript( prefs.dont_send_key_events_to_javascript); settings->SetWebAppScope(WebString::FromASCII(prefs.web_app_scope.spec())); +#if defined(TIZEN_ATK_SUPPORT) + settings->SetAccessibilityEnabled(prefs.atk_enabled); +#endif #if BUILDFLAG(IS_EFL) settings->SetTizenVersion(prefs.tizen_version_major, diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc index fe5e2a6..f18293c 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc @@ -1639,6 +1639,9 @@ ScriptPromise LocalDOMWindow::getComputedAccessibleNode( ScriptState* script_state, Element* element) { DCHECK(element); +#if defined(TIZEN_ATK_SUPPORT) + element->GetDocument().GetPage()->GetSettings().SetAccessibilityEnabled(true); +#endif auto* resolver = MakeGarbageCollected( script_state, *element); ScriptPromise promise = resolver->Promise(); diff --git a/third_party/blink/renderer/core/frame/settings.h b/third_party/blink/renderer/core/frame/settings.h index ed4731e8..d90bbd1 100644 --- a/third_party/blink/renderer/core/frame/settings.h +++ b/third_party/blink/renderer/core/frame/settings.h @@ -72,6 +72,12 @@ class CORE_EXPORT Settings { bool TizenCompatibilityModeEnabled() const; #endif +#if defined(TIZEN_ATK_SUPPORT) + void SetAccessibilityEnabled(bool enabled) { atk_enabled_ = enabled; } + + bool GetAccessibilityEnabled() { return atk_enabled_; } +#endif + void SetDelegate(SettingsDelegate*); private: @@ -90,6 +96,10 @@ class CORE_EXPORT Settings { } tizen_compatibility_settings_; #endif +#if defined(TIZEN_ATK_SUPPORT) + bool atk_enabled_ : 1 = false; +#endif + SETTINGS_MEMBER_VARIABLES }; diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index c0f2ae0..2d5b4d2 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc @@ -147,6 +147,10 @@ #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/gfx/geometry/transform.h" +#if defined(TIZEN_ATK_SUPPORT) +#include "tizen/system_info.h" +#endif + namespace { // It is not easily possible to find out if an element is the target of an @@ -670,6 +674,15 @@ AXObjectInclusion AXNodeObject::ShouldIncludeBasedOnSemantics( if (IsPotentialInPageLinkTarget(*element)) return kIncludeObject; +#if defined(TIZEN_ATK_SUPPORT) + if (IsMobileProfile() && IsA(node)) { + return kIncludeObject; + } else if (IsA(node)) { + if (ignored_reasons) + ignored_reasons->push_back(IgnoredReason(kAXUninteresting)); + return kIgnoreObject; + } +#else // tags are inline tags and not meant to convey information if they // have no other ARIA information on them. If we don't ignore them, they may // emit signals expected to come from their parent. @@ -678,6 +691,7 @@ AXObjectInclusion AXNodeObject::ShouldIncludeBasedOnSemantics( ignored_reasons->push_back(IgnoredReason(kAXUninteresting)); return kIgnoreObject; } +#endif // Ignore labels that are already used to name a control. // See IsRedundantLabel() for more commentary. @@ -1067,6 +1081,12 @@ ax::mojom::blink::Role AXNodeObject::NativeRoleIgnoringAria() const { return ax::mojom::blink::Role::kTextField; } +#if defined(TIZEN_ATK_SUPPORT) + if (IsMobileProfile() && IsA(*GetNode())) { + return ax::mojom::Role::kGroup; + } +#endif + if (auto* select_element = DynamicTo(*GetNode())) { if (select_element->IsMultiple()) return ax::mojom::blink::Role::kListBox; @@ -1088,9 +1108,18 @@ ax::mojom::blink::Role AXNodeObject::NativeRoleIgnoringAria() const { if (HeadingLevel()) return ax::mojom::blink::Role::kHeading; - if (IsA(*GetNode())) + if (IsA(*GetNode()) +#if defined(TIZEN_ATK_SUPPORT) && !defined(TIZEN_ATK_FEATURE_VD) + && IsMobileProfile() && + // Skip empty div tag in mobile and wearable profile. + (!ComputedName().empty() && HasAttribute(html_names::kRoleAttr) && + (!GetAttribute(html_names::kAriaValuetextAttr).GetString().empty() || + !GetValueForControl().empty()) && + !GetAttribute(html_names::kAriaDescribedbyAttr).GetString().empty()) +#endif + ) { return RoleFromLayoutObjectOrNode(); - + } if (IsA(*GetNode()) || IsA(*GetNode()) || IsA(*GetNode())) { // is a deprecated feature of HTML 5, but is included for semantic @@ -4600,8 +4629,13 @@ bool AXNodeObject::OnNativeFocusAction() { document->UpdateStyleAndLayoutTreeForNode(node); +#if defined(TIZEN_ATK_SUPPORT) + if (!IsMobileProfile() && !CanSetFocusAttribute()) + return false; +#else if (!CanSetFocusAttribute()) return false; +#endif if (IsWebArea()) { // If another Frame has focused content (e.g. nested iframe), then we diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index cb4d1a6..3ecac84 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc @@ -6267,10 +6267,24 @@ ax::mojom::blink::Role AXObject::AriaRoleStringToRoleEnum(const String& value) { } bool AXObject::SupportsNameFromContents(bool recursive) const { +#if defined(TIZEN_ATK_SUPPORT) + // From Tizen 3.0, use role="" for div to avoid speaking role name + // and at the same time get name from content + if (GetNode() && IsA(*GetNode()) && + HasAttribute(html_names::kRoleAttr) && + GetAttribute(html_names::kRoleAttr).empty()) { + return true; + } +#endif + // ARIA 1.1, section 5.2.7.5. bool result = false; switch (RoleValue()) { +#if defined(TIZEN_ATK_SUPPORT) + case ax::mojom::blink::Role::kUnknown: + return false; +#endif // ----- NameFrom: contents ------------------------- // Get their own name from contents, or contribute to ancestors case ax::mojom::blink::Role::kButton: @@ -6571,9 +6585,11 @@ bool AXObject::SupportsNameFromContents(bool recursive) const { case ax::mojom::blink::Role::kPdfRoot: case ax::mojom::blink::Role::kTableHeaderContainer: case ax::mojom::blink::Role::kTitleBar: +#if !defined(TIZEN_ATK_SUPPORT) case ax::mojom::blink::Role::kUnknown: case ax::mojom::blink::Role::kWebView: case ax::mojom::blink::Role::kWindow: +#endif NOTREACHED() << "Role shouldn't occur in Blink: " << ToString(true, true); break; } diff --git a/tizen_src/build/BUILD.gn b/tizen_src/build/BUILD.gn index f354554..e7c5d68f 100644 --- a/tizen_src/build/BUILD.gn +++ b/tizen_src/build/BUILD.gn @@ -95,6 +95,22 @@ tizen_pkg_config("libecore-evas") { config("ecore-evas-public") { } +config("atk") { + ldflags = [ "-latk-1.0" ] +} + +tizen_pkg_config("libatk") { + packages = [ "atk" ] +} + +config("atk-bridge") { + ldflags = [ "-latk-bridge-2.0" ] +} + +tizen_pkg_config("libatk-bridge") { + packages = [ "atk-bridge-2.0" ] +} + config("ecore-input") { ldflags = [ "-lecore_input" ] } diff --git a/tizen_src/build/config/BUILD.gn b/tizen_src/build/config/BUILD.gn index 68fb742..92914cb 100644 --- a/tizen_src/build/config/BUILD.gn +++ b/tizen_src/build/config/BUILD.gn @@ -48,6 +48,14 @@ config("tizen_feature_flags") { if (wayland_bringup) { defines += [ "WAYLAND_BRINGUP" ] } + + if (tizen_atk_support) { + defines += [ "TIZEN_ATK_SUPPORT" ] + if (tizen_atk_feature_vd) { + defines += [ "TIZEN_ATK_FEATURE_VD" ] + } + } + if (tizen_product_tv) { defines += [ "OS_TIZEN_TV_PRODUCT" ] } diff --git a/tizen_src/build/config/tizen_features.gni b/tizen_src/build/config/tizen_features.gni index 85f5827..8854b6a 100644 --- a/tizen_src/build/config/tizen_features.gni +++ b/tizen_src/build/config/tizen_features.gni @@ -76,6 +76,18 @@ if (is_tizen && tizen_multimedia_support) { tizen_multimedia_eme_support = false } +if (use_atk) { + tizen_atk_support = true + if (tizen_product_tv) { + tizen_atk_feature_vd = true + } else { + tizen_atk_feature_vd = false + } +} else { + tizen_atk_support = false + tizen_atk_feature_vd = false +} + werror = false # This file should contain gn code that is supposed to be common diff --git a/tizen_src/chromium_impl/content/browser/browser_efl.gni b/tizen_src/chromium_impl/content/browser/browser_efl.gni index e8e3ce9..3c93549 100644 --- a/tizen_src/chromium_impl/content/browser/browser_efl.gni +++ b/tizen_src/chromium_impl/content/browser/browser_efl.gni @@ -122,6 +122,7 @@ if (is_tizen) { "tracing/tracing_ui.h", ] } + if (tizen_multimedia_support) { external_content_browser_efl_sources += [ "//tizen_src/chromium_impl/content/browser/media/efl/browser_demuxer_efl.cc", @@ -133,6 +134,15 @@ if (tizen_multimedia_support) { ] } +if (tizen_atk_support) { + external_content_browser_efl_configs += [ + "//tizen_src/build:atk", + "//tizen_src/build:libatk", + "//tizen_src/build:atk-bridge", + "//tizen_src/build:libatk-bridge", + ] +} + if (tizen_multimedia) { external_content_browser_efl_configs += [ "//tizen_src/build:esplusplayer", diff --git a/tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.cc b/tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.cc new file mode 100644 index 0000000..2af52a7 --- /dev/null +++ b/tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.cc @@ -0,0 +1,501 @@ +// Copyright (c) 2019 Samsung Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/accessibility/platform/ax_platform_node_efl.h" + +#include "base/strings/string_util.h" +#include "tizen/system_info.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/display/device_display_info_efl.h" +#include "ui/display/display.h" +#include "ui/display/screen.h" +#include "ui/gfx/geometry/dip_util.h" +#include "ui/gfx/geometry/point.h" + +namespace ui { + +AXCoordinateSystem AXPlatformNodeEfl::preferred_ax_coordinate_system_ = + AXCoordinateSystem::kScreenPhysicalPixels; + +AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { + AXPlatformNodeEfl* node = new AXPlatformNodeEfl(); + node->Init(delegate); + return node; +} + +AXPlatformNodeEfl* ToAXPlatformNodeEfl(AXPlatformNode* obj) { + DCHECK(!obj); + return static_cast(obj); +} + +AXPlatformNodeEfl* ToAXPlatformNodeEfl(AXPlatformNodeAuraLinux* obj) { + DCHECK(!obj); + return static_cast(obj); +} + +// +// AtkText interface +// + +static gchar* AxPlatformNodeGetText(AtkText* atk_text, + gint start_offset, + gint end_offset) { + g_return_val_if_fail(ATK_IS_TEXT(atk_text), nullptr); + + AXPlatformNode* obj = + AXPlatformNode::FromNativeViewAccessible(ATK_OBJECT(atk_text)); + if (!obj) + return nullptr; + + return ToAXPlatformNodeEfl(obj)->GetObjectText(); +} + +static void TextInterfaceInit(AtkTextIface* iface) { + iface->get_text = AxPlatformNodeGetText; +} + +static const GInterfaceInfo TextInfo = { + reinterpret_cast(TextInterfaceInit), 0, 0}; + +AXPlatformNodeEfl::AXPlatformNodeEfl() {} + +void AXPlatformNodeEfl::NotifyAccessibilityEvent(ax::mojom::Event event_type) { + AXPlatformNodeAuraLinux::NotifyAccessibilityEvent(event_type); + + switch (event_type) { +#if defined(TIZEN_ATK_FEATURE_VD) && !defined(EWK_BRINGUP) + case ax::mojom::Event::kLiveRegionChanged: + OnLiveRegionChanged(); + break; +#endif + case ax::mojom::Event::kFocus: + case ax::mojom::Event::kFocusContext: + OnFocused(); + break; + case ax::mojom::Event::kSelection: + OnSelected(); + // When changing tabs also fire a name changed event. + if (GetData().role == ax::mojom::Role::kTab) + OnDocumentTitleChanged(); + break; + case ax::mojom::Event::kTextSelectionChanged: + OnTextSelectionChanged(); + break; + case ax::mojom::Event::kValueChanged: + OnValueChanged(); + break; + default: + break; + } +} + +void AXPlatformNodeEfl::SetInterfaceFromObject( + GType type, + ui::ImplementedAtkInterfaces interface_mask) { + if (interface_mask_.Implements(ImplementedAtkInterfaces::Value::kText)) + g_type_add_interface_static(type, ATK_TYPE_TEXT, &TextInfo); +} + +void AXPlatformNodeEfl::SetInterfaceMaskFromObject( + ui::ImplementedAtkInterfaces& interface_mask) { + // AX_ATTR_NAME attribute value which contains link or heading contents will + // be read by screen-reader anyway as it is returned from get_name atk + // interface. + switch (GetData().role) { + case ax::mojom::Role::kHeading: + case ax::mojom::Role::kLink: + return; + } + if (IsTextObjectType() || IsColorWell() || IsSection()) + interface_mask.Add(ImplementedAtkInterfaces::Value::kText); + + // FIXME ATK_EDITABLE_TEXT_INTERFACE is removed +#if !defined(EWK_BRINGUP) + if (GetData().role == ax::mojom::Role::kTextField) + interface_mask |= 1 << ATK_EDITABLE_TEXT_INTERFACE; +#endif +} + +bool AXPlatformNodeEfl::IsColorWell() const { + return GetData().role == ax::mojom::Role::kColorWell; +} + +bool AXPlatformNodeEfl::IsChecked() const { + return GetData().GetCheckedState() == ax::mojom::CheckedState::kTrue; +} + +bool AXPlatformNodeEfl::IsCheckable() const { + return GetData().HasIntAttribute(ax::mojom::IntAttribute::kCheckedState); +} + +bool AXPlatformNodeEfl::IsStaticText() const { + return GetData().role == ax::mojom::Role::kStaticText; +} + +bool AXPlatformNodeEfl::IsTextObjectType() const { + switch (GetData().role) { + case ax::mojom::Role::kHeading: + case ax::mojom::Role::kLink: + case ax::mojom::Role::kParagraph: + case ax::mojom::Role::kPopUpButton: + case ax::mojom::Role::kStaticText: + case ax::mojom::Role::kTextField: + case ax::mojom::Role::kInlineTextBox: + return true; + default: + return false; + } + return false; +} + +bool AXPlatformNodeEfl::IsSection() const { + switch (GetData().role) { + case ax::mojom::Role::kGroup: + case ax::mojom::Role::kGenericContainer: + return true; + default: + return false; + } +} + +gchar* AXPlatformNodeEfl::GetObjectText() const { + // For color inputs, return string with attributes + if (IsColorWell()) { + int color = GetData().GetIntAttribute(ax::mojom::IntAttribute::kColorValue); + int red = (color >> 16) & 0xFF; + int green = (color >> 8) & 0xFF; + int blue = color & 0xFF; + return g_strdup_printf("rgb %7.5f %7.5f %7.5f 1", red / 255., green / 255., + blue / 255.); + } + +#if defined(TIZEN_ATK_FEATURE_VD) + if (IsTvProfile()) + return g_strdup(GetSupplementaryText().c_str()); +#endif + + // Always prefer value first + std::string text = + GetData().GetStringAttribute(ax::mojom::StringAttribute::kValue); + if (!text.empty()) + return g_strdup(text.c_str()); + if ((GetData().role == ax::mojom::Role::kTextField) && + GetData().GetHtmlAttribute("placeholder", &text)) { + if (!text.empty()) + return g_strdup(text.c_str()); + } + text = GetData().GetStringAttribute(ax::mojom::StringAttribute::kDescription); + if (!text.empty()) + return g_strdup(text.c_str()); + + return g_strdup(GetObjectValue().c_str()); +} + +std::string AXPlatformNodeEfl::GetObjectValue() const { + std::string value = + GetData().GetStringAttribute(ax::mojom::StringAttribute::kValue); + if (!value.empty()) + return value; + for (uint32_t i = 0; i < GetChildCount(); i++) { + AXPlatformNodeEfl* obj = ToAXPlatformNodeEfl( + AXPlatformNode::FromNativeViewAccessible(ChildAtIndex(i))); + if (obj) { + std::string child_value = obj->GetObjectValue(); + if (value.length() > 0 && + !base::IsAsciiWhitespace(value[value.length() - 1]) && + (!child_value.empty() && !base::IsAsciiWhitespace(child_value[0]))) + value += " "; + value += child_value; + } + } + return value; +} + +void AXPlatformNodeEfl::RecalculateHighlightableSingle() { + AtkObject* atk_obj = GetAtkObject(); + if (atk_obj) + highlightable_ = IsAccessible(); +} + +bool AXPlatformNodeEfl::IsUnknown() const { + return GetData().role == ax::mojom::Role::kUnknown; +} + +bool AXPlatformNodeEfl::IsInlineTextBox() const { + return GetData().role == ax::mojom::Role::kInlineTextBox; +} + +bool AXPlatformNodeEfl::IsFocusable() const { + return GetData().HasState(ax::mojom::State::kFocusable); +} + +bool AXPlatformNodeEfl::HasTextValue() const { + if (IsTextObjectType() && + !GetData().GetStringAttribute(ax::mojom::StringAttribute::kValue).empty()) + return true; + + for (int i = 0; i < GetChildCount(); i++) { + AXPlatformNodeEfl* obj = ToAXPlatformNodeEfl( + AXPlatformNode::FromNativeViewAccessible(ChildAtIndex(i))); + if (obj && obj->HasTextValue()) + return true; + } + return false; +} + +bool AXPlatformNodeEfl::HasText() const { + std::string text = + GetData().GetStringAttribute(ax::mojom::StringAttribute::kValue); + if (!text.empty()) + return true; + + std::string description = + GetData().GetStringAttribute(ax::mojom::StringAttribute::kDescription); + if (!description.empty()) + return true; + + for (int i = 0; i < GetChildCount(); i++) { + AXPlatformNodeEfl* obj = ToAXPlatformNodeEfl( + AXPlatformNode::FromNativeViewAccessible(ChildAtIndex(i))); + if (obj && obj->HasTextValue()) + return true; + } + return false; +} + +bool AXPlatformNodeEfl::MoreThanOneKnownChild() const { + int count = 0; + for (int i = 0; i < GetChildCount(); i++) { + AXPlatformNodeEfl* obj = ToAXPlatformNodeEfl( + AXPlatformNode::FromNativeViewAccessible(ChildAtIndex(i))); + if (obj && (obj->IsUnknown() || + obj->GetData().role == ax::mojom::Role::kLineBreak)) + continue; + ++count; + if (count > 1) + return true; + } + return false; +} + +bool AXPlatformNodeEfl::IsDocument() const { + switch (GetData().role) { + case ax::mojom::Role::kDocument: + case ax::mojom::Role::kRootWebArea: + case ax::mojom::Role::kPdfRoot: + return true; + } + return false; +} + +bool AXPlatformNodeEfl::IsAccessible() const { + if (IsUnknown()) + return false; + if (IsFocusable()) + return true; + // Text object types are accessible for its direct reading purpose, while + // sections allow to group accessible nodes for better ui experiance. + if (!IsTextObjectType() && !IsSection()) + return false; + // There is no need to group only one child (skipping unknown ones). + if (IsSection() && !MoreThanOneKnownChild()) + return false; + // Some text object may be empty. + if (!HasText()) + return false; + // We continue further check on parent as static text can be grouped. + if (!IsStaticText() && !IsInlineTextBox()) + return true; + + // Check if parent can be a grouper of static text. If not then static text + // is accessible. + gfx::NativeViewAccessible parent = GetParent(); + if (!parent) + return false; + AXPlatformNodeEfl* parent_efl = + ToAXPlatformNodeEfl(AXPlatformNode::FromNativeViewAccessible(parent)); + if (!parent_efl) + return false; + if (parent_efl->IsUnknown()) + return true; + // Don't let document be grouping object + if (parent_efl->IsDocument()) + return true; + if (parent_efl->IsFocusable()) + return false; + if (!parent_efl->IsTextObjectType() && !parent_efl->IsSection()) + return true; + if (parent_efl->IsSection() && !parent_efl->MoreThanOneKnownChild()) + return true; + return false; +} + +#if defined(TIZEN_ATK_FEATURE_VD) +std::string AXPlatformNodeEfl::GetSupplementaryText() const { + // name and description have been got from BrowserAccessibilityAuraLinux + // if return here again, it will be speak twice + if (!GetData() + .GetStringAttribute(ax::mojom::StringAttribute::kName) + .empty() || + !GetData() + .GetStringAttribute(ax::mojom::StringAttribute::kDescription) + .empty()) + return std::string(); + + // Always prefer value first + std::string text = + GetData().GetStringAttribute(ax::mojom::StringAttribute::kValue); + if (!text.empty()) + return text; + + if ((GetData().role == ax::mojom::Role::kTextField) && + GetData().GetHtmlAttribute("placeholder", &text)) { + if (!text.empty()) + return text; + } + + if (HasOnlyTextChildren() || + (IsFocusable() && HasOnlyTextAndImageChildren())) { + text.append(GetChildrenText()); + if (!text.empty()) + return text; + } + + return GetObjectValue(); +} + +std::string AXPlatformNodeEfl::GetChildrenText() const { + std::string text; + for (uint32_t i = 0; i < GetChildCount(); i++) { + AXPlatformNodeEfl* obj = ToAXPlatformNodeEfl( + AXPlatformNode::FromNativeViewAccessible(ChildAtIndex(i))); + if (obj) { + std::string name = + obj->GetData().GetStringAttribute(ax::mojom::StringAttribute::kName); + if (!text.empty() && !name.empty()) + text.append(" "); + text.append(name); + + std::string description = obj->GetData().GetStringAttribute( + ax::mojom::StringAttribute::kDescription); + if (!text.empty() && !description.empty()) + text.append(" "); + text.append(description); + + std::string supplementary = obj->GetSupplementaryText(); + if (!text.empty() && !supplementary.empty()) + text.append(" "); + text.append(supplementary); + } + } + return text; +} + +bool AXPlatformNodeEfl::HasOnlyTextChildren() const { + for (uint32_t i = 0; i < GetChildCount(); i++) { + AXPlatformNodeEfl* obj = ToAXPlatformNodeEfl( + AXPlatformNode::FromNativeViewAccessible(ChildAtIndex(i))); + if (obj && !obj->IsTextObjectType()) + return false; + } + return true; +} + +bool AXPlatformNodeEfl::HasOnlyTextAndImageChildren() const { + for (uint32_t i = 0; i < GetChildCount(); i++) { + AXPlatformNodeEfl* obj = ToAXPlatformNodeEfl( + AXPlatformNode::FromNativeViewAccessible(ChildAtIndex(i))); + if (obj && !obj->IsTextObjectType() && + obj->GetData().role != ax::mojom::Role::kImage) + return false; + } + return true; +} + +bool AXPlatformNodeEfl::HasSpeechContent() { + if (!GetData() + .GetStringAttribute(ax::mojom::StringAttribute::kName) + .empty() || + !GetData() + .GetStringAttribute(ax::mojom::StringAttribute::kDescription) + .empty()) + return true; + + ImplementedAtkInterfaces mask; + mask.Add(ImplementedAtkInterfaces::Value::kDefault); + SetInterfaceMaskFromObject(mask); + return (interface_mask_.Implements(ImplementedAtkInterfaces::Value::kText) && + !GetSupplementaryText().empty()) + ? true + : false; +} + +std::string AXPlatformNodeEfl::GetSpeechContent() { + std::string result; + if (!GetData() + .GetStringAttribute(ax::mojom::StringAttribute::kName) + .empty()) { + result.append( + GetData().GetStringAttribute(ax::mojom::StringAttribute::kName)); + result.append(" "); + } + + if (!GetData() + .GetStringAttribute(ax::mojom::StringAttribute::kDescription) + .empty()) { + result.append( + GetData().GetStringAttribute(ax::mojom::StringAttribute::kDescription)); + result.append(" "); + } + + ImplementedAtkInterfaces mask; + mask.Add(ImplementedAtkInterfaces::Value::kDefault); + SetInterfaceMaskFromObject(mask); + if (interface_mask_.Implements(ImplementedAtkInterfaces::Value::kText) && + !GetSupplementaryText().empty()) { + result.append(GetSupplementaryText()); + result.append(" "); + } + + return result; +} +std::string AXPlatformNodeEfl::GetSubTreeSpeechContent() const { + std::string result; + + // if name from contents, will get it from children + if (static_cast( + GetData().GetIntAttribute(ax::mojom::IntAttribute::kNameFrom)) != + ax::mojom::NameFrom::kContents && + !GetData() + .GetStringAttribute(ax::mojom::StringAttribute::kName) + .empty()) { + result.append( + GetData().GetStringAttribute(ax::mojom::StringAttribute::kName)); + result.append(" "); + } + + if (!GetData() + .GetStringAttribute(ax::mojom::StringAttribute::kDescription) + .empty()) { + result.append( + GetData().GetStringAttribute(ax::mojom::StringAttribute::kDescription)); + result.append(" "); + } + + for (uint32_t i = 0; i < GetChildCount(); i++) { + AXPlatformNodeEfl* obj = ToAXPlatformNodeEfl( + AXPlatformNode::FromNativeViewAccessible(ChildAtIndex(i))); + if (obj) { + std::string child_string = obj->GetSpeechContent(); + if (!result.empty() && !child_string.empty()) + result.append(" "); + result.append(child_string); + } + } + + return result; +} +#endif + +} // namespace ui diff --git a/tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.h b/tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.h new file mode 100644 index 0000000..25d0910 --- /dev/null +++ b/tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.h @@ -0,0 +1,68 @@ +// Copyright (c) 2019 Samsung Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "ui/accessibility/ax_coordinate_system.h" +#include "ui/accessibility/ax_export.h" +#include "ui/accessibility/platform/ax_platform_node_auralinux.h" +#include "ui/accessibility/platform/ax_platform_node_base.h" + +namespace ui { +// Implements accessibility on Aura Linux using ATK. +class AX_EXPORT AXPlatformNodeEfl : public AXPlatformNodeAuraLinux { + public: + AXPlatformNodeEfl(); + ~AXPlatformNodeEfl() {} + + AXPlatformNodeEfl(const AXPlatformNodeEfl&) = delete; + AXPlatformNodeEfl& operator=(const AXPlatformNodeEfl&) = delete; + + // AXPlatformNode overrides. + void NotifyAccessibilityEvent(ax::mojom::Event event_type) override; + + void RecalculateHighlightableSingle(); + bool IsChecked() const; + bool IsCheckable() const; + bool IsColorWell() const; + bool Ishighlightable() const { return highlightable_; } + bool IsStaticText() const; + bool IsTextObjectType() const; + + void SetInterfaceFromObject(GType, ui::ImplementedAtkInterfaces); + void SetInterfaceMaskFromObject(ui::ImplementedAtkInterfaces&); + gchar* GetObjectText() const; + bool IsAccessible() const; +#if defined(TIZEN_ATK_FEATURE_VD) + std::string GetSupplementaryText() const; + std::string GetSubTreeSpeechContent() const; + std::string GetSpeechContent(); + bool HasSpeechContent(); +#endif + + static AXCoordinateSystem GetPreferredAXCoordinateSystem() { + return preferred_ax_coordinate_system_; + } + + private: + bool MoreThanOneKnownChild() const; + bool IsDocument() const; + bool IsFocusable() const; + bool IsInlineTextBox() const; + bool IsSection() const; + bool IsUnknown() const; + bool HasTextValue() const; + bool HasText() const; + std::string GetObjectValue() const; +#if defined(TIZEN_ATK_FEATURE_VD) + bool HasOnlyTextChildren() const; + bool HasOnlyTextAndImageChildren() const; + std::string GetChildrenText() const; +#endif + + bool highlightable_ = false; + static AXCoordinateSystem preferred_ax_coordinate_system_; +}; + +} // namespace ui diff --git a/tizen_src/chromium_impl/ui/ui_efl.gni b/tizen_src/chromium_impl/ui/ui_efl.gni index cb23b88..1a0117f 100644 --- a/tizen_src/chromium_impl/ui/ui_efl.gni +++ b/tizen_src/chromium_impl/ui/ui_efl.gni @@ -130,3 +130,8 @@ if (!use_aura) { [ "gesture_detection/gesture_configuration_default.cc" ] external_ui_gesture_detection_sources += [ "//tizen_src/chromium_impl/ui/events/gesture_detection/gesture_configuration_efl.cc" ] } + +external_ui_accessiblility_platform_sources = [ + "//tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.cc", + "//tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.h", +] \ No newline at end of file diff --git a/tizen_src/ewk/efl_integration/BUILD.gn b/tizen_src/ewk/efl_integration/BUILD.gn index c43e886..4dddf77 100755 --- a/tizen_src/ewk/efl_integration/BUILD.gn +++ b/tizen_src/ewk/efl_integration/BUILD.gn @@ -594,6 +594,21 @@ shared_library("chromium-ewk") { "wrt/wrtwidget.h", ] + if (tizen_atk_support) { + configs += [ "//tizen_src/build:atk" ] + configs += [ "//tizen_src/build:libatk" ] + configs += [ "//tizen_src/build:atk-bridge" ] + configs += [ "//tizen_src/build:libatk-bridge" ] + sources += [ + "eweb_accessibility.cc", + "eweb_accessibility.h", + "eweb_accessibility_object.cc", + "eweb_accessibility_object.h", + "eweb_accessibility_util.cc", + "eweb_accessibility_util.h", + ] + } + if (tizen_product_tv) { sources += [ "common/application_type.cc", diff --git a/tizen_src/ewk/efl_integration/eweb_accessibility.cc b/tizen_src/ewk/efl_integration/eweb_accessibility.cc new file mode 100644 index 0000000..f2653f2 --- /dev/null +++ b/tizen_src/ewk/efl_integration/eweb_accessibility.cc @@ -0,0 +1,88 @@ +// Copyright 2015-17 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "eweb_accessibility.h" + +#include + +#include "base/logging.h" +#include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "eweb_accessibility_util.h" +#include "tizen/system_info.h" + +static const char* const kPlugIdKey = "__PlugID"; + +EWebAccessibility::EWebAccessibility(Evas_Object* root_object, + content::WebContents* web_contents, + std::unique_ptr observer) + : root_object_(root_object), + web_contents_(web_contents), + observer_(std::move(observer)) {} + +EWebAccessibility::~EWebAccessibility() {} + +AtkObject* EWebAccessibility::GetRootObject() { + if (!IsMobileProfile()) + return nullptr; + + if (!accessibility_object_) + accessibility_object_.reset(new EWebAccessibilityObject(web_contents_)); + return ATK_OBJECT(accessibility_object_->GetEflAtkObject()); +} + +void EWebAccessibility::OnFocusOut() { + if (!IsMobileProfile()) + return; + + if (!accessibility_object_) + return; + + accessibility_object_->InvalidateHighlighted(true); +} + +void EWebAccessibility::AddPlug() { + if (!IsMobileProfile()) + return; + + if (!accessibility_object_) + accessibility_object_.reset(new EWebAccessibilityObject(web_contents_)); + + auto plug_id = + atk_plug_get_id(ATK_PLUG(accessibility_object_->GetEflAtkObject())); + if (plug_id) { + auto data = evas_object_data_get(root_object_, kPlugIdKey); + if (data && !strcmp((char*)plug_id, (char*)data)) + return; + + evas_object_data_set(root_object_, kPlugIdKey, strdup(plug_id)); + LOG(INFO) << "Web accessibility address was set to " << plug_id; + } else if (accessibility_object_) { + accessibility_object_.reset(); + atk_bridge_adaptor_cleanup(); + LOG(ERROR) << "atk_plug_get_id failed."; + } +} + +void EWebAccessibility::RemovePlug() { + if (!IsMobileProfile()) + return; + + auto plug_id = + static_cast(evas_object_data_get(root_object_, kPlugIdKey)); + if (plug_id) { + evas_object_data_set(root_object_, kPlugIdKey, nullptr); + free(plug_id); + } + accessibility_object_.reset(); +} + +void EWebAccessibility::NotifyAccessibilityStatus(bool is_enabled) { + if (IsMobileProfile()) + (is_enabled) ? AddPlug() : RemovePlug(); + + if (observer_) { + observer_->OnSpatialNavigationStatusChanged(is_enabled); + observer_->OnAccessibilityStatusChanged(is_enabled); + } +} diff --git a/tizen_src/ewk/efl_integration/eweb_accessibility.h b/tizen_src/ewk/efl_integration/eweb_accessibility.h new file mode 100644 index 0000000..25c3268 --- /dev/null +++ b/tizen_src/ewk/efl_integration/eweb_accessibility.h @@ -0,0 +1,54 @@ +// Copyright 2015-17 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EWEB_ACCESSIBILITY_H +#define EWEB_ACCESSIBILITY_H + +#include +#include +#include "eweb_accessibility_object.h" + +namespace content { +class WebContents; +} + +// pre-conditions: +// |ecore_main_loop| SHOULD be running before using this class. +// +// This class initializes and owns all accessibility objects. +class EWebAccessibility { + public: + class Observer { + public: + virtual ~Observer() {} + + virtual void OnSpatialNavigationStatusChanged(Eina_Bool enable) = 0; + + virtual void OnAccessibilityStatusChanged(Eina_Bool enable) = 0; + }; + + EWebAccessibility(Evas_Object* root_object, + content::WebContents* web_contents, + std::unique_ptr observer); + ~EWebAccessibility(); + + EWebAccessibility(const EWebAccessibility&) = delete; + EWebAccessibility& operator=(const EWebAccessibility&) = delete; + + void NotifyAccessibilityStatus(bool); + AtkObject* GetRootObject(); + void OnFocusOut(); + + private: + void AddPlug(); + void RemovePlug(); + + Evas_Object* root_object_; + content::WebContents* web_contents_; + std::unique_ptr observer_; + + std::unique_ptr accessibility_object_; +}; + +#endif // EWEB_ACCESSIBILITY_H diff --git a/tizen_src/ewk/efl_integration/eweb_accessibility_object.cc b/tizen_src/ewk/efl_integration/eweb_accessibility_object.cc new file mode 100644 index 0000000..277fcc0 --- /dev/null +++ b/tizen_src/ewk/efl_integration/eweb_accessibility_object.cc @@ -0,0 +1,346 @@ +// Copyright 2015-17 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "eweb_accessibility_object.h" + +#include "content/browser/accessibility/browser_accessibility.h" +#include "content/browser/accessibility/browser_accessibility_auralinux.h" +#include "content/browser/accessibility/browser_accessibility_manager_auralinux.h" +#include "content/browser/renderer_host/render_frame_host_impl.h" +#include "content/browser/web_contents/web_contents_impl_efl.h" +#include "content/common/content_export.h" +#include "content/public/browser/web_contents.h" +#include "tizen/system_info.h" +#include "ui/accessibility/platform/ax_platform_node_auralinux.h" + +static const char* const kWebViewAccessibilityObjectName = "eweb accessibility"; +static const char* const kWebViewAccessibilityObjectDescription = + "chromium-efl accessibility for webview"; + +struct _WebViewAccessibilityObjectPrivate { + EWebAccessibilityObject* m_object; +}; + +G_DEFINE_TYPE_WITH_PRIVATE(WebViewAccessibilityObject, + web_view_accessibility_object, + ATK_TYPE_PLUG) + +static void web_view_accessibility_object_init( + WebViewAccessibilityObject* accessible) { + accessible->priv = static_cast<_WebViewAccessibilityObjectPrivate*>( + web_view_accessibility_object_get_instance_private(accessible)); +} + +static void web_view_accessibility_object_initialize(AtkObject* atk_object, + gpointer data) { + if (ATK_OBJECT_CLASS(web_view_accessibility_object_parent_class) + ->initialize) { + ATK_OBJECT_CLASS(web_view_accessibility_object_parent_class) + ->initialize(atk_object, data); + } + WEB_VIEW_ACCESSIBILITY_OBJECT(atk_object)->priv->m_object = + reinterpret_cast(data); +} + +static void web_view_accessibility_object_finalize(GObject* atk_object) { + G_OBJECT_CLASS(web_view_accessibility_object_parent_class) + ->finalize(atk_object); +} + +static EWebAccessibilityObject* ToEWebAccessibilityObject( + AtkObject* atk_object) { + if (!atk_object) { + LOG(ERROR) << "atk_object is null"; + return nullptr; + } + + if (!IS_WEB_VIEW_ACCESSIBILITY_OBJECT(atk_object)) { + LOG(ERROR) << "atk_object is not webview accessibility object"; + return nullptr; + } + + WebViewAccessibilityObject* ax_object = + WEB_VIEW_ACCESSIBILITY_OBJECT(atk_object); + return ax_object->priv->m_object; +} + +static gint web_view_accessibility_object_get_index_in_parent( + AtkObject* atk_object) { + g_return_val_if_fail(IS_WEB_VIEW_ACCESSIBILITY_OBJECT(atk_object), -1); + auto obj = ToEWebAccessibilityObject(atk_object); + return obj->GetIndexInParent(); +} + +static gint web_view_accessibility_object_get_n_children( + AtkObject* atk_object) { + g_return_val_if_fail(IS_WEB_VIEW_ACCESSIBILITY_OBJECT(atk_object), -1); + auto obj = ToEWebAccessibilityObject(atk_object); + return obj->GetNChildren(); +} + +static AtkObject* web_view_accessibility_object_ref_child(AtkObject* atk_object, + gint index) { + g_return_val_if_fail(IS_WEB_VIEW_ACCESSIBILITY_OBJECT(atk_object), nullptr); + auto obj = ToEWebAccessibilityObject(atk_object); + return obj->RefChild(index); +} + +static const gchar* web_view_accessibility_object_get_name( + AtkObject* atk_object) { + return kWebViewAccessibilityObjectName; +} + +static const gchar* web_view_accessibility_object_get_description( + AtkObject* atk_object) { + return kWebViewAccessibilityObjectDescription; +} + +static AtkStateSet* web_view_accessibility_object_ref_state_set( + AtkObject* atk_object) { + g_return_val_if_fail(IS_WEB_VIEW_ACCESSIBILITY_OBJECT(atk_object), nullptr); + auto obj = ToEWebAccessibilityObject(atk_object); + return obj->RefStateSet(); +} + +static void web_view_accessibility_object_class_init( + WebViewAccessibilityObjectClass* klass) { + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + AtkObjectClass* atkObjectClass = ATK_OBJECT_CLASS(klass); + gobject_class->finalize = web_view_accessibility_object_finalize; + atkObjectClass->initialize = web_view_accessibility_object_initialize; + atkObjectClass->get_index_in_parent = + web_view_accessibility_object_get_index_in_parent; + atkObjectClass->get_n_children = web_view_accessibility_object_get_n_children; + atkObjectClass->ref_child = web_view_accessibility_object_ref_child; + atkObjectClass->get_name = web_view_accessibility_object_get_name; + atkObjectClass->get_description = + web_view_accessibility_object_get_description; + atkObjectClass->ref_state_set = web_view_accessibility_object_ref_state_set; +} + +static AtkObject* web_view_accessibility_object_ref_accessible_at_point( + AtkComponent* component, + gint x, + gint y, + AtkCoordType coord_type) { + auto obj = ToEWebAccessibilityObject(ATK_OBJECT(component)); + return obj->RefAccessibleAtPoint(x, y, coord_type); +} + +static void web_view_accessibility_object_get_extents(AtkComponent* component, + gint* x, + gint* y, + gint* width, + gint* height, + AtkCoordType coord_type) { + auto obj = ToEWebAccessibilityObject(ATK_OBJECT(component)); + if (ATK_XY_SCREEN) { + obj->GetScreenGeometry(x, y, width, height); + } else { // ATK_XY_WINDOW + obj->GetWindowGeometry(x, y, width, height); + } +} + +static void web_view_accessibility_object_get_position( + AtkComponent* component, + gint* x, + gint* y, + AtkCoordType coord_type) { + auto obj = ToEWebAccessibilityObject(ATK_OBJECT(component)); + if (ATK_XY_SCREEN) { + obj->GetScreenGeometry(x, y, nullptr, nullptr); + } else { // ATK_XY_WINDOW + obj->GetWindowGeometry(x, y, nullptr, nullptr); + } +} + +static void web_view_accessibility_object_get_size(AtkComponent* component, + gint* width, + gint* height) { + auto obj = ToEWebAccessibilityObject(ATK_OBJECT(component)); + obj->GetWindowGeometry(nullptr, nullptr, width, height); +} + +EWebAccessibilityObject::EWebAccessibilityObject( + content::WebContents* web_contents) + : web_contents_(web_contents), + native_view_(static_cast(web_contents_) + ->GetEflNativeView()) { + evas_object_event_callback_add(native_view_, EVAS_CALLBACK_SHOW, + NativeViewShowCallback, this); + evas_object_event_callback_add(native_view_, EVAS_CALLBACK_HIDE, + NativeViewHideCallback, this); + efl_atk_obj_ = static_cast( + g_object_new(WEB_VIEW_ACCESSIBILITY_OBJECT_TYPE, nullptr)); + + // init + atk_object_initialize(efl_atk_obj_, this); + AtkComponent* component = ATK_COMPONENT(efl_atk_obj_); + + AtkComponentIface* iface = ATK_COMPONENT_GET_IFACE(component); + iface->ref_accessible_at_point = + web_view_accessibility_object_ref_accessible_at_point; + iface->get_extents = web_view_accessibility_object_get_extents; + iface->get_position = web_view_accessibility_object_get_position; + iface->get_size = web_view_accessibility_object_get_size; + + // Set the root atk object to BrowserAccessibilityAuraLinux(delegate) + // to access it from the ui/accessibility layer. + if (GetRootObject()) + GetRootObject()->SetRootAtkObject(efl_atk_obj_); +} + +EWebAccessibilityObject::~EWebAccessibilityObject() { + evas_object_event_callback_del(native_view_, EVAS_CALLBACK_SHOW, + NativeViewShowCallback); + evas_object_event_callback_del(native_view_, EVAS_CALLBACK_HIDE, + NativeViewHideCallback); + g_object_unref(efl_atk_obj_); +} + +// static +void EWebAccessibilityObject::NativeViewShowCallback(void* data, + Evas*, + Evas_Object*, + void*) { + auto thiz = static_cast(data); + atk_object_notify_state_change(ATK_OBJECT(thiz->GetEflAtkObject()), + ATK_STATE_SHOWING, TRUE); +} + +// static +void EWebAccessibilityObject::NativeViewHideCallback(void* data, + Evas*, + Evas_Object*, + void*) { + auto thiz = static_cast(data); + atk_object_notify_state_change(ATK_OBJECT(thiz->GetEflAtkObject()), + ATK_STATE_SHOWING, FALSE); +} + +content::BrowserAccessibilityManager* +EWebAccessibilityObject::GetBrowserAccessibilityManager() const { + auto frame = static_cast( + web_contents_->GetPrimaryMainFrame()); + if (!frame) { + LOG(ERROR) << "Unable to get render frame host impl"; + return nullptr; + } + return frame->GetOrCreateBrowserAccessibilityManager(); +} + +AtkObject* EWebAccessibilityObject::RefAccessibleAtPoint( + gint x, + gint y, + AtkCoordType coord_type) { + auto child = GetAtkObject(); + if (!child) { + LOG(ERROR) << "Child was not found"; + return nullptr; + } + return atk_component_ref_accessible_at_point(ATK_COMPONENT(child), x, y, + coord_type); +} + +void EWebAccessibilityObject::GetScreenGeometry(int* x, + int* y, + int* width, + int* height) const { + auto ee = ecore_evas_ecore_evas_get(evas_object_evas_get(native_view_)); + ecore_evas_screen_geometry_get(ee, x, y, width, height); +} + +void EWebAccessibilityObject::GetWindowGeometry(int* x, + int* y, + int* width, + int* height) const { + evas_object_geometry_get(native_view_, x, y, width, height); +} + +gint EWebAccessibilityObject::GetIndexInParent() const { + // AtkPlug is the only child of AtkSocket. + return 0; +} + +gint EWebAccessibilityObject::GetNChildren() const { + // One and only child of AtkPlug is the browser root accessibility object. + auto child = GetAtkObject(); + if (!child) { + LOG(ERROR) << "Child was not found"; + return 0; + } + return 1; +} + +AtkObject* EWebAccessibilityObject::RefChild(gint index) const { + if (index != 0) { + LOG(ERROR) << "Invalid index passed: " << index; + return nullptr; + } + + auto child = GetAtkObject(); + if (!child) { + LOG(ERROR) << "Child was not found"; + return nullptr; + } + + g_object_ref(child); + return child; +} + +AtkStateSet* EWebAccessibilityObject::RefStateSet() const { + auto atk_state_set = atk_state_set_new(); + if (evas_object_visible_get(native_view_)) + atk_state_set_add_state(atk_state_set, ATK_STATE_SHOWING); + return atk_state_set; +} + +AtkObject* EWebAccessibilityObject::GetEflAtkObject() const { + return efl_atk_obj_; +} + +content::BrowserAccessibilityAuraLinux* EWebAccessibilityObject::GetRootObject() + const { + auto manager = GetBrowserAccessibilityManager(); + if (!manager) { + LOG(ERROR) << "Unable to get accessibility manager"; + return nullptr; + } + + content::BrowserAccessibility* obj = + static_cast(manager->RootDelegate()); + if (!obj) { + LOG(ERROR) << "Unable to get root object from accessibility manager"; + return nullptr; + } + content::BrowserAccessibilityAuraLinux* root_object = + ToBrowserAccessibilityAuraLinux(obj); + + return root_object; +} + +ui::AXPlatformNodeAuraLinux* EWebAccessibilityObject::GetNode() const { + auto root_object = GetRootObject(); + if (!root_object) + return nullptr; + + auto node = root_object->GetNode(); + if (!node) { + LOG(ERROR) << "Unable to get auralinux node"; + return nullptr; + } + + return node; +} + +AtkObject* EWebAccessibilityObject::GetAtkObject() const { + auto node = GetNode(); + return (node) ? node->GetAtkObject() : nullptr; +} + +void EWebAccessibilityObject::InvalidateHighlighted(bool focus) { + auto node = GetNode(); + if (node) + node->InvalidateHighlighted(focus); +} diff --git a/tizen_src/ewk/efl_integration/eweb_accessibility_object.h b/tizen_src/ewk/efl_integration/eweb_accessibility_object.h new file mode 100644 index 0000000..6e168e0 --- /dev/null +++ b/tizen_src/ewk/efl_integration/eweb_accessibility_object.h @@ -0,0 +1,94 @@ +// Copyright 2015-17 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EWEB_ACCESSIBILITY_OBJECT_H +#define EWEB_ACCESSIBILITY_OBJECT_H + +#include +#include + +namespace ui { +class AXPlatformNodeAuraLinux; +} + +namespace content { +class BrowserAccessibility; +class BrowserAccessibilityManager; +class BrowserAccessibilityAuraLinux; +class BrowserAccessibilityManagerAuraLinux; +class BrowserAccessibilityManager; +class WebContents; +} // namespace content + +G_BEGIN_DECLS + +#define WEB_VIEW_ACCESSIBILITY_OBJECT_TYPE \ + (web_view_accessibility_object_get_type()) +#define WEB_VIEW_ACCESSIBILITY_OBJECT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), WEB_VIEW_ACCESSIBILITY_OBJECT_TYPE, \ + WebViewAccessibilityObject)) +#define WEB_VIEW_ACCESSIBILITY_OBJECT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), WEB_VIEW_ACCESSIBILITY_OBJECT_TYPE, \ + WebViewAccessibilityObjectClass)) +#define IS_WEB_VIEW_ACCESSIBILITY_OBJECT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEB_VIEW_ACCESSIBILITY_OBJECT_TYPE)) +#define IS_WEB_VIEW_ACCESSIBILITY_OBJECT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), WEB_VIEW_ACCESSIBILITY_OBJECT_TYPE)) +#define WEB_VIEW_ACCESSIBILITY_OBJECT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), WEB_VIEW_ACCESSIBILITY_OBJECT_TYPE, \ + WebViewAccessibilityObjectClass)) + +typedef struct _WebViewAccessibilityObject WebViewAccessibilityObject; +typedef struct _WebViewAccessibilityObjectClass WebViewAccessibilityObjectClass; +typedef struct _WebViewAccessibilityObjectPrivate + WebViewAccessibilityObjectPrivate; + +struct _WebViewAccessibilityObject { + AtkPlug parent; + _WebViewAccessibilityObjectPrivate* priv; +}; + +struct _WebViewAccessibilityObjectClass { + AtkPlugClass parent_class; +}; + +GType web_view_accessibility_object_get_type(); + +G_END_DECLS + +// This class represents EWebView root accessibility object. +// EWebView root accessibility object is an entry point for browser +// to have top-down navigation through accessibility objects. +class EWebAccessibilityObject { + public: + explicit EWebAccessibilityObject(content::WebContents* web_contents); + ~EWebAccessibilityObject(); + + EWebAccessibilityObject(const EWebAccessibilityObject&) = delete; + EWebAccessibilityObject& operator=(const EWebAccessibilityObject&) = delete; + + AtkObject* GetEflAtkObject() const; + AtkObject* RefAccessibleAtPoint(gint x, gint y, AtkCoordType coord_type); + void GetScreenGeometry(int* x, int* y, int* width, int* height) const; + void GetWindowGeometry(int* x, int* y, int* width, int* height) const; + gint GetIndexInParent() const; + gint GetNChildren() const; + AtkObject* RefChild(gint index) const; + AtkStateSet* RefStateSet() const; + content::BrowserAccessibilityManager* GetBrowserAccessibilityManager() const; + content::BrowserAccessibilityAuraLinux* GetRootObject() const; + void InvalidateHighlighted(bool focus); + + private: + static void NativeViewShowCallback(void*, Evas*, Evas_Object*, void*); + static void NativeViewHideCallback(void*, Evas*, Evas_Object*, void*); + ui::AXPlatformNodeAuraLinux* GetNode() const; + AtkObject* GetAtkObject() const; + + content::WebContents* web_contents_; + Evas_Object* native_view_; + AtkObject* efl_atk_obj_; +}; + +#endif // EWEB_ACCESSIBILITY_OBJECT_H diff --git a/tizen_src/ewk/efl_integration/eweb_accessibility_util.cc b/tizen_src/ewk/efl_integration/eweb_accessibility_util.cc new file mode 100644 index 0000000..74e6c1d --- /dev/null +++ b/tizen_src/ewk/efl_integration/eweb_accessibility_util.cc @@ -0,0 +1,204 @@ +// Copyright 2015-17 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "eweb_accessibility_util.h" + +#include +#include + +#include "common/web_contents_utils.h" +#include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "content/browser/web_contents/web_contents_impl_efl.h" +#include "eweb_accessibility.h" +#include "eweb_view.h" +#include "tizen/system_info.h" + +#include + +using web_contents_utils::WebViewFromWebContents; + +G_BEGIN_DECLS + +#define EWEB_ACCESSIBILITY_ROOT_TYPE (eweb_accessibility_root_get_type()) +#define EWEB_ACCESSIBILITY_ROOT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EWEB_ACCESSIBILITY_ROOT_TYPE, \ + EWebAccessibilityRoot)) +#define EWEB_ACCESSIBILITY_ROOT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EWEB_ACCESSIBILITY_ROOT_TYPE, \ + EWebAccessibilityRootClass)) +#define IS_EWEB_ACCESSIBILITY_ROOT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EWEB_ACCESSIBILITY_ROOT_TYPE)) +#define IS_EWEB_ACCESSIBILITY_ROOT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EWEB_ACCESSIBILITY_ROOT_TYPE)) +#define EWEB_ACCESSIBILITY_ROOT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), EWEB_ACCESSIBILITY_ROOT_TYPE, \ + EWebAccessibilityRootClass)) + +typedef struct _EWebAccessibilityRoot EWebAccessibilityRoot; +typedef struct _EWebAccessibilityRootClass EWebAccessibilityRootClass; +typedef struct _EWebAccessibilityRootPrivate EWebAccessibilityRootPrivate; + +struct _EWebAccessibilityRootPrivate {}; + +struct _EWebAccessibilityRoot { + AtkObject parent; + _EWebAccessibilityRootPrivate* priv; +}; + +struct _EWebAccessibilityRootClass { + AtkObjectClass parentClass; +}; + +GType eweb_accessibility_root_get_type(); + +G_END_DECLS + +G_DEFINE_TYPE_WITH_PRIVATE(EWebAccessibilityRoot, + eweb_accessibility_root, + ATK_TYPE_OBJECT); + +#if defined(ENABLE_WRT_JS) +EWebAccessibility* eweb_accessibility_ = nullptr; +#endif + +static void eweb_accessibility_root_init( + EWebAccessibilityRoot* accessible_root) { + accessible_root->priv = static_cast<_EWebAccessibilityRootPrivate*>( + eweb_accessibility_root_get_instance_private(accessible_root)); +} + +static void eweb_accessibility_root_class_init( + EWebAccessibilityRootClass* klass) {} + +static AtkObject* eweb_util_get_root() { + if (IsMobileProfile()) { + for (const auto& wc : content::WebContentsImpl::GetAllWebContents()) { + if (EWebView* ewebview = WebViewFromWebContents(wc)) + return ewebview->eweb_accessibility().GetRootObject(); + } +#if defined(ENABLE_WRT_JS) + if (eweb_accessibility_) + return eweb_accessibility_->GetRootObject(); +#endif + return nullptr; + } + + return ATK_OBJECT(g_object_new(EWEB_ACCESSIBILITY_ROOT_TYPE, nullptr)); +} + +static G_CONST_RETURN gchar* eweb_util_get_toolkit_name(void) { + return "Chromium-EFL"; +} + +static G_CONST_RETURN gchar* eweb_util_get_toolkit_version(void) { + return "1.0"; +} + +static void PropertyChangedCb(keynode_t* keynodeName, void* data) { + EWebAccessibilityUtil* obj = static_cast(data); + if (!obj) { + LOG(ERROR) << "obj is NULL"; + return; + } + + int result = 0; + if (vconf_get_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, &result) != 0) { + LOG(ERROR) + << "Could not read VCONFKEY_SETAPPL_ACCESSIBILITY_TTS key value."; + return; + } + obj->ToggleBrowserAccessibility(result); +} + +EWebAccessibilityUtil* EWebAccessibilityUtil::GetInstance() { + return base::Singleton::get(); +} + +EWebAccessibilityUtil::EWebAccessibilityUtil() { + if (getuid() == 0) + return; + + AtkUtilClass* atkUtilClass = ATK_UTIL_CLASS(g_type_class_ref(ATK_TYPE_UTIL)); + atkUtilClass->get_toolkit_name = eweb_util_get_toolkit_name; + atkUtilClass->get_toolkit_version = eweb_util_get_toolkit_version; + atkUtilClass->get_root = eweb_util_get_root; + vconf_notify_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, + PropertyChangedCb, this); +} + +EWebAccessibilityUtil::~EWebAccessibilityUtil() { + vconf_ignore_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, + PropertyChangedCb); + CleanAtkBridgeAdaptor(); +} + +void EWebAccessibilityUtil::NotifyAccessibilityStatus(bool mode) { + for (const auto& wc : content::WebContentsImpl::GetAllWebContents()) { + if (EWebView* ewebview = WebViewFromWebContents(wc)) + ewebview->eweb_accessibility().NotifyAccessibilityStatus(mode); + } + +#if defined(ENABLE_WRT_JS) + if (eweb_accessibility_) + eweb_accessibility_->NotifyAccessibilityStatus(mode); +#endif +} + +void EWebAccessibilityUtil::ToggleBrowserAccessibility(bool mode) { + content::BrowserAccessibilityStateImpl* state = + content::BrowserAccessibilityStateImpl::GetInstance(); + + if (mode) { + InitAtkBridgeAdaptor(); + state->EnableAccessibility(); + } + + NotifyAccessibilityStatus(mode); + if (!mode) { + state->DisableAccessibility(); + CleanAtkBridgeAdaptor(); + } +} + +void EWebAccessibilityUtil::InitAtkBridgeAdaptor() { + if (!atk_bridge_initialized_) { + if (!atk_bridge_adaptor_init(nullptr, nullptr)) + atk_bridge_initialized_ = true; + else + LOG(ERROR) << "atk_bridge_adaptor_init failed."; + } +} + +void EWebAccessibilityUtil::CleanAtkBridgeAdaptor() { + if (atk_bridge_initialized_) { + atk_bridge_adaptor_cleanup(); + atk_bridge_initialized_ = false; + } +} + +void EWebAccessibilityUtil::ToggleAtk(bool& atk_status) { + if (IsDesktopProfile()) + return; + + if (atk_status) + atk_status = false; + else + return; + + int result = 0; + if (vconf_get_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, &result) != 0) { + LOG(ERROR) + << "Could not read VCONFKEY_SETAPPL_ACCESSIBILITY_TTS key value."; + return; + } + + ToggleBrowserAccessibility(result); +} + +#if defined(ENABLE_WRT_JS) +void EWebAccessibilityUtil::SetEWebAccessibility( + EWebAccessibility* eweb_accessibility) { + eweb_accessibility_ = eweb_accessibility; +} +#endif diff --git a/tizen_src/ewk/efl_integration/eweb_accessibility_util.h b/tizen_src/ewk/efl_integration/eweb_accessibility_util.h new file mode 100644 index 0000000..33fcf40 --- /dev/null +++ b/tizen_src/ewk/efl_integration/eweb_accessibility_util.h @@ -0,0 +1,42 @@ +// Copyright 2015-17 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EWEB_ACCESSIBILITY_UTIL_H +#define EWEB_ACCESSIBILITY_UTIL_H + +#include "base/memory/singleton.h" + +#if defined(ENABLE_WRT_JS) +class EWebAccessibility; +#endif +// This singleton class initializes atk-bridge and +// registers an implementation of AtkUtil class. +// Global class that every accessible application +// needs to registers once. +class EWebAccessibilityUtil { + public: + static EWebAccessibilityUtil* GetInstance(); + void ToggleBrowserAccessibility(bool mode); + +#if defined(ENABLE_WRT_JS) + void SetEWebAccessibility(EWebAccessibility* eweb_accessibility); +#endif + void ToggleAtk(bool& atk_status); + + private: + EWebAccessibilityUtil(); + ~EWebAccessibilityUtil(); + + EWebAccessibilityUtil(const EWebAccessibilityUtil&) = delete; + EWebAccessibilityUtil& operator=(const EWebAccessibilityUtil&) = delete; + + void NotifyAccessibilityStatus(bool mode); + void InitAtkBridgeAdaptor(); + void CleanAtkBridgeAdaptor(); + + bool atk_bridge_initialized_ = false; + friend struct base::DefaultSingletonTraits; +}; + +#endif // EWEB_ACCESSIBILITY_UTIL_H diff --git a/tizen_src/ewk/efl_integration/eweb_view.cc b/tizen_src/ewk/efl_integration/eweb_view.cc index b99454e..be45a2d 100644 --- a/tizen_src/ewk/efl_integration/eweb_view.cc +++ b/tizen_src/ewk/efl_integration/eweb_view.cc @@ -84,6 +84,12 @@ #include +#if defined(TIZEN_ATK_SUPPORT) +#include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "eweb_accessibility.h" +#include "eweb_accessibility_util.h" +#endif + using namespace content; using web_contents_utils::WebViewFromWebContents; @@ -197,6 +203,26 @@ class WebViewAsyncRequestHitTestDataUserCallback void* user_data_; }; +#if defined(TIZEN_ATK_SUPPORT) +class EWebAccessibilityObserver : public EWebAccessibility::Observer { + public: + explicit EWebAccessibilityObserver(EWebView* webview) : webview_(webview) {} + virtual ~EWebAccessibilityObserver() {} + + // EWebAccessibility::Observer implementation + void OnSpatialNavigationStatusChanged(Eina_Bool enable) override { + webview_->UpdateSpatialNavigationStatus(enable); + } + + void OnAccessibilityStatusChanged(Eina_Bool enable) override { + webview_->UpdateAccessibilityStatus(enable); + } + + private: + EWebView* webview_; +}; +#endif + int EWebView::find_request_id_counter_ = 0; content::WebContentsEflDelegate::WebContentsCreateCallback EWebView::create_new_window_web_contents_cb_ = @@ -263,6 +289,14 @@ void EWebView::Initialize() { // RenderViewHostImpl::ComputeWebkitPrefs() based on command line switches. settings_.reset(new Ewk_Settings(evas_object_, web_contents_->GetOrCreateWebPreferences())); +#if defined(TIZEN_ATK_SUPPORT) + std::unique_ptr observer( + new EWebAccessibilityObserver(this)); + eweb_accessibility_.reset(new EWebAccessibility( + evas_object_, web_contents_.get(), std::move(observer))); + lazy_initialize_atk_ = true; +#endif + base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); if (cmdline->HasSwitch(switches::kTouchEventFeatureDetection)) { SetTouchEventsEnabled( @@ -338,6 +372,10 @@ EWebView::~EWebView() { return; } +#if defined(TIZEN_ATK_SUPPORT) + eweb_accessibility_.reset(); +#endif + context_menu_.reset(); mhtml_callback_map_.Clear(); @@ -2826,3 +2864,38 @@ bool EWebView::SetVisibility(bool enable) { return true; } + +#if defined(TIZEN_ATK_SUPPORT) +void EWebView::UpdateSpatialNavigationStatus(Eina_Bool enable) { + if (settings_->getPreferences().spatial_navigation_enabled == enable) + return; + + settings_->getPreferences().spatial_navigation_enabled = enable; + WebContentsImpl* wc = static_cast(web_contents_.get()); + if (wc) + wc->SetSpatialNavigationEnabled(enable); +} + +void EWebView::UpdateAccessibilityStatus(Eina_Bool enable) { + if (settings_->getPreferences().atk_enabled == enable) + return; + + settings_->getPreferences().atk_enabled = enable; + WebContentsImpl* wc = static_cast(web_contents_.get()); + if (wc) + wc->SetAtkEnabled(enable); +} + +void EWebView::InitAtk() { + EWebAccessibilityUtil::GetInstance()->ToggleAtk(lazy_initialize_atk_); +} + +/* LCOV_EXCL_START */ +bool EWebView::GetAtkStatus() { + auto state = content::BrowserAccessibilityStateImpl::GetInstance(); + if (!state) + return false; + return state->IsAccessibleBrowser(); +} +/* LCOV_EXCL_STOP */ +#endif diff --git a/tizen_src/ewk/efl_integration/eweb_view.h b/tizen_src/ewk/efl_integration/eweb_view.h index 676d285..0fd0d14 100644 --- a/tizen_src/ewk/efl_integration/eweb_view.h +++ b/tizen_src/ewk/efl_integration/eweb_view.h @@ -90,6 +90,10 @@ class Ewk_Context; class WebViewEvasEventHandler; class _Ewk_Quota_Permission_Request; +#if defined(TIZEN_ATK_SUPPORT) +class EWebAccessibility; +#endif + template class WebViewCallback { public: @@ -296,6 +300,12 @@ class EWebView { content::WebContents& web_contents() const { return *web_contents_.get(); } +#if defined(TIZEN_ATK_SUPPORT) + EWebAccessibility& eweb_accessibility() const { + return *eweb_accessibility_.get(); + } +#endif + template EWebViewCallbacks::CallBack SmartCallback() const { return EWebViewCallbacks::CallBack(evas_object_); @@ -637,6 +647,17 @@ class EWebView { bool SetVisibility(bool enable); +#if defined(TIZEN_ATK_SUPPORT) + void UpdateSpatialNavigationStatus(Eina_Bool enable); + void UpdateAccessibilityStatus(Eina_Bool enable); + + bool CheckLazyInitializeAtk() { + return is_initialized_ && lazy_initialize_atk_; + } + void InitAtk(); + bool GetAtkStatus(); +#endif + content::DateTimeChooserEfl* GetDateTimeChooser() { return date_time_chooser_; } @@ -841,6 +862,11 @@ class EWebView { std::unique_ptr focus_client_; std::unique_ptr window_parenting_client_; content::DateTimeChooserEfl* date_time_chooser_ = nullptr; + +#if defined(TIZEN_ATK_SUPPORT) + std::unique_ptr eweb_accessibility_; + bool lazy_initialize_atk_ = false; +#endif }; const unsigned int g_default_tilt_motion_sensitivity = 3; diff --git a/tizen_src/ewk/efl_integration/web_contents_observer_efl.cc b/tizen_src/ewk/efl_integration/web_contents_observer_efl.cc index 76e1d5e..9135f52 100644 --- a/tizen_src/ewk/efl_integration/web_contents_observer_efl.cc +++ b/tizen_src/ewk/efl_integration/web_contents_observer_efl.cc @@ -90,6 +90,10 @@ void WebContentsObserverEfl::DidStartNavigation( NavigationHandle* navigation_handle) { if (!navigation_handle->IsInMainFrame()) return; +#if defined(TIZEN_ATK_SUPPORT) + if (web_view_->CheckLazyInitializeAtk()) + web_view_->InitAtk(); +#endif web_view_->SmartCallback().call(); } diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn index 54b47dd..f604d7f 100644 --- a/ui/accessibility/BUILD.gn +++ b/ui/accessibility/BUILD.gn @@ -12,6 +12,10 @@ import("//testing/test.gni") import("//tools/json_schema_compiler/json_schema_api.gni") import("//ui/base/ui_features.gni") +if (tizen_atk_support) { + import("//tizen_src/chromium_impl/ui/ui_efl.gni") +} + if (is_android) { import("//build/config/android/rules.gni") } @@ -220,6 +224,10 @@ component("accessibility") { public_deps += [ "//ui/aura" ] } + + if (tizen_atk_support) { + sources += external_ui_accessiblility_platform_sources + } } source_set("ax_assistant") { diff --git a/ui/accessibility/platform/ax_platform_atk_hyperlink.cc b/ui/accessibility/platform/ax_platform_atk_hyperlink.cc index 40b53e6..8d3e430 100644 --- a/ui/accessibility/platform/ax_platform_atk_hyperlink.cc +++ b/ui/accessibility/platform/ax_platform_atk_hyperlink.cc @@ -132,7 +132,11 @@ static void AXPlatformAtkHyperlinkInit(AXPlatformAtkHyperlink* self, gpointer) { } GType ax_platform_atk_hyperlink_get_type() { +#if defined(TIZEN_ATK_SUPPORT) + static gsize type_volatile = 0; +#else static volatile gsize type_volatile = 0; +#endif AXPlatformNodeAuraLinux::EnsureGTypeInit(); diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc index 7412e7a..b02ca80 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc @@ -39,6 +39,12 @@ #include "ui/accessibility/platform/ax_platform_text_boundary.h" #include "ui/gfx/geometry/rect_conversions.h" +#if defined(TIZEN_ATK_SUPPORT) +#include "tizen/system_info.h" +#include "ui/accessibility/platform/ax_platform_node_efl.h" +static const char kActivateActionName[] = "activate"; +#endif + #if defined(ATK_CHECK_VERSION) && ATK_CHECK_VERSION(2, 10, 0) #define ATK_210 #endif @@ -207,6 +213,13 @@ void SetIntPointerValueIfNotNull(int* pointer, int value) { *pointer = value; } +#if defined(TIZEN_ATK_SUPPORT) +AXPlatformNodeEfl* ToAXPlatformNodeEfl(AXPlatformNodeAuraLinux* obj) { + DCHECK(!obj); + return static_cast(obj); +} +#endif + #if defined(ATK_230) bool SupportsAtkComponentScrollingInterface() { return dlsym(RTLD_DEFAULT, "atk_component_scroll_to_point"); @@ -533,7 +546,31 @@ AtkObject* RefAccesibleAtPoint(AtkComponent* atk_component, if (!obj) return nullptr; +#if defined(TIZEN_ATK_SUPPORT) + if (AXPlatformNodeEfl::GetPreferredAXCoordinateSystem() == + AXCoordinateSystem::kScreenDIPs) { + int view_x, view_y; + obj->GetDelegate()->EvasToBlinkCords(x, y, &view_x, &view_y); + x = view_x; + y = view_y; + } + + AtkObject* result = nullptr; + for (int i = 0; i < obj->GetChildCount(); ++i) { + AXPlatformNodeAuraLinux* child = + AXPlatformNodeAuraLinux::FromAtkObject(obj->ChildAtIndex(i)); + if (!child) + return nullptr; + + if (child->GetExtentsRelativeToAtkCoordinateType(coord_type) + .Contains(gfx::Point(x, y))) { + result = child->GetAtkObject(); + break; + } + } +#else AtkObject* result = obj->HitTestSync(x, y, coord_type); +#endif if (result) g_object_ref(result); return result; @@ -550,6 +587,36 @@ gboolean GrabFocus(AtkComponent* atk_component) { return obj->GrabFocus(); } +#if defined(TIZEN_ATK_SUPPORT) +static gboolean GrabHighlight(AtkComponent* atk_component) { + if (IsMobileProfile()) + return FALSE; + + g_return_val_if_fail(ATK_IS_COMPONENT(atk_component), FALSE); + AtkObject* atk_object = ATK_OBJECT(atk_component); + AXPlatformNodeAuraLinux* obj = + AXPlatformNodeAuraLinux::FromAtkObject(atk_object); + if (!obj) + return FALSE; + + return obj->SetHighlighted(atk_object); +} + +static gboolean ClearHighlight(AtkComponent* atk_component) { + if (!IsMobileProfile()) + return FALSE; + + g_return_val_if_fail(ATK_IS_COMPONENT(atk_component), FALSE); + AtkObject* atk_object = ATK_OBJECT(atk_component); + AXPlatformNodeAuraLinux* obj = + AXPlatformNodeAuraLinux::FromAtkObject(atk_object); + if (!obj) + return FALSE; + + return obj->MaybeInvalidateHighlighted(atk_object); +} +#endif + #if defined(ATK_230) gboolean ScrollTo(AtkComponent* atk_component, AtkScrollType scroll_type) { g_return_val_if_fail(ATK_IS_COMPONENT(atk_component), FALSE); @@ -574,6 +641,15 @@ gboolean ScrollToPoint(AtkComponent* atk_component, if (!obj) return FALSE; +#if defined(TIZEN_ATK_SUPPORT) + if (AXPlatformNodeEfl::GetPreferredAXCoordinateSystem() == + AXCoordinateSystem::kScreenDIPs) { + int converted_x, converted_y; + obj->GetDelegate()->EvasToBlinkCords(x, y, &converted_x, &converted_y); + x = converted_x; + y = converted_y; + } +#endif obj->ScrollToPoint(atk_coord_type, x, y); return TRUE; } @@ -585,6 +661,12 @@ void Init(AtkComponentIface* iface) { iface->get_size = GetSize; iface->ref_accessible_at_point = RefAccesibleAtPoint; iface->grab_focus = GrabFocus; +#if defined(TIZEN_ATK_SUPPORT) + if (IsMobileProfile()) { + iface->grab_highlight = GrabHighlight; + iface->clear_highlight = ClearHighlight; + } +#endif #if defined(ATK_230) if (SupportsAtkComponentScrollingInterface()) { iface->scroll_to = ScrollTo; @@ -646,6 +728,9 @@ const gchar* GetName(AtkAction* atk_action, gint index) { if (!obj) return nullptr; +#if defined(TIZEN_ATK_SUPPORT) + return kActivateActionName; +#endif const std::vector actions = obj->GetDelegate()->GetSupportedActions(); g_return_val_if_fail(index < static_cast(actions.size()), nullptr); @@ -2143,6 +2228,12 @@ gint GetIndexInParent(AtkObject* atk_object) { if (!obj) return -1; +#if defined(TIZEN_ATK_SUPPORT) + // root object so return index 0. + if (!obj->GetParent()) + return 0; +#endif + auto index_in_parent = obj->GetIndexInParent(); return index_in_parent.has_value() ? static_cast(index_in_parent.value()) @@ -2162,6 +2253,15 @@ AtkObject* GetParent(AtkObject* atk_object) { if (!obj) return nullptr; +#if defined(TIZEN_ATK_SUPPORT) + // The root atk object is created in EwebAccessibility and set to + // BrowserAccessibilityAuraLinux after initialization. If the parent + // is null here, it means this is the document web node, so set the + // root atk object as parent of document web node's atk object. + if (!obj->GetParent()) + return obj->GetDelegate()->GetRootAtkObject(); +#endif + return obj->GetParent(); } @@ -2216,6 +2316,17 @@ AtkRole GetRole(AtkObject* atk_object) { AXPlatformNodeAuraLinux::FromAtkObject(atk_object); if (!obj) return ATK_ROLE_INVALID; +#if defined(TIZEN_ATK_SUPPORT) + std::string text; + if (obj->GetData().GetHtmlAttribute("role", &text) && text.empty() && + obj->HasStringAttribute(ax::mojom::StringAttribute::kRole)) + return ATK_ROLE_INVALID; + + AtkRole role = obj->GetAtkRole(); + if (role == ATK_ROLE_UNKNOWN) + role = ATK_ROLE_INVALID; + return role; +#endif return obj->GetAtkRole(); } @@ -2282,7 +2393,11 @@ void ClassInit(gpointer class_pointer, gpointer /* class_data */) { GType GetType() { AXPlatformNodeAuraLinux::EnsureGTypeInit(); +#if defined(TIZEN_ATK_SUPPORT) + static gsize type_volatile = 0; +#else static volatile gsize type_volatile = 0; +#endif if (g_once_init_enter(&type_volatile)) { static const GTypeInfo type_info = { sizeof(AXPlatformNodeAuraLinuxClass), // class_size @@ -2475,6 +2590,16 @@ GType AXPlatformNodeAuraLinux::GetAccessibilityGType() { &atk_table_cell::Info); } } +#if defined(TIZEN_ATK_SUPPORT) + // FIXME ATK_EDITABLE_TEXT_INTERFACE is removed +#if !defined(EWK_BRINGUP) + if (interface_mask_ & (1 << ATK_EDITABLE_TEXT_INTERFACE)) { + g_type_add_interface_static(type, ATK_TYPE_EDITABLE_TEXT, + &atk_action::Info); + } +#endif + ToAXPlatformNodeEfl(this)->SetInterfaceFromObject(type, interface_mask_); +#endif return type; } @@ -2558,6 +2683,9 @@ AtkObject* AXPlatformNodeAuraLinux::CreateAtkObject() { return nullptr; EnsureGTypeInit(); interface_mask_ = GetGTypeInterfaceMask(GetData()); +#if defined(TIZEN_ATK_SUPPORT) + ToAXPlatformNodeEfl(this)->SetInterfaceMaskFromObject(interface_mask_); +#endif GType type = GetAccessibilityGType(); AtkObject* atk_object = static_cast(g_object_new(type, nullptr)); @@ -2590,12 +2718,14 @@ void AXPlatformNodeAuraLinux::DestroyAtkObjects() { } } +#if !defined(TIZEN_ATK_SUPPORT) // static AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { AXPlatformNodeAuraLinux* node = new AXPlatformNodeAuraLinux(); node->Init(delegate); return node; } +#endif // static AXPlatformNode* AXPlatformNode::FromNativeViewAccessible( @@ -2800,7 +2930,11 @@ AtkRole AXPlatformNodeAuraLinux::GetAtkRole() const { case ax::mojom::Role::kGrid: return ATK_ROLE_TABLE; case ax::mojom::Role::kGroup: +#if !defined(TIZEN_ATK_FEATURE_VD) + return ATK_ROLE_INVALID; +#else return ATK_ROLE_PANEL; +#endif case ax::mojom::Role::kHeading: return ATK_ROLE_HEADING; case ax::mojom::Role::kIframe: @@ -3110,8 +3244,10 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { atk_state_set_add_state(atk_state_set, ATK_STATE_HORIZONTAL); if (!IsInvisibleOrIgnored()) { atk_state_set_add_state(atk_state_set, ATK_STATE_VISIBLE); +#if !defined(TIZEN_ATK_SUPPORT) if (!delegate_->IsOffscreen() && !is_minimized) atk_state_set_add_state(atk_state_set, ATK_STATE_SHOWING); +#endif } if (HasState(ax::mojom::State::kMultiselectable)) atk_state_set_add_state(atk_state_set, ATK_STATE_MULTISELECTABLE); @@ -3192,6 +3328,19 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { if (!atk_object) return; +#if defined(TIZEN_ATK_SUPPORT) + if (IsMobileProfile()) { + ToAXPlatformNodeEfl(this)->RecalculateHighlightableSingle(); + if (ToAXPlatformNodeEfl(this)->Ishighlightable()) { + atk_state_set_add_state(atk_state_set, ATK_STATE_HIGHLIGHTABLE); + } + atk_state_set_add_state(atk_state_set, ATK_STATE_SHOWING); + + if (atk_object_ && IsHighlighted(atk_object_)) + atk_state_set_add_state(atk_state_set, ATK_STATE_HIGHLIGHTED); + } +#endif + // It is insufficient to compare with g_current_activedescendant due to both // timing and event ordering for objects which implement AtkSelection and also // have an active descendant. For instance, if we check the state set of a @@ -4002,6 +4151,15 @@ void AXPlatformNodeAuraLinux::OnValueChanged() { if (!GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange, &float_val)) return; +#if defined(TIZEN_ATK_SUPPORT) + // The progress bar of youtube.com continuously update its value, + // that interrupt the output of other events; so only emit value + // changed event for focus node or its active descendant + if (delegate_->GetFocus() != GetNativeViewAccessible() && + delegate_->GetActiveDescendant() != GetNativeViewAccessible()) + return; +#endif + AtkObject* atk_object = GetOrCreateAtkObject(); if (!atk_object) return; @@ -4423,8 +4581,13 @@ gfx::Vector2d AXPlatformNodeAuraLinux::GetParentFrameOriginInScreenCoordinates() gfx::Rect AXPlatformNodeAuraLinux::GetExtentsRelativeToAtkCoordinateType( AtkCoordType coord_type) const { +#if defined(TIZEN_ATK_SUPPORT) + gfx::Rect extents = delegate_->GetBoundsRect( + AXPlatformNodeEfl::GetPreferredAXCoordinateSystem(), +#else gfx::Rect extents = delegate_->GetBoundsRect(AXCoordinateSystem::kScreenDIPs, - AXClippingBehavior::kUnclipped); +#endif + AXClippingBehavior::kUnclipped); switch (coord_type) { case ATK_XY_SCREEN: break; @@ -5201,4 +5364,60 @@ std::pair AXPlatformNodeAuraLinux::GetSelectionOffsetsForAtk() { return selection; } +#if defined(TIZEN_ATK_SUPPORT) +bool AXPlatformNodeAuraLinux::SetHighlighted(AtkObject* obj) { + if (g_current_focused == obj) + return false; + + GetDelegate()->CheckFocusOnEwebview(); + InvalidateHighlighted(false); + + auto focused_obj = AXPlatformNodeAuraLinux::FromAtkObject(obj); + if (focused_obj) { + focused_obj->ScrollToNode(AXPlatformNodeBase::ScrollType::Anywhere); + focused_obj->GrabFocus(); + g_current_focused = obj; + + atk_object_notify_state_change(g_current_focused, ATK_STATE_HIGHLIGHTED, + true); + return true; + } + return false; +} + +bool AXPlatformNodeAuraLinux::IsHighlighted(AtkObject* obj) const { + return g_current_focused == obj; +} + +bool AXPlatformNodeAuraLinux::MaybeInvalidateHighlighted(AtkObject* obj) { + if (!IsHighlighted(obj)) + return false; + + InvalidateHighlighted(false); + return true; +} + +void AXPlatformNodeAuraLinux::InvalidateHighlighted(bool focus_root) { + if (!g_current_focused) + return; + + // highlighted_obj_ needs to be nullptr before notification as during it + // states are obtained. ATK_STATE_HIGHLIGHTED depends on highlighted_obj_ + // itself + auto highlighted_obj = g_current_focused; + g_current_focused = nullptr; + + if (focus_root) { + auto root = GetDelegate()->GetRoot(); + if (root) + (static_cast(root))->GrabFocus(); + } + + if (highlighted_obj) { + atk_object_notify_state_change(highlighted_obj, ATK_STATE_HIGHLIGHTED, + false); + } +} +#endif + } // namespace ui diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h index 5654404..c687348 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.h +++ b/ui/accessibility/platform/ax_platform_node_auralinux.h @@ -189,6 +189,12 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { int y); #endif // defined(ATK_CHECK_VERSION) && ATK_CHECK_VERSION(2, 32, 0) +#if defined(TIZEN_ATK_SUPPORT) + bool SetHighlighted(AtkObject* obj); + bool IsHighlighted(AtkObject* obj) const; + bool MaybeInvalidateHighlighted(AtkObject* obj); + void InvalidateHighlighted(bool focus_root); +#endif // Misc helpers void GetFloatAttributeInGValue(ax::mojom::FloatAttribute attr, GValue* value); @@ -292,8 +298,17 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { // nullopt. absl::optional> GetEmbeddedObjectIndices(); +#if defined(TIZEN_ATK_SUPPORT) + AtkObject* GetAtkObject() { return atk_object_; } + // We own a reference to these ref-counted objects. + AtkObject* atk_object_ = nullptr; + + // Keep information of latest ImplementedAtkInterfaces mask to rebuild the + // ATK object accordingly when the platform node changes. + ImplementedAtkInterfaces interface_mask_; +#endif std::string accessible_name_; - + protected: AXPlatformNodeAuraLinux(); @@ -393,12 +408,14 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { gfx::Point ConvertPointToScreenCoordinates(const gfx::Point& point, AtkCoordType atk_coord_type); +#if !defined(TIZEN_ATK_SUPPORT) // Keep information of latest ImplementedAtkInterfaces mask to rebuild the // ATK object accordingly when the platform node changes. ImplementedAtkInterfaces interface_mask_; // We own a reference to these ref-counted objects. AtkObject* atk_object_ = nullptr; +#endif AtkHyperlink* atk_hyperlink_ = nullptr; // A weak pointers which help us track the ATK embeds relation. diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h index 7510229..8776371 100644 --- a/ui/accessibility/platform/ax_platform_node_delegate.h +++ b/ui/accessibility/platform/ax_platform_node_delegate.h @@ -453,6 +453,16 @@ class AX_EXPORT AXPlatformNodeDelegate { // document through its ancestry chain. virtual gfx::NativeViewAccessible GetFocus() const = 0; +#if defined(TIZEN_ATK_SUPPORT) + // Return the node within this node's subtree (inclusive) that currently + // is the active descendant of focus or is the focus itself. + virtual gfx::NativeViewAccessible GetActiveDescendant() = 0; + virtual void EvasToBlinkCords(int x, int y, int* view_x, int* view_y) = 0; + virtual AXPlatformNode* GetRoot() = 0; + virtual void CheckFocusOnEwebview() = 0; + virtual gfx::NativeViewAccessible GetRootAtkObject() = 0; +#endif + // Get whether this node is offscreen. virtual bool IsOffscreen() const = 0; diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/ui/accessibility/platform/ax_platform_node_delegate_base.cc index 43d40a2..e0ae5f6 100644 --- a/ui/accessibility/platform/ax_platform_node_delegate_base.cc +++ b/ui/accessibility/platform/ax_platform_node_delegate_base.cc @@ -624,6 +624,22 @@ gfx::NativeViewAccessible AXPlatformNodeDelegateBase::GetFocus() const { return nullptr; } +#if defined(TIZEN_ATK_SUPPORT) +gfx::NativeViewAccessible AXPlatformNodeDelegateBase::GetActiveDescendant() { + return nullptr; +} +void AXPlatformNodeDelegateBase::EvasToBlinkCords(int x, + int y, + int* view_x, + int* view_y) {} + +AXPlatformNode* AXPlatformNodeDelegateBase::GetRoot() { + return nullptr; +} + +void AXPlatformNodeDelegateBase::CheckFocusOnEwebview() {} +#endif + AXPlatformNode* AXPlatformNodeDelegateBase::GetFromNodeID(int32_t id) { return nullptr; } diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.h b/ui/accessibility/platform/ax_platform_node_delegate_base.h index 3b12fdf..015fd48 100644 --- a/ui/accessibility/platform/ax_platform_node_delegate_base.h +++ b/ui/accessibility/platform/ax_platform_node_delegate_base.h @@ -217,6 +217,16 @@ class AX_EXPORT AXPlatformNodeDelegateBase : public AXPlatformNodeDelegate { // has focus. gfx::NativeViewAccessible GetFocus() const override; +#if defined(TIZEN_ATK_SUPPORT) + // Return the node within this node's subtree (inclusive) that currently + // is the active descendant of focus or is the focus itself. + gfx::NativeViewAccessible GetActiveDescendant() override; + void EvasToBlinkCords(int x, int y, int* view_x, int* view_y) override; + AXPlatformNode* GetRoot() override; + void CheckFocusOnEwebview() override; + gfx::NativeViewAccessible GetRootAtkObject() override { return nullptr; } +#endif + // Get whether this node is offscreen. bool IsOffscreen() const override; diff --git a/ui/base/ui_features.gni b/ui/base/ui_features.gni index 1307fe4..4261136 100644 --- a/ui/base/ui_features.gni +++ b/ui/base/ui_features.gni @@ -15,7 +15,7 @@ declare_args() { # Whether the platform provides a native accessibility toolkit, in other words # the platform has a C/C++ interface for accessibility that Chrome # implements/subclasses in some way - win, mac, linux. - has_native_accessibility = use_atk || is_win || is_mac + has_native_accessibility = use_atk || is_win || is_mac || is_tizen # Whether the platform provide platform-specific accessibility implementation, # i.e. there an accessibility API of some kind on this platform that's diff --git a/ui/gfx/native_widget_types.h b/ui/gfx/native_widget_types.h index 39c826d..da4525a 100644 --- a/ui/gfx/native_widget_types.h +++ b/ui/gfx/native_widget_types.h @@ -105,7 +105,8 @@ class SkBitmap; // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch // of lacros-chrome is complete. -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) || \ + defined(TIZEN_ATK_SUPPORT) extern "C" { struct _AtkObject; typedef struct _AtkObject AtkObject; @@ -215,7 +216,8 @@ typedef NSFont* NativeFont; typedef id NativeViewAccessible; // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch // of lacros-chrome is complete. -#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) || \ + defined(TIZEN_ATK_SUPPORT) // Linux doesn't have a native font type. typedef AtkObject* NativeViewAccessible; #else -- 2.7.4