[M108 Migration][Accessibility] Bringup Accessibility. 70/287870/7
authorayush.k123 <ayush.k123@samsung.com>
Fri, 3 Feb 2023 09:35:58 +0000 (15:05 +0530)
committerBot Blink <blinkbot@samsung.com>
Tue, 14 Feb 2023 08:57:43 +0000 (08:57 +0000)
- 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 <ayush.k123@samsung.com>
57 files changed:
build/config/linux/atk/BUILD.gn
build/config/ui.gni
content/browser/accessibility/browser_accessibility.cc
content/browser/accessibility/browser_accessibility.h
content/browser/accessibility/browser_accessibility_auralinux.cc
content/browser/accessibility/browser_accessibility_auralinux.h
content/browser/accessibility/browser_accessibility_manager.cc
content/browser/accessibility/browser_accessibility_manager.h
content/browser/accessibility/web_ax_platform_tree_manager_delegate.h
content/browser/renderer_host/render_frame_host_impl.cc
content/browser/renderer_host/render_frame_host_impl.h
content/browser/renderer_host/render_widget_host_view_aura.cc
content/browser/web_contents/web_contents_impl.cc
content/browser/web_contents/web_contents_impl.h
content/renderer/accessibility/render_accessibility_impl.cc
packaging/chromium-efl.spec
third_party/blink/common/web_preferences/web_preferences.cc
third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
third_party/blink/public/common/web_preferences/web_preferences.h
third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
third_party/blink/public/mojom/webpreferences/web_preferences.mojom
third_party/blink/public/web/web_settings.h
third_party/blink/renderer/core/dom/document.cc
third_party/blink/renderer/core/dom/element.cc
third_party/blink/renderer/core/exported/web_settings_impl.cc
third_party/blink/renderer/core/exported/web_settings_impl.h
third_party/blink/renderer/core/exported/web_view_impl.cc
third_party/blink/renderer/core/frame/local_dom_window.cc
third_party/blink/renderer/core/frame/settings.h
third_party/blink/renderer/modules/accessibility/ax_node_object.cc
third_party/blink/renderer/modules/accessibility/ax_object.cc
tizen_src/build/BUILD.gn
tizen_src/build/config/BUILD.gn
tizen_src/build/config/tizen_features.gni
tizen_src/chromium_impl/content/browser/browser_efl.gni
tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.cc [new file with mode: 0644]
tizen_src/chromium_impl/ui/accessibility/platform/ax_platform_node_efl.h [new file with mode: 0644]
tizen_src/chromium_impl/ui/ui_efl.gni
tizen_src/ewk/efl_integration/BUILD.gn
tizen_src/ewk/efl_integration/eweb_accessibility.cc [new file with mode: 0644]
tizen_src/ewk/efl_integration/eweb_accessibility.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/eweb_accessibility_object.cc [new file with mode: 0644]
tizen_src/ewk/efl_integration/eweb_accessibility_object.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/eweb_accessibility_util.cc [new file with mode: 0644]
tizen_src/ewk/efl_integration/eweb_accessibility_util.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/eweb_view.cc
tizen_src/ewk/efl_integration/eweb_view.h
tizen_src/ewk/efl_integration/web_contents_observer_efl.cc
ui/accessibility/BUILD.gn
ui/accessibility/platform/ax_platform_atk_hyperlink.cc
ui/accessibility/platform/ax_platform_node_auralinux.cc
ui/accessibility/platform/ax_platform_node_auralinux.h
ui/accessibility/platform/ax_platform_node_delegate.h
ui/accessibility/platform/ax_platform_node_delegate_base.cc
ui/accessibility/platform/ax_platform_node_delegate_base.h
ui/base/ui_features.gni
ui/gfx/native_widget_types.h

index 239c387..fc0926b 100644 (file)
@@ -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")
 }
 
index 84d9e50..40d04cc 100644 (file)
@@ -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.
index 31726d1..243ffae 100644 (file)
@@ -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)
index e2fee78..16609e0 100644 (file)
@@ -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;
index 913575b..58f6053 100644 (file)
@@ -69,4 +69,24 @@ ui::TextAttributeList BrowserAccessibilityAuraLinux::ComputeTextAttributes()
   return GetNode()->ComputeTextAttributes();
 }
 
+#if defined(TIZEN_ATK_SUPPORT)
+ui::AXPlatformNode* BrowserAccessibilityAuraLinux::GetRoot() {
+  BrowserAccessibility* accessibility =
+      static_cast<BrowserAccessibility*>(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
index cb876af..2f17216 100644 (file)
@@ -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<ui::AXPlatformNodeAuraLinux> platform_node_;
+#if defined(TIZEN_ATK_SUPPORT)
+  gfx::NativeViewAccessible root_atk_object_ = nullptr;
+#endif
 };
 
 CONTENT_EXPORT BrowserAccessibilityAuraLinux* ToBrowserAccessibilityAuraLinux(
index d225fd0..58f9f36 100644 (file)
 #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<RenderWidgetHostViewAura*>(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<RenderWidgetHostViewAura*>(delegate_->AccessibilityGetView());
+
+  if (!view)
+    return;
+
+  if (!view->offscreen_helper()->HasFocus())
+    view->offscreen_helper()->Focus(true);
+}
+#endif
+
 }  // namespace content
