diff options
author | Dominic Mazzoni <dmazzoni@chromium.org> | 2015-03-16 13:51:09 -0700 |
---|---|---|
committer | Dominic Mazzoni <dmazzoni@chromium.org> | 2015-03-16 20:54:11 +0000 |
commit | 0e9bcce45c12479259c705a07a6b4b1e73b84bf2 (patch) | |
tree | baf231cb4ce2f6dadc976db8cce21d564071e82c | |
parent | 772e590605971ee9c165e4ffdfec844edaf017f8 (diff) | |
download | chromium_src-0e9bcce45c12479259c705a07a6b4b1e73b84bf2.zip chromium_src-0e9bcce45c12479259c705a07a6b4b1e73b84bf2.tar.gz chromium_src-0e9bcce45c12479259c705a07a6b4b1e73b84bf2.tar.bz2 |
Re-land: Resurrect Aura Linux accessibility.
1. Avoids overloading in a C linkage block (mostynb)
2. Only compiles ATK support if use_x11 is true
to avoid ChromeCast issue (gunsch)
3. Updates chrome/installer/linux expectations
Original changelist: https://codereview.chromium.org/975113002/
BUG=463671
R=thestig@chromium.org
TBR=dpranke, plundblad, sky
Review URL: https://codereview.chromium.org/1005293002
Cr-Commit-Position: refs/heads/master@{#320790}
26 files changed, 889 insertions, 4 deletions
diff --git a/build/config/linux/BUILD.gn b/build/config/linux/BUILD.gn index 202fd73..3d65937 100644 --- a/build/config/linux/BUILD.gn +++ b/build/config/linux/BUILD.gn @@ -35,6 +35,25 @@ config("sdk") { } } +pkg_config("atk") { + packages = [ "atk" ] + atk_lib_dir = exec_script(pkg_config_script, + [ + "--libdir", + "atk", + ], + "string") + defines = [ "ATK_LIB_DIR=\"$atk_lib_dir\"" ] +} + +# gn orders flags on a target before flags from configs. The default config +# adds -Wall, and these flags have to be after -Wall -- so they need to come +# from a config and can't be on the target directly. +config("atk_warnings") { + # glib uses the pre-c++11 typedef-as-static_assert hack. + cflags = [ "-Wno-unused-local-typedef" ] +} + config("fontconfig") { libs = [ "fontconfig" ] } diff --git a/build/config/linux/pkg-config.py b/build/config/linux/pkg-config.py index a4f4703..fadcc0b 100644 --- a/build/config/linux/pkg-config.py +++ b/build/config/linux/pkg-config.py @@ -112,6 +112,7 @@ parser.add_option('-s', action='store', dest='sysroot', type='string') parser.add_option('-a', action='store', dest='arch', type='string') parser.add_option('--atleast-version', action='store', dest='atleast_version', type='string') +parser.add_option('--libdir', action='store_true', dest='libdir') (options, args) = parser.parse_args() # Make a list of regular expressions to strip out. @@ -138,6 +139,18 @@ if options.atleast_version: print "false" sys.exit(0) +if options.libdir: + try: + libdir = subprocess.check_output([options.pkg_config, + "--variable=libdir"] + + args, + env=os.environ) + except: + print "Error from pkg-config." + sys.exit(1) + sys.stdout.write(libdir.strip()) + sys.exit(0) + try: flag_string = subprocess.check_output( [ options.pkg_config, "--cflags", "--libs-only-l", "--libs-only-L" ] + diff --git a/build/linux/system.gyp b/build/linux/system.gyp index 59fc13a..5333798 100644 --- a/build/linux/system.gyp +++ b/build/linux/system.gyp @@ -91,6 +91,30 @@ # added back to Chrome OS and Ozone. Don't try to use GTK on Chrome OS and Ozone. 'targets': [ { + 'target_name': 'atk', + 'type': 'none', + 'conditions': [ + ['_toolset=="target"', { + 'direct_dependent_settings': { + 'cflags': [ + '<!@(<(pkg-config) --cflags atk)', + ], + 'defines': [ + 'ATK_LIB_DIR="<!@(<(pkg-config) --variable=libdir atk)"', + ], + }, + 'link_settings': { + 'ldflags': [ + '<!@(<(pkg-config) --libs-only-L --libs-only-other atk)', + ], + 'libraries': [ + '<!@(<(pkg-config) --libs-only-l atk)', + ], + }, + }], + ], + }, + { 'target_name': 'gdk', 'type': 'none', 'conditions': [ diff --git a/chrome/browser/ui/views/chrome_views_delegate.cc b/chrome/browser/ui/views/chrome_views_delegate.cc index 82c9190a..daba64b 100644 --- a/chrome/browser/ui/views/chrome_views_delegate.cc +++ b/chrome/browser/ui/views/chrome_views_delegate.cc @@ -13,6 +13,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser_window_state.h" +#include "chrome/common/chrome_version_info.h" #include "content/public/browser/context_factory.h" #include "grit/chrome_unscaled_resources.h" #include "ui/base/resource/resource_bundle.h" @@ -383,6 +384,10 @@ ui::ContextFactory* ChromeViewsDelegate::GetContextFactory() { return content::GetContextFactory(); } +std::string ChromeViewsDelegate::GetApplicationName() { + return chrome::VersionInfo().Name(); +} + #if defined(OS_WIN) int ChromeViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor, const base::Closure& callback) { diff --git a/chrome/browser/ui/views/chrome_views_delegate.h b/chrome/browser/ui/views/chrome_views_delegate.h index 3846d9e..d4d197f 100644 --- a/chrome/browser/ui/views/chrome_views_delegate.h +++ b/chrome/browser/ui/views/chrome_views_delegate.h @@ -48,6 +48,7 @@ class ChromeViewsDelegate : public views::ViewsDelegate { bool WindowManagerProvidesTitleBar(bool maximized) override; #endif ui::ContextFactory* GetContextFactory() override; + std::string GetApplicationName() override; #if defined(OS_WIN) virtual int GetAppbarAutohideEdges(HMONITOR monitor, const base::Closure& callback) override; diff --git a/chrome/installer/linux/debian/expected_deps_ia32 b/chrome/installer/linux/debian/expected_deps_ia32 index 62dfcce..b0a61a9 100644 --- a/chrome/installer/linux/debian/expected_deps_ia32 +++ b/chrome/installer/linux/debian/expected_deps_ia32 @@ -1,5 +1,6 @@ gconf-service libasound2 (>= 1.0.23) +libatk1.0-0 (>= 1.12.4) libc6 (>= 2.11) libcairo2 (>= 1.6.0) libcap2 (>= 2.10) diff --git a/chrome/installer/linux/debian/expected_deps_x64 b/chrome/installer/linux/debian/expected_deps_x64 index 62dfcce..b0a61a9 100644 --- a/chrome/installer/linux/debian/expected_deps_x64 +++ b/chrome/installer/linux/debian/expected_deps_x64 @@ -1,5 +1,6 @@ gconf-service libasound2 (>= 1.0.23) +libatk1.0-0 (>= 1.12.4) libc6 (>= 2.11) libcairo2 (>= 1.6.0) libcap2 (>= 2.10) diff --git a/chrome/installer/linux/rpm/expected_deps_i386 b/chrome/installer/linux/rpm/expected_deps_i386 index 215d0f7..7931d51 100644 --- a/chrome/installer/linux/rpm/expected_deps_i386 +++ b/chrome/installer/linux/rpm/expected_deps_i386 @@ -2,6 +2,7 @@ ld-linux.so.2 ld-linux.so.2(GLIBC_2.1) ld-linux.so.2(GLIBC_2.3) libasound.so.2 +libatk-1.0.so.0 libcairo.so.2 libcap.so.2 libc.so.6 @@ -33,6 +34,7 @@ libgdk_pixbuf-2.0.so.0 libgdk-x11-2.0.so.0 libgio-2.0.so.0 libglib-2.0.so.0 +libgmodule-2.0.so.0 libgobject-2.0.so.0 libgtk-x11-2.0.so.0 libm.so.6 diff --git a/chrome/installer/linux/rpm/expected_deps_x86_64 b/chrome/installer/linux/rpm/expected_deps_x86_64 index 5cbde42..92c02ff 100644 --- a/chrome/installer/linux/rpm/expected_deps_x86_64 +++ b/chrome/installer/linux/rpm/expected_deps_x86_64 @@ -2,6 +2,7 @@ ld-linux-x86-64.so.2()(64bit) ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit) ld-linux-x86-64.so.2(GLIBC_2.3)(64bit) libasound.so.2()(64bit) +libatk-1.0.so.0()(64bit) libcairo.so.2()(64bit) libcap.so.2()(64bit) libc.so.6()(64bit) @@ -26,6 +27,7 @@ libgdk_pixbuf-2.0.so.0()(64bit) libgdk-x11-2.0.so.0()(64bit) libgio-2.0.so.0()(64bit) libglib-2.0.so.0()(64bit) +libgmodule-2.0.so.0()(64bit) libgobject-2.0.so.0()(64bit) libgtk-x11-2.0.so.0()(64bit) libm.so.6()(64bit) diff --git a/tools/accessibility/dump_accessibility_tree_auralinux.py b/tools/accessibility/dump_accessibility_tree_auralinux.py new file mode 100755 index 0000000..4c352d1 --- /dev/null +++ b/tools/accessibility/dump_accessibility_tree_auralinux.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Dump Chrome's ATK accessibility tree to the command line. + +Accerciser is slow and buggy. This is a quick way to check that Chrome is +exposing its interface to ATK from the command line. +""" + +import pyatspi + +def Dump(obj, indent): + if not obj: + return + indent_str = ' ' * indent + role = obj.get_role_name() + name = obj.get_name() + print '%s%s name="%s"' % (indent_str, role, name) + + # Don't recurse into applications other than Chrome + if role == 'application': + if (name.lower().find('chrom') != 0 and + name.lower().find('google chrome') != 0): + return + + for i in range(obj.get_child_count()): + Dump(obj.get_child_at_index(i), indent + 1) + +desktop = pyatspi.Registry.getDesktop(0) +Dump(desktop, 0) diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn index d168b5e..a5c67ba 100644 --- a/ui/accessibility/BUILD.gn +++ b/ui/accessibility/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/ui.gni") import("//build/json_schema_api.gni") import("//testing/test.gni") @@ -47,6 +48,22 @@ component("accessibility") { if (is_win) { public_deps += [ "//third_party/iaccessible2" ] } + + if (use_aura && !is_chromeos && is_linux && use_x11) { + sources += [ + "platform/atk_util_auralinux.cc", + "platform/atk_util_auralinux.h", + "platform/ax_platform_node_auralinux.cc", + "platform/ax_platform_node_auralinux.h", + ] + + configs += [ + "//build/config/linux:atk", + "//build/config/linux:atk_warnings", + "//build/config/linux:gconf", + "//build/config/linux:glib", + ] + } } source_set("test_support") { diff --git a/ui/accessibility/accessibility.gyp b/ui/accessibility/accessibility.gyp index 88613e6..e417f10 100644 --- a/ui/accessibility/accessibility.gyp +++ b/ui/accessibility/accessibility.gyp @@ -43,8 +43,12 @@ 'ax_tree_update.h', 'ax_view_state.cc', 'ax_view_state.h', + 'platform/atk_util_auralinux.cc', + 'platform/atk_util_auralinux.h', 'platform/ax_platform_node.cc', 'platform/ax_platform_node.h', + 'platform/ax_platform_node_auralinux.cc', + 'platform/ax_platform_node_auralinux.h', 'platform/ax_platform_node_base.cc', 'platform/ax_platform_node_base.h', 'platform/ax_platform_node_delegate.h', @@ -59,6 +63,27 @@ '../../third_party/iaccessible2/iaccessible2.gyp:iaccessible2' ], }], + ['OS=="linux" and chromeos==0 and use_x11==1', { + 'dependencies': [ + '../../build/linux/system.gyp:atk', + '../../build/linux/system.gyp:gconf', + '../../build/linux/system.gyp:glib', + ], + 'variables': { + 'clang_warning_flags': [ + # glib uses the pre-c++11 typedef-as-static_assert hack. + '-Wno-unused-local-typedefs', + ], + }, + }], + ['OS!="linux" or chromeos==1 or use_x11==0', { + 'sources!': [ + 'platform/ax_platform_node_auralinux.cc', + 'platform/ax_platform_node_auralinux.h', + 'platform/atk_util_auralinux.cc', + 'platform/atk_util_auralinux.h', + ], + }], ], }, { diff --git a/ui/accessibility/platform/atk_util_auralinux.cc b/ui/accessibility/platform/atk_util_auralinux.cc new file mode 100644 index 0000000..a15df08 --- /dev/null +++ b/ui/accessibility/platform/atk_util_auralinux.cc @@ -0,0 +1,161 @@ +// Copyright 2015 The Chromium 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 <gconf/gconf-client.h> +#include <glib-2.0/gmodule.h> + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "ui/accessibility/platform/atk_util_auralinux.h" +#include "ui/accessibility/platform/ax_platform_node_auralinux.h" + +namespace { + +const char kGnomeAccessibilityEnabledKey[] = + "/desktop/gnome/interface/accessibility"; + +bool ShouldEnableAccessibility() { + GConfClient* client = gconf_client_get_default(); + if (!client) { + LOG(ERROR) << "gconf_client_get_default failed"; + return false; + } + + GError* error = nullptr; + gboolean value = gconf_client_get_bool(client, + kGnomeAccessibilityEnabledKey, + &error); + if (error) { + VLOG(1) << "gconf_client_get_bool failed"; + g_error_free(error); + g_object_unref(client); + return false; + } + + g_object_unref(client); + return value; +} + +} // namespace + +G_BEGIN_DECLS + +// +// atk_util_auralinux AtkObject definition and implementation. +// + +#define ATK_UTIL_AURALINUX_TYPE (atk_util_auralinux_get_type()) +#define ATK_UTIL_AURALINUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + ATK_UTIL_AURALINUX_TYPE, \ + AtkUtilAuraLinux)) +#define ATK_UTIL_AURALINUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + ATK_UTIL_AURALINUX_TYPE, \ + AtkUtilAuraLinuxClass)) +#define IS_ATK_UTIL_AURALINUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), ATK_UTIL_AURALINUX_TYPE)) +#define IS_ATK_UTIL_AURALINUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), ATK_UTIL_AURALINUX_TYPE)) +#define ATK_UTIL_AURALINUX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), \ + ATK_UTIL_AURALINUX_TYPE, \ + AtkUtilAuraLinuxClass)) + +typedef struct _AtkUtilAuraLinux AtkUtilAuraLinux; +typedef struct _AtkUtilAuraLinuxClass AtkUtilAuraLinuxClass; + +struct _AtkUtilAuraLinux +{ + AtkUtil parent; +}; + +struct _AtkUtilAuraLinuxClass +{ + AtkUtilClass parent_class; +}; + +GType atk_util_auralinux_get_type(); + +G_DEFINE_TYPE(AtkUtilAuraLinux, atk_util_auralinux, ATK_TYPE_UTIL); + +static void atk_util_auralinux_init(AtkUtilAuraLinux *ax_util) { +} + +static AtkObject* atk_util_auralinux_get_root() { + ui::AXPlatformNode* application = ui::AXPlatformNodeAuraLinux::application(); + if (application) { + return application->GetNativeViewAccessible(); + } + return nullptr; +} + +static G_CONST_RETURN gchar* atk_util_auralinux_get_toolkit_name(void) { + return "Chromium"; +} + +static G_CONST_RETURN gchar* atk_util_auralinux_get_toolkit_version(void) { + return "1.0"; +} + +static void atk_util_auralinux_class_init(AtkUtilAuraLinuxClass *klass) { + AtkUtilClass *atk_class; + gpointer data; + + data = g_type_class_peek(ATK_TYPE_UTIL); + atk_class = ATK_UTIL_CLASS(data); + + atk_class->get_root = atk_util_auralinux_get_root; + atk_class->get_toolkit_name = atk_util_auralinux_get_toolkit_name; + atk_class->get_toolkit_version = atk_util_auralinux_get_toolkit_version; +} + +G_END_DECLS + +// +// AtkUtilAuraLinuxClass implementation. +// + +namespace ui { + +// static +AtkUtilAuraLinux* AtkUtilAuraLinux::GetInstance() { + return Singleton<AtkUtilAuraLinux>::get(); +} + +AtkUtilAuraLinux::AtkUtilAuraLinux() { + // Register our util class. + g_type_class_unref(g_type_class_ref(ATK_UTIL_AURALINUX_TYPE)); + + if (!ShouldEnableAccessibility()) { + VLOG(1) << "Will not enable ATK accessibility support."; + return; + } + + VLOG(1) << "Enabling ATK accessibility support."; + + // Try to load libatk-bridge.so. + base::FilePath atk_bridge_path(ATK_LIB_DIR); + atk_bridge_path = atk_bridge_path.Append("gtk-2.0/modules/libatk-bridge.so"); + GModule* bridge = g_module_open(atk_bridge_path.value().c_str(), + static_cast<GModuleFlags>(0)); + if (!bridge) { + VLOG(1) << "Unable to open module " << atk_bridge_path.value(); + return; + } + + // Try to call gnome_accessibility_module_init from libatk-bridge.so. + void (*gnome_accessibility_module_init)(); + if (g_module_symbol(bridge, "gnome_accessibility_module_init", + (gpointer *)&gnome_accessibility_module_init)) { + (*gnome_accessibility_module_init)(); + } +} + +AtkUtilAuraLinux::~AtkUtilAuraLinux() { +} + +} // namespace ui diff --git a/ui/accessibility/platform/atk_util_auralinux.h b/ui/accessibility/platform/atk_util_auralinux.h new file mode 100644 index 0000000..349c41a --- /dev/null +++ b/ui/accessibility/platform/atk_util_auralinux.h @@ -0,0 +1,30 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_ +#define UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_ + +#include "base/memory/singleton.h" +#include "ui/accessibility/ax_export.h" + +namespace ui { + +// This singleton class initializes ATK (accessibility toolkit) and +// registers an implementation of the AtkUtil class, a global class that +// every accessible application needs to register once. +class AtkUtilAuraLinux { + public: + // Get the single instance of this class. + static AtkUtilAuraLinux* GetInstance(); + + AtkUtilAuraLinux(); + virtual ~AtkUtilAuraLinux(); + + private: + friend struct DefaultSingletonTraits<AtkUtilAuraLinux>; +}; + +} // namespace ui + +#endif // UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_ diff --git a/ui/accessibility/platform/ax_platform_node.cc b/ui/accessibility/platform/ax_platform_node.cc index 363b3ee..ec268a0 100644 --- a/ui/accessibility/platform/ax_platform_node.cc +++ b/ui/accessibility/platform/ax_platform_node.cc @@ -9,7 +9,7 @@ namespace ui { -#if !defined(OS_MACOSX) && !defined(OS_WIN) +#if !defined(OS_MACOSX) && !defined(OS_WIN) && !(defined(OS_LINUX) && !defined(OS_CHROMEOS)) // static AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { return nullptr; diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc new file mode 100644 index 0000000..b675dc4 --- /dev/null +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc @@ -0,0 +1,283 @@ +// Copyright 2015 The Chromium 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_auralinux.h" + +#include "base/command_line.h" +#include "base/strings/sys_string_conversions.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/platform/atk_util_auralinux.h" +#include "ui/accessibility/platform/ax_platform_node_delegate.h" + +// +// ax_platform_node_auralinux AtkObject definition and implementation. +// + +G_BEGIN_DECLS + +#define AX_PLATFORM_NODE_AURALINUX_TYPE (ax_platform_node_auralinux_get_type()) +#define AX_PLATFORM_NODE_AURALINUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST( \ + (obj), AX_PLATFORM_NODE_AURALINUX_TYPE, AXPlatformNodeAuraLinuxObject)) +#define AX_PLATFORM_NODE_AURALINUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST( \ + (klass), AX_PLATFORM_NODE_AURALINUX_TYPE, AXPlatformNodeAuraLinuxClass)) +#define IS_AX_PLATFORM_NODE_AURALINUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), AX_PLATFORM_NODE_AURALINUX_TYPE)) +#define IS_AX_PLATFORM_NODE_AURALINUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), AX_PLATFORM_NODE_AURALINUX_TYPE)) +#define AX_PLATFORM_NODE_AURALINUX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS( \ + (obj), AX_PLATFORM_NODE_AURALINUX_TYPE, AXPlatformNodeAuraLinuxClass)) + +typedef struct _AXPlatformNodeAuraLinuxObject AXPlatformNodeAuraLinuxObject; +typedef struct _AXPlatformNodeAuraLinuxClass AXPlatformNodeAuraLinuxClass; + +struct _AXPlatformNodeAuraLinuxObject { + AtkObject parent; + ui::AXPlatformNodeAuraLinux* m_object; +}; + +struct _AXPlatformNodeAuraLinuxClass { + AtkObjectClass parent_class; +}; + +GType ax_platform_node_auralinux_get_type(); + +static ui::AXPlatformNodeAuraLinux* ToAXPlatformNodeAuraLinux( + AXPlatformNodeAuraLinuxObject* atk_object) { + if (!atk_object) + return nullptr; + + return atk_object->m_object; +} + +static ui::AXPlatformNodeAuraLinux* AtkObjectToAXPlatformNodeAuraLinux( + AtkObject* atk_object) { + if (!atk_object) + return nullptr; + + if (IS_AX_PLATFORM_NODE_AURALINUX(atk_object)) + return ToAXPlatformNodeAuraLinux(AX_PLATFORM_NODE_AURALINUX(atk_object)); + + return nullptr; +} + +static const gchar* ax_platform_node_auralinux_get_name(AtkObject* atk_object) { + ui::AXPlatformNodeAuraLinux* obj = + AtkObjectToAXPlatformNodeAuraLinux(atk_object); + if (!obj) + return nullptr; + + return obj->GetStringAttribute(ui::AX_ATTR_NAME).c_str(); +} + +static const gchar* ax_platform_node_auralinux_get_description( + AtkObject* atk_object) { + ui::AXPlatformNodeAuraLinux* obj = + AtkObjectToAXPlatformNodeAuraLinux(atk_object); + if (!obj) + return nullptr; + + return obj->GetStringAttribute( + ui::AX_ATTR_DESCRIPTION).c_str(); +} + +static AtkObject* ax_platform_node_auralinux_get_parent(AtkObject* atk_object) { + ui::AXPlatformNodeAuraLinux* obj = + AtkObjectToAXPlatformNodeAuraLinux(atk_object); + if (!obj) + return nullptr; + + return obj->GetParent(); +} + +static gint ax_platform_node_auralinux_get_n_children(AtkObject* atk_object) { + ui::AXPlatformNodeAuraLinux* obj = + AtkObjectToAXPlatformNodeAuraLinux(atk_object); + if (!obj) + return 0; + + return obj->GetChildCount(); +} + +static AtkObject* ax_platform_node_auralinux_ref_child( + AtkObject* atk_object, gint index) { + ui::AXPlatformNodeAuraLinux* obj = + AtkObjectToAXPlatformNodeAuraLinux(atk_object); + if (!obj) + return nullptr; + + AtkObject* result = obj->ChildAtIndex(index); + if (result) + g_object_ref(result); + return result; +} + +static AtkRole ax_platform_node_auralinux_get_role(AtkObject* atk_object) { + ui::AXPlatformNodeAuraLinux* obj = + AtkObjectToAXPlatformNodeAuraLinux(atk_object); + if (!obj) + return ATK_ROLE_INVALID; + return obj->GetAtkRole(); +} + +// +// The rest of the AXPlatformNodeAuraLinux code, not specific to one +// of the Atk* interfaces. +// + +static gpointer ax_platform_node_auralinux_parent_class = nullptr; + +static void ax_platform_node_auralinux_init(AtkObject* atk_object, + gpointer data) { + if (ATK_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)->initialize) { + ATK_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)->initialize( + atk_object, data); + } + + AX_PLATFORM_NODE_AURALINUX(atk_object)->m_object = + reinterpret_cast<ui::AXPlatformNodeAuraLinux*>(data); +} + +static void ax_platform_node_auralinux_finalize(GObject* atk_object) { + G_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)->finalize(atk_object); +} + +static void ax_platform_node_auralinux_class_init(AtkObjectClass* klass) { + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + ax_platform_node_auralinux_parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = ax_platform_node_auralinux_finalize; + klass->initialize = ax_platform_node_auralinux_init; + klass->get_name = ax_platform_node_auralinux_get_name; + klass->get_description = ax_platform_node_auralinux_get_description; + klass->get_parent = ax_platform_node_auralinux_get_parent; + klass->get_n_children = ax_platform_node_auralinux_get_n_children; + klass->ref_child = ax_platform_node_auralinux_ref_child; + klass->get_role = ax_platform_node_auralinux_get_role; +} + +GType ax_platform_node_auralinux_get_type() { + static volatile gsize type_volatile = 0; + + if (g_once_init_enter(&type_volatile)) { + static const GTypeInfo tinfo = { + sizeof(AXPlatformNodeAuraLinuxClass), + (GBaseInitFunc) 0, + (GBaseFinalizeFunc) 0, + (GClassInitFunc) ax_platform_node_auralinux_class_init, + (GClassFinalizeFunc) 0, + 0, /* class data */ + sizeof(AXPlatformNodeAuraLinuxObject), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) 0, + 0 /* value table */ + }; + + GType type = g_type_register_static( + ATK_TYPE_OBJECT, "AXPlatformNodeAuraLinux", &tinfo, GTypeFlags(0)); + g_once_init_leave(&type_volatile, type); + } + + return type_volatile; +} + +AXPlatformNodeAuraLinuxObject* ax_platform_node_auralinux_new( + ui::AXPlatformNodeAuraLinux* obj) { + #if !GLIB_CHECK_VERSION(2, 36, 0) + static bool first_time = true; + if (first_time) { + g_type_init(); + first_time = false; + } + #endif + + GType type = AX_PLATFORM_NODE_AURALINUX_TYPE; + AtkObject* atk_object = static_cast<AtkObject*>(g_object_new(type, 0)); + atk_object_initialize(atk_object, obj); + return AX_PLATFORM_NODE_AURALINUX(atk_object); +} + +void ax_platform_node_auralinux_detach( + AXPlatformNodeAuraLinuxObject* atk_object) { + atk_object->m_object = nullptr; +} + +G_END_DECLS + +// +// AXPlatformNodeAuraLinux implementation. +// + +namespace ui { + +// static +AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { + AXPlatformNodeAuraLinux* node = new AXPlatformNodeAuraLinux(); + node->Init(delegate); + return node; +} + +// static +AXPlatformNode* AXPlatformNodeAuraLinux::application_ = nullptr; + +// static +void AXPlatformNodeAuraLinux::SetApplication(AXPlatformNode* application) { + application_ = application; + AtkUtilAuraLinux::GetInstance(); +} + +AtkRole AXPlatformNodeAuraLinux::GetAtkRole() { + switch (GetData().role) { + case ui::AX_ROLE_APPLICATION: + return ATK_ROLE_APPLICATION; + case ui::AX_ROLE_BUTTON: + return ATK_ROLE_PUSH_BUTTON; + case ui::AX_ROLE_CHECK_BOX: + return ATK_ROLE_CHECK_BOX; + case ui::AX_ROLE_COMBO_BOX: + return ATK_ROLE_COMBO_BOX; + case ui::AX_ROLE_STATIC_TEXT: + return ATK_ROLE_TEXT; + case ui::AX_ROLE_TEXT_FIELD: + return ATK_ROLE_ENTRY; + case ui::AX_ROLE_WINDOW: + return ATK_ROLE_WINDOW; + default: + return ATK_ROLE_UNKNOWN; + } +} + +AXPlatformNodeAuraLinux::AXPlatformNodeAuraLinux() + : atk_object_(nullptr) { +} + +AXPlatformNodeAuraLinux::~AXPlatformNodeAuraLinux() { + g_object_unref(atk_object_); +} + +void AXPlatformNodeAuraLinux::Init(AXPlatformNodeDelegate* delegate) { + // Initialize ATK. + AXPlatformNodeBase::Init(delegate); + atk_object_ = ATK_OBJECT(ax_platform_node_auralinux_new(this)); +} + +void AXPlatformNodeAuraLinux::Destroy() { + delegate_ = nullptr; + delete this; +} + +gfx::NativeViewAccessible AXPlatformNodeAuraLinux::GetNativeViewAccessible() { + return atk_object_; +} + +void AXPlatformNodeAuraLinux::NotifyAccessibilityEvent(ui::AXEvent event_type) { +} + +int AXPlatformNodeAuraLinux::GetIndexInParent() { + return 0; +} + +} // namespace ui diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h new file mode 100644 index 0000000..eea1427 --- /dev/null +++ b/ui/accessibility/platform/ax_platform_node_auralinux.h @@ -0,0 +1,51 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_ACCESSIBILITY_AX_PLATFORM_NODE_AURALINUX_H_ +#define UI_ACCESSIBILITY_AX_PLATFORM_NODE_AURALINUX_H_ + +#include <atk/atk.h> + +#include "ui/accessibility/ax_export.h" +#include "ui/accessibility/platform/ax_platform_node_base.h" + +namespace ui { + +// Implements accessibility on Aura Linux using ATK. +class AXPlatformNodeAuraLinux : public AXPlatformNodeBase { + public: + AXPlatformNodeAuraLinux(); + + // Set or get the root-level Application object that's the parent of all + // top-level windows. + AX_EXPORT static void SetApplication(AXPlatformNode* application); + static AXPlatformNode* application() { return application_; } + + AtkRole GetAtkRole(); + + // AXPlatformNode overrides. + void Destroy() override; + gfx::NativeViewAccessible GetNativeViewAccessible() override; + void NotifyAccessibilityEvent(ui::AXEvent event_type) override; + + // AXPlatformNodeBase overrides. + void Init(AXPlatformNodeDelegate* delegate) override; + int GetIndexInParent() override; + + private: + ~AXPlatformNodeAuraLinux() override; + + // We own a reference to this ref-counted object. + AtkObject* atk_object_; + + // The root-level Application object that's the parent of all + // top-level windows. + static AXPlatformNode* application_; + + DISALLOW_COPY_AND_ASSIGN(AXPlatformNodeAuraLinux); +}; + +} // namespace ui + +#endif // UI_ACCESSIBILITY_AX_PLATFORM_NODE_AURALINUX_H_ diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h index 7555ec0..324b239 100644 --- a/ui/accessibility/platform/ax_platform_node_base.h +++ b/ui/accessibility/platform/ax_platform_node_base.h @@ -61,6 +61,8 @@ class AXPlatformNodeBase : public AXPlatformNode { base::string16 GetString16Attribute( ui::AXStringAttribute attribute) const; + AXPlatformNodeDelegate* delegate_; // Weak. Owns this. + protected: AXPlatformNodeBase(); ~AXPlatformNodeBase() override; @@ -70,7 +72,7 @@ class AXPlatformNodeBase : public AXPlatformNode { static AXPlatformNodeBase* FromNativeViewAccessible( gfx::NativeViewAccessible accessible); - AXPlatformNodeDelegate* delegate_; // Weak. Owns this. + private: DISALLOW_COPY_AND_ASSIGN(AXPlatformNodeBase); diff --git a/ui/gfx/native_widget_types.h b/ui/gfx/native_widget_types.h index 8cf8a5f..1a8256b 100644 --- a/ui/gfx/native_widget_types.h +++ b/ui/gfx/native_widget_types.h @@ -106,6 +106,13 @@ class ViewAndroid; #endif class SkBitmap; +#if defined(USE_X11) && !defined(OS_CHROMEOS) +extern "C" { +struct _AtkObject; +typedef struct _AtkObject AtkObject; +} +#endif + namespace gfx { #if defined(USE_AURA) @@ -164,8 +171,12 @@ typedef cairo_t* NativeDrawingContext; #else typedef void* NativeDrawingContext; #endif // defined(USE_CAIRO) +#if defined(USE_X11) && !defined(OS_CHROMEOS) +typedef AtkObject* NativeViewAccessible; +#else typedef void* NativeViewAccessible; #endif +#endif // A constant value to indicate that gfx::NativeCursor refers to no cursor. #if defined(USE_AURA) diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index 922ce0c..0ed3920 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn @@ -102,6 +102,7 @@ component("views") { sources += gypi_values.views_desktop_aura_sources if (use_x11) { sources += gypi_values.views_desktop_aura_x11_sources + configs += [ "//build/config/linux:atk" ] } else if (is_win) { sources += gypi_values.views_desktop_aura_win_sources } else if (use_ozone) { diff --git a/ui/views/accessibility/native_view_accessibility.cc b/ui/views/accessibility/native_view_accessibility.cc index aeb2c70..a11f773 100644 --- a/ui/views/accessibility/native_view_accessibility.cc +++ b/ui/views/accessibility/native_view_accessibility.cc @@ -13,7 +13,7 @@ namespace views { -#if !defined(OS_WIN) +#if !defined(OS_WIN) && !(defined(OS_LINUX) && !defined(OS_CHROMEOS)) // static NativeViewAccessibility* NativeViewAccessibility::Create(View* view) { return new NativeViewAccessibility(view); @@ -23,7 +23,8 @@ NativeViewAccessibility* NativeViewAccessibility::Create(View* view) { NativeViewAccessibility::NativeViewAccessibility(View* view) : view_(view), parent_widget_(nullptr), - ax_node_(ui::AXPlatformNode::Create(this)) { + ax_node_(nullptr) { + ax_node_ = ui::AXPlatformNode::Create(this); } NativeViewAccessibility::~NativeViewAccessibility() { diff --git a/ui/views/accessibility/native_view_accessibility_auralinux.cc b/ui/views/accessibility/native_view_accessibility_auralinux.cc new file mode 100644 index 0000000..afee535 --- /dev/null +++ b/ui/views/accessibility/native_view_accessibility_auralinux.cc @@ -0,0 +1,164 @@ +// Copyright 2015 The Chromium 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/views/accessibility/native_view_accessibility_auralinux.h" + +#include <algorithm> +#include <vector> + +#include "base/memory/singleton.h" +#include "ui/accessibility/ax_enums.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/platform/ax_platform_node_auralinux.h" +#include "ui/accessibility/platform/ax_platform_node_delegate.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/views/views_delegate.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" + +namespace views { + +namespace { + +// ATK requires that we have a single root "application" object that's the +// owner of all other windows. This is a simple class that implements the +// AXPlatformNodeDelegate interface so we can create such an application +// object. Every time we create an accessibility object for a View, we add its +// top-level widget to a vector so we can return the list of all top-level +// windows as children of this application object. +class AuraLinuxApplication + : public ui::AXPlatformNodeDelegate, + public WidgetObserver { + public: + // Get the single instance of this class. + static AuraLinuxApplication* GetInstance() { + return Singleton<AuraLinuxApplication>::get(); + } + + // Called every time we create a new accessibility on a View. + // Add the top-level widget to our registry so that we can enumerate all + // top-level widgets. + void RegisterWidget(Widget* widget) { + if (!widget) + return; + + widget = widget->GetTopLevelWidget(); + if (std::find(widgets_.begin(), widgets_.end(), widget) != widgets_.end()) + return; + + widgets_.push_back(widget); + widget->AddObserver(this); + } + + gfx::NativeViewAccessible GetNativeViewAccessible() { + return platform_node_->GetNativeViewAccessible(); + } + + // + // WidgetObserver overrides. + // + + void OnWidgetDestroying(Widget* widget) override { + auto iter = std::find(widgets_.begin(), widgets_.end(), widget); + if (iter != widgets_.end()) + widgets_.erase(iter); + } + + // + // ui::AXPlatformNodeDelegate overrides. + // + + const ui::AXNodeData& GetData() override { + return data_; + } + + gfx::NativeViewAccessible GetParent() override { + return nullptr; + } + + int GetChildCount() override { + return static_cast<int>(widgets_.size()); + } + + gfx::NativeViewAccessible ChildAtIndex(int index) override { + if (index < 0 || index >= GetChildCount()) + return nullptr; + + Widget* widget = widgets_[index]; + CHECK(widget); + return widget->GetRootView()->GetNativeViewAccessible(); + } + + gfx::Vector2d GetGlobalCoordinateOffset() override { + return gfx::Vector2d(); + } + + gfx::NativeViewAccessible HitTestSync(int x, int y) override { + return nullptr; + } + + gfx::NativeViewAccessible GetFocus() override { + return nullptr; + } + + gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override { + return gfx::kNullAcceleratedWidget; + } + + void DoDefaultAction() override { + } + + bool SetStringValue(const base::string16& new_value) override { + return false; + } + + private: + friend struct DefaultSingletonTraits<AuraLinuxApplication>; + + AuraLinuxApplication() + : platform_node_(ui::AXPlatformNode::Create(this)) { + data_.role = ui::AX_ROLE_APPLICATION; + if (ViewsDelegate::views_delegate) { + data_.AddStringAttribute( + ui::AX_ATTR_NAME, + ViewsDelegate::views_delegate->GetApplicationName()); + } + ui::AXPlatformNodeAuraLinux::SetApplication(platform_node_); + } + + ~AuraLinuxApplication() override { + platform_node_->Destroy(); + platform_node_ = nullptr; + } + + ui::AXPlatformNode* platform_node_; + ui::AXNodeData data_; + std::vector<Widget*> widgets_; + + DISALLOW_COPY_AND_ASSIGN(AuraLinuxApplication); +}; + +} // namespace + +// static +NativeViewAccessibility* NativeViewAccessibility::Create(View* view) { + AuraLinuxApplication::GetInstance()->RegisterWidget(view->GetWidget()); + return new NativeViewAccessibilityAuraLinux(view); +} + +NativeViewAccessibilityAuraLinux::NativeViewAccessibilityAuraLinux(View* view) + : NativeViewAccessibility(view) { +} + +NativeViewAccessibilityAuraLinux::~NativeViewAccessibilityAuraLinux() { +} + +gfx::NativeViewAccessible NativeViewAccessibilityAuraLinux::GetParent() { + gfx::NativeViewAccessible parent = NativeViewAccessibility::GetParent(); + if (!parent) + parent = AuraLinuxApplication::GetInstance()->GetNativeViewAccessible(); + return parent; +} + +} // namespace views diff --git a/ui/views/accessibility/native_view_accessibility_auralinux.h b/ui/views/accessibility/native_view_accessibility_auralinux.h new file mode 100644 index 0000000..f817dc7 --- /dev/null +++ b/ui/views/accessibility/native_view_accessibility_auralinux.h @@ -0,0 +1,27 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_AURALINUX_H_ +#define UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_AURALINUX_H_ + +#include "ui/views/accessibility/native_view_accessibility.h" +#include "ui/views/view.h" + +namespace views { + +class NativeViewAccessibilityAuraLinux : public NativeViewAccessibility { + public: + NativeViewAccessibilityAuraLinux(View* view); + ~NativeViewAccessibilityAuraLinux() override; + + // NativeViewAccessibility. + gfx::NativeViewAccessible GetParent() override; + + private: + DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityAuraLinux); +}; + +} // namespace views + +#endif // UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_AURALINUX_H_ diff --git a/ui/views/views.gyp b/ui/views/views.gyp index 0b92a2c..e73f361 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -423,6 +423,8 @@ 'widget/desktop_aura/desktop_cursor_loader_updater_auralinux.h', ], 'views_desktop_aura_x11_sources': [ + 'accessibility/native_view_accessibility_auralinux.cc', + 'accessibility/native_view_accessibility_auralinux.h', 'widget/desktop_aura/desktop_drag_drop_client_aurax11.cc', 'widget/desktop_aura/desktop_drag_drop_client_aurax11.h', 'widget/desktop_aura/desktop_screen_x11.cc', @@ -635,6 +637,7 @@ }], ['OS=="linux" and chromeos==0', { 'dependencies': [ + '../../build/linux/system.gyp:atk', '../shell_dialogs/shell_dialogs.gyp:shell_dialogs', ], 'sources!': [ diff --git a/ui/views/views_delegate.cc b/ui/views/views_delegate.cc index de9ccbb..4c1ae4f 100644 --- a/ui/views/views_delegate.cc +++ b/ui/views/views_delegate.cc @@ -4,6 +4,7 @@ #include "ui/views/views_delegate.h" +#include "base/command_line.h" #include "ui/views/views_touch_selection_controller_factory.h" namespace views { @@ -85,6 +86,11 @@ ui::ContextFactory* ViewsDelegate::GetContextFactory() { return NULL; } +std::string ViewsDelegate::GetApplicationName() { + base::FilePath program = base::CommandLine::ForCurrentProcess()->GetProgram(); + return program.BaseName().AsUTF8Unsafe(); +} + #if defined(OS_WIN) int ViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor, const base::Closure& callback) { diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h index 6cc5a6b..ea14e78 100644 --- a/ui/views/views_delegate.h +++ b/ui/views/views_delegate.h @@ -135,6 +135,9 @@ class VIEWS_EXPORT ViewsDelegate { // Returns the context factory for new windows. virtual ui::ContextFactory* GetContextFactory(); + // Returns the user-visible name of the application. + virtual std::string GetApplicationName(); + #if defined(OS_WIN) // Starts a query for the appbar autohide edges of the specified monitor and // returns the current value. If the query finds the edges have changed from |