index 048ad2f..a2ba368 100644 (file)
@@ -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);
index 45862ce..7175bab 100644 (file)
@@ -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;
 };
index a614089..1bc379a 100644 (file)
@@ -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
index db82798..6a6b83b 100644 (file)
@@ -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;
index 78b93f7..325ae91 100644 (file)
@@ -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() {
index 17895cb..4700997 100644 (file)
@@ -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
index b8ab515..3d3216f 100755 (executable)
@@ -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<WebContentsImpl> loading_weak_factory_{this};
index 5ee30b5..ccd850a 100644 (file)
@@ -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.
index 111d603..d16e310 100644 (file)
@@ -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)
index 67b358e..f58b446 100644 (file)
@@ -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),
index 99b78f7..93330bf 100644 (file)
@@ -162,6 +162,9 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
 #if BUILDFLAG(IS_TIZEN)
   out->max_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();
index 21c156f..47e2b5c 100644 (file)
@@ -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;
index 3f2fbaf..31b0f1e 100644 (file)
@@ -529,6 +529,10 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
   static bool link_effect_enabled(const blink::web_pref::WebPreferences& r) {
     return r.link_effect_enabled;
 }
+
+  static bool atk_enabled(const blink::web_pref::WebPreferences& r) {
+    return r.atk_enabled;
+}
 #endif
 
 #if BUILDFLAG(IS_TIZEN)
index 52fa0a7..c9a26d6 100644 (file)
@@ -264,6 +264,9 @@ struct WebPreferences {
   [EnableIf=is_efl]
   bool link_effect_enabled;
 
+  [EnableIf=is_efl]
+  bool atk_enabled;
+
   // Representation of the Web App Manifest scope if any.
   url.mojom.Url web_app_scope;
 
index 1513d89..0e49c67 100644 (file)
@@ -286,6 +286,11 @@ class WebSettings {
   virtual bool LinkEffectEnabled() const = 0;
 #endif
 
+#if defined(TIZEN_ATK_SUPPORT)
+  virtual void SetAccessibilityEnabled(bool) = 0;
+  virtual bool GetAccessibilityEnabled() = 0;
+#endif
+
  protected:
   ~WebSettings() = default;
 };
index b805b35..c33b310 100644 (file)
@@ -5077,7 +5077,15 @@ bool Document::SetFocusedElement(Element* new_focused_element,
 
   if (new_focused_element)
     UpdateStyleAndLayoutTreeForNode(new_focused_element);
+#if defined(TIZEN_ATK_SUPPORT) && !defined(TIZEN_ATK_FEATURE_VD)
+  LocalFrame* frame = this->GetFrame();
+  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
index 69013aa..6f4a5a2 100644 (file)
@@ -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;
index d9c8252..1f5a363 100644 (file)
@@ -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
index f5dd42e..dc092d8 100644 (file)
@@ -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_;
   }
index 79aac67..f4f53be 100644 (file)
@@ -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,
index fe5e2a6..f18293c 100644 (file)
@@ -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<ComputedAccessibleNodePromiseResolver>(
       script_state, *element);
   ScriptPromise promise = resolver->Promise();
index ed4731e..d90bbd1 100644 (file)
@@ -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
 };
 
index c0f2ae0..2d5b4d2 100644 (file)
 #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<HTMLSpanElement>(node)) {
+    return kIncludeObject;
+  } else if (IsA<HTMLSpanElement>(node)) {
+    if (ignored_reasons)
+      ignored_reasons->push_back(IgnoredReason(kAXUninteresting));
+    return kIgnoreObject;
+  }
+#else
   // <span> 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<HTMLSpanElement>(*GetNode())) {
+    return ax::mojom::Role::kGroup;
+  }
+#endif
+
   if (auto* select_element = DynamicTo<HTMLSelectElement>(*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<HTMLDivElement>(*GetNode()))
+  if (IsA<HTMLDivElement>(*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<HTMLMenuElement>(*GetNode()) || IsA<HTMLUListElement>(*GetNode()) ||
       IsA<HTMLOListElement>(*GetNode())) {
     // <menu> 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
index cb4d1a6..3ecac84 100644 (file)
@@ -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<HTMLDivElement>(*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;
   }
index f354554..e7c5d68 100644 (file)
@@ -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" ]
 }
index 68fb742..92914cb 100644 (file)
@@ -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" ]
     }
index 85f5827..8854b6a 100644 (file)
@@ -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
index e8e3ce9..3c93549 100644 (file)
@@ -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 (file)
index 0000000..2af52a7
--- /dev/null
@@ -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<AXPlatformNodeEfl*>(obj);
+}
+
+AXPlatformNodeEfl* ToAXPlatformNodeEfl(AXPlatformNodeAuraLinux* obj) {
+  DCHECK(!obj);
+  return static_cast<AXPlatformNodeEfl*>(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<GInterfaceInitFunc>(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<ax::mojom::NameFrom>(
+          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 (file)
index 0000000..25d0910
--- /dev/null
@@ -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 <atk/atk.h>
+
+#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
index cb23b88..1a0117f 100644 (file)
@@ -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
index c43e886..4dddf77 100755 (executable)
@@ -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 (file)
index 0000000..f2653f2
--- /dev/null
@@ -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 <atk-bridge.h>
+
+#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> 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<char*>(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 (file)
index 0000000..25c3268
--- /dev/null
@@ -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 <Ecore_Evas.h>
+#include <memory>
+#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> 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> observer_;
+
+  std::unique_ptr<EWebAccessibilityObject> 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 (file)
index 0000000..277fcc0
--- /dev/null
@@ -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<EWebAccessibilityObject*>(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<content::WebContentsImplEfl*>(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<AtkObject*>(
+      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<EWebAccessibilityObject*>(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<EWebAccessibilityObject*>(data);
+  atk_object_notify_state_change(ATK_OBJECT(thiz->GetEflAtkObject()),
+                                 ATK_STATE_SHOWING, FALSE);
+}
+
+content::BrowserAccessibilityManager*
+EWebAccessibilityObject::GetBrowserAccessibilityManager() const {
+  auto frame = static_cast<content::RenderFrameHostImpl*>(
+      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<content::BrowserAccessibility*>(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 (file)
index 0000000..6e168e0
--- /dev/null
@@ -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 <Ecore_Evas.h>
+#include <atk/atk.h>
+
+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 (file)
index 0000000..74e6c1d
--- /dev/null
@@ -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 <atk-bridge.h>
+#include <atk/atk.h>
+
+#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 <vconf.h>
+
+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<EWebAccessibilityUtil*>(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<EWebAccessibilityUtil>::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 (file)
index 0000000..33fcf40
--- /dev/null
@@ -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<EWebAccessibilityUtil>;
+};
+
+#endif  // EWEB_ACCESSIBILITY_UTIL_H
index b99454e..be45a2d 100644 (file)
 
 #include <iostream>
 
+#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<EWebAccessibilityObserver> 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<WebContentsImpl*>(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<WebContentsImpl*>(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
index 676d285..0fd0d14 100644 (file)
@@ -90,6 +90,10 @@ class Ewk_Context;
 class WebViewEvasEventHandler;
 class _Ewk_Quota_Permission_Request;
 
+#if defined(TIZEN_ATK_SUPPORT)
+class EWebAccessibility;
+#endif
+
 template <typename CallbackPtr, typename CallbackParameter>
 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::CallbackType callbackType>
   EWebViewCallbacks::CallBack<callbackType> SmartCallback() const {
     return EWebViewCallbacks::CallBack<callbackType>(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<aura::client::FocusClient> focus_client_;
   std::unique_ptr<aura::client::WindowParentingClient> window_parenting_client_;
   content::DateTimeChooserEfl* date_time_chooser_ = nullptr;
+
+#if defined(TIZEN_ATK_SUPPORT)
+  std::unique_ptr<EWebAccessibility> eweb_accessibility_;
+  bool lazy_initialize_atk_ = false;
+#endif
 };
 
 const unsigned int g_default_tilt_motion_sensitivity = 3;
index 76e1d5e..9135f52 100644 (file)
@@ -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<EWebViewCallbacks::ProvisionalLoadStarted>().call();
 }
 
index 54b47dd..f604d7f 100644 (file)
@@ -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") {
index 40b53e6..8d3e430 100644 (file)
@@ -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();
 
index 7412e7a..b02ca80 100644 (file)
 #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<AXPlatformNodeEfl*>(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<ax::mojom::Action> actions =
       obj->GetDelegate()->GetSupportedActions();
   g_return_val_if_fail(index < static_cast<gint>(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<gint>(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<AtkObject*>(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<int, int> 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<AXPlatformNodeAuraLinux*>(root))->GrabFocus();
+  }
+
+  if (highlighted_obj) {
+    atk_object_notify_state_change(highlighted_obj, ATK_STATE_HIGHLIGHTED,
+                                   false);
+  }
+}
+#endif
+
 }  // namespace ui
index 5654404..c687348 100644 (file)
@@ -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<std::pair<int, int>> 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.
index 7510229..8776371 100644 (file)
@@ -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;
 
index 43d40a2..e0ae5f6 100644 (file)
@@ -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;
 }
index 3b12fdf..015fd48 100644 (file)
@@ -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;
 
index 1307fe4..4261136 100644 (file)
@@ -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
index 39c826d..da4525a 100644 (file)
@@ -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