summaryrefslogtreecommitdiff
path: root/chromium/content/browser/accessibility
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/content/browser/accessibility
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/content/browser/accessibility')
-rw-r--r--chromium/content/browser/accessibility/OWNERS2
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter.cc197
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter.h153
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc152
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_gtk.cc108
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm250
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc265
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.h27
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc319
-rw-r--r--chromium/content/browser/accessibility/accessibility_ui.cc253
-rw-r--r--chromium/content/browser/accessibility/accessibility_ui.h31
-rw-r--r--chromium/content/browser/accessibility/accessibility_win_browsertest.cc883
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility.cc341
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility.h296
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_android.cc406
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_android.h74
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_cocoa.h112
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_cocoa.mm1505
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_delegate_mac.h26
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_gtk.cc519
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_gtk.h96
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_mac.h51
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_mac.mm71
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm165
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager.cc419
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager.h236
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_android.cc366
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_android.h92
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc84
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h48
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_mac.h43
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm121
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc603
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_win.cc203
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_win.h82
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_state_impl.cc147
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_state_impl.h76
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc77
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_win.cc3626
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_win.h899
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc660
-rw-r--r--chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc465
-rw-r--r--chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc456
43 files changed, 15005 insertions, 0 deletions
diff --git a/chromium/content/browser/accessibility/OWNERS b/chromium/content/browser/accessibility/OWNERS
new file mode 100644
index 00000000000..11e8fd837ee
--- /dev/null
+++ b/chromium/content/browser/accessibility/OWNERS
@@ -0,0 +1,2 @@
+dmazzoni@chromium.org
+dtseng@chromium.org
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter.cc
new file mode 100644
index 00000000000..4368cf13217
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/accessibility_tree_formatter.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+
+namespace content {
+namespace {
+const int kIndentSpaces = 4;
+const char* kSkipString = "@NO_DUMP";
+const char* kChildrenDictAttr = "children";
+}
+
+AccessibilityTreeFormatter::AccessibilityTreeFormatter(
+ BrowserAccessibility* root)
+ : root_(root) {
+ Initialize();
+}
+
+// static
+AccessibilityTreeFormatter* AccessibilityTreeFormatter::Create(
+ RenderViewHost* rvh) {
+ RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>(
+ WebContents::FromRenderViewHost(rvh)->GetRenderWidgetHostView());
+
+ BrowserAccessibilityManager* manager =
+ host_view->GetBrowserAccessibilityManager();
+ if (!manager)
+ return NULL;
+
+ BrowserAccessibility* root = manager->GetRoot();
+ return new AccessibilityTreeFormatter(root);
+}
+
+
+AccessibilityTreeFormatter::~AccessibilityTreeFormatter() {
+}
+
+scoped_ptr<base::DictionaryValue>
+AccessibilityTreeFormatter::BuildAccessibilityTree() {
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ RecursiveBuildAccessibilityTree(*root_, dict.get());
+ return dict.Pass();
+}
+
+void AccessibilityTreeFormatter::FormatAccessibilityTree(
+ string16* contents) {
+ scoped_ptr<base::DictionaryValue> dict = BuildAccessibilityTree();
+ RecursiveFormatAccessibilityTree(*(dict.get()), contents);
+}
+
+void AccessibilityTreeFormatter::RecursiveBuildAccessibilityTree(
+ const BrowserAccessibility& node, base::DictionaryValue* dict) {
+ AddProperties(node, dict);
+
+ base::ListValue* children = new base::ListValue;
+ dict->Set(kChildrenDictAttr, children);
+ if (!IncludeChildren(node))
+ return;
+
+ for (size_t i = 0; i < node.children().size(); ++i) {
+ BrowserAccessibility* child_node = node.children()[i];
+ base::DictionaryValue* child_dict = new base::DictionaryValue;
+ children->Append(child_dict);
+ RecursiveBuildAccessibilityTree(*child_node, child_dict);
+ }
+}
+
+void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree(
+ const base::DictionaryValue& dict, string16* contents, int depth) {
+ string16 line = ToString(dict, string16(depth * kIndentSpaces, ' '));
+ if (line.find(ASCIIToUTF16(kSkipString)) != string16::npos)
+ return;
+
+ *contents += line;
+ const base::ListValue* children;
+ dict.GetList(kChildrenDictAttr, &children);
+ const base::DictionaryValue* child_dict;
+ for (size_t i = 0; i < children->GetSize(); i++) {
+ children->GetDictionary(i, &child_dict);
+ RecursiveFormatAccessibilityTree(*child_dict, contents, depth + 1);
+ }
+}
+
+#if !defined(OS_ANDROID)
+bool AccessibilityTreeFormatter::IncludeChildren(
+ const BrowserAccessibility& node) {
+ return true;
+}
+#endif
+
+#if (!defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
+ !defined(TOOLKIT_GTK))
+void AccessibilityTreeFormatter::AddProperties(const BrowserAccessibility& node,
+ base::DictionaryValue* dict) {
+ dict->SetInteger("id", node.renderer_id());
+}
+
+string16 AccessibilityTreeFormatter::ToString(const base::DictionaryValue& node,
+ const string16& indent) {
+ int id_value;
+ node.GetInteger("id", &id_value);
+ return indent + base::IntToString16(id_value) +
+ ASCIIToUTF16("\n");
+}
+
+void AccessibilityTreeFormatter::Initialize() {}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetActualFileSuffix() {
+ return base::FilePath::StringType();
+}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetExpectedFileSuffix() {
+ return base::FilePath::StringType();
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetAllowEmptyString() {
+ return std::string();
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetAllowString() {
+ return std::string();
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetDenyString() {
+ return std::string();
+}
+#endif
+
+void AccessibilityTreeFormatter::SetFilters(
+ const std::vector<Filter>& filters) {
+ filters_ = filters;
+}
+
+bool AccessibilityTreeFormatter::MatchesFilters(
+ const string16& text, bool default_result) const {
+ std::vector<Filter>::const_iterator iter = filters_.begin();
+ bool allow = default_result;
+ for (iter = filters_.begin(); iter != filters_.end(); ++iter) {
+ if (MatchPattern(text, iter->match_str)) {
+ if (iter->type == Filter::ALLOW_EMPTY)
+ allow = true;
+ else if (iter->type == Filter::ALLOW)
+ allow = (!MatchPattern(text, UTF8ToUTF16("*=''")));
+ else
+ allow = false;
+ }
+ }
+ return allow;
+}
+
+string16 AccessibilityTreeFormatter::FormatCoordinates(
+ const char* name, const char* x_name, const char* y_name,
+ const base::DictionaryValue& value) {
+ int x, y;
+ value.GetInteger(x_name, &x);
+ value.GetInteger(y_name, &y);
+ std::string xy_str(base::StringPrintf("%s=(%d, %d)", name, x, y));
+
+ return UTF8ToUTF16(xy_str);
+}
+
+void AccessibilityTreeFormatter::WriteAttribute(
+ bool include_by_default, const std::string& attr, string16* line) {
+ WriteAttribute(include_by_default, UTF8ToUTF16(attr), line);
+}
+
+void AccessibilityTreeFormatter::WriteAttribute(
+ bool include_by_default, const string16& attr, string16* line) {
+ if (attr.empty())
+ return;
+ if (!MatchesFilters(attr, include_by_default))
+ return;
+ if (!line->empty())
+ *line += ASCIIToUTF16(" ");
+ *line += attr;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter.h b/chromium/content/browser/accessibility/accessibility_tree_formatter.h
new file mode 100644
index 00000000000..3c36b2f6e52
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter.h
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_H_
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class RenderViewHost;
+
+// A utility class for formatting platform-specific accessibility information,
+// for use in testing, debugging, and developer tools.
+// This is extended by a subclass for each platform where accessibility is
+// implemented.
+class CONTENT_EXPORT AccessibilityTreeFormatter {
+ public:
+ explicit AccessibilityTreeFormatter(BrowserAccessibility* root);
+ virtual ~AccessibilityTreeFormatter();
+
+ static AccessibilityTreeFormatter* Create(RenderViewHost* rvh);
+
+ // Populates the given DictionaryValue with the accessibility tree.
+ // The dictionary contains a key/value pair for each attribute of the node,
+ // plus a "children" attribute containing a list of all child nodes.
+ // {
+ // "AXName": "node", /* actual attributes will vary by platform */
+ // "position": { /* some attributes may be dictionaries */
+ // "x": 0,
+ // "y": 0
+ // },
+ // /* ... more attributes of |node| */
+ // "children": [ { /* list of children created recursively */
+ // "AXName": "child node 1",
+ // /* ... more attributes */
+ // "children": [ ]
+ // }, {
+ // "AXName": "child name 2",
+ // /* ... more attributes */
+ // "children": [ ]
+ // } ]
+ // }
+ scoped_ptr<base::DictionaryValue> BuildAccessibilityTree();
+
+ // Dumps a BrowserAccessibility tree into a string.
+ void FormatAccessibilityTree(string16* contents);
+
+ // A single filter specification. See GetAllowString() and GetDenyString()
+ // for more information.
+ struct Filter {
+ enum Type {
+ ALLOW,
+ ALLOW_EMPTY,
+ DENY
+ };
+ string16 match_str;
+ Type type;
+
+ Filter(string16 match_str, Type type)
+ : match_str(match_str), type(type) {}
+ };
+
+ // Set regular expression filters that apply to each component of every
+ // line before it's output.
+ void SetFilters(const std::vector<Filter>& filters);
+
+ // Suffix of the expectation file corresponding to html file.
+ // Example:
+ // HTML test: test-file.html
+ // Expected: test-file-expected-mac.txt.
+ // Auto-generated: test-file-actual-mac.txt
+ static const base::FilePath::StringType GetActualFileSuffix();
+ static const base::FilePath::StringType GetExpectedFileSuffix();
+
+ // A platform-specific string that indicates a given line in a file
+ // is an allow-empty, allow or deny filter. Example:
+ // Mac values:
+ // GetAllowEmptyString() -> "@MAC-ALLOW-EMPTY:"
+ // GetAllowString() -> "@MAC-ALLOW:"
+ // GetDenyString() -> "@MAC-DENY:"
+ // Example html:
+ // <!--
+ // @MAC-ALLOW-EMPTY:description*
+ // @MAC-ALLOW:roleDescription*
+ // @MAC-DENY:subrole*
+ // -->
+ // <p>Text</p>
+ static const std::string GetAllowEmptyString();
+ static const std::string GetAllowString();
+ static const std::string GetDenyString();
+
+ protected:
+ void RecursiveFormatAccessibilityTree(const BrowserAccessibility& node,
+ string16* contents,
+ int indent);
+ void RecursiveBuildAccessibilityTree(const BrowserAccessibility& node,
+ base::DictionaryValue* tree_node);
+ void RecursiveFormatAccessibilityTree(const base::DictionaryValue& tree_node,
+ string16* contents,
+ int depth = 0);
+
+ // Overridden by each platform to add the required attributes for each node
+ // into the given dict.
+ void AddProperties(const BrowserAccessibility& node,
+ base::DictionaryValue* dict);
+
+ // Returns true by default; can be overridden by the platform to
+ // prune some children from the tree when they wouldn't be exposed
+ // natively on that platform.
+ virtual bool IncludeChildren(const BrowserAccessibility& node);
+
+ string16 FormatCoordinates(const char* name,
+ const char* x_name,
+ const char* y_name,
+ const base::DictionaryValue& value);
+
+ // Returns a platform specific representation of a BrowserAccessibility.
+ // Should be zero or more complete lines, each with |prefix| prepended
+ // (to indent each line).
+ string16 ToString(const base::DictionaryValue& node, const string16& indent);
+
+ void Initialize();
+
+ bool MatchesFilters(const string16& text, bool default_result) const;
+
+ // Writes the given attribute string out to |line| if it matches the filters.
+ void WriteAttribute(bool include_by_default,
+ const string16& attr,
+ string16* line);
+ void WriteAttribute(bool include_by_default,
+ const std::string& attr,
+ string16* line);
+
+ BrowserAccessibility* root_;
+
+ // Filters used when formatting the accessibility tree as text.
+ std::vector<Filter> filters_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccessibilityTreeFormatter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_H_
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc
new file mode 100644
index 00000000000..11f4be64627
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc
@@ -0,0 +1,152 @@
+// Copyright 2013 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 "content/browser/accessibility/accessibility_tree_formatter.h"
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/files/file_path.h"
+#include "base/json/json_writer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility_android.h"
+#include "content/common/accessibility_node_data.h"
+
+using base::StringPrintf;
+
+namespace content {
+
+namespace {
+const char* BOOL_ATTRIBUTES[] = {
+ "checkable",
+ "checked",
+ "clickable",
+ "disabled",
+ "editable_text",
+ "focusable",
+ "focused",
+ "invisible",
+ "password",
+ "scrollable",
+ "selected"
+};
+
+const char* STRING_ATTRIBUTES[] = {
+ "name"
+};
+
+const char* INT_ATTRIBUTES[] = {
+ "item_index",
+ "item_count"
+};
+}
+
+void AccessibilityTreeFormatter::Initialize() {
+}
+
+void AccessibilityTreeFormatter::AddProperties(
+ const BrowserAccessibility& node, DictionaryValue* dict) {
+ const BrowserAccessibilityAndroid* android_node =
+ static_cast<const BrowserAccessibilityAndroid*>(&node);
+
+ // Class name.
+ dict->SetString("class", android_node->GetClassName());
+
+ // Bool attributes.
+ dict->SetBoolean("focusable", android_node->IsFocusable());
+ dict->SetBoolean("focused", android_node->IsFocused());
+ dict->SetBoolean("clickable", android_node->IsClickable());
+ dict->SetBoolean("editable_text", android_node->IsEditableText());
+ dict->SetBoolean("checkable", android_node->IsCheckable());
+ dict->SetBoolean("checked", android_node->IsChecked());
+ dict->SetBoolean("disabled", !android_node->IsEnabled());
+ dict->SetBoolean("scrollable", android_node->IsScrollable());
+ dict->SetBoolean("password", android_node->IsPassword());
+ dict->SetBoolean("selected", android_node->IsSelected());
+ dict->SetBoolean("invisible", !android_node->IsVisibleToUser());
+
+ // String attributes.
+ dict->SetString("name", android_node->GetText());
+
+ // Int attributes.
+ dict->SetInteger("item_index", android_node->GetItemIndex());
+ dict->SetInteger("item_count", android_node->GetItemCount());
+}
+
+bool AccessibilityTreeFormatter::IncludeChildren(
+ const BrowserAccessibility& node) {
+ const BrowserAccessibilityAndroid* android_node =
+ static_cast<const BrowserAccessibilityAndroid*>(&node);
+ return !android_node->IsLeaf();
+}
+
+string16 AccessibilityTreeFormatter::ToString(const DictionaryValue& dict,
+ const string16& indent) {
+ string16 line;
+
+ string16 class_value;
+ dict.GetString("class", &class_value);
+ WriteAttribute(true, UTF16ToUTF8(class_value), &line);
+
+ for (unsigned i = 0; i < arraysize(BOOL_ATTRIBUTES); i++) {
+ const char* attribute_name = BOOL_ATTRIBUTES[i];
+ bool value;
+ if (dict.GetBoolean(attribute_name, &value) && value)
+ WriteAttribute(true, attribute_name, &line);
+ }
+
+ for (unsigned i = 0; i < arraysize(STRING_ATTRIBUTES); i++) {
+ const char* attribute_name = STRING_ATTRIBUTES[i];
+ std::string value;
+ if (!dict.GetString(attribute_name, &value) || value.empty())
+ continue;
+ WriteAttribute(true,
+ StringPrintf("%s='%s'", attribute_name, value.c_str()),
+ &line);
+ }
+
+ for (unsigned i = 0; i < arraysize(INT_ATTRIBUTES); i++) {
+ const char* attribute_name = INT_ATTRIBUTES[i];
+ int value;
+ if (!dict.GetInteger(attribute_name, &value) || value == 0)
+ continue;
+ WriteAttribute(true,
+ StringPrintf("%s=%d", attribute_name, value),
+ &line);
+ }
+
+ return indent + line + ASCIIToUTF16("\n");
+}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetActualFileSuffix() {
+ return FILE_PATH_LITERAL("-actual-android.txt");
+}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetExpectedFileSuffix() {
+ return FILE_PATH_LITERAL("-expected-android.txt");
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetAllowEmptyString() {
+ return "@ANDROID-ALLOW-EMPTY:";
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetAllowString() {
+ return "@ANDROID-ALLOW:";
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetDenyString() {
+ return "@ANDROID-DENY:";
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_gtk.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_gtk.cc
new file mode 100644
index 00000000000..8dd6b9328a7
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_gtk.cc
@@ -0,0 +1,108 @@
+// Copyright 2013 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 "content/browser/accessibility/accessibility_tree_formatter.h"
+
+#include <atk/atk.h>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility_gtk.h"
+
+namespace content {
+
+void AccessibilityTreeFormatter::AddProperties(const BrowserAccessibility& node,
+ base::DictionaryValue* dict) {
+ BrowserAccessibilityGtk* node_gtk =
+ const_cast<BrowserAccessibility*>(&node)->ToBrowserAccessibilityGtk();
+ AtkObject* atk_object = node_gtk->GetAtkObject();
+ AtkRole role = atk_object_get_role(atk_object);
+ if (role != ATK_ROLE_UNKNOWN)
+ dict->SetString("role", atk_role_get_name(role));
+ dict->SetString("name", atk_object_get_name(atk_object));
+ dict->SetString("description", atk_object_get_description(atk_object));
+ AtkStateSet* state_set =
+ atk_object_ref_state_set(atk_object);
+ ListValue* states = new base::ListValue;
+ for (int i = ATK_STATE_INVALID; i < ATK_STATE_LAST_DEFINED; i++) {
+ AtkStateType state_type = static_cast<AtkStateType>(i);
+ if (atk_state_set_contains_state(state_set, state_type))
+ states->AppendString(atk_state_type_get_name(state_type));
+ }
+ dict->Set("states", states);
+ dict->SetInteger("id", node.renderer_id());
+}
+
+string16 AccessibilityTreeFormatter::ToString(const base::DictionaryValue& node,
+ const string16& indent) {
+ string16 line;
+ std::string role_value;
+ node.GetString("role", &role_value);
+ if (!role_value.empty())
+ WriteAttribute(true, base::StringPrintf("[%s]", role_value.c_str()), &line);
+
+ std::string name_value;
+ node.GetString("name", &name_value);
+ WriteAttribute(true, base::StringPrintf("name='%s'", name_value.c_str()),
+ &line);
+
+ std::string description_value;
+ node.GetString("description", &description_value);
+ WriteAttribute(false,
+ base::StringPrintf("description='%s'",
+ description_value.c_str()),
+ &line);
+
+ const base::ListValue* states_value;
+ node.GetList("states", &states_value);
+ for (base::ListValue::const_iterator it = states_value->begin();
+ it != states_value->end();
+ ++it) {
+ std::string state_value;
+ if ((*it)->GetAsString(&state_value))
+ WriteAttribute(true, state_value, &line);
+ }
+
+ int id_value;
+ node.GetInteger("id", &id_value);
+ WriteAttribute(false,
+ base::StringPrintf("id=%d", id_value),
+ &line);
+
+ return indent + line + ASCIIToUTF16("\n");
+}
+
+void AccessibilityTreeFormatter::Initialize() {}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetActualFileSuffix() {
+ return FILE_PATH_LITERAL("-actual-gtk.txt");
+}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetExpectedFileSuffix() {
+ return FILE_PATH_LITERAL("-expected-gtk.txt");
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetAllowEmptyString() {
+ return "@GTK-ALLOW-EMPTY:";
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetAllowString() {
+ return "@GTK-ALLOW:";
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetDenyString() {
+ return "@GTK-DENY:";
+}
+
+}
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm
new file mode 100644
index 00000000000..58881b4af4b
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -0,0 +1,250 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/accessibility_tree_formatter.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/json/json_writer.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility_cocoa.h"
+#include "content/browser/accessibility/browser_accessibility_mac.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+
+using base::StringPrintf;
+using base::SysNSStringToUTF8;
+using base::SysNSStringToUTF16;
+using std::string;
+
+namespace content {
+
+namespace {
+
+const char* kPositionDictAttr = "position";
+const char* kXCoordDictAttr = "x";
+const char* kYCoordDictAttr = "y";
+const char* kSizeDictAttr = "size";
+const char* kWidthDictAttr = "width";
+const char* kHeightDictAttr = "height";
+const char* kRangeLocDictAttr = "loc";
+const char* kRangeLenDictAttr = "len";
+
+scoped_ptr<base::DictionaryValue> PopulatePosition(
+ const BrowserAccessibility& node) {
+ scoped_ptr<base::DictionaryValue> position(new base::DictionaryValue);
+ // The NSAccessibility position of an object is in global coordinates and
+ // based on the lower-left corner of the object. To make this easier and less
+ // confusing, convert it to local window coordinates using the top-left
+ // corner when dumping the position.
+ BrowserAccessibility* root = node.manager()->GetRoot();
+ BrowserAccessibilityCocoa* cocoa_root = root->ToBrowserAccessibilityCocoa();
+ NSPoint root_position = [[cocoa_root position] pointValue];
+ NSSize root_size = [[cocoa_root size] sizeValue];
+ int root_top = -static_cast<int>(root_position.y + root_size.height);
+ int root_left = static_cast<int>(root_position.x);
+
+ BrowserAccessibilityCocoa* cocoa_node =
+ const_cast<BrowserAccessibility*>(&node)->ToBrowserAccessibilityCocoa();
+ NSPoint node_position = [[cocoa_node position] pointValue];
+ NSSize node_size = [[cocoa_node size] sizeValue];
+
+ position->SetInteger(kXCoordDictAttr,
+ static_cast<int>(node_position.x - root_left));
+ position->SetInteger(kYCoordDictAttr,
+ static_cast<int>(-node_position.y - node_size.height - root_top));
+ return position.Pass();
+}
+
+scoped_ptr<base::DictionaryValue>
+PopulateSize(const BrowserAccessibilityCocoa* cocoa_node) {
+ scoped_ptr<base::DictionaryValue> size(new base::DictionaryValue);
+ NSSize node_size = [[cocoa_node size] sizeValue];
+ size->SetInteger(kHeightDictAttr, static_cast<int>(node_size.height));
+ size->SetInteger(kWidthDictAttr, static_cast<int>(node_size.width));
+ return size.Pass();
+}
+
+scoped_ptr<base::DictionaryValue> PopulateRange(NSRange range) {
+ scoped_ptr<base::DictionaryValue> rangeDict(new base::DictionaryValue);
+ rangeDict->SetInteger(kRangeLocDictAttr, static_cast<int>(range.location));
+ rangeDict->SetInteger(kRangeLenDictAttr, static_cast<int>(range.length));
+ return rangeDict.Pass();
+}
+
+// Returns true if |value| is an NSValue containing a NSRange.
+bool IsRangeValue(id value) {
+ if (![value isKindOfClass:[NSValue class]])
+ return false;
+ return 0 == strcmp([value objCType], @encode(NSRange));
+}
+
+NSArray* BuildAllAttributesArray() {
+ NSArray* array = [NSArray arrayWithObjects:
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityValueAttribute,
+ NSAccessibilityMinValueAttribute,
+ NSAccessibilityMaxValueAttribute,
+ NSAccessibilityValueDescriptionAttribute,
+ NSAccessibilityDescriptionAttribute,
+ NSAccessibilityHelpAttribute,
+ @"AXInvalid",
+ NSAccessibilityDisclosingAttribute,
+ NSAccessibilityDisclosureLevelAttribute,
+ @"AXAccessKey",
+ @"AXARIAAtomic",
+ @"AXARIABusy",
+ @"AXARIALive",
+ @"AXARIARelevant",
+ NSAccessibilityColumnIndexRangeAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilityFocusedAttribute,
+ NSAccessibilityIndexAttribute,
+ @"AXLoaded",
+ @"AXLoadingProcess",
+ NSAccessibilityNumberOfCharactersAttribute,
+ NSAccessibilityOrientationAttribute,
+ @"AXRequired",
+ NSAccessibilityRowIndexRangeAttribute,
+ NSAccessibilityURLAttribute,
+ NSAccessibilityVisibleCharacterRangeAttribute,
+ @"AXVisited",
+ nil];
+ return [array retain];
+}
+
+} // namespace
+
+void AccessibilityTreeFormatter::Initialize() {
+}
+
+
+void AccessibilityTreeFormatter::AddProperties(const BrowserAccessibility& node,
+ base::DictionaryValue* dict) {
+ BrowserAccessibilityCocoa* cocoa_node =
+ const_cast<BrowserAccessibility*>(&node)->ToBrowserAccessibilityCocoa();
+ NSArray* supportedAttributes = [cocoa_node accessibilityAttributeNames];
+
+ string role = SysNSStringToUTF8(
+ [cocoa_node accessibilityAttributeValue:NSAccessibilityRoleAttribute]);
+ dict->SetString(SysNSStringToUTF8(NSAccessibilityRoleAttribute), role);
+
+ NSString* subrole =
+ [cocoa_node accessibilityAttributeValue:NSAccessibilitySubroleAttribute];
+ if (subrole != nil) {
+ dict->SetString(SysNSStringToUTF8(NSAccessibilitySubroleAttribute),
+ SysNSStringToUTF8(subrole));
+ }
+
+ CR_DEFINE_STATIC_LOCAL(NSArray*, all_attributes, (BuildAllAttributesArray()));
+ for (NSString* requestedAttribute in all_attributes) {
+ if (![supportedAttributes containsObject:requestedAttribute]) {
+ continue;
+ }
+ id value = [cocoa_node accessibilityAttributeValue:requestedAttribute];
+ if (IsRangeValue(value)) {
+ dict->Set(
+ SysNSStringToUTF8(requestedAttribute),
+ PopulateRange([value rangeValue]).release());
+ } else if (value != nil) {
+ dict->SetString(
+ SysNSStringToUTF8(requestedAttribute),
+ SysNSStringToUTF16([NSString stringWithFormat:@"%@", value]));
+ }
+ }
+ dict->Set(kPositionDictAttr, PopulatePosition(node).release());
+ dict->Set(kSizeDictAttr, PopulateSize(cocoa_node).release());
+}
+
+string16 AccessibilityTreeFormatter::ToString(const base::DictionaryValue& dict,
+ const string16& indent) {
+ string16 line;
+ NSArray* defaultAttributes =
+ [NSArray arrayWithObjects:NSAccessibilityTitleAttribute,
+ NSAccessibilityValueAttribute,
+ nil];
+ string s_value;
+ dict.GetString(SysNSStringToUTF8(NSAccessibilityRoleAttribute), &s_value);
+ WriteAttribute(true, UTF8ToUTF16(s_value), &line);
+
+ string subroleAttribute = SysNSStringToUTF8(NSAccessibilitySubroleAttribute);
+ if (dict.GetString(subroleAttribute, &s_value)) {
+ WriteAttribute(false,
+ StringPrintf("%s=%s",
+ subroleAttribute.c_str(), s_value.c_str()),
+ &line);
+ }
+
+ CR_DEFINE_STATIC_LOCAL(NSArray*, all_attributes, (BuildAllAttributesArray()));
+ for (NSString* requestedAttribute in all_attributes) {
+ string requestedAttributeUTF8 = SysNSStringToUTF8(requestedAttribute);
+ const base::DictionaryValue* d_value;
+ if (dict.GetDictionary(requestedAttributeUTF8, &d_value)) {
+ std::string json_value;
+ base::JSONWriter::Write(d_value, &json_value);
+ WriteAttribute(
+ [defaultAttributes containsObject:requestedAttribute],
+ StringPrintf("%s=%s",
+ requestedAttributeUTF8.c_str(),
+ json_value.c_str()),
+ &line);
+ }
+ if (!dict.GetString(requestedAttributeUTF8, &s_value))
+ continue;
+ WriteAttribute([defaultAttributes containsObject:requestedAttribute],
+ StringPrintf("%s='%s'",
+ requestedAttributeUTF8.c_str(),
+ s_value.c_str()),
+ &line);
+ }
+ const base::DictionaryValue* d_value = NULL;
+ if (dict.GetDictionary(kPositionDictAttr, &d_value)) {
+ WriteAttribute(false,
+ FormatCoordinates(kPositionDictAttr,
+ kXCoordDictAttr, kYCoordDictAttr,
+ *d_value),
+ &line);
+ }
+ if (dict.GetDictionary(kSizeDictAttr, &d_value)) {
+ WriteAttribute(false,
+ FormatCoordinates(kSizeDictAttr,
+ kWidthDictAttr, kHeightDictAttr, *d_value),
+ &line);
+ }
+
+ return indent + line + ASCIIToUTF16("\n");
+}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetActualFileSuffix() {
+ return FILE_PATH_LITERAL("-actual-mac.txt");
+}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetExpectedFileSuffix() {
+ return FILE_PATH_LITERAL("-expected-mac.txt");
+}
+
+// static
+const string AccessibilityTreeFormatter::GetAllowEmptyString() {
+ return "@MAC-ALLOW-EMPTY:";
+}
+
+// static
+const string AccessibilityTreeFormatter::GetAllowString() {
+ return "@MAC-ALLOW:";
+}
+
+// static
+const string AccessibilityTreeFormatter::GetDenyString() {
+ return "@MAC-DENY:";
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
new file mode 100644
index 00000000000..053e39b9b09
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
@@ -0,0 +1,265 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
+
+#include <oleacc.h>
+
+#include <map>
+#include <string>
+
+#include "base/memory/singleton.h"
+#include "base/strings/string_util.h"
+#include "third_party/iaccessible2/ia2_api_all.h"
+
+namespace content {
+namespace {
+
+class AccessibilityRoleStateMap {
+ public:
+ static AccessibilityRoleStateMap* GetInstance();
+
+ std::map<int32, string16> ia_role_string_map;
+ std::map<int32, string16> ia2_role_string_map;
+ std::map<int32, string16> ia_state_string_map;
+ std::map<int32, string16> ia2_state_string_map;
+
+ private:
+ AccessibilityRoleStateMap();
+ virtual ~AccessibilityRoleStateMap() {}
+
+ friend struct DefaultSingletonTraits<AccessibilityRoleStateMap>;
+
+ DISALLOW_COPY_AND_ASSIGN(AccessibilityRoleStateMap);
+};
+
+// static
+AccessibilityRoleStateMap* AccessibilityRoleStateMap::GetInstance() {
+ return Singleton<AccessibilityRoleStateMap,
+ LeakySingletonTraits<AccessibilityRoleStateMap> >::get();
+}
+
+AccessibilityRoleStateMap::AccessibilityRoleStateMap() {
+// Convenience macros for generating readable strings.
+#define IA_ROLE_MAP(x) ia_role_string_map[x] = L#x; \
+ ia2_role_string_map[x] = L#x;
+#define IA2_ROLE_MAP(x) ia2_role_string_map[x] = L#x;
+#define IA_STATE_MAP(x) ia_state_string_map[STATE_SYSTEM_##x] = L#x;
+#define IA2_STATE_MAP(x) ia2_state_string_map[x] = L#x;
+
+ // MSAA / IAccessible roles. Each one of these is also a valid
+ // IAccessible2 role, the IA_ROLE_MAP macro adds it to both.
+ IA_ROLE_MAP(ROLE_SYSTEM_ALERT)
+ IA_ROLE_MAP(ROLE_SYSTEM_ANIMATION)
+ IA_ROLE_MAP(ROLE_SYSTEM_APPLICATION)
+ IA_ROLE_MAP(ROLE_SYSTEM_BORDER)
+ IA_ROLE_MAP(ROLE_SYSTEM_BUTTONDROPDOWN)
+ IA_ROLE_MAP(ROLE_SYSTEM_BUTTONDROPDOWNGRID)
+ IA_ROLE_MAP(ROLE_SYSTEM_BUTTONMENU)
+ IA_ROLE_MAP(ROLE_SYSTEM_CARET)
+ IA_ROLE_MAP(ROLE_SYSTEM_CELL)
+ IA_ROLE_MAP(ROLE_SYSTEM_CHARACTER)
+ IA_ROLE_MAP(ROLE_SYSTEM_CHART)
+ IA_ROLE_MAP(ROLE_SYSTEM_CHECKBUTTON)
+ IA_ROLE_MAP(ROLE_SYSTEM_CLIENT)
+ IA_ROLE_MAP(ROLE_SYSTEM_CLOCK)
+ IA_ROLE_MAP(ROLE_SYSTEM_COLUMN)
+ IA_ROLE_MAP(ROLE_SYSTEM_COLUMNHEADER)
+ IA_ROLE_MAP(ROLE_SYSTEM_COMBOBOX)
+ IA_ROLE_MAP(ROLE_SYSTEM_CURSOR)
+ IA_ROLE_MAP(ROLE_SYSTEM_DIAGRAM)
+ IA_ROLE_MAP(ROLE_SYSTEM_DIAL)
+ IA_ROLE_MAP(ROLE_SYSTEM_DIALOG)
+ IA_ROLE_MAP(ROLE_SYSTEM_DOCUMENT)
+ IA_ROLE_MAP(ROLE_SYSTEM_DROPLIST)
+ IA_ROLE_MAP(ROLE_SYSTEM_EQUATION)
+ IA_ROLE_MAP(ROLE_SYSTEM_GRAPHIC)
+ IA_ROLE_MAP(ROLE_SYSTEM_GRIP)
+ IA_ROLE_MAP(ROLE_SYSTEM_GROUPING)
+ IA_ROLE_MAP(ROLE_SYSTEM_HELPBALLOON)
+ IA_ROLE_MAP(ROLE_SYSTEM_HOTKEYFIELD)
+ IA_ROLE_MAP(ROLE_SYSTEM_INDICATOR)
+ IA_ROLE_MAP(ROLE_SYSTEM_IPADDRESS)
+ IA_ROLE_MAP(ROLE_SYSTEM_LINK)
+ IA_ROLE_MAP(ROLE_SYSTEM_LIST)
+ IA_ROLE_MAP(ROLE_SYSTEM_LISTITEM)
+ IA_ROLE_MAP(ROLE_SYSTEM_MENUBAR)
+ IA_ROLE_MAP(ROLE_SYSTEM_MENUITEM)
+ IA_ROLE_MAP(ROLE_SYSTEM_MENUPOPUP)
+ IA_ROLE_MAP(ROLE_SYSTEM_OUTLINE)
+ IA_ROLE_MAP(ROLE_SYSTEM_OUTLINEBUTTON)
+ IA_ROLE_MAP(ROLE_SYSTEM_OUTLINEITEM)
+ IA_ROLE_MAP(ROLE_SYSTEM_PAGETAB)
+ IA_ROLE_MAP(ROLE_SYSTEM_PAGETABLIST)
+ IA_ROLE_MAP(ROLE_SYSTEM_PANE)
+ IA_ROLE_MAP(ROLE_SYSTEM_PROGRESSBAR)
+ IA_ROLE_MAP(ROLE_SYSTEM_PROPERTYPAGE)
+ IA_ROLE_MAP(ROLE_SYSTEM_PUSHBUTTON)
+ IA_ROLE_MAP(ROLE_SYSTEM_RADIOBUTTON)
+ IA_ROLE_MAP(ROLE_SYSTEM_ROW)
+ IA_ROLE_MAP(ROLE_SYSTEM_ROWHEADER)
+ IA_ROLE_MAP(ROLE_SYSTEM_SCROLLBAR)
+ IA_ROLE_MAP(ROLE_SYSTEM_SEPARATOR)
+ IA_ROLE_MAP(ROLE_SYSTEM_SLIDER)
+ IA_ROLE_MAP(ROLE_SYSTEM_SOUND)
+ IA_ROLE_MAP(ROLE_SYSTEM_SPINBUTTON)
+ IA_ROLE_MAP(ROLE_SYSTEM_SPLITBUTTON)
+ IA_ROLE_MAP(ROLE_SYSTEM_STATICTEXT)
+ IA_ROLE_MAP(ROLE_SYSTEM_STATUSBAR)
+ IA_ROLE_MAP(ROLE_SYSTEM_TABLE)
+ IA_ROLE_MAP(ROLE_SYSTEM_TEXT)
+ IA_ROLE_MAP(ROLE_SYSTEM_TITLEBAR)
+ IA_ROLE_MAP(ROLE_SYSTEM_TOOLBAR)
+ IA_ROLE_MAP(ROLE_SYSTEM_TOOLTIP)
+ IA_ROLE_MAP(ROLE_SYSTEM_WHITESPACE)
+ IA_ROLE_MAP(ROLE_SYSTEM_WINDOW)
+
+ // IAccessible2 roles.
+ IA2_ROLE_MAP(IA2_ROLE_CANVAS)
+ IA2_ROLE_MAP(IA2_ROLE_CAPTION)
+ IA2_ROLE_MAP(IA2_ROLE_CHECK_MENU_ITEM)
+ IA2_ROLE_MAP(IA2_ROLE_COLOR_CHOOSER)
+ IA2_ROLE_MAP(IA2_ROLE_DATE_EDITOR)
+ IA2_ROLE_MAP(IA2_ROLE_DESKTOP_ICON)
+ IA2_ROLE_MAP(IA2_ROLE_DESKTOP_PANE)
+ IA2_ROLE_MAP(IA2_ROLE_DIRECTORY_PANE)
+ IA2_ROLE_MAP(IA2_ROLE_EDITBAR)
+ IA2_ROLE_MAP(IA2_ROLE_EMBEDDED_OBJECT)
+ IA2_ROLE_MAP(IA2_ROLE_ENDNOTE)
+ IA2_ROLE_MAP(IA2_ROLE_FILE_CHOOSER)
+ IA2_ROLE_MAP(IA2_ROLE_FONT_CHOOSER)
+ IA2_ROLE_MAP(IA2_ROLE_FOOTER)
+ IA2_ROLE_MAP(IA2_ROLE_FOOTNOTE)
+ IA2_ROLE_MAP(IA2_ROLE_FORM)
+ IA2_ROLE_MAP(IA2_ROLE_FRAME)
+ IA2_ROLE_MAP(IA2_ROLE_GLASS_PANE)
+ IA2_ROLE_MAP(IA2_ROLE_HEADER)
+ IA2_ROLE_MAP(IA2_ROLE_HEADING)
+ IA2_ROLE_MAP(IA2_ROLE_ICON)
+ IA2_ROLE_MAP(IA2_ROLE_IMAGE_MAP)
+ IA2_ROLE_MAP(IA2_ROLE_INPUT_METHOD_WINDOW)
+ IA2_ROLE_MAP(IA2_ROLE_INTERNAL_FRAME)
+ IA2_ROLE_MAP(IA2_ROLE_LABEL)
+ IA2_ROLE_MAP(IA2_ROLE_LAYERED_PANE)
+ IA2_ROLE_MAP(IA2_ROLE_NOTE)
+ IA2_ROLE_MAP(IA2_ROLE_OPTION_PANE)
+ IA2_ROLE_MAP(IA2_ROLE_PAGE)
+ IA2_ROLE_MAP(IA2_ROLE_PARAGRAPH)
+ IA2_ROLE_MAP(IA2_ROLE_RADIO_MENU_ITEM)
+ IA2_ROLE_MAP(IA2_ROLE_REDUNDANT_OBJECT)
+ IA2_ROLE_MAP(IA2_ROLE_ROOT_PANE)
+ IA2_ROLE_MAP(IA2_ROLE_RULER)
+ IA2_ROLE_MAP(IA2_ROLE_SCROLL_PANE)
+ IA2_ROLE_MAP(IA2_ROLE_SECTION)
+ IA2_ROLE_MAP(IA2_ROLE_SHAPE)
+ IA2_ROLE_MAP(IA2_ROLE_SPLIT_PANE)
+ IA2_ROLE_MAP(IA2_ROLE_TEAR_OFF_MENU)
+ IA2_ROLE_MAP(IA2_ROLE_TERMINAL)
+ IA2_ROLE_MAP(IA2_ROLE_TEXT_FRAME)
+ IA2_ROLE_MAP(IA2_ROLE_TOGGLE_BUTTON)
+ IA2_ROLE_MAP(IA2_ROLE_UNKNOWN)
+ IA2_ROLE_MAP(IA2_ROLE_VIEW_PORT)
+
+ // MSAA / IAccessible states. Unlike roles, these are not also IA2 states.
+ IA_STATE_MAP(ALERT_HIGH)
+ IA_STATE_MAP(ALERT_LOW)
+ IA_STATE_MAP(ALERT_MEDIUM)
+ IA_STATE_MAP(ANIMATED)
+ IA_STATE_MAP(BUSY)
+ IA_STATE_MAP(CHECKED)
+ IA_STATE_MAP(COLLAPSED)
+ IA_STATE_MAP(DEFAULT)
+ IA_STATE_MAP(EXPANDED)
+ IA_STATE_MAP(EXTSELECTABLE)
+ IA_STATE_MAP(FLOATING)
+ IA_STATE_MAP(FOCUSABLE)
+ IA_STATE_MAP(FOCUSED)
+ IA_STATE_MAP(HASPOPUP)
+ IA_STATE_MAP(HOTTRACKED)
+ IA_STATE_MAP(INVISIBLE)
+ IA_STATE_MAP(LINKED)
+ IA_STATE_MAP(MARQUEED)
+ IA_STATE_MAP(MIXED)
+ IA_STATE_MAP(MOVEABLE)
+ IA_STATE_MAP(MULTISELECTABLE)
+ IA_STATE_MAP(OFFSCREEN)
+ IA_STATE_MAP(PRESSED)
+ IA_STATE_MAP(PROTECTED)
+ IA_STATE_MAP(READONLY)
+ IA_STATE_MAP(SELECTABLE)
+ IA_STATE_MAP(SELECTED)
+ IA_STATE_MAP(SELFVOICING)
+ IA_STATE_MAP(SIZEABLE)
+ IA_STATE_MAP(TRAVERSED)
+ IA_STATE_MAP(UNAVAILABLE)
+
+ // IAccessible2 states.
+ IA2_STATE_MAP(IA2_STATE_ACTIVE)
+ IA2_STATE_MAP(IA2_STATE_ARMED)
+ IA2_STATE_MAP(IA2_STATE_DEFUNCT)
+ IA2_STATE_MAP(IA2_STATE_EDITABLE)
+ IA2_STATE_MAP(IA2_STATE_ICONIFIED)
+ IA2_STATE_MAP(IA2_STATE_INVALID_ENTRY)
+ IA2_STATE_MAP(IA2_STATE_MANAGES_DESCENDANTS)
+ IA2_STATE_MAP(IA2_STATE_MODAL)
+ IA2_STATE_MAP(IA2_STATE_MULTI_LINE)
+ IA2_STATE_MAP(IA2_STATE_REQUIRED)
+ IA2_STATE_MAP(IA2_STATE_SELECTABLE_TEXT)
+ IA2_STATE_MAP(IA2_STATE_SINGLE_LINE)
+ IA2_STATE_MAP(IA2_STATE_STALE)
+ IA2_STATE_MAP(IA2_STATE_SUPPORTS_AUTOCOMPLETION)
+ IA2_STATE_MAP(IA2_STATE_TRANSIENT)
+
+ // Untested states include those that would be repeated on nearly every node,
+ // or would vary based on window size.
+ // IA2_STATE_MAP(IA2_STATE_HORIZONTAL) // Untested.
+ // IA2_STATE_MAP(IA2_STATE_OPAQUE) // Untested.
+ // IA2_STATE_MAP(IA2_STATE_VERTICAL) // Untested.
+}
+
+} // namespace.
+
+string16 IAccessibleRoleToString(int32 ia_role) {
+ return AccessibilityRoleStateMap::GetInstance()->ia_role_string_map[ia_role];
+}
+
+string16 IAccessible2RoleToString(int32 ia_role) {
+ return AccessibilityRoleStateMap::GetInstance()->ia2_role_string_map[ia_role];
+}
+
+void IAccessibleStateToStringVector(int32 ia_state,
+ std::vector<string16>* result) {
+ const std::map<int32, string16>& state_string_map =
+ AccessibilityRoleStateMap::GetInstance()->ia_state_string_map;
+ std::map<int32, string16>::const_iterator it;
+ for (it = state_string_map.begin(); it != state_string_map.end(); ++it) {
+ if (it->first & ia_state)
+ result->push_back(it->second);
+ }
+}
+
+string16 IAccessibleStateToString(int32 ia_state) {
+ std::vector<string16> strings;
+ IAccessibleStateToStringVector(ia_state, &strings);
+ return JoinString(strings, ',');
+}
+
+void IAccessible2StateToStringVector(int32 ia2_state,
+ std::vector<string16>* result) {
+ const std::map<int32, string16>& state_string_map =
+ AccessibilityRoleStateMap::GetInstance()->ia2_state_string_map;
+ std::map<int32, string16>::const_iterator it;
+ for (it = state_string_map.begin(); it != state_string_map.end(); ++it) {
+ if (it->first & ia2_state)
+ result->push_back(it->second);
+ }
+}
+
+string16 IAccessible2StateToString(int32 ia2_state) {
+ std::vector<string16> strings;
+ IAccessible2StateToStringVector(ia2_state, &strings);
+ return JoinString(strings, ',');
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.h b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.h
new file mode 100644
index 00000000000..1baf20d54a8
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UTILS_WIN_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UTILS_WIN_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+CONTENT_EXPORT string16 IAccessibleRoleToString(int32 ia_role);
+CONTENT_EXPORT string16 IAccessible2RoleToString(int32 ia_role);
+CONTENT_EXPORT string16 IAccessibleStateToString(int32 ia_state);
+CONTENT_EXPORT void IAccessibleStateToStringVector(
+ int32 ia_state, std::vector<string16>* result);
+CONTENT_EXPORT string16 IAccessible2StateToString(int32 ia2_state);
+CONTENT_EXPORT void IAccessible2StateToStringVector(
+ int32 ia_state, std::vector<string16>* result);
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UTILS_WIN_H_
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc
new file mode 100644
index 00000000000..679843dc42e
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -0,0 +1,319 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/accessibility_tree_formatter.h"
+
+#include <oleacc.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/accessibility/browser_accessibility_win.h"
+#include "content/common/accessibility_node_data.h"
+#include "third_party/iaccessible2/ia2_api_all.h"
+#include "ui/base/win/atl_module.h"
+
+using base::StringPrintf;
+
+namespace content {
+
+const char* ALL_ATTRIBUTES[] = {
+ "name",
+ "value",
+ "states",
+ "attributes",
+ "role_name",
+ "currentValue",
+ "minimumValue",
+ "maximumValue",
+ "description",
+ "default_action",
+ "keyboard_shortcut",
+ "location",
+ "size",
+ "index_in_parent",
+ "n_relations",
+ "group_level",
+ "similar_items_in_group",
+ "position_in_group",
+ "table_rows",
+ "table_columns",
+ "row_index",
+ "column_index",
+ "n_characters",
+ "caret_offset",
+ "n_selections",
+ "selection_start",
+ "selection_end"
+};
+
+void AccessibilityTreeFormatter::Initialize() {
+ ui::win::CreateATLModuleIfNeeded();
+}
+
+void AccessibilityTreeFormatter::AddProperties(
+ const BrowserAccessibility& node, base::DictionaryValue* dict) {
+ BrowserAccessibilityWin* acc_obj =
+ const_cast<BrowserAccessibility*>(&node)->ToBrowserAccessibilityWin();
+
+ VARIANT variant_self;
+ variant_self.vt = VT_I4;
+ variant_self.lVal = CHILDID_SELF;
+
+ dict->SetString("role", IAccessible2RoleToString(acc_obj->ia2_role()));
+
+ CComBSTR msaa_variant;
+ HRESULT hresult = acc_obj->get_accName(variant_self, &msaa_variant);
+ if (hresult == S_OK)
+ dict->SetString("name", msaa_variant.m_str);
+ hresult = acc_obj->get_accValue(variant_self, &msaa_variant);
+ if (hresult == S_OK)
+ dict->SetString("value", msaa_variant.m_str);
+
+ std::vector<string16> state_strings;
+ int32 ia_state = acc_obj->ia_state();
+
+ // Avoid flakiness: these states depend on whether the window is focused
+ // and the position of the mouse cursor.
+ ia_state &= ~STATE_SYSTEM_HOTTRACKED;
+ ia_state &= ~STATE_SYSTEM_OFFSCREEN;
+
+ IAccessibleStateToStringVector(ia_state, &state_strings);
+ IAccessible2StateToStringVector(acc_obj->ia2_state(), &state_strings);
+ base::ListValue* states = new base::ListValue;
+ for (std::vector<string16>::const_iterator it = state_strings.begin();
+ it != state_strings.end();
+ ++it) {
+ states->AppendString(UTF16ToUTF8(*it));
+ }
+ dict->Set("states", states);
+
+ const std::vector<string16>& ia2_attributes = acc_obj->ia2_attributes();
+ base::ListValue* attributes = new base::ListValue;
+ for (std::vector<string16>::const_iterator it = ia2_attributes.begin();
+ it != ia2_attributes.end();
+ ++it) {
+ attributes->AppendString(UTF16ToUTF8(*it));
+ }
+ dict->Set("attributes", attributes);
+
+ dict->SetString("role_name", acc_obj->role_name());
+
+ VARIANT currentValue;
+ if (acc_obj->get_currentValue(&currentValue) == S_OK)
+ dict->SetDouble("currentValue", V_R8(&currentValue));
+
+ VARIANT minimumValue;
+ if (acc_obj->get_minimumValue(&minimumValue) == S_OK)
+ dict->SetDouble("minimumValue", V_R8(&minimumValue));
+
+ VARIANT maximumValue;
+ if (acc_obj->get_maximumValue(&maximumValue) == S_OK)
+ dict->SetDouble("maximumValue", V_R8(&maximumValue));
+
+ hresult = acc_obj->get_accDescription(variant_self, &msaa_variant);
+ if (hresult == S_OK)
+ dict->SetString("description", msaa_variant.m_str);
+
+ hresult = acc_obj->get_accDefaultAction(variant_self, &msaa_variant);
+ if (hresult == S_OK)
+ dict->SetString("default_action", msaa_variant.m_str);
+
+ hresult = acc_obj->get_accKeyboardShortcut(variant_self, &msaa_variant);
+ if (hresult == S_OK)
+ dict->SetString("keyboard_shortcut", msaa_variant.m_str);
+
+ hresult = acc_obj->get_accHelp(variant_self, &msaa_variant);
+ if (S_OK == hresult)
+ dict->SetString("help", msaa_variant.m_str);
+
+ BrowserAccessibility* root = node.manager()->GetRoot();
+ LONG left, top, width, height;
+ LONG root_left, root_top, root_width, root_height;
+ if (acc_obj->accLocation(&left, &top, &width, &height, variant_self)
+ != S_FALSE
+ && root->ToBrowserAccessibilityWin()->accLocation(
+ &root_left, &root_top, &root_width, &root_height, variant_self)
+ != S_FALSE) {
+ base::DictionaryValue* location = new base::DictionaryValue;
+ location->SetInteger("x", left - root_left);
+ location->SetInteger("y", top - root_top);
+ dict->Set("location", location);
+
+ base::DictionaryValue* size = new base::DictionaryValue;
+ size->SetInteger("width", width);
+ size->SetInteger("height", height);
+ dict->Set("size", size);
+ }
+
+ LONG index_in_parent;
+ if (acc_obj->get_indexInParent(&index_in_parent) == S_OK)
+ dict->SetInteger("index_in_parent", index_in_parent);
+
+ LONG n_relations;
+ if (acc_obj->get_nRelations(&n_relations) == S_OK)
+ dict->SetInteger("n_relations", n_relations);
+
+ LONG group_level, similar_items_in_group, position_in_group;
+ if (acc_obj->get_groupPosition(&group_level,
+ &similar_items_in_group,
+ &position_in_group) == S_OK) {
+ dict->SetInteger("group_level", group_level);
+ dict->SetInteger("similar_items_in_group", similar_items_in_group);
+ dict->SetInteger("position_in_group", position_in_group);
+ }
+ LONG table_rows;
+ if (acc_obj->get_nRows(&table_rows) == S_OK)
+ dict->SetInteger("table_rows", table_rows);
+ LONG table_columns;
+ if (acc_obj->get_nRows(&table_columns) == S_OK)
+ dict->SetInteger("table_columns", table_columns);
+ LONG row_index;
+ if (acc_obj->get_rowIndex(&row_index) == S_OK)
+ dict->SetInteger("row_index", row_index);
+ LONG column_index;
+ if (acc_obj->get_columnIndex(&column_index) == S_OK)
+ dict->SetInteger("column_index", column_index);
+ LONG n_characters;
+ if (acc_obj->get_nCharacters(&n_characters) == S_OK)
+ dict->SetInteger("n_characters", n_characters);
+ LONG caret_offset;
+ if (acc_obj->get_caretOffset(&caret_offset) == S_OK)
+ dict->SetInteger("caret_offset", caret_offset);
+ LONG n_selections;
+ if (acc_obj->get_nSelections(&n_selections) == S_OK) {
+ dict->SetInteger("n_selections", n_selections);
+ if (n_selections > 0) {
+ LONG start, end;
+ if (acc_obj->get_selection(0, &start, &end) == S_OK) {
+ dict->SetInteger("selection_start", start);
+ dict->SetInteger("selection_end", end);
+ }
+ }
+ }
+}
+
+string16 AccessibilityTreeFormatter::ToString(const base::DictionaryValue& dict,
+ const string16& indent) {
+ string16 line;
+
+ string16 role_value;
+ dict.GetString("role", &role_value);
+ WriteAttribute(true, UTF16ToUTF8(role_value), &line);
+
+ string16 name_value;
+ dict.GetString("name", &name_value);
+ WriteAttribute(true, base::StringPrintf(L"name='%ls'", name_value.c_str()),
+ &line);
+
+ for (int i = 0; i < arraysize(ALL_ATTRIBUTES); i++) {
+ const char* attribute_name = ALL_ATTRIBUTES[i];
+ const base::Value* value;
+ if (!dict.Get(attribute_name, &value))
+ continue;
+
+ switch (value->GetType()) {
+ case base::Value::TYPE_STRING: {
+ string16 string_value;
+ value->GetAsString(&string_value);
+ WriteAttribute(false,
+ StringPrintf(L"%ls='%ls'",
+ UTF8ToUTF16(attribute_name).c_str(),
+ string_value.c_str()),
+ &line);
+ break;
+ }
+ case base::Value::TYPE_INTEGER: {
+ int int_value;
+ value->GetAsInteger(&int_value);
+ WriteAttribute(false,
+ base::StringPrintf(L"%ls=%d",
+ UTF8ToUTF16(attribute_name).c_str(),
+ int_value),
+ &line);
+ break;
+ }
+ case base::Value::TYPE_DOUBLE: {
+ double double_value;
+ value->GetAsDouble(&double_value);
+ WriteAttribute(false,
+ base::StringPrintf(L"%ls=%.2f",
+ UTF8ToUTF16(attribute_name).c_str(),
+ double_value),
+ &line);
+ break;
+ }
+ case base::Value::TYPE_LIST: {
+ // Currently all list values are string and are written without
+ // attribute names.
+ const base::ListValue* list_value;
+ value->GetAsList(&list_value);
+ for (base::ListValue::const_iterator it = list_value->begin();
+ it != list_value->end();
+ ++it) {
+ string16 string_value;
+ if ((*it)->GetAsString(&string_value))
+ WriteAttribute(false, string_value, &line);
+ }
+ break;
+ }
+ case base::Value::TYPE_DICTIONARY: {
+ // Currently all dictionary values are coordinates.
+ // Revisit this if that changes.
+ const base::DictionaryValue* dict_value;
+ value->GetAsDictionary(&dict_value);
+ if (strcmp(attribute_name, "size") == 0) {
+ WriteAttribute(false,
+ FormatCoordinates("size", "width", "height",
+ *dict_value),
+ &line);
+ } else if (strcmp(attribute_name, "location") == 0) {
+ WriteAttribute(false,
+ FormatCoordinates("location", "x", "y", *dict_value),
+ &line);
+ }
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ return indent + line + ASCIIToUTF16("\n");
+}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetActualFileSuffix() {
+ return FILE_PATH_LITERAL("-actual-win.txt");
+}
+
+// static
+const base::FilePath::StringType
+AccessibilityTreeFormatter::GetExpectedFileSuffix() {
+ return FILE_PATH_LITERAL("-expected-win.txt");
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetAllowEmptyString() {
+ return "@WIN-ALLOW-EMPTY:";
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetAllowString() {
+ return "@WIN-ALLOW:";
+}
+
+// static
+const std::string AccessibilityTreeFormatter::GetDenyString() {
+ return "@WIN-DENY:";
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_ui.cc b/chromium/content/browser/accessibility/accessibility_ui.cc
new file mode 100644
index 00000000000..585be6806a1
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_ui.cc
@@ -0,0 +1,253 @@
+// Copyright (c) 2013 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 "content/browser/accessibility/accessibility_ui.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/accessibility/browser_accessibility_state_impl.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/view_message_enums.h"
+#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/favicon_status.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/url_constants.h"
+#include "grit/content_resources.h"
+#include "net/base/escape.h"
+
+static const char kDataFile[] = "targets-data.json";
+
+static const char kProcessIdField[] = "processId";
+static const char kRouteIdField[] = "routeId";
+static const char kUrlField[] = "url";
+static const char kNameField[] = "name";
+static const char kFaviconUrlField[] = "favicon_url";
+static const char kPidField[] = "pid";
+static const char kAccessibilityModeField[] = "a11y_mode";
+
+namespace content {
+
+namespace {
+
+base::DictionaryValue* BuildTargetDescriptor(
+ const GURL& url,
+ const std::string& name,
+ const GURL& favicon_url,
+ int process_id,
+ int route_id,
+ AccessibilityMode accessibility_mode,
+ base::ProcessHandle handle = base::kNullProcessHandle) {
+ base::DictionaryValue* target_data = new base::DictionaryValue();
+ target_data->SetInteger(kProcessIdField, process_id);
+ target_data->SetInteger(kRouteIdField, route_id);
+ target_data->SetString(kUrlField, url.spec());
+ target_data->SetString(kNameField, net::EscapeForHTML(name));
+ target_data->SetInteger(kPidField, base::GetProcId(handle));
+ target_data->SetString(kFaviconUrlField, favicon_url.spec());
+ target_data->SetInteger(kAccessibilityModeField,
+ accessibility_mode);
+ return target_data;
+}
+
+base::DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh) {
+ WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
+ std::string title;
+ RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rvh);
+ AccessibilityMode accessibility_mode = rwhi->accessibility_mode();
+
+ GURL url;
+ GURL favicon_url;
+ if (web_contents) {
+ url = web_contents->GetURL();
+ title = UTF16ToUTF8(web_contents->GetTitle());
+ NavigationController& controller = web_contents->GetController();
+ NavigationEntry* entry = controller.GetActiveEntry();
+ if (entry != NULL && entry->GetURL().is_valid())
+ favicon_url = entry->GetFavicon().url;
+ }
+
+ return BuildTargetDescriptor(url,
+ title,
+ favicon_url,
+ rvh->GetProcess()->GetID(),
+ rvh->GetRoutingID(),
+ accessibility_mode);
+}
+
+void SendTargetsData(
+ const WebUIDataSource::GotDataCallback& callback) {
+ scoped_ptr<base::ListValue> rvh_list(new base::ListValue());
+
+ RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts();
+ for (size_t i = 0; i < widgets.size(); ++i) {
+ // Ignore processes that don't have a connection, such as crashed tabs.
+ if (!widgets[i]->GetProcess()->HasConnection())
+ continue;
+ if (!widgets[i]->IsRenderView())
+ continue;
+
+ RenderViewHost* rvh = RenderViewHost::From(widgets[i]);
+ rvh_list->Append(BuildTargetDescriptor(rvh));
+ }
+
+ scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue());
+ data->Set("list", rvh_list.release());
+ scoped_ptr<base::FundamentalValue> a11y_mode(new base::FundamentalValue(
+ BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode()));
+ data->Set("global_a11y_mode", a11y_mode.release());
+
+ std::string json_string;
+ base::JSONWriter::Write(data.get(), &json_string);
+
+ callback.Run(base::RefCountedString::TakeString(&json_string));
+}
+
+bool HandleRequestCallback(
+ const std::string& path,
+ const WebUIDataSource::GotDataCallback& callback) {
+ if (path != kDataFile)
+ return false;
+
+ SendTargetsData(callback);
+ return true;
+}
+
+} // namespace
+
+AccessibilityUI::AccessibilityUI(WebUI* web_ui)
+ : WebUIController(web_ui) {
+ // Set up the chrome://accessibility source.
+ WebUIDataSource* html_source =
+ WebUIDataSource::Create(kChromeUIAccessibilityHost);
+ html_source->SetUseJsonJSFormatV2();
+
+ web_ui->RegisterMessageCallback(
+ "toggleAccessibility",
+ base::Bind(&AccessibilityUI::ToggleAccessibility,
+ base::Unretained(this)));
+ web_ui->RegisterMessageCallback(
+ "toggleGlobalAccessibility",
+ base::Bind(&AccessibilityUI::ToggleGlobalAccessibility,
+ base::Unretained(this)));
+ web_ui->RegisterMessageCallback(
+ "requestAccessibilityTree",
+ base::Bind(&AccessibilityUI::RequestAccessibilityTree,
+ base::Unretained(this)));
+
+ // Add required resources.
+ html_source->SetJsonPath("strings.js");
+ html_source->AddResourcePath("accessibility.css", IDR_ACCESSIBILITY_CSS);
+ html_source->AddResourcePath("accessibility.js", IDR_ACCESSIBILITY_JS);
+ html_source->SetDefaultResource(IDR_ACCESSIBILITY_HTML);
+ html_source->SetRequestFilter(base::Bind(&HandleRequestCallback));
+
+ BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+ WebUIDataSource::Add(browser_context, html_source);
+}
+
+AccessibilityUI::~AccessibilityUI() {
+}
+
+void AccessibilityUI::ToggleAccessibility(const base::ListValue* args) {
+ std::string process_id_str;
+ std::string route_id_str;
+ int process_id;
+ int route_id;
+ CHECK(args->GetSize() == 2);
+ CHECK(args->GetString(0, &process_id_str));
+ CHECK(args->GetString(1, &route_id_str));
+ CHECK(base::StringToInt(process_id_str,
+ &process_id));
+ CHECK(base::StringToInt(route_id_str, &route_id));
+
+ RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
+ if (!rvh)
+ return;
+ RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rvh);
+ if (!rwhi)
+ return;
+ AccessibilityMode mode = rwhi->accessibility_mode();
+ if (mode == AccessibilityModeOff)
+ rwhi->SetAccessibilityMode(AccessibilityModeComplete);
+ else
+ rwhi->SetAccessibilityMode(AccessibilityModeOff);
+}
+
+void AccessibilityUI::ToggleGlobalAccessibility(const base::ListValue* args) {
+ BrowserAccessibilityStateImpl* state =
+ BrowserAccessibilityStateImpl::GetInstance();
+ AccessibilityMode mode = state->accessibility_mode();
+ AccessibilityMode new_mode = (mode == AccessibilityModeOff
+ ? AccessibilityModeComplete
+ : AccessibilityModeOff);
+ state->SetAccessibilityMode(new_mode);
+}
+
+void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) {
+ std::string process_id_str;
+ std::string route_id_str;
+ int process_id;
+ int route_id;
+ CHECK(args->GetSize() == 2);
+ CHECK(args->GetString(0, &process_id_str));
+ CHECK(args->GetString(1, &route_id_str));
+ CHECK(base::StringToInt(process_id_str, &process_id));
+ CHECK(base::StringToInt(route_id_str, &route_id));
+
+ RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
+ if (!rvh) {
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ result->SetInteger(kProcessIdField, process_id);
+ result->SetInteger(kRouteIdField, route_id);
+ result->Set("error", new base::StringValue("Renderer no longer exists."));
+ web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get()));
+ return;
+ }
+
+ scoped_ptr<base::DictionaryValue> result(BuildTargetDescriptor(rvh));
+ RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>(
+ WebContents::FromRenderViewHost(rvh)->GetRenderWidgetHostView());
+ if (!host_view) {
+ result->Set("error",
+ new base::StringValue("Could not get accessibility tree."));
+ web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get()));
+ return;
+ }
+ scoped_ptr<AccessibilityTreeFormatter> formatter(
+ AccessibilityTreeFormatter::Create(rvh));
+ string16 accessibility_contents_utf16;
+ BrowserAccessibilityManager* manager =
+ host_view->GetBrowserAccessibilityManager();
+ if (!manager) {
+ result->Set("error",
+ new base::StringValue("Could not get accessibility tree."));
+ web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get()));
+ return;
+ }
+ std::vector<AccessibilityTreeFormatter::Filter> filters;
+ filters.push_back(AccessibilityTreeFormatter::Filter(
+ ASCIIToUTF16("*"),
+ AccessibilityTreeFormatter::Filter::ALLOW));
+ formatter->SetFilters(filters);
+ formatter->FormatAccessibilityTree(&accessibility_contents_utf16);
+
+ result->Set("tree",
+ new base::StringValue(UTF16ToUTF8(accessibility_contents_utf16)));
+ web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get()));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_ui.h b/chromium/content/browser/accessibility/accessibility_ui.h
new file mode 100644
index 00000000000..1b239f8033b
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_ui.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 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 CHROME_BROWSER_UI_WEBUI_ACCESSIBILITY_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ACCESSIBILITY_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+
+namespace base {
+ class ListValue;
+} // namespace base
+
+namespace content {
+
+class AccessibilityUI : public WebUIController {
+ public:
+ explicit AccessibilityUI(WebUI* web_ui);
+ virtual ~AccessibilityUI();
+
+ private:
+ void ToggleAccessibility(const base::ListValue* args);
+ void ToggleGlobalAccessibility(const base::ListValue* args);
+ void RequestAccessibilityTree(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(AccessibilityUI);
+};
+
+} // namespace content
+
+#endif // CHROME_BROWSER_UI_WEBUI_ACCESSIBILITY_UI_H_
diff --git a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc
new file mode 100644
index 00000000000..0be9ba5067d
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -0,0 +1,883 @@
+// Copyright (c) 2012 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 <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_variant.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/url_constants.h"
+#include "content/shell/shell.h"
+#include "content/test/accessibility_browser_test_utils.h"
+#include "content/test/content_browser_test.h"
+#include "content/test/content_browser_test_utils.h"
+#include "third_party/iaccessible2/ia2_api_all.h"
+#include "third_party/isimpledom/ISimpleDOMNode.h"
+
+// TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
+#if defined(ARCH_CPU_X86_64)
+#define MAYBE(x) DISABLED_##x
+#else
+#define MAYBE(x) x
+#endif
+
+namespace content {
+
+namespace {
+
+
+// Helpers --------------------------------------------------------------------
+
+base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant(
+ IAccessible* parent,
+ VARIANT* var) {
+ base::win::ScopedComPtr<IAccessible> ptr;
+ switch (V_VT(var)) {
+ case VT_DISPATCH: {
+ IDispatch* dispatch = V_DISPATCH(var);
+ if (dispatch)
+ ptr.QueryFrom(dispatch);
+ break;
+ }
+
+ case VT_I4: {
+ base::win::ScopedComPtr<IDispatch> dispatch;
+ HRESULT hr = parent->get_accChild(*var, dispatch.Receive());
+ EXPECT_TRUE(SUCCEEDED(hr));
+ if (dispatch)
+ dispatch.QueryInterface(ptr.Receive());
+ break;
+ }
+ }
+ return ptr;
+}
+
+HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
+ // TODO(ctguil): For some reason querying the IAccessible2 interface from
+ // IAccessible fails.
+ base::win::ScopedComPtr<IServiceProvider> service_provider;
+ HRESULT hr = accessible->QueryInterface(service_provider.Receive());
+ return SUCCEEDED(hr) ?
+ service_provider->QueryService(IID_IAccessible2, accessible2) : hr;
+}
+
+// Recursively search through all of the descendants reachable from an
+// IAccessible node and return true if we find one with the given role
+// and name.
+void RecursiveFindNodeInAccessibilityTree(IAccessible* node,
+ int32 expected_role,
+ const std::wstring& expected_name,
+ int32 depth,
+ bool* found) {
+ base::win::ScopedBstr name_bstr;
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ node->get_accName(childid_self, name_bstr.Receive());
+ std::wstring name(name_bstr, name_bstr.Length());
+ base::win::ScopedVariant role;
+ node->get_accRole(childid_self, role.Receive());
+ ASSERT_EQ(VT_I4, role.type());
+
+ // Print the accessibility tree as we go, because if this test fails
+ // on the bots, this is really helpful in figuring out why.
+ for (int i = 0; i < depth; i++)
+ printf(" ");
+ printf("role=%d name=%s\n", V_I4(&role), WideToUTF8(name).c_str());
+
+ if (expected_role == V_I4(&role) && expected_name == name) {
+ *found = true;
+ return;
+ }
+
+ LONG child_count = 0;
+ HRESULT hr = node->get_accChildCount(&child_count);
+ ASSERT_EQ(S_OK, hr);
+
+ scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]);
+ LONG obtained_count = 0;
+ hr = AccessibleChildren(
+ node, 0, child_count, child_array.get(), &obtained_count);
+ ASSERT_EQ(S_OK, hr);
+ ASSERT_EQ(child_count, obtained_count);
+
+ for (int index = 0; index < obtained_count; index++) {
+ base::win::ScopedComPtr<IAccessible> child_accessible(
+ GetAccessibleFromResultVariant(node, &child_array.get()[index]));
+ if (child_accessible) {
+ RecursiveFindNodeInAccessibilityTree(
+ child_accessible.get(), expected_role, expected_name, depth + 1,
+ found);
+ if (*found)
+ return;
+ }
+ }
+}
+
+
+// AccessibilityWinBrowserTest ------------------------------------------------
+
+class AccessibilityWinBrowserTest : public ContentBrowserTest {
+ public:
+ AccessibilityWinBrowserTest();
+ virtual ~AccessibilityWinBrowserTest();
+
+ protected:
+ void LoadInitialAccessibilityTreeFromHtml(const std::string& html);
+ IAccessible* GetRendererAccessible();
+ void ExecuteScript(const std::wstring& script);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest);
+};
+
+AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {
+}
+
+AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {
+}
+
+void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
+ const std::string& html) {
+ AccessibilityNotificationWaiter waiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationLoadComplete);
+ GURL html_data_url("data:text/html," + html);
+ NavigateToURL(shell(), html_data_url);
+ waiter.WaitForNotification();
+}
+
+// Retrieve the MSAA client accessibility object for the Render Widget Host View
+// of the selected tab.
+IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() {
+ HWND hwnd_render_widget_host_view =
+ shell()->web_contents()->GetRenderWidgetHostView()->GetNativeView();
+
+ // Invoke windows screen reader detection by sending the WM_GETOBJECT message
+ // with kIdCustom as the LPARAM.
+ const int32 kIdCustom = 1;
+ SendMessage(
+ hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom);
+
+ IAccessible* accessible;
+ HRESULT hr = AccessibleObjectFromWindow(
+ hwnd_render_widget_host_view, OBJID_CLIENT,
+ IID_IAccessible, reinterpret_cast<void**>(&accessible));
+
+ EXPECT_EQ(S_OK, hr);
+ EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL));
+
+ return accessible;
+}
+
+void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) {
+ shell()->web_contents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
+ std::wstring(), script);
+}
+
+
+// AccessibleChecker ----------------------------------------------------------
+
+class AccessibleChecker {
+ public:
+ // This constructor can be used if the IA2 role will be the same as the MSAA
+ // role.
+ AccessibleChecker(const std::wstring& expected_name,
+ int32 expected_role,
+ const std::wstring& expected_value);
+ AccessibleChecker(const std::wstring& expected_name,
+ int32 expected_role,
+ int32 expected_ia2_role,
+ const std::wstring& expected_value);
+ AccessibleChecker(const std::wstring& expected_name,
+ const std::wstring& expected_role,
+ int32 expected_ia2_role,
+ const std::wstring& expected_value);
+
+ // Append an AccessibleChecker that verifies accessibility information for
+ // a child IAccessible. Order is important.
+ void AppendExpectedChild(AccessibleChecker* expected_child);
+
+ // Check that the name and role of the given IAccessible instance and its
+ // descendants match the expected names and roles that this object was
+ // initialized with.
+ void CheckAccessible(IAccessible* accessible);
+
+ // Set the expected value for this AccessibleChecker.
+ void SetExpectedValue(const std::wstring& expected_value);
+
+ // Set the expected state for this AccessibleChecker.
+ void SetExpectedState(LONG expected_state);
+
+ private:
+ typedef std::vector<AccessibleChecker*> AccessibleCheckerVector;
+
+ void CheckAccessibleName(IAccessible* accessible);
+ void CheckAccessibleRole(IAccessible* accessible);
+ void CheckIA2Role(IAccessible* accessible);
+ void CheckAccessibleValue(IAccessible* accessible);
+ void CheckAccessibleState(IAccessible* accessible);
+ void CheckAccessibleChildren(IAccessible* accessible);
+ string16 RoleVariantToString(const base::win::ScopedVariant& role);
+
+ // Expected accessible name. Checked against IAccessible::get_accName.
+ std::wstring name_;
+
+ // Expected accessible role. Checked against IAccessible::get_accRole.
+ base::win::ScopedVariant role_;
+
+ // Expected IAccessible2 role. Checked against IAccessible2::role.
+ int32 ia2_role_;
+
+ // Expected accessible value. Checked against IAccessible::get_accValue.
+ std::wstring value_;
+
+ // Expected accessible state. Checked against IAccessible::get_accState.
+ LONG state_;
+
+ // Expected accessible children. Checked using IAccessible::get_accChildCount
+ // and ::AccessibleChildren.
+ AccessibleCheckerVector children_;
+};
+
+AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
+ int32 expected_role,
+ const std::wstring& expected_value)
+ : name_(expected_name),
+ role_(expected_role),
+ ia2_role_(expected_role),
+ value_(expected_value),
+ state_(-1) {
+}
+
+AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
+ int32 expected_role,
+ int32 expected_ia2_role,
+ const std::wstring& expected_value)
+ : name_(expected_name),
+ role_(expected_role),
+ ia2_role_(expected_ia2_role),
+ value_(expected_value),
+ state_(-1) {
+}
+
+AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
+ const std::wstring& expected_role,
+ int32 expected_ia2_role,
+ const std::wstring& expected_value)
+ : name_(expected_name),
+ role_(expected_role.c_str()),
+ ia2_role_(expected_ia2_role),
+ value_(expected_value),
+ state_(-1) {
+}
+
+void AccessibleChecker::AppendExpectedChild(
+ AccessibleChecker* expected_child) {
+ children_.push_back(expected_child);
+}
+
+void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
+ SCOPED_TRACE("while checking " + UTF16ToUTF8(RoleVariantToString(role_)));
+ CheckAccessibleName(accessible);
+ CheckAccessibleRole(accessible);
+ CheckIA2Role(accessible);
+ CheckAccessibleValue(accessible);
+ CheckAccessibleState(accessible);
+ CheckAccessibleChildren(accessible);
+}
+
+void AccessibleChecker::SetExpectedValue(const std::wstring& expected_value) {
+ value_ = expected_value;
+}
+
+void AccessibleChecker::SetExpectedState(LONG expected_state) {
+ state_ = expected_state;
+}
+
+void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
+ base::win::ScopedBstr name;
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ HRESULT hr = accessible->get_accName(childid_self, name.Receive());
+
+ if (name_.empty()) {
+ // If the object doesn't have name S_FALSE should be returned.
+ EXPECT_EQ(S_FALSE, hr);
+ } else {
+ // Test that the correct string was returned.
+ EXPECT_EQ(S_OK, hr);
+ EXPECT_EQ(name_, std::wstring(name, name.Length()));
+ }
+}
+
+void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
+ base::win::ScopedVariant role;
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_EQ(0, role_.Compare(role))
+ << "Expected role: " << RoleVariantToString(role_)
+ << "\nGot role: " << RoleVariantToString(role);
+}
+
+void AccessibleChecker::CheckIA2Role(IAccessible* accessible) {
+ base::win::ScopedComPtr<IAccessible2> accessible2;
+ HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive());
+ ASSERT_EQ(S_OK, hr);
+ long ia2_role = 0;
+ hr = accessible2->role(&ia2_role);
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_EQ(ia2_role_, ia2_role)
+ << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_)
+ << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role);
+}
+
+void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
+ // Don't check the value if if's a DOCUMENT role, because the value
+ // is supposed to be the url (and we don't keep track of that in the
+ // test expectations).
+ base::win::ScopedVariant role;
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
+ ASSERT_EQ(S_OK, hr);
+ if (role.type() == VT_I4 && V_I4(&role) == ROLE_SYSTEM_DOCUMENT)
+ return;
+
+ // Get the value.
+ base::win::ScopedBstr value;
+ hr = accessible->get_accValue(childid_self, value.Receive());
+ EXPECT_EQ(S_OK, hr);
+
+ // Test that the correct string was returned.
+ EXPECT_EQ(value_, std::wstring(value, value.Length()));
+}
+
+void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) {
+ if (state_ < 0)
+ return;
+
+ base::win::ScopedVariant state;
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ HRESULT hr = accessible->get_accState(childid_self, state.Receive());
+ EXPECT_EQ(S_OK, hr);
+ ASSERT_EQ(VT_I4, state.type());
+ LONG obj_state = V_I4(&state);
+ // Avoid flakiness. The "offscreen" state depends on whether the browser
+ // window is frontmost or not, and "hottracked" depends on whether the
+ // mouse cursor happens to be over the element.
+ obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED);
+ EXPECT_EQ(state_, obj_state)
+ << "Expected state: " << IAccessibleStateToString(state_)
+ << "\nGot state: " << IAccessibleStateToString(obj_state);
+}
+
+void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) {
+ LONG child_count = 0;
+ HRESULT hr = parent->get_accChildCount(&child_count);
+ EXPECT_EQ(S_OK, hr);
+ ASSERT_EQ(child_count, children_.size());
+
+ scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]);
+ LONG obtained_count = 0;
+ hr = AccessibleChildren(parent, 0, child_count,
+ child_array.get(), &obtained_count);
+ ASSERT_EQ(S_OK, hr);
+ ASSERT_EQ(child_count, obtained_count);
+
+ VARIANT* child = child_array.get();
+ for (AccessibleCheckerVector::iterator child_checker = children_.begin();
+ child_checker != children_.end();
+ ++child_checker, ++child) {
+ base::win::ScopedComPtr<IAccessible> child_accessible(
+ GetAccessibleFromResultVariant(parent, child));
+ ASSERT_TRUE(child_accessible.get());
+ (*child_checker)->CheckAccessible(child_accessible);
+ }
+}
+
+string16 AccessibleChecker::RoleVariantToString(
+ const base::win::ScopedVariant& role) {
+ if (role.type() == VT_I4)
+ return IAccessibleRoleToString(V_I4(&role));
+ if (role.type() == VT_BSTR)
+ return string16(V_BSTR(&role), SysStringLen(V_BSTR(&role)));
+ return string16();
+}
+
+} // namespace
+
+
+// Tests ----------------------------------------------------------------------
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ MAYBE(TestBusyAccessibilityTree)) {
+ NavigateToURL(shell(), GURL(kAboutBlankURL));
+
+ // The initial accessible returned should have state STATE_SYSTEM_BUSY while
+ // the accessibility tree is being requested from the renderer.
+ AccessibleChecker document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ document1_checker.SetExpectedState(
+ STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
+ STATE_SYSTEM_BUSY);
+ document1_checker.CheckAccessible(GetRendererAccessible());
+}
+
+// Flaky, http://crbug.com/167320 .
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ DISABLED_TestRendererAccessibilityTree) {
+ LoadInitialAccessibilityTreeFromHtml(
+ "<html><head><title>Accessibility Win Test</title></head>"
+ "<body><input type='button' value='push' /><input type='checkbox' />"
+ "</body></html>");
+
+ // Check the browser's copy of the renderer accessibility tree.
+ AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON,
+ std::wstring());
+ AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON,
+ std::wstring());
+ AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
+ std::wstring());
+ AccessibleChecker document2_checker(L"Accessibility Win Test",
+ ROLE_SYSTEM_DOCUMENT, std::wstring());
+ body_checker.AppendExpectedChild(&button_checker);
+ body_checker.AppendExpectedChild(&checkbox_checker);
+ document2_checker.AppendExpectedChild(&body_checker);
+ document2_checker.CheckAccessible(GetRendererAccessible());
+
+ // Check that document accessible has a parent accessible.
+ base::win::ScopedComPtr<IAccessible> document_accessible(
+ GetRendererAccessible());
+ ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
+ base::win::ScopedComPtr<IDispatch> parent_dispatch;
+ HRESULT hr = document_accessible->get_accParent(parent_dispatch.Receive());
+ EXPECT_EQ(S_OK, hr);
+ EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL));
+
+ // Navigate to another page.
+ NavigateToURL(shell(), GURL(kAboutBlankURL));
+
+ // Verify that the IAccessible reference still points to a valid object and
+ // that calls to its methods fail since the tree is no longer valid after
+ // the page navagation.
+ base::win::ScopedBstr name;
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ hr = document_accessible->get_accName(childid_self, name.Receive());
+ ASSERT_EQ(E_FAIL, hr);
+}
+
+// Periodically failing. See crbug.com/145537
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ DISABLED_TestNotificationActiveDescendantChanged) {
+ LoadInitialAccessibilityTreeFromHtml(
+ "<ul tabindex='-1' role='radiogroup' aria-label='ul'>"
+ "<li id='li'>li</li></ul>");
+
+ // Check the browser's copy of the renderer accessibility tree.
+ AccessibleChecker list_marker_checker(L"\x2022", ROLE_SYSTEM_TEXT,
+ std::wstring());
+ AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT,
+ std::wstring());
+ AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM,
+ std::wstring());
+ list_item_checker.SetExpectedState(STATE_SYSTEM_READONLY);
+ AccessibleChecker radio_group_checker(L"ul", ROLE_SYSTEM_GROUPING,
+ IA2_ROLE_SECTION, std::wstring());
+ radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
+ AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ list_item_checker.AppendExpectedChild(&list_marker_checker);
+ list_item_checker.AppendExpectedChild(&static_text_checker);
+ radio_group_checker.AppendExpectedChild(&list_item_checker);
+ document_checker.AppendExpectedChild(&radio_group_checker);
+ document_checker.CheckAccessible(GetRendererAccessible());
+
+ // Set focus to the radio group.
+ scoped_ptr<AccessibilityNotificationWaiter> waiter(
+ new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationFocusChanged));
+ ExecuteScript(L"document.body.children[0].focus()");
+ waiter->WaitForNotification();
+
+ // Check that the accessibility tree of the browser has been updated.
+ radio_group_checker.SetExpectedState(
+ STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
+ document_checker.CheckAccessible(GetRendererAccessible());
+
+ // Set the active descendant of the radio group
+ waiter.reset(new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationFocusChanged));
+ ExecuteScript(
+ L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
+ waiter->WaitForNotification();
+
+ // Check that the accessibility tree of the browser has been updated.
+ list_item_checker.SetExpectedState(
+ STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
+ radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
+ document_checker.CheckAccessible(GetRendererAccessible());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ MAYBE(TestNotificationCheckedStateChanged)) {
+ LoadInitialAccessibilityTreeFromHtml(
+ "<body><input type='checkbox' /></body>");
+
+ // Check the browser's copy of the renderer accessibility tree.
+ AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON,
+ std::wstring());
+ checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
+ AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
+ std::wstring());
+ AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ body_checker.AppendExpectedChild(&checkbox_checker);
+ document_checker.AppendExpectedChild(&body_checker);
+ document_checker.CheckAccessible(GetRendererAccessible());
+
+ // Check the checkbox.
+ scoped_ptr<AccessibilityNotificationWaiter> waiter(
+ new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationCheckStateChanged));
+ ExecuteScript(L"document.body.children[0].checked=true");
+ waiter->WaitForNotification();
+
+ // Check that the accessibility tree of the browser has been updated.
+ checkbox_checker.SetExpectedState(
+ STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE);
+ document_checker.CheckAccessible(GetRendererAccessible());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ MAYBE(TestNotificationChildrenChanged)) {
+ // The role attribute causes the node to be in the accessibility tree.
+ LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
+
+ // Check the browser's copy of the renderer accessibility tree.
+ AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
+ std::wstring());
+ AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ document_checker.AppendExpectedChild(&group_checker);
+ document_checker.CheckAccessible(GetRendererAccessible());
+
+ // Change the children of the document body.
+ scoped_ptr<AccessibilityNotificationWaiter> waiter(
+ new AccessibilityNotificationWaiter(
+ shell(),
+ AccessibilityModeComplete,
+ AccessibilityNotificationChildrenChanged));
+ ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
+ waiter->WaitForNotification();
+
+ // Check that the accessibility tree of the browser has been updated.
+ AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, std::wstring());
+ group_checker.AppendExpectedChild(&text_checker);
+ document_checker.CheckAccessible(GetRendererAccessible());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ MAYBE(TestNotificationChildrenChanged2)) {
+ // The role attribute causes the node to be in the accessibility tree.
+ LoadInitialAccessibilityTreeFromHtml(
+ "<div role=group style='visibility: hidden'>text</div>");
+
+ // Check the accessible tree of the browser.
+ AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ document_checker.CheckAccessible(GetRendererAccessible());
+
+ // Change the children of the document body.
+ scoped_ptr<AccessibilityNotificationWaiter> waiter(
+ new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationChildrenChanged));
+ ExecuteScript(L"document.body.children[0].style.visibility='visible'");
+ waiter->WaitForNotification();
+
+ // Check that the accessibility tree of the browser has been updated.
+ AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT,
+ std::wstring());
+ AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
+ std::wstring());
+ document_checker.AppendExpectedChild(&group_checker);
+ group_checker.AppendExpectedChild(&static_text_checker);
+ document_checker.CheckAccessible(GetRendererAccessible());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ MAYBE(TestNotificationFocusChanged)) {
+ // The role attribute causes the node to be in the accessibility tree.
+ LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
+
+ // Check the browser's copy of the renderer accessibility tree.
+ SCOPED_TRACE("Check initial tree");
+ AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
+ std::wstring());
+ group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
+ AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ document_checker.AppendExpectedChild(&group_checker);
+ document_checker.CheckAccessible(GetRendererAccessible());
+
+ // Focus the div in the document
+ scoped_ptr<AccessibilityNotificationWaiter> waiter(
+ new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationFocusChanged));
+ ExecuteScript(L"document.body.children[0].focus()");
+ waiter->WaitForNotification();
+
+ // Check that the accessibility tree of the browser has been updated.
+ SCOPED_TRACE("Check updated tree after focusing div");
+ group_checker.SetExpectedState(
+ STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
+ document_checker.CheckAccessible(GetRendererAccessible());
+
+ // Focus the document accessible. This will un-focus the current node.
+ waiter.reset(
+ new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationBlur));
+ base::win::ScopedComPtr<IAccessible> document_accessible(
+ GetRendererAccessible());
+ ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ HRESULT hr = document_accessible->accSelect(SELFLAG_TAKEFOCUS, childid_self);
+ ASSERT_EQ(S_OK, hr);
+ waiter->WaitForNotification();
+
+ // Check that the accessibility tree of the browser has been updated.
+ SCOPED_TRACE("Check updated tree after focusing document again");
+ group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
+ document_checker.CheckAccessible(GetRendererAccessible());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ MAYBE(TestNotificationValueChanged)) {
+ LoadInitialAccessibilityTreeFromHtml(
+ "<body><input type='text' value='old value'/></body>");
+
+ // Check the browser's copy of the renderer accessibility tree.
+ AccessibleChecker text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT,
+ L"old value");
+ text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
+ AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
+ std::wstring());
+ AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ body_checker.AppendExpectedChild(&text_field_checker);
+ document_checker.AppendExpectedChild(&body_checker);
+ document_checker.CheckAccessible(GetRendererAccessible());
+
+ // Set the value of the text control
+ scoped_ptr<AccessibilityNotificationWaiter> waiter(
+ new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationValueChanged));
+ ExecuteScript(L"document.body.children[0].value='new value'");
+ waiter->WaitForNotification();
+
+ // Check that the accessibility tree of the browser has been updated.
+ text_field_checker.SetExpectedValue(L"new value");
+ document_checker.CheckAccessible(GetRendererAccessible());
+}
+
+// This test verifies that the web content's accessibility tree is a
+// descendant of the main browser window's accessibility tree, so that
+// tools like AccExplorer32 or AccProbe can be used to examine Chrome's
+// accessibility support.
+//
+// If you made a change and this test now fails, check that the NativeViewHost
+// that wraps the tab contents returns the IAccessible implementation
+// provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ MAYBE(ContainsRendererAccessibilityTree)) {
+ LoadInitialAccessibilityTreeFromHtml(
+ "<html><head><title>MyDocument</title></head>"
+ "<body>Content</body></html>");
+
+ // Get the accessibility object for the browser window.
+ HWND browser_hwnd = shell()->window();
+ base::win::ScopedComPtr<IAccessible> browser_accessible;
+ HRESULT hr = AccessibleObjectFromWindow(
+ browser_hwnd,
+ OBJID_WINDOW,
+ IID_IAccessible,
+ reinterpret_cast<void**>(browser_accessible.Receive()));
+ ASSERT_EQ(S_OK, hr);
+
+ bool found = false;
+ RecursiveFindNodeInAccessibilityTree(
+ browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found);
+ ASSERT_EQ(found, true);
+}
+
+// Disabled because of http://crbug.com/144390.
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ DISABLED_TestToggleButtonRoleAndStates) {
+ AccessibleChecker* button_checker;
+ std::string button_html("data:text/html,");
+ AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
+ std::wstring());
+ document_checker.AppendExpectedChild(&body_checker);
+
+// Temporary macro
+#define ADD_BUTTON(html, ia2_role, state) \
+ button_html += html; \
+ button_checker = new AccessibleChecker(L"x", ROLE_SYSTEM_PUSHBUTTON, \
+ ia2_role, std::wstring()); \
+ button_checker->SetExpectedState(state); \
+ body_checker.AppendExpectedChild(button_checker)
+
+ // If aria-pressed is 'undefined', empty or not present, use PUSHBUTTON
+ // Otherwise use TOGGLE_BUTTON, even if the value is invalid.
+ // The spec does this in an attempt future-proof in case new values are added.
+ ADD_BUTTON("<span role='button' aria-pressed='false'>x</span>",
+ IA2_ROLE_TOGGLE_BUTTON, 0);
+ ADD_BUTTON("<span role='button' aria-pressed='true'>x</span>",
+ IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_PRESSED);
+ ADD_BUTTON("<span role='button' aria-pressed='mixed'>x</span>",
+ IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_MIXED);
+ ADD_BUTTON("<span role='button' aria-pressed='xyz'>x</span>",
+ IA2_ROLE_TOGGLE_BUTTON, 0);
+ ADD_BUTTON("<span role='button' aria-pressed=''>x</span>",
+ ROLE_SYSTEM_PUSHBUTTON, 0);
+ ADD_BUTTON("<span role='button' aria-pressed>x</span>",
+ ROLE_SYSTEM_PUSHBUTTON, 0);
+ ADD_BUTTON("<span role='button' aria-pressed='undefined'>x</span>",
+ ROLE_SYSTEM_PUSHBUTTON, 0);
+ ADD_BUTTON("<span role='button'>x</span>", ROLE_SYSTEM_PUSHBUTTON, 0);
+ ADD_BUTTON("<input type='button' aria-pressed='true' value='x'/>",
+ IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_PRESSED);
+ ADD_BUTTON("<input type='button' aria-pressed='false' value='x'/>",
+ IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<input type='button' aria-pressed='mixed' value='x'>",
+ IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_MIXED);
+ ADD_BUTTON("<input type='button' aria-pressed='xyz' value='x'/>",
+ IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<input type='button' aria-pressed='' value='x'/>",
+ ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<input type='button' aria-pressed value='x'>",
+ ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<input type='button' aria-pressed='undefined' value='x'>",
+ ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<input type='button' value='x'>",
+ ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<button aria-pressed='true'>x</button>",
+ IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_PRESSED);
+ ADD_BUTTON("<button aria-pressed='false'>x</button>",
+ IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<button aria-pressed='mixed'>x</button>", IA2_ROLE_TOGGLE_BUTTON,
+ STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_MIXED);
+ ADD_BUTTON("<button aria-pressed='xyz'>x</button>", IA2_ROLE_TOGGLE_BUTTON,
+ STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<button aria-pressed=''>x</button>",
+ ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<button aria-pressed>x</button>",
+ ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<button aria-pressed='undefined'>x</button>",
+ ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
+ ADD_BUTTON("<button>x</button>", ROLE_SYSTEM_PUSHBUTTON,
+ STATE_SYSTEM_FOCUSABLE);
+#undef ADD_BUTTON // Temporary macro
+
+ LoadInitialAccessibilityTreeFromHtml(button_html);
+ document_checker.CheckAccessible(GetRendererAccessible());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ MAYBE(SupportsISimpleDOM)) {
+ LoadInitialAccessibilityTreeFromHtml(
+ "<body><input type='checkbox' /></body>");
+
+ // Get the IAccessible object for the document.
+ base::win::ScopedComPtr<IAccessible> document_accessible(
+ GetRendererAccessible());
+ ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
+
+ // Get the ISimpleDOM object for the document.
+ base::win::ScopedComPtr<IServiceProvider> service_provider;
+ HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface(
+ service_provider.Receive());
+ ASSERT_EQ(S_OK, hr);
+ const GUID refguid = {0x0c539790, 0x12e4, 0x11cf,
+ 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
+ base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
+ hr = static_cast<IServiceProvider *>(service_provider)->QueryService(
+ refguid, IID_ISimpleDOMNode,
+ reinterpret_cast<void**>(document_isimpledomnode.Receive()));
+ ASSERT_EQ(S_OK, hr);
+
+ base::win::ScopedBstr node_name;
+ short name_space_id; // NOLINT
+ base::win::ScopedBstr node_value;
+ unsigned int num_children;
+ unsigned int unique_id;
+ unsigned short node_type; // NOLINT
+ hr = document_isimpledomnode->get_nodeInfo(
+ node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
+ &unique_id, &node_type);
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
+ EXPECT_EQ(1, num_children);
+ node_name.Reset();
+ node_value.Reset();
+
+ base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
+ hr = document_isimpledomnode->get_firstChild(
+ body_isimpledomnode.Receive());
+ ASSERT_EQ(S_OK, hr);
+ hr = body_isimpledomnode->get_nodeInfo(
+ node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
+ &unique_id, &node_type);
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_EQ(L"body", std::wstring(node_name, node_name.Length()));
+ EXPECT_EQ(NODETYPE_ELEMENT, node_type);
+ EXPECT_EQ(1, num_children);
+ node_name.Reset();
+ node_value.Reset();
+
+ base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
+ hr = body_isimpledomnode->get_firstChild(
+ checkbox_isimpledomnode.Receive());
+ ASSERT_EQ(S_OK, hr);
+ hr = checkbox_isimpledomnode->get_nodeInfo(
+ node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
+ &unique_id, &node_type);
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_EQ(L"input", std::wstring(node_name, node_name.Length()));
+ EXPECT_EQ(NODETYPE_ELEMENT, node_type);
+ EXPECT_EQ(0, num_children);
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, MAYBE(TestRoleGroup)) {
+ LoadInitialAccessibilityTreeFromHtml(
+ "<fieldset></fieldset><div role=group></div>");
+
+ // Check the browser's copy of the renderer accessibility tree.
+ AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
+ std::wstring());
+ AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
+ std::wstring());
+ AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
+ std::wstring());
+ document_checker.AppendExpectedChild(&grouping1_checker);
+ document_checker.AppendExpectedChild(&grouping2_checker);
+ document_checker.CheckAccessible(GetRendererAccessible());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility.cc b/chromium/content/browser/accessibility/browser_accessibility.cc
new file mode 100644
index 00000000000..ae7efe2f660
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility.cc
@@ -0,0 +1,341 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/common/accessibility_messages.h"
+
+namespace content {
+
+typedef AccessibilityNodeData::BoolAttribute BoolAttribute;
+typedef AccessibilityNodeData::FloatAttribute FloatAttribute;
+typedef AccessibilityNodeData::IntAttribute IntAttribute;
+typedef AccessibilityNodeData::StringAttribute StringAttribute;
+
+#if !defined(OS_MACOSX) && \
+ !defined(OS_WIN) && \
+ !defined(TOOLKIT_GTK) && \
+ !defined(OS_ANDROID)
+// We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
+// and Win. For any other platform, instantiate the base class.
+// static
+BrowserAccessibility* BrowserAccessibility::Create() {
+ return new BrowserAccessibility();
+}
+#endif
+
+BrowserAccessibility::BrowserAccessibility()
+ : manager_(NULL),
+ parent_(NULL),
+ index_in_parent_(0),
+ renderer_id_(0),
+ role_(0),
+ state_(0),
+ instance_active_(false) {
+}
+
+BrowserAccessibility::~BrowserAccessibility() {
+}
+
+void BrowserAccessibility::DetachTree(
+ std::vector<BrowserAccessibility*>* nodes) {
+ nodes->push_back(this);
+ for (size_t i = 0; i < children_.size(); i++)
+ children_[i]->DetachTree(nodes);
+ children_.clear();
+ parent_ = NULL;
+}
+
+void BrowserAccessibility::InitializeTreeStructure(
+ BrowserAccessibilityManager* manager,
+ BrowserAccessibility* parent,
+ int32 renderer_id,
+ int32 index_in_parent) {
+ manager_ = manager;
+ parent_ = parent;
+ renderer_id_ = renderer_id;
+ index_in_parent_ = index_in_parent;
+}
+
+void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) {
+ DCHECK_EQ(renderer_id_, src.id);
+ name_ = src.name;
+ value_ = src.value;
+ role_ = src.role;
+ state_ = src.state;
+ string_attributes_ = src.string_attributes;
+ int_attributes_ = src.int_attributes;
+ float_attributes_ = src.float_attributes;
+ bool_attributes_ = src.bool_attributes;
+ html_attributes_ = src.html_attributes;
+ location_ = src.location;
+ indirect_child_ids_ = src.indirect_child_ids;
+ line_breaks_ = src.line_breaks;
+ cell_ids_ = src.cell_ids;
+ unique_cell_ids_ = src.unique_cell_ids;
+ instance_active_ = true;
+
+ PreInitialize();
+}
+
+bool BrowserAccessibility::IsNative() const {
+ return false;
+}
+
+void BrowserAccessibility::SwapChildren(
+ std::vector<BrowserAccessibility*>& children) {
+ children.swap(children_);
+}
+
+void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
+ int index_in_parent) {
+ parent_ = parent;
+ index_in_parent_ = index_in_parent;
+}
+
+void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
+ location_ = new_location;
+}
+
+bool BrowserAccessibility::IsDescendantOf(
+ BrowserAccessibility* ancestor) {
+ if (this == ancestor) {
+ return true;
+ } else if (parent_) {
+ return parent_->IsDescendantOf(ancestor);
+ }
+
+ return false;
+}
+
+BrowserAccessibility* BrowserAccessibility::GetChild(uint32 child_index) const {
+ DCHECK(child_index < children_.size());
+ return children_[child_index];
+}
+
+BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
+ if (parent_ && index_in_parent_ > 0)
+ return parent_->children_[index_in_parent_ - 1];
+
+ return NULL;
+}
+
+BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
+ if (parent_ &&
+ index_in_parent_ >= 0 &&
+ index_in_parent_ < static_cast<int>(parent_->children_.size() - 1)) {
+ return parent_->children_[index_in_parent_ + 1];
+ }
+
+ return NULL;
+}
+
+gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
+ gfx::Rect bounds = location_;
+
+ // Walk up the parent chain. Every time we encounter a Web Area, offset
+ // based on the scroll bars and then offset based on the origin of that
+ // nested web area.
+ BrowserAccessibility* parent = parent_;
+ bool need_to_offset_web_area =
+ (role_ == AccessibilityNodeData::ROLE_WEB_AREA ||
+ role_ == AccessibilityNodeData::ROLE_ROOT_WEB_AREA);
+ while (parent) {
+ if (need_to_offset_web_area &&
+ parent->location().width() > 0 &&
+ parent->location().height() > 0) {
+ bounds.Offset(parent->location().x(), parent->location().y());
+ need_to_offset_web_area = false;
+ }
+
+ // On some platforms, we don't want to take the root scroll offsets
+ // into account.
+ if (parent->role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA &&
+ !manager()->UseRootScrollOffsetsWhenComputingBounds()) {
+ break;
+ }
+
+ if (parent->role() == AccessibilityNodeData::ROLE_WEB_AREA ||
+ parent->role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA) {
+ int sx = 0;
+ int sy = 0;
+ if (parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
+ parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &sy)) {
+ bounds.Offset(-sx, -sy);
+ }
+ need_to_offset_web_area = true;
+ }
+ parent = parent->parent();
+ }
+
+ return bounds;
+}
+
+gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
+ gfx::Rect bounds = GetLocalBoundsRect();
+
+ // Adjust the bounds by the top left corner of the containing view's bounds
+ // in screen coordinates.
+ bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
+
+ return bounds;
+}
+
+BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
+ const gfx::Point& point) {
+ // Walk the children recursively looking for the BrowserAccessibility that
+ // most tightly encloses the specified point.
+ for (int i = children_.size() - 1; i >= 0; --i) {
+ BrowserAccessibility* child = children_[i];
+ if (child->GetGlobalBoundsRect().Contains(point))
+ return child->BrowserAccessibilityForPoint(point);
+ }
+ return this;
+}
+
+void BrowserAccessibility::Destroy() {
+ for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
+ iter != children_.end();
+ ++iter) {
+ (*iter)->Destroy();
+ }
+ children_.clear();
+
+ // Allow the object to fire a TextRemoved notification.
+ name_.clear();
+ value_.clear();
+ PostInitialize();
+
+ manager_->NotifyAccessibilityEvent(
+ AccessibilityNotificationObjectHide, this);
+
+ instance_active_ = false;
+ manager_->RemoveNode(this);
+ NativeReleaseReference();
+}
+
+void BrowserAccessibility::NativeReleaseReference() {
+ delete this;
+}
+
+bool BrowserAccessibility::GetBoolAttribute(
+ BoolAttribute attribute, bool* value) const {
+ BoolAttrMap::const_iterator iter = bool_attributes_.find(attribute);
+ if (iter != bool_attributes_.end()) {
+ *value = iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+bool BrowserAccessibility::GetFloatAttribute(
+ FloatAttribute attribute, float* value) const {
+ FloatAttrMap::const_iterator iter = float_attributes_.find(attribute);
+ if (iter != float_attributes_.end()) {
+ *value = iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+bool BrowserAccessibility::GetIntAttribute(
+ IntAttribute attribute, int* value) const {
+ IntAttrMap::const_iterator iter = int_attributes_.find(attribute);
+ if (iter != int_attributes_.end()) {
+ *value = iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+bool BrowserAccessibility::GetStringAttribute(
+ StringAttribute attribute,
+ string16* value) const {
+ StringAttrMap::const_iterator iter = string_attributes_.find(attribute);
+ if (iter != string_attributes_.end()) {
+ *value = iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+bool BrowserAccessibility::GetHtmlAttribute(
+ const char* html_attr, string16* value) const {
+ for (size_t i = 0; i < html_attributes_.size(); i++) {
+ const string16& attr = html_attributes_[i].first;
+ if (LowerCaseEqualsASCII(attr, html_attr)) {
+ *value = html_attributes_[i].second;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool BrowserAccessibility::GetAriaTristate(
+ const char* html_attr,
+ bool* is_defined,
+ bool* is_mixed) const {
+ *is_defined = false;
+ *is_mixed = false;
+
+ string16 value;
+ if (!GetHtmlAttribute(html_attr, &value) ||
+ value.empty() ||
+ EqualsASCII(value, "undefined")) {
+ return false; // Not set (and *is_defined is also false)
+ }
+
+ *is_defined = true;
+
+ if (EqualsASCII(value, "true"))
+ return true;
+
+ if (EqualsASCII(value, "mixed"))
+ *is_mixed = true;
+
+ return false; // Not set
+}
+
+bool BrowserAccessibility::HasState(
+ AccessibilityNodeData::State state_enum) const {
+ return (state_ >> state_enum) & 1;
+}
+
+bool BrowserAccessibility::IsEditableText() const {
+ // These roles don't have readonly set, but they're not editable text.
+ if (role_ == AccessibilityNodeData::ROLE_SCROLLAREA ||
+ role_ == AccessibilityNodeData::ROLE_COLUMN ||
+ role_ == AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER) {
+ return false;
+ }
+
+ // Note: STATE_READONLY being false means it's either a text control,
+ // or contenteditable. We also check for editable text roles to cover
+ // another element that has role=textbox set on it.
+ return (!HasState(AccessibilityNodeData::STATE_READONLY) ||
+ role_ == AccessibilityNodeData::ROLE_TEXT_FIELD ||
+ role_ == AccessibilityNodeData::ROLE_TEXTAREA);
+}
+
+string16 BrowserAccessibility::GetTextRecursive() const {
+ if (!name_.empty()) {
+ return name_;
+ }
+
+ string16 result;
+ for (size_t i = 0; i < children_.size(); ++i)
+ result += children_[i]->GetTextRecursive();
+ return result;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility.h b/chromium/content/browser/accessibility/browser_accessibility.h
new file mode 100644
index 00000000000..8fda68fc079
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility.h
@@ -0,0 +1,296 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_H_
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "content/common/accessibility_node_data.h"
+#include "content/common/content_export.h"
+
+#if defined(OS_MACOSX) && __OBJC__
+@class BrowserAccessibilityCocoa;
+#endif
+
+namespace content {
+class BrowserAccessibilityManager;
+#if defined(OS_WIN)
+class BrowserAccessibilityWin;
+#elif defined(TOOLKIT_GTK)
+class BrowserAccessibilityGtk;
+#endif
+
+typedef std::map<AccessibilityNodeData::BoolAttribute, bool> BoolAttrMap;
+typedef std::map<AccessibilityNodeData::FloatAttribute, float> FloatAttrMap;
+typedef std::map<AccessibilityNodeData::IntAttribute, int> IntAttrMap;
+typedef std::map<AccessibilityNodeData::StringAttribute, string16>
+ StringAttrMap;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// BrowserAccessibility
+//
+// Class implementing the cross platform interface for the Browser-Renderer
+// communication of accessibility information, providing accessibility
+// to be used by screen readers and other assistive technology (AT).
+//
+// An implementation for each platform handles platform specific accessibility
+// APIs.
+//
+////////////////////////////////////////////////////////////////////////////////
+class CONTENT_EXPORT BrowserAccessibility {
+ public:
+ // Creates a platform specific BrowserAccessibility. Ownership passes to the
+ // caller.
+ static BrowserAccessibility* Create();
+
+ virtual ~BrowserAccessibility();
+
+ // Detach all descendants of this subtree and push all of the node pointers,
+ // including this node, onto the end of |nodes|.
+ virtual void DetachTree(std::vector<BrowserAccessibility*>* nodes);
+
+ // Perform platform-specific initialization. This can be called multiple times
+ // during the lifetime of this instance after the members of this base object
+ // have been reset with new values from the renderer process.
+ // Child dependent initialization can be done here.
+ virtual void PostInitialize() {}
+
+ // Returns true if this is a native platform-specific object, vs a
+ // cross-platform generic object.
+ virtual bool IsNative() const;
+
+ // Initialize the tree structure of this object.
+ void InitializeTreeStructure(
+ BrowserAccessibilityManager* manager,
+ BrowserAccessibility* parent,
+ int32 renderer_id,
+ int32 index_in_parent);
+
+ // Initialize this object's data.
+ void InitializeData(const AccessibilityNodeData& src);
+
+ virtual void SwapChildren(std::vector<BrowserAccessibility*>& children);
+
+ // Update the parent and index in parent if this node has been moved.
+ void UpdateParent(BrowserAccessibility* parent, int index_in_parent);
+
+ // Update this node's location, leaving everything else the same.
+ virtual void SetLocation(const gfx::Rect& new_location);
+
+ // Return true if this object is equal to or a descendant of |ancestor|.
+ bool IsDescendantOf(BrowserAccessibility* ancestor);
+
+ // Returns the parent of this object, or NULL if it's the root.
+ BrowserAccessibility* parent() const { return parent_; }
+
+ // Returns the number of children of this object.
+ uint32 child_count() const { return children_.size(); }
+
+ // Return a pointer to the child with the given index.
+ BrowserAccessibility* GetChild(uint32 child_index) const;
+
+ // Return the previous sibling of this object, or NULL if it's the first
+ // child of its parent.
+ BrowserAccessibility* GetPreviousSibling();
+
+ // Return the next sibling of this object, or NULL if it's the last child
+ // of its parent.
+ BrowserAccessibility* GetNextSibling();
+
+ // Returns the bounds of this object in coordinates relative to the
+ // top-left corner of the overall web area.
+ gfx::Rect GetLocalBoundsRect() const;
+
+ // Returns the bounds of this object in screen coordinates.
+ gfx::Rect GetGlobalBoundsRect() const;
+
+ // Returns the deepest descendant that contains the specified point
+ // (in global screen coordinates).
+ BrowserAccessibility* BrowserAccessibilityForPoint(const gfx::Point& point);
+
+ // Marks this object for deletion, releases our reference to it, and
+ // recursively calls Destroy() on its children. May not delete
+ // immediately due to reference counting.
+ //
+ // Reference counting is used on some platforms because the
+ // operating system may hold onto a reference to a BrowserAccessibility
+ // object even after we're through with it. When a BrowserAccessibility
+ // has had Destroy() called but its reference count is not yet zero,
+ // queries on this object return failure
+ virtual void Destroy();
+
+ // Subclasses should override this to support platform reference counting.
+ virtual void NativeAddReference() { }
+
+ // Subclasses should override this to support platform reference counting.
+ virtual void NativeReleaseReference();
+
+ //
+ // Accessors
+ //
+
+ const BoolAttrMap& bool_attributes() const {
+ return bool_attributes_;
+ }
+
+ const FloatAttrMap& float_attributes() const {
+ return float_attributes_;
+ }
+
+ const IntAttrMap& int_attributes() const {
+ return int_attributes_;
+ }
+
+ const StringAttrMap& string_attributes() const {
+ return string_attributes_;
+ }
+
+ const std::vector<BrowserAccessibility*>& children() const {
+ return children_;
+ }
+ const std::vector<std::pair<string16, string16> >& html_attributes() const {
+ return html_attributes_;
+ }
+ int32 index_in_parent() const { return index_in_parent_; }
+ const std::vector<int32>& indirect_child_ids() const {
+ return indirect_child_ids_;
+ }
+ const std::vector<int32>& line_breaks() const {
+ return line_breaks_;
+ }
+ const std::vector<int32>& cell_ids() const {
+ return cell_ids_;
+ }
+ const std::vector<int32>& unique_cell_ids() const {
+ return unique_cell_ids_;
+ }
+ gfx::Rect location() const { return location_; }
+ BrowserAccessibilityManager* manager() const { return manager_; }
+ const string16& name() const { return name_; }
+ int32 renderer_id() const { return renderer_id_; }
+ int32 role() const { return role_; }
+ const string16& role_name() const { return role_name_; }
+ int32 state() const { return state_; }
+ const string16& value() const { return value_; }
+ bool instance_active() const { return instance_active_; }
+
+#if defined(OS_MACOSX) && __OBJC__
+ BrowserAccessibilityCocoa* ToBrowserAccessibilityCocoa();
+#elif defined(OS_WIN)
+ BrowserAccessibilityWin* ToBrowserAccessibilityWin();
+#elif defined(TOOLKIT_GTK)
+ BrowserAccessibilityGtk* ToBrowserAccessibilityGtk();
+#endif
+
+ // Retrieve the value of a bool attribute from the bool attribute
+ // map and returns true if found.
+ bool GetBoolAttribute(
+ AccessibilityNodeData::BoolAttribute attr, bool* value) const;
+
+ // Retrieve the value of a float attribute from the float attribute
+ // map and returns true if found.
+ bool GetFloatAttribute(AccessibilityNodeData::FloatAttribute attr,
+ float* value) const;
+
+ // Retrieve the value of an integer attribute from the integer attribute
+ // map and returns true if found.
+ bool GetIntAttribute(AccessibilityNodeData::IntAttribute attribute,
+ int* value) const;
+
+ // Retrieve the value of a string attribute from the attribute map and
+ // returns true if found.
+ bool GetStringAttribute(
+ AccessibilityNodeData::StringAttribute attribute, string16* value) const;
+
+ // Retrieve the value of a html attribute from the attribute map and
+ // returns true if found.
+ bool GetHtmlAttribute(const char* attr, string16* value) const;
+
+ // Utility method to handle special cases for ARIA booleans, tristates and
+ // booleans which have a "mixed" state.
+ //
+ // Warning: the term "Tristate" is used loosely by the spec and here,
+ // as some attributes support a 4th state.
+ //
+ // The following attributes are appropriate to use with this method:
+ // aria-selected (selectable)
+ // aria-grabbed (grabbable)
+ // aria-expanded (expandable)
+ // aria-pressed (toggleable/pressable) -- supports 4th "mixed" state
+ // aria-checked (checkable) -- supports 4th "mixed state"
+ bool GetAriaTristate(const char* attr_name,
+ bool* is_defined,
+ bool* is_mixed) const;
+
+ // Returns true if the bit corresponding to the given state enum is 1.
+ bool HasState(AccessibilityNodeData::State state_enum) const;
+
+ // Returns true if this node is an editable text field of any kind.
+ bool IsEditableText() const;
+
+ // Append the text from this node and its children.
+ string16 GetTextRecursive() const;
+
+ protected:
+ // Perform platform specific initialization. This can be called multiple times
+ // during the lifetime of this instance after the members of this base object
+ // have been reset with new values from the renderer process.
+ // Perform child independent initialization in this method.
+ virtual void PreInitialize() {}
+
+ BrowserAccessibility();
+
+ // The manager of this tree of accessibility objects; needed for
+ // global operations like focus tracking.
+ BrowserAccessibilityManager* manager_;
+
+ // The parent of this object, may be NULL if we're the root object.
+ BrowserAccessibility* parent_;
+
+ // The index of this within its parent object.
+ int32 index_in_parent_;
+
+ // The ID of this object in the renderer process.
+ int32 renderer_id_;
+
+ // The children of this object.
+ std::vector<BrowserAccessibility*> children_;
+
+ // Accessibility metadata from the renderer
+ string16 name_;
+ string16 value_;
+ BoolAttrMap bool_attributes_;
+ IntAttrMap int_attributes_;
+ FloatAttrMap float_attributes_;
+ StringAttrMap string_attributes_;
+ std::vector<std::pair<string16, string16> > html_attributes_;
+ int32 role_;
+ int32 state_;
+ string16 role_name_;
+ gfx::Rect location_;
+ std::vector<int32> indirect_child_ids_;
+ std::vector<int32> line_breaks_;
+ std::vector<int32> cell_ids_;
+ std::vector<int32> unique_cell_ids_;
+
+ // BrowserAccessibility objects are reference-counted on some platforms.
+ // When we're done with this object and it's removed from our accessibility
+ // tree, a client may still be holding onto a pointer to this object, so
+ // we mark it as inactive so that calls to any of this object's methods
+ // immediately return failure.
+ bool instance_active_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibility);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.cc b/chromium/content/browser/accessibility/browser_accessibility_android.cc
new file mode 100644
index 00000000000..8ff37ec0c7d
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_android.cc
@@ -0,0 +1,406 @@
+// Copyright 2013 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 "content/browser/accessibility/browser_accessibility_android.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility_manager_android.h"
+#include "content/common/accessibility_messages.h"
+#include "content/common/accessibility_node_data.h"
+
+namespace content {
+
+// static
+BrowserAccessibility* BrowserAccessibility::Create() {
+ return new BrowserAccessibilityAndroid();
+}
+
+BrowserAccessibilityAndroid::BrowserAccessibilityAndroid() {
+ first_time_ = true;
+}
+
+bool BrowserAccessibilityAndroid::IsNative() const {
+ return true;
+}
+
+bool BrowserAccessibilityAndroid::IsLeaf() const {
+ if (child_count() == 0)
+ return true;
+
+ // Iframes are always allowed to contain children.
+ if (IsIframe() ||
+ role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA ||
+ role() == AccessibilityNodeData::ROLE_WEB_AREA) {
+ return false;
+ }
+
+ // If it has a focusable child, we definitely can't leave out children.
+ if (HasFocusableChild())
+ return false;
+
+ // Headings with text can drop their children.
+ string16 name = GetText();
+ if (role() == AccessibilityNodeData::ROLE_HEADING && !name.empty())
+ return true;
+
+ // Focusable nodes with text can drop their children.
+ if (HasState(AccessibilityNodeData::STATE_FOCUSABLE) && !name.empty())
+ return true;
+
+ // Nodes with only static text as children can drop their children.
+ if (HasOnlyStaticTextChildren())
+ return true;
+
+ return false;
+}
+
+bool BrowserAccessibilityAndroid::IsCheckable() const {
+ bool checkable = false;
+ bool is_aria_pressed_defined;
+ bool is_mixed;
+ GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed);
+ if (role() == AccessibilityNodeData::ROLE_CHECKBOX ||
+ role() == AccessibilityNodeData::ROLE_RADIO_BUTTON ||
+ is_aria_pressed_defined) {
+ checkable = true;
+ }
+ if (HasState(AccessibilityNodeData::STATE_CHECKED))
+ checkable = true;
+ return checkable;
+}
+
+bool BrowserAccessibilityAndroid::IsChecked() const {
+ return HasState(AccessibilityNodeData::STATE_CHECKED);
+}
+
+bool BrowserAccessibilityAndroid::IsClickable() const {
+ return (IsLeaf() && !GetText().empty());
+}
+
+bool BrowserAccessibilityAndroid::IsEnabled() const {
+ return !HasState(AccessibilityNodeData::STATE_UNAVAILABLE);
+}
+
+bool BrowserAccessibilityAndroid::IsFocusable() const {
+ bool focusable = HasState(AccessibilityNodeData::STATE_FOCUSABLE);
+ if (IsIframe() ||
+ role() == AccessibilityNodeData::ROLE_WEB_AREA) {
+ focusable = false;
+ }
+ return focusable;
+}
+
+bool BrowserAccessibilityAndroid::IsFocused() const {
+ return manager()->GetFocus(manager()->GetRoot()) == this;
+}
+
+bool BrowserAccessibilityAndroid::IsPassword() const {
+ return HasState(AccessibilityNodeData::STATE_PROTECTED);
+}
+
+bool BrowserAccessibilityAndroid::IsScrollable() const {
+ int dummy;
+ return GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X_MAX, &dummy);
+}
+
+bool BrowserAccessibilityAndroid::IsSelected() const {
+ return HasState(AccessibilityNodeData::STATE_SELECTED);
+}
+
+bool BrowserAccessibilityAndroid::IsVisibleToUser() const {
+ return !HasState(AccessibilityNodeData::STATE_INVISIBLE);
+}
+
+const char* BrowserAccessibilityAndroid::GetClassName() const {
+ const char* class_name = NULL;
+
+ switch(role()) {
+ case AccessibilityNodeData::ROLE_EDITABLE_TEXT:
+ case AccessibilityNodeData::ROLE_SPIN_BUTTON:
+ case AccessibilityNodeData::ROLE_TEXTAREA:
+ case AccessibilityNodeData::ROLE_TEXT_FIELD:
+ class_name = "android.widget.EditText";
+ break;
+ case AccessibilityNodeData::ROLE_SLIDER:
+ class_name = "android.widget.SeekBar";
+ break;
+ case AccessibilityNodeData::ROLE_COMBO_BOX:
+ class_name = "android.widget.Spinner";
+ break;
+ case AccessibilityNodeData::ROLE_BUTTON:
+ case AccessibilityNodeData::ROLE_MENU_BUTTON:
+ case AccessibilityNodeData::ROLE_POPUP_BUTTON:
+ class_name = "android.widget.Button";
+ break;
+ case AccessibilityNodeData::ROLE_CHECKBOX:
+ class_name = "android.widget.CheckBox";
+ break;
+ case AccessibilityNodeData::ROLE_RADIO_BUTTON:
+ class_name = "android.widget.RadioButton";
+ break;
+ case AccessibilityNodeData::ROLE_TOGGLE_BUTTON:
+ class_name = "android.widget.ToggleButton";
+ break;
+ case AccessibilityNodeData::ROLE_CANVAS:
+ case AccessibilityNodeData::ROLE_IMAGE:
+ class_name = "android.widget.Image";
+ break;
+ case AccessibilityNodeData::ROLE_PROGRESS_INDICATOR:
+ class_name = "android.widget.ProgressBar";
+ break;
+ case AccessibilityNodeData::ROLE_TAB_LIST:
+ class_name = "android.widget.TabWidget";
+ break;
+ case AccessibilityNodeData::ROLE_GRID:
+ case AccessibilityNodeData::ROLE_TABLE:
+ class_name = "android.widget.GridView";
+ break;
+ case AccessibilityNodeData::ROLE_LIST:
+ case AccessibilityNodeData::ROLE_LISTBOX:
+ class_name = "android.widget.ListView";
+ break;
+ default:
+ class_name = "android.view.View";
+ break;
+ }
+
+ return class_name;
+}
+
+string16 BrowserAccessibilityAndroid::GetText() const {
+ if (IsIframe() ||
+ role() == AccessibilityNodeData::ROLE_WEB_AREA) {
+ return string16();
+ }
+
+ string16 description;
+ GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description);
+
+ string16 text;
+ if (!name().empty())
+ text = name();
+ else if (!description.empty())
+ text = description;
+ else if (!value().empty())
+ text = value();
+
+ if (text.empty() && HasOnlyStaticTextChildren()) {
+ for (uint32 i = 0; i < child_count(); i++) {
+ BrowserAccessibility* child = GetChild(i);
+ text += static_cast<BrowserAccessibilityAndroid*>(child)->GetText();
+ }
+ }
+
+ switch(role()) {
+ case AccessibilityNodeData::ROLE_IMAGE_MAP_LINK:
+ case AccessibilityNodeData::ROLE_LINK:
+ case AccessibilityNodeData::ROLE_WEBCORE_LINK:
+ if (!text.empty())
+ text += ASCIIToUTF16(" ");
+ text += ASCIIToUTF16("Link");
+ break;
+ case AccessibilityNodeData::ROLE_HEADING:
+ // Only append "heading" if this node already has text.
+ if (!text.empty())
+ text += ASCIIToUTF16(" Heading");
+ break;
+ }
+
+ return text;
+}
+
+int BrowserAccessibilityAndroid::GetItemIndex() const {
+ int index = 0;
+ switch(role()) {
+ case AccessibilityNodeData::ROLE_LIST_ITEM:
+ case AccessibilityNodeData::ROLE_LISTBOX_OPTION:
+ index = index_in_parent();
+ break;
+ case AccessibilityNodeData::ROLE_SLIDER:
+ case AccessibilityNodeData::ROLE_PROGRESS_INDICATOR: {
+ float value_for_range;
+ if (GetFloatAttribute(
+ AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &value_for_range)) {
+ index = static_cast<int>(value_for_range);
+ }
+ break;
+ }
+ }
+ return index;
+}
+
+int BrowserAccessibilityAndroid::GetItemCount() const {
+ int count = 0;
+ switch(role()) {
+ case AccessibilityNodeData::ROLE_LIST:
+ case AccessibilityNodeData::ROLE_LISTBOX:
+ count = child_count();
+ break;
+ case AccessibilityNodeData::ROLE_SLIDER:
+ case AccessibilityNodeData::ROLE_PROGRESS_INDICATOR: {
+ float max_value_for_range;
+ if (GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
+ &max_value_for_range)) {
+ count = static_cast<int>(max_value_for_range);
+ }
+ break;
+ }
+ }
+ return count;
+}
+
+int BrowserAccessibilityAndroid::GetScrollX() const {
+ int value = 0;
+ GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &value);
+ return value;
+}
+
+int BrowserAccessibilityAndroid::GetScrollY() const {
+ int value = 0;
+ GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &value);
+ return value;
+}
+
+int BrowserAccessibilityAndroid::GetMaxScrollX() const {
+ int value = 0;
+ GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X_MAX, &value);
+ return value;
+}
+
+int BrowserAccessibilityAndroid::GetMaxScrollY() const {
+ int value = 0;
+ GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y_MAX, &value);
+ return value;
+}
+
+int BrowserAccessibilityAndroid::GetTextChangeFromIndex() const {
+ size_t index = 0;
+ while (index < old_value_.length() &&
+ index < new_value_.length() &&
+ old_value_[index] == new_value_[index]) {
+ index++;
+ }
+ return index;
+}
+
+int BrowserAccessibilityAndroid::GetTextChangeAddedCount() const {
+ size_t old_len = old_value_.length();
+ size_t new_len = new_value_.length();
+ size_t left = 0;
+ while (left < old_len &&
+ left < new_len &&
+ old_value_[left] == new_value_[left]) {
+ left++;
+ }
+ size_t right = 0;
+ while (right < old_len &&
+ right < new_len &&
+ old_value_[old_len - right - 1] == new_value_[new_len - right - 1]) {
+ right++;
+ }
+ return (new_len - left - right);
+}
+
+int BrowserAccessibilityAndroid::GetTextChangeRemovedCount() const {
+ size_t old_len = old_value_.length();
+ size_t new_len = new_value_.length();
+ size_t left = 0;
+ while (left < old_len &&
+ left < new_len &&
+ old_value_[left] == new_value_[left]) {
+ left++;
+ }
+ size_t right = 0;
+ while (right < old_len &&
+ right < new_len &&
+ old_value_[old_len - right - 1] == new_value_[new_len - right - 1]) {
+ right++;
+ }
+ return (old_len - left - right);
+}
+
+string16 BrowserAccessibilityAndroid::GetTextChangeBeforeText() const {
+ return old_value_;
+}
+
+int BrowserAccessibilityAndroid::GetSelectionStart() const {
+ int sel_start = 0;
+ GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start);
+ return sel_start;
+}
+
+int BrowserAccessibilityAndroid::GetSelectionEnd() const {
+ int sel_end = 0;
+ GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end);
+ return sel_end;
+}
+
+int BrowserAccessibilityAndroid::GetEditableTextLength() const {
+ return value().length();
+}
+
+bool BrowserAccessibilityAndroid::HasFocusableChild() const {
+ for (uint32 i = 0; i < child_count(); i++) {
+ BrowserAccessibility* child = GetChild(i);
+ if (child->HasState(AccessibilityNodeData::STATE_FOCUSABLE))
+ return true;
+ if (static_cast<BrowserAccessibilityAndroid*>(child)->HasFocusableChild())
+ return true;
+ }
+ return false;
+}
+
+bool BrowserAccessibilityAndroid::HasOnlyStaticTextChildren() const {
+ for (uint32 i = 0; i < child_count(); i++) {
+ BrowserAccessibility* child = GetChild(i);
+ if (child->role() != AccessibilityNodeData::ROLE_STATIC_TEXT)
+ return false;
+ }
+ return true;
+}
+
+bool BrowserAccessibilityAndroid::IsIframe() const {
+ string16 html_tag;
+ GetStringAttribute(AccessibilityNodeData::ATTR_HTML_TAG, &html_tag);
+ return html_tag == ASCIIToUTF16("iframe");
+}
+
+void BrowserAccessibilityAndroid::PostInitialize() {
+ BrowserAccessibility::PostInitialize();
+
+ if (IsEditableText()) {
+ if (value_ != new_value_) {
+ old_value_ = new_value_;
+ new_value_ = value_;
+ }
+ }
+
+ if (role_ == AccessibilityNodeData::ROLE_ALERT && first_time_)
+ manager_->NotifyAccessibilityEvent(AccessibilityNotificationAlert, this);
+
+ string16 live;
+ if (GetStringAttribute(AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS,
+ &live)) {
+ NotifyLiveRegionUpdate(live);
+ }
+
+ first_time_ = false;
+}
+
+void BrowserAccessibilityAndroid::NotifyLiveRegionUpdate(string16& aria_live) {
+ if (!EqualsASCII(aria_live, aria_strings::kAriaLivePolite) &&
+ !EqualsASCII(aria_live, aria_strings::kAriaLiveAssertive))
+ return;
+
+ string16 text = GetText();
+ if (cached_text_ != text) {
+ if (!text.empty()) {
+ manager_->NotifyAccessibilityEvent(AccessibilityNotificationObjectShow,
+ this);
+ }
+ cached_text_ = text;
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.h b/chromium/content/browser/accessibility/browser_accessibility_android.h
new file mode 100644
index 00000000000..8b4ed84bc9e
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_android.h
@@ -0,0 +1,74 @@
+// Copyright 2013 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_ANDROID_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_ANDROID_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+
+namespace content {
+
+class BrowserAccessibilityAndroid : public BrowserAccessibility {
+ public:
+ // Overrides from BrowserAccessibility.
+ virtual void PostInitialize() OVERRIDE;
+ virtual bool IsNative() const OVERRIDE;
+
+ bool IsLeaf() const;
+
+ bool IsCheckable() const;
+ bool IsChecked() const;
+ bool IsClickable() const;
+ bool IsEnabled() const;
+ bool IsFocusable() const;
+ bool IsFocused() const;
+ bool IsPassword() const;
+ bool IsScrollable() const;
+ bool IsSelected() const;
+ bool IsVisibleToUser() const;
+
+ const char* GetClassName() const;
+ string16 GetText() const;
+
+ int GetItemIndex() const;
+ int GetItemCount() const;
+
+ int GetScrollX() const;
+ int GetScrollY() const;
+ int GetMaxScrollX() const;
+ int GetMaxScrollY() const;
+
+ int GetTextChangeFromIndex() const;
+ int GetTextChangeAddedCount() const;
+ int GetTextChangeRemovedCount() const;
+ string16 GetTextChangeBeforeText() const;
+
+ int GetSelectionStart() const;
+ int GetSelectionEnd() const;
+ int GetEditableTextLength() const;
+
+ private:
+ // This gives BrowserAccessibility::Create access to the class constructor.
+ friend class BrowserAccessibility;
+
+ BrowserAccessibilityAndroid();
+
+ bool HasFocusableChild() const;
+ bool HasOnlyStaticTextChildren() const;
+ bool IsIframe() const;
+
+ void NotifyLiveRegionUpdate(string16& aria_live);
+
+ string16 cached_text_;
+ bool first_time_;
+ string16 old_value_;
+ string16 new_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityAndroid);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_ANDROID_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h
new file mode 100644
index 00000000000..a394822961c
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_COCOA_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_COCOA_H_
+
+#import <Cocoa/Cocoa.h>
+
+#import "base/mac/scoped_nsobject.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+#import "content/browser/accessibility/browser_accessibility_delegate_mac.h"
+#include "content/common/accessibility_node_data.h"
+
+// BrowserAccessibilityCocoa is a cocoa wrapper around the BrowserAccessibility
+// object. The renderer converts webkit's accessibility tree into a
+// WebAccessibility tree and passes it to the browser process over IPC.
+// This class converts it into a format Cocoa can query.
+@interface BrowserAccessibilityCocoa : NSObject {
+ @private
+ content::BrowserAccessibility* browserAccessibility_;
+ base::scoped_nsobject<NSMutableArray> children_;
+ id<BrowserAccessibilityDelegateCocoa> delegate_;
+}
+
+// This creates a cocoa browser accessibility object around
+// the cross platform BrowserAccessibility object. The delegate is
+// used to communicate with the host renderer. None of these
+// parameters can be null.
+- (id)initWithObject:(content::BrowserAccessibility*)accessibility
+ delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate;
+
+// Clear this object's pointer to the wrapped BrowserAccessibility object
+// because the wrapped object has been deleted, but this object may
+// persist if the system still has references to it.
+- (void)detach;
+
+// Invalidate children for a non-ignored ancestor (including self).
+- (void)childrenChanged;
+
+// Convenience method to get the internal, cross-platform role
+// from browserAccessibility_.
+- (content::AccessibilityNodeData::Role)internalRole;
+
+// Return the method name for the given attribute. For testing only.
+- (NSString*)methodNameForAttribute:(NSString*)attribute;
+
+// Internally-used method.
+@property(nonatomic, readonly) NSPoint origin;
+
+// Children is an array of BrowserAccessibility objects, representing
+// the accessibility children of this object.
+@property(nonatomic, readonly) NSString* accessKey;
+@property(nonatomic, readonly) NSNumber* ariaAtomic;
+@property(nonatomic, readonly) NSNumber* ariaBusy;
+@property(nonatomic, readonly) NSString* ariaLive;
+@property(nonatomic, readonly) NSString* ariaRelevant;
+@property(nonatomic, readonly) NSArray* children;
+@property(nonatomic, readonly) NSArray* columns;
+@property(nonatomic, readonly) NSArray* columnHeaders;
+@property(nonatomic, readonly) NSValue* columnIndexRange;
+@property(nonatomic, readonly) NSString* description;
+@property(nonatomic, readonly) NSNumber* disclosing;
+@property(nonatomic, readonly) id disclosedByRow;
+@property(nonatomic, readonly) NSNumber* disclosureLevel;
+@property(nonatomic, readonly) id disclosedRows;
+@property(nonatomic, readonly) NSNumber* enabled;
+@property(nonatomic, readonly) NSNumber* focused;
+@property(nonatomic, readonly) NSString* help;
+// isIgnored returns whether or not the accessibility object
+// should be ignored by the accessibility hierarchy.
+@property(nonatomic, readonly, getter=isIgnored) BOOL ignored;
+// Index of a row, column, or tree item.
+@property(nonatomic, readonly) NSNumber* index;
+@property(nonatomic, readonly) NSString* invalid;
+@property(nonatomic, readonly) NSNumber* loaded;
+@property(nonatomic, readonly) NSNumber* loadingProgress;
+@property(nonatomic, readonly) NSNumber* maxValue;
+@property(nonatomic, readonly) NSNumber* minValue;
+@property(nonatomic, readonly) NSNumber* numberOfCharacters;
+@property(nonatomic, readonly) NSString* orientation;
+@property(nonatomic, readonly) id parent;
+@property(nonatomic, readonly) NSValue* position;
+@property(nonatomic, readonly) NSNumber* required;
+// A string indicating the role of this object as far as accessibility
+// is concerned.
+@property(nonatomic, readonly) NSString* role;
+@property(nonatomic, readonly) NSString* roleDescription;
+@property(nonatomic, readonly) NSArray* rowHeaders;
+@property(nonatomic, readonly) NSValue* rowIndexRange;
+@property(nonatomic, readonly) NSArray* rows;
+// The size of this object.
+@property(nonatomic, readonly) NSValue* size;
+// A string indicating the subrole of this object as far as accessibility
+// is concerned.
+@property(nonatomic, readonly) NSString* subrole;
+// The tabs owned by a tablist.
+@property(nonatomic, readonly) NSArray* tabs;
+@property(nonatomic, readonly) NSString* title;
+@property(nonatomic, readonly) id titleUIElement;
+@property(nonatomic, readonly) NSString* url;
+@property(nonatomic, readonly) NSString* value;
+@property(nonatomic, readonly) NSString* valueDescription;
+@property(nonatomic, readonly) NSValue* visibleCharacterRange;
+@property(nonatomic, readonly) NSArray* visibleCells;
+@property(nonatomic, readonly) NSArray* visibleColumns;
+@property(nonatomic, readonly) NSArray* visibleRows;
+@property(nonatomic, readonly) NSNumber* visited;
+@property(nonatomic, readonly) id window;
+@end
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_COCOA_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm
new file mode 100644
index 00000000000..9d49767d266
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -0,0 +1,1505 @@
+// Copyright (c) 2012 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 <execinfo.h>
+
+#import "content/browser/accessibility/browser_accessibility_cocoa.h"
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
+#include "content/public/common/content_client.h"
+#include "grit/webkit_strings.h"
+
+// See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5,
+// 10.6, and 10.7. It allows accessibility clients to observe events posted on
+// this object.
+extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
+
+using content::AccessibilityNodeData;
+using content::BrowserAccessibility;
+using content::BrowserAccessibilityManager;
+using content::BrowserAccessibilityManagerMac;
+using content::ContentClient;
+typedef AccessibilityNodeData::StringAttribute StringAttribute;
+
+namespace {
+
+// Returns an autoreleased copy of the AccessibilityNodeData's attribute.
+NSString* NSStringForStringAttribute(
+ const std::map<StringAttribute, string16>& attributes,
+ StringAttribute attribute) {
+ std::map<StringAttribute, string16>::const_iterator iter =
+ attributes.find(attribute);
+ NSString* returnValue = @"";
+ if (iter != attributes.end()) {
+ returnValue = base::SysUTF16ToNSString(iter->second);
+ }
+ return returnValue;
+}
+
+struct MapEntry {
+ AccessibilityNodeData::Role webKitValue;
+ NSString* nativeValue;
+};
+
+typedef std::map<AccessibilityNodeData::Role, NSString*> RoleMap;
+
+// GetState checks the bitmask used in AccessibilityNodeData to check
+// if the given state was set on the accessibility object.
+bool GetState(BrowserAccessibility* accessibility, int state) {
+ return ((accessibility->state() >> state) & 1);
+}
+
+RoleMap BuildRoleMap() {
+ const MapEntry roles[] = {
+ { AccessibilityNodeData::ROLE_ALERT, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_ALERT_DIALOG, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_ANNOTATION, NSAccessibilityUnknownRole },
+ { AccessibilityNodeData::ROLE_APPLICATION, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_ARTICLE, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_BROWSER, NSAccessibilityBrowserRole },
+ { AccessibilityNodeData::ROLE_BUSY_INDICATOR,
+ NSAccessibilityBusyIndicatorRole },
+ { AccessibilityNodeData::ROLE_BUTTON, NSAccessibilityButtonRole },
+ { AccessibilityNodeData::ROLE_CANVAS, NSAccessibilityImageRole },
+ { AccessibilityNodeData::ROLE_CANVAS_WITH_FALLBACK_CONTENT,
+ NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_CELL, @"AXCell" },
+ { AccessibilityNodeData::ROLE_CHECKBOX, NSAccessibilityCheckBoxRole },
+ { AccessibilityNodeData::ROLE_COLOR_WELL, NSAccessibilityColorWellRole },
+ { AccessibilityNodeData::ROLE_COMBO_BOX, NSAccessibilityComboBoxRole },
+ { AccessibilityNodeData::ROLE_COLUMN, NSAccessibilityColumnRole },
+ { AccessibilityNodeData::ROLE_COLUMN_HEADER, @"AXCell" },
+ { AccessibilityNodeData::ROLE_DEFINITION, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_DESCRIPTION_LIST_DETAIL,
+ NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_DESCRIPTION_LIST_TERM,
+ NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_DIALOG, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_DIRECTORY, NSAccessibilityListRole },
+ { AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE,
+ NSAccessibilityDisclosureTriangleRole },
+ { AccessibilityNodeData::ROLE_DIV, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_DOCUMENT, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_DRAWER, NSAccessibilityDrawerRole },
+ { AccessibilityNodeData::ROLE_EDITABLE_TEXT, NSAccessibilityTextFieldRole },
+ { AccessibilityNodeData::ROLE_FOOTER, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_FORM, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_GRID, NSAccessibilityGridRole },
+ { AccessibilityNodeData::ROLE_GROUP, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_GROW_AREA, NSAccessibilityGrowAreaRole },
+ { AccessibilityNodeData::ROLE_HEADING, @"AXHeading" },
+ { AccessibilityNodeData::ROLE_HELP_TAG, NSAccessibilityHelpTagRole },
+ { AccessibilityNodeData::ROLE_HORIZONTAL_RULE, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_IGNORED, NSAccessibilityUnknownRole },
+ { AccessibilityNodeData::ROLE_IMAGE, NSAccessibilityImageRole },
+ { AccessibilityNodeData::ROLE_IMAGE_MAP, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_IMAGE_MAP_LINK, NSAccessibilityLinkRole },
+ { AccessibilityNodeData::ROLE_INCREMENTOR, NSAccessibilityIncrementorRole },
+ { AccessibilityNodeData::ROLE_LABEL, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LANDMARK_APPLICATION,
+ NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LANDMARK_BANNER, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY,
+ NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO,
+ NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LANDMARK_MAIN, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION,
+ NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LANDMARK_SEARCH, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LINK, NSAccessibilityLinkRole },
+ { AccessibilityNodeData::ROLE_LIST, NSAccessibilityListRole },
+ { AccessibilityNodeData::ROLE_LIST_ITEM, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_LIST_MARKER, @"AXListMarker" },
+ { AccessibilityNodeData::ROLE_LISTBOX, NSAccessibilityListRole },
+ { AccessibilityNodeData::ROLE_LISTBOX_OPTION,
+ NSAccessibilityStaticTextRole },
+ { AccessibilityNodeData::ROLE_LOG, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_MARQUEE, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_MATH, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_MATTE, NSAccessibilityMatteRole },
+ { AccessibilityNodeData::ROLE_MENU, NSAccessibilityMenuRole },
+ { AccessibilityNodeData::ROLE_MENU_BAR, NSAccessibilityMenuBarRole },
+ { AccessibilityNodeData::ROLE_MENU_ITEM, NSAccessibilityMenuItemRole },
+ { AccessibilityNodeData::ROLE_MENU_BUTTON, NSAccessibilityButtonRole },
+ { AccessibilityNodeData::ROLE_MENU_LIST_OPTION,
+ NSAccessibilityMenuItemRole },
+ { AccessibilityNodeData::ROLE_MENU_LIST_POPUP, NSAccessibilityUnknownRole },
+ { AccessibilityNodeData::ROLE_NOTE, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_OUTLINE, NSAccessibilityOutlineRole },
+ { AccessibilityNodeData::ROLE_PARAGRAPH, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_POPUP_BUTTON,
+ NSAccessibilityPopUpButtonRole },
+ { AccessibilityNodeData::ROLE_PRESENTATIONAL, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_PROGRESS_INDICATOR,
+ NSAccessibilityProgressIndicatorRole },
+ { AccessibilityNodeData::ROLE_RADIO_BUTTON,
+ NSAccessibilityRadioButtonRole },
+ { AccessibilityNodeData::ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole },
+ { AccessibilityNodeData::ROLE_REGION, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_ROOT_WEB_AREA, @"AXWebArea" },
+ { AccessibilityNodeData::ROLE_ROW, NSAccessibilityRowRole },
+ { AccessibilityNodeData::ROLE_ROW_HEADER, @"AXCell" },
+ { AccessibilityNodeData::ROLE_RULER, NSAccessibilityRulerRole },
+ { AccessibilityNodeData::ROLE_RULER_MARKER,
+ NSAccessibilityRulerMarkerRole },
+ // TODO(dtseng): we don't correctly support the attributes for these roles.
+ // { AccessibilityNodeData::ROLE_SCROLLAREA,
+ // NSAccessibilityScrollAreaRole },
+ { AccessibilityNodeData::ROLE_SCROLLBAR, NSAccessibilityScrollBarRole },
+ { AccessibilityNodeData::ROLE_SHEET, NSAccessibilitySheetRole },
+ { AccessibilityNodeData::ROLE_SLIDER, NSAccessibilitySliderRole },
+ { AccessibilityNodeData::ROLE_SLIDER_THUMB,
+ NSAccessibilityValueIndicatorRole },
+ { AccessibilityNodeData::ROLE_SPIN_BUTTON, NSAccessibilitySliderRole },
+ { AccessibilityNodeData::ROLE_SPLITTER, NSAccessibilitySplitterRole },
+ { AccessibilityNodeData::ROLE_SPLIT_GROUP, NSAccessibilitySplitGroupRole },
+ { AccessibilityNodeData::ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole },
+ { AccessibilityNodeData::ROLE_STATUS, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_SVG_ROOT, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_SYSTEM_WIDE, NSAccessibilityUnknownRole },
+ { AccessibilityNodeData::ROLE_TAB, NSAccessibilityRadioButtonRole },
+ { AccessibilityNodeData::ROLE_TAB_LIST, NSAccessibilityTabGroupRole },
+ { AccessibilityNodeData::ROLE_TAB_PANEL, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_TABLE, NSAccessibilityTableRole },
+ { AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER,
+ NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED,
+ NSAccessibilityTabGroupRole },
+ { AccessibilityNodeData::ROLE_TEXTAREA, NSAccessibilityTextAreaRole },
+ { AccessibilityNodeData::ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole },
+ { AccessibilityNodeData::ROLE_TIMER, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_TOGGLE_BUTTON, NSAccessibilityButtonRole },
+ { AccessibilityNodeData::ROLE_TOOLBAR, NSAccessibilityToolbarRole },
+ { AccessibilityNodeData::ROLE_TOOLTIP, NSAccessibilityGroupRole },
+ { AccessibilityNodeData::ROLE_TREE, NSAccessibilityOutlineRole },
+ { AccessibilityNodeData::ROLE_TREE_GRID, NSAccessibilityTableRole },
+ { AccessibilityNodeData::ROLE_TREE_ITEM, NSAccessibilityRowRole },
+ { AccessibilityNodeData::ROLE_VALUE_INDICATOR,
+ NSAccessibilityValueIndicatorRole },
+ { AccessibilityNodeData::ROLE_WEBCORE_LINK, NSAccessibilityLinkRole },
+ { AccessibilityNodeData::ROLE_WEB_AREA, @"AXWebArea" },
+ { AccessibilityNodeData::ROLE_WINDOW, NSAccessibilityWindowRole },
+ };
+
+ RoleMap role_map;
+ for (size_t i = 0; i < arraysize(roles); ++i)
+ role_map[roles[i].webKitValue] = roles[i].nativeValue;
+ return role_map;
+}
+
+// A mapping of webkit roles to native roles.
+NSString* NativeRoleFromAccessibilityNodeDataRole(
+ const AccessibilityNodeData::Role& role) {
+ CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_role,
+ (BuildRoleMap()));
+ RoleMap::iterator it = web_accessibility_to_native_role.find(role);
+ if (it != web_accessibility_to_native_role.end())
+ return it->second;
+ else
+ return NSAccessibilityUnknownRole;
+}
+
+RoleMap BuildSubroleMap() {
+ const MapEntry subroles[] = {
+ { AccessibilityNodeData::ROLE_ALERT, @"AXApplicationAlert" },
+ { AccessibilityNodeData::ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog" },
+ { AccessibilityNodeData::ROLE_ARTICLE, @"AXDocumentArticle" },
+ { AccessibilityNodeData::ROLE_DEFINITION, @"AXDefinition" },
+ { AccessibilityNodeData::ROLE_DESCRIPTION_LIST_DETAIL, @"AXDescription" },
+ { AccessibilityNodeData::ROLE_DESCRIPTION_LIST_TERM, @"AXTerm" },
+ { AccessibilityNodeData::ROLE_DIALOG, @"AXApplicationDialog" },
+ { AccessibilityNodeData::ROLE_DOCUMENT, @"AXDocument" },
+ { AccessibilityNodeData::ROLE_FOOTER, @"AXLandmarkContentInfo" },
+ { AccessibilityNodeData::ROLE_LANDMARK_APPLICATION,
+ @"AXLandmarkApplication" },
+ { AccessibilityNodeData::ROLE_LANDMARK_BANNER, @"AXLandmarkBanner" },
+ { AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY,
+ @"AXLandmarkComplementary" },
+ { AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO,
+ @"AXLandmarkContentInfo" },
+ { AccessibilityNodeData::ROLE_LANDMARK_MAIN, @"AXLandmarkMain" },
+ { AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION,
+ @"AXLandmarkNavigation" },
+ { AccessibilityNodeData::ROLE_LANDMARK_SEARCH, @"AXLandmarkSearch" },
+ { AccessibilityNodeData::ROLE_LOG, @"AXApplicationLog" },
+ { AccessibilityNodeData::ROLE_MARQUEE, @"AXApplicationMarquee" },
+ { AccessibilityNodeData::ROLE_MATH, @"AXDocumentMath" },
+ { AccessibilityNodeData::ROLE_NOTE, @"AXDocumentNote" },
+ { AccessibilityNodeData::ROLE_REGION, @"AXDocumentRegion" },
+ { AccessibilityNodeData::ROLE_STATUS, @"AXApplicationStatus" },
+ { AccessibilityNodeData::ROLE_TAB_PANEL, @"AXTabPanel" },
+ { AccessibilityNodeData::ROLE_TIMER, @"AXApplicationTimer" },
+ { AccessibilityNodeData::ROLE_TOOLTIP, @"AXUserInterfaceTooltip" },
+ { AccessibilityNodeData::ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole },
+ };
+
+ RoleMap subrole_map;
+ for (size_t i = 0; i < arraysize(subroles); ++i)
+ subrole_map[subroles[i].webKitValue] = subroles[i].nativeValue;
+ return subrole_map;
+}
+
+// A mapping of webkit roles to native subroles.
+NSString* NativeSubroleFromAccessibilityNodeDataRole(
+ const AccessibilityNodeData::Role& role) {
+ CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
+ (BuildSubroleMap()));
+ RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
+ if (it != web_accessibility_to_native_subrole.end())
+ return it->second;
+ else
+ return nil;
+}
+
+// A mapping from an accessibility attribute to its method name.
+NSDictionary* attributeToMethodNameMap = nil;
+
+} // namespace
+
+@implementation BrowserAccessibilityCocoa
+
++ (void)initialize {
+ const struct {
+ NSString* attribute;
+ NSString* methodName;
+ } attributeToMethodNameContainer[] = {
+ { NSAccessibilityChildrenAttribute, @"children" },
+ { NSAccessibilityColumnsAttribute, @"columns" },
+ { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
+ { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
+ { NSAccessibilityContentsAttribute, @"contents" },
+ { NSAccessibilityDescriptionAttribute, @"description" },
+ { NSAccessibilityDisclosingAttribute, @"disclosing" },
+ { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
+ { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
+ { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
+ { NSAccessibilityEnabledAttribute, @"enabled" },
+ { NSAccessibilityFocusedAttribute, @"focused" },
+ { NSAccessibilityHeaderAttribute, @"header" },
+ { NSAccessibilityHelpAttribute, @"help" },
+ { NSAccessibilityIndexAttribute, @"index" },
+ { NSAccessibilityMaxValueAttribute, @"maxValue" },
+ { NSAccessibilityMinValueAttribute, @"minValue" },
+ { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
+ { NSAccessibilityOrientationAttribute, @"orientation" },
+ { NSAccessibilityParentAttribute, @"parent" },
+ { NSAccessibilityPositionAttribute, @"position" },
+ { NSAccessibilityRoleAttribute, @"role" },
+ { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
+ { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
+ { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
+ { NSAccessibilityRowsAttribute, @"rows" },
+ { NSAccessibilitySizeAttribute, @"size" },
+ { NSAccessibilitySubroleAttribute, @"subrole" },
+ { NSAccessibilityTabsAttribute, @"tabs" },
+ { NSAccessibilityTitleAttribute, @"title" },
+ { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
+ { NSAccessibilityTopLevelUIElementAttribute, @"window" },
+ { NSAccessibilityURLAttribute, @"url" },
+ { NSAccessibilityValueAttribute, @"value" },
+ { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
+ { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
+ { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
+ { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
+ { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
+ { NSAccessibilityWindowAttribute, @"window" },
+ { @"AXAccessKey", @"accessKey" },
+ { @"AXARIAAtomic", @"ariaAtomic" },
+ { @"AXARIABusy", @"ariaBusy" },
+ { @"AXARIALive", @"ariaLive" },
+ { @"AXARIARelevant", @"ariaRelevant" },
+ { @"AXInvalid", @"invalid" },
+ { @"AXLoaded", @"loaded" },
+ { @"AXLoadingProgress", @"loadingProgress" },
+ { @"AXRequired", @"required" },
+ { @"AXVisited", @"visited" },
+ };
+
+ NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
+ const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
+ sizeof(attributeToMethodNameContainer[0]);
+ for (size_t i = 0; i < numAttributes; ++i) {
+ [dict setObject:attributeToMethodNameContainer[i].methodName
+ forKey:attributeToMethodNameContainer[i].attribute];
+ }
+ attributeToMethodNameMap = dict;
+ dict = nil;
+}
+
+- (id)initWithObject:(BrowserAccessibility*)accessibility
+ delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate {
+ if ((self = [super init])) {
+ browserAccessibility_ = accessibility;
+ delegate_ = delegate;
+ }
+ return self;
+}
+
+- (void)detach {
+ if (browserAccessibility_) {
+ NSAccessibilityUnregisterUniqueIdForUIElement(self);
+ browserAccessibility_ = NULL;
+ }
+}
+
+- (NSString*)accessKey {
+ return NSStringForStringAttribute(
+ browserAccessibility_->string_attributes(),
+ AccessibilityNodeData::ATTR_ACCESS_KEY);
+}
+
+- (NSNumber*)ariaAtomic {
+ bool boolValue = false;
+ browserAccessibility_->GetBoolAttribute(
+ AccessibilityNodeData::ATTR_LIVE_ATOMIC, &boolValue);
+ return [NSNumber numberWithBool:boolValue];
+}
+
+- (NSNumber*)ariaBusy {
+ bool boolValue = false;
+ browserAccessibility_->GetBoolAttribute(
+ AccessibilityNodeData::ATTR_LIVE_BUSY, &boolValue);
+ return [NSNumber numberWithBool:boolValue];
+}
+
+- (NSString*)ariaLive {
+ return NSStringForStringAttribute(
+ browserAccessibility_->string_attributes(),
+ AccessibilityNodeData::ATTR_LIVE_STATUS);
+}
+
+- (NSString*)ariaRelevant {
+ return NSStringForStringAttribute(
+ browserAccessibility_->string_attributes(),
+ AccessibilityNodeData::ATTR_LIVE_RELEVANT);
+}
+
+// Returns an array of BrowserAccessibilityCocoa objects, representing the
+// accessibility children of this object.
+- (NSArray*)children {
+ if (!children_) {
+ children_.reset([[NSMutableArray alloc]
+ initWithCapacity:browserAccessibility_->child_count()] );
+ for (uint32 index = 0;
+ index < browserAccessibility_->child_count();
+ ++index) {
+ BrowserAccessibilityCocoa* child =
+ browserAccessibility_->GetChild(index)->ToBrowserAccessibilityCocoa();
+ if ([child isIgnored])
+ [children_ addObjectsFromArray:[child children]];
+ else
+ [children_ addObject:child];
+ }
+
+ // Also, add indirect children (if any).
+ for (uint32 i = 0;
+ i < browserAccessibility_->indirect_child_ids().size();
+ ++i) {
+ int32 child_id = browserAccessibility_->indirect_child_ids()[i];
+ BrowserAccessibility* child =
+ browserAccessibility_->manager()->GetFromRendererID(child_id);
+
+ // This only became necessary as a result of crbug.com/93095. It should be
+ // a DCHECK in the future.
+ if (child) {
+ BrowserAccessibilityCocoa* child_cocoa =
+ child->ToBrowserAccessibilityCocoa();
+ [children_ addObject:child_cocoa];
+ }
+ }
+ }
+ return children_;
+}
+
+- (void)childrenChanged {
+ if (![self isIgnored]) {
+ children_.reset();
+ } else {
+ [browserAccessibility_->parent()->ToBrowserAccessibilityCocoa()
+ childrenChanged];
+ }
+}
+
+- (NSArray*)columnHeaders {
+ if ([self internalRole] != AccessibilityNodeData::ROLE_TABLE &&
+ [self internalRole] != AccessibilityNodeData::ROLE_GRID) {
+ return nil;
+ }
+
+ NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
+ const std::vector<int32>& uniqueCellIds =
+ browserAccessibility_->unique_cell_ids();
+ for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
+ int id = uniqueCellIds[i];
+ BrowserAccessibility* cell =
+ browserAccessibility_->manager()->GetFromRendererID(id);
+ if (cell && cell->role() == AccessibilityNodeData::ROLE_COLUMN_HEADER)
+ [ret addObject:cell->ToBrowserAccessibilityCocoa()];
+ }
+ return ret;
+}
+
+- (NSValue*)columnIndexRange {
+ if ([self internalRole] != AccessibilityNodeData::ROLE_CELL)
+ return nil;
+
+ int column = -1;
+ int colspan = -1;
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column);
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
+ if (column >= 0 && colspan >= 1)
+ return [NSValue valueWithRange:NSMakeRange(column, colspan)];
+ return nil;
+}
+
+- (NSArray*)columns {
+ NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
+ for (BrowserAccessibilityCocoa* child in [self children]) {
+ if ([[child role] isEqualToString:NSAccessibilityColumnRole])
+ [ret addObject:child];
+ }
+ return ret;
+}
+
+- (NSString*)description {
+ const std::map<StringAttribute, string16>& attributes =
+ browserAccessibility_->string_attributes();
+ std::map<StringAttribute, string16>::const_iterator iter =
+ attributes.find(AccessibilityNodeData::ATTR_DESCRIPTION);
+ if (iter != attributes.end())
+ return base::SysUTF16ToNSString(iter->second);
+
+ // If the role is anything other than an image, or if there's
+ // a title or title UI element, just return an empty string.
+ if (![[self role] isEqualToString:NSAccessibilityImageRole])
+ return @"";
+ if (!browserAccessibility_->name().empty())
+ return @"";
+ if ([self titleUIElement])
+ return @"";
+
+ // The remaining case is an image where there's no other title.
+ // Return the base part of the filename as the description.
+ iter = attributes.find(AccessibilityNodeData::ATTR_URL);
+ if (iter != attributes.end()) {
+ string16 filename = iter->second;
+ // Given a url like http://foo.com/bar/baz.png, just return the
+ // base name, e.g., "baz.png".
+ size_t leftIndex = filename.size();
+ while (leftIndex > 0 && filename[leftIndex - 1] != '/')
+ leftIndex--;
+ string16 basename = filename.substr(leftIndex);
+
+ return base::SysUTF16ToNSString(basename);
+ }
+
+ return @"";
+}
+
+- (NSNumber*)disclosing {
+ if ([self internalRole] == AccessibilityNodeData::ROLE_TREE_ITEM) {
+ return [NSNumber numberWithBool:
+ GetState(browserAccessibility_, AccessibilityNodeData::STATE_EXPANDED)];
+ } else {
+ return nil;
+ }
+}
+
+- (id)disclosedByRow {
+ // The row that contains this row.
+ // It should be the same as the first parent that is a treeitem.
+ return nil;
+}
+
+- (NSNumber*)disclosureLevel {
+ AccessibilityNodeData::Role role = [self internalRole];
+ if (role == AccessibilityNodeData::ROLE_ROW ||
+ role == AccessibilityNodeData::ROLE_TREE_ITEM) {
+ int level = 0;
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, &level);
+ // Mac disclosureLevel is 0-based, but web levels are 1-based.
+ if (level > 0)
+ level--;
+ return [NSNumber numberWithInt:level];
+ } else {
+ return nil;
+ }
+}
+
+- (id)disclosedRows {
+ // The rows that are considered inside this row.
+ return nil;
+}
+
+- (NSNumber*)enabled {
+ return [NSNumber numberWithBool:
+ !GetState(browserAccessibility_,
+ AccessibilityNodeData::STATE_UNAVAILABLE)];
+}
+
+- (NSNumber*)focused {
+ BrowserAccessibilityManager* manager = browserAccessibility_->manager();
+ NSNumber* ret = [NSNumber numberWithBool:
+ manager->GetFocus(NULL) == browserAccessibility_];
+ return ret;
+}
+
+- (id)header {
+ int headerElementId = -1;
+ if ([self internalRole] == AccessibilityNodeData::ROLE_TABLE ||
+ [self internalRole] == AccessibilityNodeData::ROLE_GRID) {
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_HEADER_ID, &headerElementId);
+ } else if ([self internalRole] == AccessibilityNodeData::ROLE_COLUMN) {
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
+ } else if ([self internalRole] == AccessibilityNodeData::ROLE_ROW) {
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
+ }
+
+ if (headerElementId > 0) {
+ BrowserAccessibility* headerObject =
+ browserAccessibility_->manager()->GetFromRendererID(headerElementId);
+ if (headerObject)
+ return headerObject->ToBrowserAccessibilityCocoa();
+ }
+ return nil;
+}
+
+- (NSString*)help {
+ return NSStringForStringAttribute(
+ browserAccessibility_->string_attributes(),
+ AccessibilityNodeData::ATTR_HELP);
+}
+
+- (NSNumber*)index {
+ if ([self internalRole] == AccessibilityNodeData::ROLE_COLUMN) {
+ int columnIndex;
+ if (browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_INDEX, &columnIndex)) {
+ return [NSNumber numberWithInt:columnIndex];
+ }
+ } else if ([self internalRole] == AccessibilityNodeData::ROLE_ROW) {
+ int rowIndex;
+ if (browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_ROW_INDEX, &rowIndex)) {
+ return [NSNumber numberWithInt:rowIndex];
+ }
+ }
+
+ return nil;
+}
+
+// Returns whether or not this node should be ignored in the
+// accessibility tree.
+- (BOOL)isIgnored {
+ return [[self role] isEqualToString:NSAccessibilityUnknownRole];
+}
+
+- (NSString*)invalid {
+ string16 invalidUTF;
+ if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
+ return NULL;
+ NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
+ if ([invalid isEqualToString:@"false"] ||
+ [invalid isEqualToString:@""]) {
+ return @"false";
+ }
+ return invalid;
+}
+
+- (NSNumber*)loaded {
+ return [NSNumber numberWithBool:YES];
+}
+
+- (NSNumber*)loadingProgress {
+ float floatValue = 0.0;
+ browserAccessibility_->GetFloatAttribute(
+ AccessibilityNodeData::ATTR_DOC_LOADING_PROGRESS, &floatValue);
+ return [NSNumber numberWithFloat:floatValue];
+}
+
+- (NSNumber*)maxValue {
+ float floatValue = 0.0;
+ browserAccessibility_->GetFloatAttribute(
+ AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE, &floatValue);
+ return [NSNumber numberWithFloat:floatValue];
+}
+
+- (NSNumber*)minValue {
+ float floatValue = 0.0;
+ browserAccessibility_->GetFloatAttribute(
+ AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE, &floatValue);
+ return [NSNumber numberWithFloat:floatValue];
+}
+
+- (NSString*)orientation {
+ // We present a spin button as a vertical slider, with a role description
+ // of "spin button".
+ if ([self internalRole] == AccessibilityNodeData::ROLE_SPIN_BUTTON)
+ return NSAccessibilityVerticalOrientationValue;
+
+ if (GetState(browserAccessibility_, AccessibilityNodeData::STATE_VERTICAL))
+ return NSAccessibilityVerticalOrientationValue;
+ else
+ return NSAccessibilityHorizontalOrientationValue;
+}
+
+- (NSNumber*)numberOfCharacters {
+ return [NSNumber numberWithInt:browserAccessibility_->value().length()];
+}
+
+// The origin of this accessibility object in the page's document.
+// This is relative to webkit's top-left origin, not Cocoa's
+// bottom-left origin.
+- (NSPoint)origin {
+ gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
+ return NSMakePoint(bounds.x(), bounds.y());
+}
+
+- (id)parent {
+ // A nil parent means we're the root.
+ if (browserAccessibility_->parent()) {
+ return NSAccessibilityUnignoredAncestor(
+ browserAccessibility_->parent()->ToBrowserAccessibilityCocoa());
+ } else {
+ // Hook back up to RenderWidgetHostViewCocoa.
+ BrowserAccessibilityManagerMac* manager =
+ static_cast<BrowserAccessibilityManagerMac*>(
+ browserAccessibility_->manager());
+ return manager->parent_view();
+ }
+}
+
+- (NSValue*)position {
+ return [NSValue valueWithPoint:[delegate_ accessibilityPointInScreen:self]];
+}
+
+- (NSNumber*)required {
+ return [NSNumber numberWithBool:
+ GetState(browserAccessibility_, AccessibilityNodeData::STATE_REQUIRED)];
+}
+
+// Returns an enum indicating the role from browserAccessibility_.
+- (AccessibilityNodeData::Role)internalRole {
+ return static_cast<AccessibilityNodeData::Role>(
+ browserAccessibility_->role());
+}
+
+// Returns a string indicating the NSAccessibility role of this object.
+- (NSString*)role {
+ return NativeRoleFromAccessibilityNodeDataRole([self internalRole]);
+}
+
+// Returns a string indicating the role description of this object.
+- (NSString*)roleDescription {
+ NSString* role = [self role];
+
+ ContentClient* content_client = content::GetContentClient();
+
+ // The following descriptions are specific to webkit.
+ if ([role isEqualToString:@"AXWebArea"]) {
+ return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+ IDS_AX_ROLE_WEB_AREA));
+ }
+
+ if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
+ return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+ IDS_AX_ROLE_LINK));
+ }
+
+ if ([role isEqualToString:@"AXHeading"]) {
+ return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+ IDS_AX_ROLE_HEADING));
+ }
+
+ if ([role isEqualToString:NSAccessibilityGroupRole] ||
+ [role isEqualToString:NSAccessibilityRadioButtonRole]) {
+ const std::vector<std::pair<string16, string16> >& htmlAttributes =
+ browserAccessibility_->html_attributes();
+ AccessibilityNodeData::Role browserAccessibilityRole = [self internalRole];
+ if ((browserAccessibilityRole != AccessibilityNodeData::ROLE_GROUP &&
+ browserAccessibilityRole != AccessibilityNodeData::ROLE_LIST_ITEM) ||
+ browserAccessibilityRole == AccessibilityNodeData::ROLE_TAB) {
+ for (size_t i = 0; i < htmlAttributes.size(); ++i) {
+ const std::pair<string16, string16>& htmlAttribute = htmlAttributes[i];
+ if (htmlAttribute.first == ASCIIToUTF16("role")) {
+ // TODO(dtseng): This is not localized; see crbug/84814.
+ return base::SysUTF16ToNSString(htmlAttribute.second);
+ }
+ }
+ }
+ }
+
+ switch([self internalRole]) {
+ case AccessibilityNodeData::ROLE_FOOTER:
+ return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+ IDS_AX_ROLE_FOOTER));
+ case AccessibilityNodeData::ROLE_SPIN_BUTTON:
+ // This control is similar to what VoiceOver calls a "stepper".
+ return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+ IDS_AX_ROLE_STEPPER));
+ default:
+ break;
+ }
+
+ return NSAccessibilityRoleDescription(role, nil);
+}
+
+- (NSArray*)rowHeaders {
+ if ([self internalRole] != AccessibilityNodeData::ROLE_TABLE &&
+ [self internalRole] != AccessibilityNodeData::ROLE_GRID) {
+ return nil;
+ }
+
+ NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
+ const std::vector<int32>& uniqueCellIds =
+ browserAccessibility_->unique_cell_ids();
+ for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
+ int id = uniqueCellIds[i];
+ BrowserAccessibility* cell =
+ browserAccessibility_->manager()->GetFromRendererID(id);
+ if (cell && cell->role() == AccessibilityNodeData::ROLE_ROW_HEADER)
+ [ret addObject:cell->ToBrowserAccessibilityCocoa()];
+ }
+ return ret;
+}
+
+- (NSValue*)rowIndexRange {
+ if ([self internalRole] != AccessibilityNodeData::ROLE_CELL)
+ return nil;
+
+ int row = -1;
+ int rowspan = -1;
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row);
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
+ if (row >= 0 && rowspan >= 1)
+ return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
+ return nil;
+}
+
+- (NSArray*)rows {
+ NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
+
+ if ([self internalRole] == AccessibilityNodeData::ROLE_TABLE||
+ [self internalRole] == AccessibilityNodeData::ROLE_GRID) {
+ for (BrowserAccessibilityCocoa* child in [self children]) {
+ if ([[child role] isEqualToString:NSAccessibilityRowRole])
+ [ret addObject:child];
+ }
+ } else if ([self internalRole] == AccessibilityNodeData::ROLE_COLUMN) {
+ const std::vector<int32>& indirectChildIds =
+ browserAccessibility_->indirect_child_ids();
+ for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
+ int id = indirectChildIds[i];
+ BrowserAccessibility* rowElement =
+ browserAccessibility_->manager()->GetFromRendererID(id);
+ if (rowElement)
+ [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
+ }
+ }
+
+ return ret;
+}
+
+// Returns the size of this object.
+- (NSValue*)size {
+ gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
+ return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
+}
+
+// Returns a subrole based upon the role.
+- (NSString*) subrole {
+ AccessibilityNodeData::Role browserAccessibilityRole = [self internalRole];
+ if (browserAccessibilityRole == AccessibilityNodeData::ROLE_TEXT_FIELD &&
+ GetState(browserAccessibility_, AccessibilityNodeData::STATE_PROTECTED)) {
+ return @"AXSecureTextField";
+ }
+
+ NSString* htmlTag = NSStringForStringAttribute(
+ browserAccessibility_->string_attributes(),
+ AccessibilityNodeData::ATTR_HTML_TAG);
+
+ if (browserAccessibilityRole == AccessibilityNodeData::ROLE_LIST) {
+ if ([htmlTag isEqualToString:@"ul"] ||
+ [htmlTag isEqualToString:@"ol"]) {
+ return @"AXContentList";
+ } else if ([htmlTag isEqualToString:@"dl"]) {
+ return @"AXDescriptionList";
+ }
+ }
+
+ return NativeSubroleFromAccessibilityNodeDataRole(browserAccessibilityRole);
+}
+
+// Returns all tabs in this subtree.
+- (NSArray*)tabs {
+ NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
+
+ if ([self internalRole] == AccessibilityNodeData::ROLE_TAB)
+ [tabSubtree addObject:self];
+
+ for (uint i=0; i < [[self children] count]; ++i) {
+ NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
+ if ([tabChildren count] > 0)
+ [tabSubtree addObjectsFromArray:tabChildren];
+ }
+
+ return tabSubtree;
+}
+
+- (NSString*)title {
+ return base::SysUTF16ToNSString(browserAccessibility_->name());
+}
+
+- (id)titleUIElement {
+ int titleElementId;
+ if (browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
+ BrowserAccessibility* titleElement =
+ browserAccessibility_->manager()->GetFromRendererID(titleElementId);
+ if (titleElement)
+ return titleElement->ToBrowserAccessibilityCocoa();
+ }
+ return nil;
+}
+
+- (NSString*)url {
+ StringAttribute urlAttribute =
+ [[self role] isEqualToString:@"AXWebArea"] ?
+ AccessibilityNodeData::ATTR_DOC_URL :
+ AccessibilityNodeData::ATTR_URL;
+ return NSStringForStringAttribute(
+ browserAccessibility_->string_attributes(),
+ urlAttribute);
+}
+
+- (id)value {
+ // WebCore uses an attachmentView to get the below behavior.
+ // We do not have any native views backing this object, so need
+ // to approximate Cocoa ax behavior best as we can.
+ NSString* role = [self role];
+ if ([role isEqualToString:@"AXHeading"]) {
+ int level;
+ if (browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, &level)) {
+ return [NSNumber numberWithInt:level];
+ }
+ } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
+ // AXValue does not make sense for pure buttons.
+ return @"";
+ } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
+ [role isEqualToString:NSAccessibilityRadioButtonRole]) {
+ int value = 0;
+ value = GetState(
+ browserAccessibility_, AccessibilityNodeData::STATE_CHECKED) ? 1 : 0;
+ value = GetState(
+ browserAccessibility_, AccessibilityNodeData::STATE_SELECTED) ?
+ 1 :
+ value;
+
+ bool mixed = false;
+ browserAccessibility_->GetBoolAttribute(
+ AccessibilityNodeData::ATTR_BUTTON_MIXED, &mixed);
+ if (mixed)
+ value = 2;
+ return [NSNumber numberWithInt:value];
+ } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
+ [role isEqualToString:NSAccessibilitySliderRole] ||
+ [role isEqualToString:NSAccessibilityScrollBarRole]) {
+ float floatValue;
+ if (browserAccessibility_->GetFloatAttribute(
+ AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &floatValue)) {
+ return [NSNumber numberWithFloat:floatValue];
+ }
+ } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
+ int r, g, b;
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_COLOR_VALUE_RED, &r);
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN, &g);
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE, &b);
+ // This string matches the one returned by a native Mac color well.
+ return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
+ r / 255., g / 255., b / 255.];
+ }
+
+ return base::SysUTF16ToNSString(browserAccessibility_->value());
+}
+
+- (NSString*)valueDescription {
+ if (!browserAccessibility_->value().empty())
+ return base::SysUTF16ToNSString(browserAccessibility_->value());
+ else
+ return nil;
+}
+
+- (NSValue*)visibleCharacterRange {
+ return [NSValue valueWithRange:
+ NSMakeRange(0, browserAccessibility_->value().length())];
+}
+
+- (NSArray*)visibleCells {
+ NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
+ const std::vector<int32>& uniqueCellIds =
+ browserAccessibility_->unique_cell_ids();
+ for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
+ int id = uniqueCellIds[i];
+ BrowserAccessibility* cell =
+ browserAccessibility_->manager()->GetFromRendererID(id);
+ if (cell)
+ [ret addObject:cell->ToBrowserAccessibilityCocoa()];
+ }
+ return ret;
+}
+
+- (NSArray*)visibleColumns {
+ return [self columns];
+}
+
+- (NSArray*)visibleRows {
+ return [self rows];
+}
+
+- (NSNumber*)visited {
+ return [NSNumber numberWithBool:
+ GetState(browserAccessibility_, AccessibilityNodeData::STATE_TRAVERSED)];
+}
+
+- (id)window {
+ return [delegate_ window];
+}
+
+- (NSString*)methodNameForAttribute:(NSString*)attribute {
+ return [attributeToMethodNameMap objectForKey:attribute];
+}
+
+// Returns the accessibility value for the given attribute. If the value isn't
+// supported this will return nil.
+- (id)accessibilityAttributeValue:(NSString*)attribute {
+ if (!browserAccessibility_)
+ return nil;
+
+ SEL selector =
+ NSSelectorFromString([self methodNameForAttribute:attribute]);
+ if (selector)
+ return [self performSelector:selector];
+
+ // TODO(dtseng): refactor remaining attributes.
+ int selStart, selEnd;
+ if (browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TEXT_SEL_START, &selStart) &&
+ browserAccessibility_->
+ GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &selEnd)) {
+ if (selStart > selEnd)
+ std::swap(selStart, selEnd);
+ int selLength = selEnd - selStart;
+ if ([attribute isEqualToString:
+ NSAccessibilityInsertionPointLineNumberAttribute]) {
+ const std::vector<int32>& line_breaks =
+ browserAccessibility_->line_breaks();
+ for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
+ if (line_breaks[i] > selStart)
+ return [NSNumber numberWithInt:i];
+ }
+ return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
+ }
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
+ return base::SysUTF16ToNSString(browserAccessibility_->value().substr(
+ selStart, selLength));
+ }
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
+ return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
+ }
+ }
+ return nil;
+}
+
+// Returns the accessibility value for the given attribute and parameter. If the
+// value isn't supported this will return nil.
+- (id)accessibilityAttributeValue:(NSString*)attribute
+ forParameter:(id)parameter {
+ if (!browserAccessibility_)
+ return nil;
+
+ const std::vector<int32>& line_breaks = browserAccessibility_->line_breaks();
+ int len = static_cast<int>(browserAccessibility_->value().size());
+
+ if ([attribute isEqualToString:
+ NSAccessibilityStringForRangeParameterizedAttribute]) {
+ NSRange range = [(NSValue*)parameter rangeValue];
+ return base::SysUTF16ToNSString(
+ browserAccessibility_->value().substr(range.location, range.length));
+ }
+
+ if ([attribute isEqualToString:
+ NSAccessibilityLineForIndexParameterizedAttribute]) {
+ int index = [(NSNumber*)parameter intValue];
+ for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
+ if (line_breaks[i] > index)
+ return [NSNumber numberWithInt:i];
+ }
+ return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
+ }
+
+ if ([attribute isEqualToString:
+ NSAccessibilityRangeForLineParameterizedAttribute]) {
+ int line_index = [(NSNumber*)parameter intValue];
+ int line_count = static_cast<int>(line_breaks.size()) + 1;
+ if (line_index < 0 || line_index >= line_count)
+ return nil;
+ int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
+ int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
+ return [NSValue valueWithRange:
+ NSMakeRange(start, end - start)];
+ }
+
+ if ([attribute isEqualToString:
+ NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
+ if ([self internalRole] != AccessibilityNodeData::ROLE_TABLE &&
+ [self internalRole] != AccessibilityNodeData::ROLE_GRID) {
+ return nil;
+ }
+ if (![parameter isKindOfClass:[NSArray self]])
+ return nil;
+ NSArray* array = parameter;
+ int column = [[array objectAtIndex:0] intValue];
+ int row = [[array objectAtIndex:1] intValue];
+ int num_columns = 0;
+ int num_rows = 0;
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &num_columns);
+ browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &num_rows);
+ if (column < 0 || column >= num_columns ||
+ row < 0 || row >= num_rows) {
+ return nil;
+ }
+ for (size_t i = 0;
+ i < browserAccessibility_->child_count();
+ ++i) {
+ BrowserAccessibility* child = browserAccessibility_->GetChild(i);
+ if (child->role() != AccessibilityNodeData::ROLE_ROW)
+ continue;
+ int rowIndex;
+ if (!child->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_ROW_INDEX, &rowIndex)) {
+ continue;
+ }
+ if (rowIndex < row)
+ continue;
+ if (rowIndex > row)
+ break;
+ for (size_t j = 0;
+ j < child->child_count();
+ ++j) {
+ BrowserAccessibility* cell = child->GetChild(j);
+ if (cell->role() != AccessibilityNodeData::ROLE_CELL)
+ continue;
+ int colIndex;
+ if (!cell->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX,
+ &colIndex)) {
+ continue;
+ }
+ if (colIndex == column)
+ return cell->ToBrowserAccessibilityCocoa();
+ if (colIndex > column)
+ break;
+ }
+ }
+ return nil;
+ }
+
+ // TODO(dtseng): support the following attributes.
+ if ([attribute isEqualTo:
+ NSAccessibilityRangeForPositionParameterizedAttribute] ||
+ [attribute isEqualTo:
+ NSAccessibilityRangeForIndexParameterizedAttribute] ||
+ [attribute isEqualTo:
+ NSAccessibilityBoundsForRangeParameterizedAttribute] ||
+ [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
+ [attribute isEqualTo:
+ NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
+ return nil;
+ }
+ return nil;
+}
+
+// Returns an array of parameterized attributes names that this object will
+// respond to.
+- (NSArray*)accessibilityParameterizedAttributeNames {
+ if (!browserAccessibility_)
+ return nil;
+
+ if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
+ [[self role] isEqualToString:NSAccessibilityGridRole]) {
+ return [NSArray arrayWithObjects:
+ NSAccessibilityCellForColumnAndRowParameterizedAttribute,
+ nil];
+ }
+ if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
+ [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
+ return [NSArray arrayWithObjects:
+ NSAccessibilityLineForIndexParameterizedAttribute,
+ NSAccessibilityRangeForLineParameterizedAttribute,
+ NSAccessibilityStringForRangeParameterizedAttribute,
+ NSAccessibilityRangeForPositionParameterizedAttribute,
+ NSAccessibilityRangeForIndexParameterizedAttribute,
+ NSAccessibilityBoundsForRangeParameterizedAttribute,
+ NSAccessibilityRTFForRangeParameterizedAttribute,
+ NSAccessibilityAttributedStringForRangeParameterizedAttribute,
+ NSAccessibilityStyleRangeForIndexParameterizedAttribute,
+ nil];
+ }
+ return nil;
+}
+
+// Returns an array of action names that this object will respond to.
+- (NSArray*)accessibilityActionNames {
+ if (!browserAccessibility_)
+ return nil;
+
+ NSMutableArray* ret =
+ [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
+ NSString* role = [self role];
+ // TODO(dtseng): this should only get set when there's a default action.
+ if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
+ ![role isEqualToString:NSAccessibilityTextAreaRole] &&
+ ![role isEqualToString:NSAccessibilityTextFieldRole]) {
+ [ret addObject:NSAccessibilityPressAction];
+ }
+
+ return ret;
+}
+
+// Returns a sub-array of values for the given attribute value, starting at
+// index, with up to maxCount items. If the given index is out of bounds,
+// or there are no values for the given attribute, it will return nil.
+// This method is used for querying subsets of values, without having to
+// return a large set of data, such as elements with a large number of
+// children.
+- (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
+ index:(NSUInteger)index
+ maxCount:(NSUInteger)maxCount {
+ if (!browserAccessibility_)
+ return nil;
+
+ NSArray* fullArray = [self accessibilityAttributeValue:attribute];
+ if (!fullArray)
+ return nil;
+ NSUInteger arrayCount = [fullArray count];
+ if (index >= arrayCount)
+ return nil;
+ NSRange subRange;
+ if ((index + maxCount) > arrayCount) {
+ subRange = NSMakeRange(index, arrayCount - index);
+ } else {
+ subRange = NSMakeRange(index, maxCount);
+ }
+ return [fullArray subarrayWithRange:subRange];
+}
+
+// Returns the count of the specified accessibility array attribute.
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
+ if (!browserAccessibility_)
+ return nil;
+
+ NSArray* fullArray = [self accessibilityAttributeValue:attribute];
+ return [fullArray count];
+}
+
+// Returns the list of accessibility attributes that this object supports.
+- (NSArray*)accessibilityAttributeNames {
+ if (!browserAccessibility_)
+ return nil;
+
+ // General attributes.
+ NSMutableArray* ret = [NSMutableArray arrayWithObjects:
+ NSAccessibilityChildrenAttribute,
+ NSAccessibilityDescriptionAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilityFocusedAttribute,
+ NSAccessibilityHelpAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityPositionAttribute,
+ NSAccessibilityRoleAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilitySizeAttribute,
+ NSAccessibilitySubroleAttribute,
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityTopLevelUIElementAttribute,
+ NSAccessibilityValueAttribute,
+ NSAccessibilityWindowAttribute,
+ NSAccessibilityURLAttribute,
+ @"AXAccessKey",
+ @"AXInvalid",
+ @"AXRequired",
+ @"AXVisited",
+ nil];
+
+ // Specific role attributes.
+ NSString* role = [self role];
+ NSString* subrole = [self subrole];
+ if ([role isEqualToString:NSAccessibilityTableRole] ||
+ [role isEqualToString:NSAccessibilityGridRole]) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityColumnsAttribute,
+ NSAccessibilityVisibleColumnsAttribute,
+ NSAccessibilityRowsAttribute,
+ NSAccessibilityVisibleRowsAttribute,
+ NSAccessibilityVisibleCellsAttribute,
+ NSAccessibilityHeaderAttribute,
+ NSAccessibilityColumnHeaderUIElementsAttribute,
+ NSAccessibilityRowHeaderUIElementsAttribute,
+ nil]];
+ } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityIndexAttribute,
+ NSAccessibilityHeaderAttribute,
+ NSAccessibilityRowsAttribute,
+ NSAccessibilityVisibleRowsAttribute,
+ nil]];
+ } else if ([role isEqualToString:NSAccessibilityCellRole]) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityColumnIndexRangeAttribute,
+ NSAccessibilityRowIndexRangeAttribute,
+ nil]];
+ } else if ([role isEqualToString:@"AXWebArea"]) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ @"AXLoaded",
+ @"AXLoadingProgress",
+ nil]];
+ } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
+ [role isEqualToString:NSAccessibilityTextAreaRole]) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityInsertionPointLineNumberAttribute,
+ NSAccessibilityNumberOfCharactersAttribute,
+ NSAccessibilitySelectedTextAttribute,
+ NSAccessibilitySelectedTextRangeAttribute,
+ NSAccessibilityVisibleCharacterRangeAttribute,
+ nil]];
+ } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
+ [ret addObject:NSAccessibilityTabsAttribute];
+ } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
+ [role isEqualToString:NSAccessibilitySliderRole] ||
+ [role isEqualToString:NSAccessibilityScrollBarRole]) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityMaxValueAttribute,
+ NSAccessibilityMinValueAttribute,
+ NSAccessibilityOrientationAttribute,
+ NSAccessibilityValueDescriptionAttribute,
+ nil]];
+ } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityDisclosingAttribute,
+ NSAccessibilityDisclosedByRowAttribute,
+ NSAccessibilityDisclosureLevelAttribute,
+ NSAccessibilityDisclosedRowsAttribute,
+ nil]];
+ } else if ([role isEqualToString:NSAccessibilityRowRole]) {
+ if (browserAccessibility_->parent()) {
+ string16 parentRole;
+ browserAccessibility_->parent()->GetHtmlAttribute(
+ "role", &parentRole);
+ const string16 treegridRole(ASCIIToUTF16("treegrid"));
+ if (parentRole == treegridRole) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityDisclosingAttribute,
+ NSAccessibilityDisclosedByRowAttribute,
+ NSAccessibilityDisclosureLevelAttribute,
+ NSAccessibilityDisclosedRowsAttribute,
+ nil]];
+ } else {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityIndexAttribute,
+ nil]];
+ }
+ }
+ }
+
+ // Live regions.
+ string16 s;
+ if (browserAccessibility_->GetStringAttribute(
+ AccessibilityNodeData::ATTR_LIVE_STATUS, &s)) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ @"AXARIALive",
+ @"AXARIARelevant",
+ nil]];
+ }
+ if (browserAccessibility_->GetStringAttribute(
+ AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS, &s)) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ @"AXARIAAtomic",
+ @"AXARIABusy",
+ nil]];
+ }
+
+ // Title UI Element.
+ int i;
+ if (browserAccessibility_->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, &i)) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityTitleUIElementAttribute,
+ nil]];
+ }
+
+ return ret;
+}
+
+// Returns the index of the child in this objects array of children.
+- (NSUInteger)accessibilityGetIndexOf:(id)child {
+ if (!browserAccessibility_)
+ return nil;
+
+ NSUInteger index = 0;
+ for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
+ if ([child isEqual:childToCheck])
+ return index;
+ ++index;
+ }
+ return NSNotFound;
+}
+
+// Returns whether or not the specified attribute can be set by the
+// accessibility API via |accessibilitySetValue:forAttribute:|.
+- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
+ if (!browserAccessibility_)
+ return nil;
+
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
+ return GetState(browserAccessibility_,
+ AccessibilityNodeData::STATE_FOCUSABLE);
+ if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
+ bool canSetValue = false;
+ browserAccessibility_->GetBoolAttribute(
+ AccessibilityNodeData::ATTR_CAN_SET_VALUE, &canSetValue);
+ return canSetValue;
+ }
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
+ ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
+ [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
+ return YES;
+
+ return NO;
+}
+
+// Returns whether or not this object should be ignored in the accessibilty
+// tree.
+- (BOOL)accessibilityIsIgnored {
+ if (!browserAccessibility_)
+ return true;
+
+ return [self isIgnored];
+}
+
+// Performs the given accessibilty action on the webkit accessibility object
+// that backs this object.
+- (void)accessibilityPerformAction:(NSString*)action {
+ if (!browserAccessibility_)
+ return;
+
+ // TODO(feldstein): Support more actions.
+ if ([action isEqualToString:NSAccessibilityPressAction])
+ [delegate_ doDefaultAction:browserAccessibility_->renderer_id()];
+ else if ([action isEqualToString:NSAccessibilityShowMenuAction])
+ [delegate_ performShowMenuAction:self];
+}
+
+// Returns the description of the given action.
+- (NSString*)accessibilityActionDescription:(NSString*)action {
+ if (!browserAccessibility_)
+ return nil;
+
+ return NSAccessibilityActionDescription(action);
+}
+
+// Sets an override value for a specific accessibility attribute.
+// This class does not support this.
+- (BOOL)accessibilitySetOverrideValue:(id)value
+ forAttribute:(NSString*)attribute {
+ return NO;
+}
+
+// Sets the value for an accessibility attribute via the accessibility API.
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
+ if (!browserAccessibility_)
+ return;
+
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+ NSNumber* focusedNumber = value;
+ BOOL focused = [focusedNumber intValue];
+ [delegate_ setAccessibilityFocus:focused
+ accessibilityId:browserAccessibility_->renderer_id()];
+ }
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
+ NSRange range = [(NSValue*)value rangeValue];
+ [delegate_
+ accessibilitySetTextSelection:browserAccessibility_->renderer_id()
+ startOffset:range.location
+ endOffset:range.location + range.length];
+ }
+}
+
+// Returns the deepest accessibility child that should not be ignored.
+// It is assumed that the hit test has been narrowed down to this object
+// or one of its children, so this will never return nil unless this
+// object is invalid.
+- (id)accessibilityHitTest:(NSPoint)point {
+ if (!browserAccessibility_)
+ return nil;
+
+ BrowserAccessibilityCocoa* hit = self;
+ for (BrowserAccessibilityCocoa* child in [self children]) {
+ NSPoint origin = [child origin];
+ NSSize size = [[child size] sizeValue];
+ NSRect rect;
+ rect.origin = origin;
+ rect.size = size;
+ if (NSPointInRect(point, rect)) {
+ hit = child;
+ id childResult = [child accessibilityHitTest:point];
+ if (![childResult accessibilityIsIgnored]) {
+ hit = childResult;
+ break;
+ }
+ }
+ }
+ return NSAccessibilityUnignoredAncestor(hit);
+}
+
+- (BOOL)isEqual:(id)object {
+ if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
+ return NO;
+ return ([self hash] == [object hash]);
+}
+
+- (NSUInteger)hash {
+ // Potentially called during dealloc.
+ if (!browserAccessibility_)
+ return [super hash];
+ return browserAccessibility_->renderer_id();
+}
+
+- (BOOL)accessibilityShouldUseUniqueId {
+ return YES;
+}
+
+@end
+
diff --git a/chromium/content/browser/accessibility/browser_accessibility_delegate_mac.h b/chromium/content/browser/accessibility/browser_accessibility_delegate_mac.h
new file mode 100644
index 00000000000..8b9abff3a73
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_delegate_mac.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_
+
+@class BrowserAccessibilityCocoa;
+@class NSWindow;
+
+// This protocol is used by the BrowserAccessibility objects to pass messages
+// to, or otherwise communicate with, their underlying WebAccessibility
+// objects over the IPC boundary.
+@protocol BrowserAccessibilityDelegateCocoa
+- (NSPoint)accessibilityPointInScreen:(BrowserAccessibilityCocoa*)accessibility;
+- (void)doDefaultAction:(int32)accessibilityObjectId;
+- (void)accessibilitySetTextSelection:(int32)accId
+ startOffset:(int32)startOffset
+ endOffset:(int32)endOffset;
+- (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility;
+- (void)setAccessibilityFocus:(BOOL)focus
+ accessibilityId:(int32)accessibilityObjectId;
+- (NSWindow*)window;
+@end
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_gtk.cc b/chromium/content/browser/accessibility/browser_accessibility_gtk.cc
new file mode 100644
index 00000000000..50364282b3d
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_gtk.cc
@@ -0,0 +1,519 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_gtk.h"
+
+#include <gtk/gtk.h>
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility_manager_gtk.h"
+#include "content/common/accessibility_messages.h"
+
+namespace content {
+
+static gpointer browser_accessibility_parent_class = NULL;
+
+static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
+ BrowserAccessibilityAtk* atk_object) {
+ if (!atk_object)
+ return NULL;
+
+ return atk_object->m_object;
+}
+
+//
+// AtkComponent interface.
+//
+
+static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
+ AtkComponent* atk_object) {
+ if (!IS_BROWSER_ACCESSIBILITY(atk_object))
+ return NULL;
+
+ return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
+}
+
+static AtkObject* browser_accessibility_accessible_at_point(
+ AtkComponent* component, gint x, gint y, AtkCoordType coord_type) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
+ if (!obj)
+ return NULL;
+
+ gfx::Point point(x, y);
+ if (!obj->GetGlobalBoundsRect().Contains(point))
+ return NULL;
+
+ BrowserAccessibility* result = obj->BrowserAccessibilityForPoint(point);
+ if (!result)
+ return NULL;
+
+ AtkObject* atk_result = result->ToBrowserAccessibilityGtk()->GetAtkObject();
+ g_object_ref(atk_result);
+ return atk_result;
+}
+
+static void browser_accessibility_get_extents(
+ AtkComponent* component, gint* x, gint* y, gint* width, gint* height,
+ AtkCoordType coord_type) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
+ if (!obj)
+ return;
+
+ gfx::Rect bounds = obj->GetGlobalBoundsRect();
+ *x = bounds.x();
+ *y = bounds.y();
+ *width = bounds.width();
+ *height = bounds.height();
+}
+
+static gboolean browser_accessibility_grab_focus(AtkComponent* component) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
+ if (!obj)
+ return false;
+
+ obj->manager()->SetFocus(obj, true);
+ return true;
+}
+
+static void ComponentInterfaceInit(AtkComponentIface* iface) {
+ iface->ref_accessible_at_point = browser_accessibility_accessible_at_point;
+ iface->get_extents = browser_accessibility_get_extents;
+ iface->grab_focus = browser_accessibility_grab_focus;
+}
+
+static const GInterfaceInfo ComponentInfo = {
+ reinterpret_cast<GInterfaceInitFunc>(ComponentInterfaceInit), 0, 0
+};
+
+//
+// AtkValue interface.
+//
+
+static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
+ AtkValue* atk_object) {
+ if (!IS_BROWSER_ACCESSIBILITY(atk_object))
+ return NULL;
+
+ return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
+}
+
+static void browser_accessibility_get_current_value(
+ AtkValue* atk_object, GValue* value) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return;
+
+ float float_val;
+ if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE,
+ &float_val)) {
+ memset(value, 0, sizeof(*value));
+ g_value_init(value, G_TYPE_FLOAT);
+ g_value_set_float(value, float_val);
+ }
+}
+
+static void browser_accessibility_get_minimum_value(
+ AtkValue* atk_object, GValue* value) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return;
+
+ float float_val;
+ if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE,
+ &float_val)) {
+ memset(value, 0, sizeof(*value));
+ g_value_init(value, G_TYPE_FLOAT);
+ g_value_set_float(value, float_val);
+ }
+}
+
+static void browser_accessibility_get_maximum_value(
+ AtkValue* atk_object, GValue* value) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return;
+
+ float float_val;
+ if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
+ &float_val)) {
+ memset(value, 0, sizeof(*value));
+ g_value_init(value, G_TYPE_FLOAT);
+ g_value_set_float(value, float_val);
+ }
+}
+
+static void browser_accessibility_get_minimum_increment(
+ AtkValue* atk_object, GValue* value) {
+ // TODO(dmazzoni): get the correct value from an <input type=range>.
+ memset(value, 0, sizeof(*value));
+ g_value_init(value, G_TYPE_FLOAT);
+ g_value_set_float(value, 1.0);
+}
+
+static void ValueInterfaceInit(AtkValueIface* iface) {
+ iface->get_current_value = browser_accessibility_get_current_value;
+ iface->get_minimum_value = browser_accessibility_get_minimum_value;
+ iface->get_maximum_value = browser_accessibility_get_maximum_value;
+ iface->get_minimum_increment = browser_accessibility_get_minimum_increment;
+}
+
+static const GInterfaceInfo ValueInfo = {
+ reinterpret_cast<GInterfaceInitFunc>(ValueInterfaceInit), 0, 0
+};
+
+//
+// AtkObject interface
+//
+
+static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
+ AtkObject* atk_object) {
+ if (!IS_BROWSER_ACCESSIBILITY(atk_object))
+ return NULL;
+
+ return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
+}
+
+static const gchar* browser_accessibility_get_name(AtkObject* atk_object) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return NULL;
+ return obj->atk_acc_name().c_str();
+}
+
+static const gchar* browser_accessibility_get_description(
+ AtkObject* atk_object) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return NULL;
+ return obj->atk_acc_description().c_str();
+}
+
+static AtkObject* browser_accessibility_get_parent(AtkObject* atk_object) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return NULL;
+ if (obj->parent())
+ return obj->parent()->ToBrowserAccessibilityGtk()->GetAtkObject();
+
+ BrowserAccessibilityManagerGtk* manager =
+ static_cast<BrowserAccessibilityManagerGtk*>(obj->manager());
+ return gtk_widget_get_accessible(manager->parent_widget());
+}
+
+static gint browser_accessibility_get_n_children(AtkObject* atk_object) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return 0;
+ return obj->children().size();
+}
+
+static AtkObject* browser_accessibility_ref_child(
+ AtkObject* atk_object, gint index) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return NULL;
+ AtkObject* result =
+ obj->children()[index]->ToBrowserAccessibilityGtk()->GetAtkObject();
+ g_object_ref(result);
+ return result;
+}
+
+static gint browser_accessibility_get_index_in_parent(AtkObject* atk_object) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return 0;
+ return obj->index_in_parent();
+}
+
+static AtkAttributeSet* browser_accessibility_get_attributes(
+ AtkObject* atk_object) {
+ return NULL;
+}
+
+static AtkRole browser_accessibility_get_role(AtkObject* atk_object) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return ATK_ROLE_INVALID;
+ return obj->atk_role();
+}
+
+static AtkStateSet* browser_accessibility_ref_state_set(AtkObject* atk_object) {
+ BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
+ if (!obj)
+ return NULL;
+ AtkStateSet* state_set =
+ ATK_OBJECT_CLASS(browser_accessibility_parent_class)->
+ ref_state_set(atk_object);
+ int32 state = obj->state();
+
+ if (state & (1 << AccessibilityNodeData::STATE_FOCUSABLE))
+ atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE);
+ if (obj->manager()->GetFocus(NULL) == obj)
+ atk_state_set_add_state(state_set, ATK_STATE_FOCUSED);
+ if (!(state & (1 << AccessibilityNodeData::STATE_UNAVAILABLE)))
+ atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
+
+ return state_set;
+}
+
+static AtkRelationSet* browser_accessibility_ref_relation_set(
+ AtkObject* atk_object) {
+ AtkRelationSet* relation_set =
+ ATK_OBJECT_CLASS(browser_accessibility_parent_class)
+ ->ref_relation_set(atk_object);
+ return relation_set;
+}
+
+//
+// The rest of the BrowserAccessibilityGtk code, not specific to one
+// of the Atk* interfaces.
+//
+
+static void browser_accessibility_init(AtkObject* atk_object, gpointer data) {
+ if (ATK_OBJECT_CLASS(browser_accessibility_parent_class)->initialize) {
+ ATK_OBJECT_CLASS(browser_accessibility_parent_class)->initialize(
+ atk_object, data);
+ }
+
+ BROWSER_ACCESSIBILITY(atk_object)->m_object =
+ reinterpret_cast<BrowserAccessibilityGtk*>(data);
+}
+
+static void browser_accessibility_finalize(GObject* atk_object) {
+ G_OBJECT_CLASS(browser_accessibility_parent_class)->finalize(atk_object);
+}
+
+static void browser_accessibility_class_init(AtkObjectClass* klass) {
+ GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
+ browser_accessibility_parent_class = g_type_class_peek_parent(klass);
+
+ gobject_class->finalize = browser_accessibility_finalize;
+ klass->initialize = browser_accessibility_init;
+ klass->get_name = browser_accessibility_get_name;
+ klass->get_description = browser_accessibility_get_description;
+ klass->get_parent = browser_accessibility_get_parent;
+ klass->get_n_children = browser_accessibility_get_n_children;
+ klass->ref_child = browser_accessibility_ref_child;
+ klass->get_role = browser_accessibility_get_role;
+ klass->ref_state_set = browser_accessibility_ref_state_set;
+ klass->get_index_in_parent = browser_accessibility_get_index_in_parent;
+ klass->get_attributes = browser_accessibility_get_attributes;
+ klass->ref_relation_set = browser_accessibility_ref_relation_set;
+}
+
+GType browser_accessibility_get_type() {
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter(&type_volatile)) {
+ static const GTypeInfo tinfo = {
+ sizeof(BrowserAccessibilityAtkClass),
+ (GBaseInitFunc) 0,
+ (GBaseFinalizeFunc) 0,
+ (GClassInitFunc) browser_accessibility_class_init,
+ (GClassFinalizeFunc) 0,
+ 0, /* class data */
+ sizeof(BrowserAccessibilityAtk), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) 0,
+ 0 /* value table */
+ };
+
+ GType type = g_type_register_static(
+ ATK_TYPE_OBJECT, "BrowserAccessibility", &tinfo, GTypeFlags(0));
+ g_once_init_leave(&type_volatile, type);
+ }
+
+ return type_volatile;
+}
+
+static const char* GetUniqueAccessibilityTypeName(int interface_mask)
+{
+ // 20 characters is enough for "Chrome%x" with any integer value.
+ static char name[20];
+ snprintf(name, sizeof(name), "Chrome%x", interface_mask);
+ return name;
+}
+
+enum AtkInterfaces {
+ ATK_ACTION_INTERFACE,
+ ATK_COMPONENT_INTERFACE,
+ ATK_DOCUMENT_INTERFACE,
+ ATK_EDITABLE_TEXT_INTERFACE,
+ ATK_HYPERLINK_INTERFACE,
+ ATK_HYPERTEXT_INTERFACE,
+ ATK_IMAGE_INTERFACE,
+ ATK_SELECTION_INTERFACE,
+ ATK_TABLE_INTERFACE,
+ ATK_TEXT_INTERFACE,
+ ATK_VALUE_INTERFACE,
+};
+
+static int GetInterfaceMaskFromObject(BrowserAccessibilityGtk* obj) {
+ int interface_mask = 0;
+
+ // Component interface is always supported.
+ interface_mask |= 1 << ATK_COMPONENT_INTERFACE;
+
+ int role = obj->role();
+ if (role == AccessibilityNodeData::ROLE_PROGRESS_INDICATOR ||
+ role == AccessibilityNodeData::ROLE_SCROLLBAR ||
+ role == AccessibilityNodeData::ROLE_SLIDER) {
+ interface_mask |= 1 << ATK_VALUE_INTERFACE;
+ }
+
+ return interface_mask;
+}
+
+static GType GetAccessibilityTypeFromObject(BrowserAccessibilityGtk* obj) {
+ static const GTypeInfo type_info = {
+ sizeof(BrowserAccessibilityAtkClass),
+ (GBaseInitFunc) 0,
+ (GBaseFinalizeFunc) 0,
+ (GClassInitFunc) 0,
+ (GClassFinalizeFunc) 0,
+ 0, /* class data */
+ sizeof(BrowserAccessibilityAtk), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) 0,
+ 0 /* value table */
+ };
+
+ int interface_mask = GetInterfaceMaskFromObject(obj);
+ const char* atk_type_name = GetUniqueAccessibilityTypeName(interface_mask);
+ GType type = g_type_from_name(atk_type_name);
+ if (type)
+ return type;
+
+ type = g_type_register_static(BROWSER_ACCESSIBILITY_TYPE,
+ atk_type_name,
+ &type_info,
+ GTypeFlags(0));
+ if (interface_mask & (1 << ATK_COMPONENT_INTERFACE))
+ g_type_add_interface_static(type, ATK_TYPE_COMPONENT, &ComponentInfo);
+ if (interface_mask & (1 << ATK_VALUE_INTERFACE))
+ g_type_add_interface_static(type, ATK_TYPE_VALUE, &ValueInfo);
+
+ return type;
+}
+
+BrowserAccessibilityAtk* browser_accessibility_new(
+ BrowserAccessibilityGtk* obj) {
+ GType type = GetAccessibilityTypeFromObject(obj);
+ AtkObject* atk_object = static_cast<AtkObject*>(g_object_new(type, 0));
+
+ atk_object_initialize(atk_object, obj);
+
+ return BROWSER_ACCESSIBILITY(atk_object);
+}
+
+void browser_accessibility_detach(BrowserAccessibilityAtk* atk_object) {
+ atk_object->m_object = NULL;
+}
+
+// static
+BrowserAccessibility* BrowserAccessibility::Create() {
+ return new BrowserAccessibilityGtk();
+}
+
+BrowserAccessibilityGtk* BrowserAccessibility::ToBrowserAccessibilityGtk() {
+ return static_cast<BrowserAccessibilityGtk*>(this);
+}
+
+BrowserAccessibilityGtk::BrowserAccessibilityGtk()
+ : atk_object_(NULL) {
+}
+
+BrowserAccessibilityGtk::~BrowserAccessibilityGtk() {
+ browser_accessibility_detach(BROWSER_ACCESSIBILITY(atk_object_));
+ if (atk_object_)
+ g_object_unref(atk_object_);
+}
+
+AtkObject* BrowserAccessibilityGtk::GetAtkObject() const {
+ if (!G_IS_OBJECT(atk_object_))
+ return NULL;
+ return atk_object_;
+}
+
+void BrowserAccessibilityGtk::PreInitialize() {
+ BrowserAccessibility::PreInitialize();
+ InitRoleAndState();
+
+ if (atk_object_) {
+ // If the object's role changes and that causes its
+ // interface mask to change, we need to create a new
+ // AtkObject for it.
+ int interface_mask = GetInterfaceMaskFromObject(this);
+ if (interface_mask != interface_mask_) {
+ g_object_unref(atk_object_);
+ atk_object_ = NULL;
+ }
+ }
+
+ if (!atk_object_) {
+ interface_mask_ = GetInterfaceMaskFromObject(this);
+ atk_object_ = ATK_OBJECT(browser_accessibility_new(this));
+ if (this->parent()) {
+ atk_object_set_parent(
+ atk_object_,
+ this->parent()->ToBrowserAccessibilityGtk()->GetAtkObject());
+ }
+ }
+}
+
+bool BrowserAccessibilityGtk::IsNative() const {
+ return true;
+}
+
+void BrowserAccessibilityGtk::InitRoleAndState() {
+ atk_acc_name_ = UTF16ToUTF8(name());
+
+ string16 description;
+ GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description);
+ atk_acc_description_ = UTF16ToUTF8(description);
+
+ switch(role_) {
+ case AccessibilityNodeData::ROLE_DOCUMENT:
+ case AccessibilityNodeData::ROLE_ROOT_WEB_AREA:
+ case AccessibilityNodeData::ROLE_WEB_AREA:
+ atk_role_ = ATK_ROLE_DOCUMENT_WEB;
+ break;
+ case AccessibilityNodeData::ROLE_GROUP:
+ case AccessibilityNodeData::ROLE_DIV:
+ atk_role_ = ATK_ROLE_SECTION;
+ break;
+ case AccessibilityNodeData::ROLE_BUTTON:
+ atk_role_ = ATK_ROLE_PUSH_BUTTON;
+ break;
+ case AccessibilityNodeData::ROLE_CHECKBOX:
+ atk_role_ = ATK_ROLE_CHECK_BOX;
+ break;
+ case AccessibilityNodeData::ROLE_COMBO_BOX:
+ atk_role_ = ATK_ROLE_COMBO_BOX;
+ break;
+ case AccessibilityNodeData::ROLE_LINK:
+ atk_role_ = ATK_ROLE_LINK;
+ break;
+ case AccessibilityNodeData::ROLE_RADIO_BUTTON:
+ atk_role_ = ATK_ROLE_RADIO_BUTTON;
+ break;
+ case AccessibilityNodeData::ROLE_STATIC_TEXT:
+ atk_role_ = ATK_ROLE_TEXT;
+ break;
+ case AccessibilityNodeData::ROLE_TEXTAREA:
+ atk_role_ = ATK_ROLE_ENTRY;
+ break;
+ case AccessibilityNodeData::ROLE_TEXT_FIELD:
+ atk_role_ = ATK_ROLE_ENTRY;
+ break;
+ case AccessibilityNodeData::ROLE_WEBCORE_LINK:
+ atk_role_ = ATK_ROLE_LINK;
+ break;
+ default:
+ atk_role_ = ATK_ROLE_UNKNOWN;
+ break;
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_gtk.h b/chromium/content/browser/accessibility/browser_accessibility_gtk.h
new file mode 100644
index 00000000000..8f3a2e31e5b
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_gtk.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_GTK_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_GTK_H_
+
+#include <atk/atk.h>
+
+#include "base/compiler_specific.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+
+namespace content {
+
+class BrowserAccessibilityGtk;
+class BrowserAccessibilityManagerGtk;
+
+G_BEGIN_DECLS
+
+#define BROWSER_ACCESSIBILITY_TYPE (browser_accessibility_get_type())
+#define BROWSER_ACCESSIBILITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST( \
+ (obj), BROWSER_ACCESSIBILITY_TYPE, BrowserAccessibilityAtk))
+#define BROWSER_ACCESSIBILITY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST( \
+ (klass), BROWSER_ACCESSIBILITY_TYPE, BrowserAccessibilityAtkClass))
+#define IS_BROWSER_ACCESSIBILITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_ACCESSIBILITY_TYPE))
+#define IS_BROWSER_ACCESSIBILITY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_ACCESSIBILITY_TYPE))
+#define BROWSER_ACCESSIBILITY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS( \
+ (obj), BROWSER_ACCESSIBILITY_TYPE, BrowserAccessibilityAtkClass))
+
+typedef struct _BrowserAccessibilityAtk BrowserAccessibilityAtk;
+typedef struct _BrowserAccessibilityAtkClass BrowserAccessibilityAtkClass;
+
+struct _BrowserAccessibilityAtk {
+ AtkObject parent;
+ BrowserAccessibilityGtk* m_object;
+};
+
+struct _BrowserAccessibilityAtkClass {
+ AtkObjectClass parent_class;
+};
+
+GType browser_accessibility_get_type (void) G_GNUC_CONST;
+
+BrowserAccessibilityAtk* browser_accessibility_new(
+ BrowserAccessibilityGtk* object);
+
+BrowserAccessibilityGtk* browser_accessibility_get_object(
+ BrowserAccessibilityAtk* atk_object);
+
+void browser_accessibility_detach (BrowserAccessibilityAtk* atk_object);
+
+AtkObject* browser_accessibility_get_focused_element(
+ BrowserAccessibilityAtk* atk_object);
+
+G_END_DECLS
+
+class BrowserAccessibilityGtk : public BrowserAccessibility {
+ public:
+ BrowserAccessibilityGtk();
+
+ virtual ~BrowserAccessibilityGtk();
+
+ AtkObject* GetAtkObject() const;
+
+ AtkRole atk_role() { return atk_role_; }
+ const std::string& atk_acc_name() { return atk_acc_name_; }
+ const std::string& atk_acc_description() { return atk_acc_description_; }
+
+ // BrowserAccessibility methods.
+ virtual void PreInitialize() OVERRIDE;
+ virtual bool IsNative() const OVERRIDE;
+
+ private:
+ virtual void InitRoleAndState();
+
+ // Give BrowserAccessibility::Create access to our constructor.
+ friend class BrowserAccessibility;
+
+ AtkObject* atk_object_;
+ AtkRole atk_role_;
+ std::string atk_acc_name_;
+ std::string atk_acc_description_;
+ int interface_mask_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityGtk);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_GTK_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac.h b/chromium/content/browser/accessibility/browser_accessibility_mac.h
new file mode 100644
index 00000000000..63a9d007d3e
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_mac.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MAC_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MAC_H_
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "content/browser/accessibility/browser_accessibility.h"
+
+@class BrowserAccessibilityCocoa;
+
+namespace content {
+
+class BrowserAccessibilityMac : public BrowserAccessibility {
+ public:
+ // Implementation of BrowserAccessibility.
+ virtual void PreInitialize() OVERRIDE;
+ virtual void NativeReleaseReference() OVERRIDE;
+ virtual bool IsNative() const OVERRIDE;
+
+ // Overrides from BrowserAccessibility.
+ virtual void DetachTree(std::vector<BrowserAccessibility*>* nodes) OVERRIDE;
+ virtual void SwapChildren(std::vector<BrowserAccessibility*>& children)
+ OVERRIDE;
+
+ // The BrowserAccessibilityCocoa associated with us.
+ BrowserAccessibilityCocoa* native_view() const {
+ return browser_accessibility_cocoa_;
+ }
+
+ private:
+ // This gives BrowserAccessibility::Create access to the class constructor.
+ friend class BrowserAccessibility;
+
+ BrowserAccessibilityMac();
+
+ // Allows access to the BrowserAccessibilityCocoa which wraps this.
+ // BrowserAccessibility.
+ // We own this object until our manager calls ReleaseReference;
+ // thereafter, the cocoa object owns us.
+ BrowserAccessibilityCocoa* browser_accessibility_cocoa_;
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityMac);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MAC_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_mac.mm
new file mode 100644
index 00000000000..11595a10e28
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_mac.mm
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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.
+
+#import <Cocoa/Cocoa.h>
+
+#import "content/browser/accessibility/browser_accessibility_mac.h"
+
+#import "content/browser/accessibility/browser_accessibility_cocoa.h"
+#import "content/browser/accessibility/browser_accessibility_delegate_mac.h"
+#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
+
+namespace content {
+
+// Static.
+BrowserAccessibility* BrowserAccessibility::Create() {
+ return new BrowserAccessibilityMac();
+}
+
+BrowserAccessibilityMac::BrowserAccessibilityMac()
+ : browser_accessibility_cocoa_(NULL) {
+}
+
+void BrowserAccessibilityMac::PreInitialize() {
+ BrowserAccessibility::PreInitialize();
+
+ if (browser_accessibility_cocoa_)
+ return;
+
+ // We take ownership of the cocoa obj here.
+ BrowserAccessibilityManagerMac* manager =
+ static_cast<BrowserAccessibilityManagerMac*>(manager_);
+ browser_accessibility_cocoa_ = [[BrowserAccessibilityCocoa alloc]
+ initWithObject:this
+ delegate:
+ (id<BrowserAccessibilityDelegateCocoa>)manager->parent_view()];
+}
+
+void BrowserAccessibilityMac::NativeReleaseReference() {
+ // Detach this object from |browser_accessibility_cocoa_| so it
+ // no longer has a pointer to this object.
+ [browser_accessibility_cocoa_ detach];
+ // Now, release it - but at this point, other processes may have a
+ // reference to the cocoa object.
+ [browser_accessibility_cocoa_ release];
+ // Finally, it's safe to delete this since we've detached.
+ delete this;
+}
+
+bool BrowserAccessibilityMac::IsNative() const {
+ return true;
+}
+
+void BrowserAccessibilityMac::DetachTree(
+ std::vector<BrowserAccessibility*>* nodes) {
+ [browser_accessibility_cocoa_ childrenChanged];
+ BrowserAccessibility::DetachTree(nodes);
+}
+
+void BrowserAccessibilityMac::SwapChildren(
+ std::vector<BrowserAccessibility*>& children) {
+ [browser_accessibility_cocoa_ childrenChanged];
+ BrowserAccessibility::SwapChildren(children);
+}
+
+BrowserAccessibilityCocoa* BrowserAccessibility::ToBrowserAccessibilityCocoa() {
+ return static_cast<BrowserAccessibilityMac*>(this)->
+ native_view();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm b/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm
new file mode 100644
index 00000000000..45137ec677a
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm
@@ -0,0 +1,165 @@
+// Copyright (c) 2012 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.
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility_cocoa.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#import "ui/base/test/ui_cocoa_test_helper.h"
+
+@interface MockAccessibilityDelegate :
+ NSView<BrowserAccessibilityDelegateCocoa>
+
+- (NSPoint)accessibilityPointInScreen:(BrowserAccessibilityCocoa*)accessibility;
+- (void)doDefaultAction:(int32)accessibilityObjectId;
+- (void)accessibilitySetTextSelection:(int32)accId
+ startOffset:(int32)startOffset
+ endOffset:(int32)endOffset;
+- (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility;
+- (void)setAccessibilityFocus:(BOOL)focus
+ accessibilityId:(int32)accessibilityObjectId;
+- (NSWindow*)window;
+
+@end
+
+@implementation MockAccessibilityDelegate
+
+- (NSPoint)accessibilityPointInScreen:
+ (BrowserAccessibilityCocoa*)accessibility {
+ return NSZeroPoint;
+}
+- (void)doDefaultAction:(int32)accessibilityObjectId {
+}
+- (void)accessibilitySetTextSelection:(int32)accId
+ startOffset:(int32)startOffset
+ endOffset:(int32)endOffset {
+}
+- (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility {
+}
+- (void)setAccessibilityFocus:(BOOL)focus
+ accessibilityId:(int32)accessibilityObjectId {
+}
+- (NSWindow*)window {
+ return nil;
+}
+
+@end
+
+namespace content {
+
+class BrowserAccessibilityTest : public ui::CocoaTest {
+ public:
+ virtual void SetUp() {
+ CocoaTest::SetUp();
+ RebuildAccessibilityTree();
+ }
+
+ protected:
+ void RebuildAccessibilityTree() {
+ AccessibilityNodeData root;
+ root.id = 1000;
+ root.location.set_width(500);
+ root.location.set_height(100);
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.string_attributes[AccessibilityNodeData::ATTR_HELP] =
+ ASCIIToUTF16("HelpText");
+ root.child_ids.push_back(1001);
+ root.child_ids.push_back(1002);
+
+ AccessibilityNodeData child1;
+ child1.id = 1001;
+ child1.name = ASCIIToUTF16("Child1");
+ child1.location.set_width(250);
+ child1.location.set_height(100);
+ child1.role = AccessibilityNodeData::ROLE_BUTTON;
+
+ AccessibilityNodeData child2;
+ child2.id = 1002;
+ child2.location.set_x(250);
+ child2.location.set_width(250);
+ child2.location.set_height(100);
+ child2.role = AccessibilityNodeData::ROLE_HEADING;
+
+ delegate_.reset([[MockAccessibilityDelegate alloc] init]);
+ manager_.reset(
+ new BrowserAccessibilityManagerMac(delegate_, root, NULL));
+ manager_->UpdateNodesForTesting(child1, child2);
+ accessibility_.reset([manager_->GetRoot()->ToBrowserAccessibilityCocoa()
+ retain]);
+ }
+
+ base::scoped_nsobject<MockAccessibilityDelegate> delegate_;
+ base::scoped_nsobject<BrowserAccessibilityCocoa> accessibility_;
+ scoped_ptr<BrowserAccessibilityManager> manager_;
+};
+
+// Standard hit test.
+TEST_F(BrowserAccessibilityTest, HitTestTest) {
+ BrowserAccessibilityCocoa* firstChild =
+ [accessibility_ accessibilityHitTest:NSMakePoint(50, 50)];
+ EXPECT_NSEQ(@"Child1",
+ [firstChild accessibilityAttributeValue:NSAccessibilityTitleAttribute]);
+}
+
+// Test doing a hit test on the edge of a child.
+TEST_F(BrowserAccessibilityTest, EdgeHitTest) {
+ BrowserAccessibilityCocoa* firstChild =
+ [accessibility_ accessibilityHitTest:NSZeroPoint];
+ EXPECT_NSEQ(@"Child1",
+ [firstChild accessibilityAttributeValue:NSAccessibilityTitleAttribute]);
+}
+
+// This will test a hit test with invalid coordinates. It is assumed that
+// the hit test has been narrowed down to this object or one of its children
+// so it should return itself since it has no better hit result.
+TEST_F(BrowserAccessibilityTest, InvalidHitTestCoordsTest) {
+ BrowserAccessibilityCocoa* hitTestResult =
+ [accessibility_ accessibilityHitTest:NSMakePoint(-50, 50)];
+ EXPECT_NSEQ(accessibility_, hitTestResult);
+}
+
+// Test to ensure querying standard attributes works.
+// http://crbug.com/173983 Test fails on Mac ASan bot
+TEST_F(BrowserAccessibilityTest, DISABLED_BasicAttributeTest) {
+ NSString* helpText = [accessibility_
+ accessibilityAttributeValue:NSAccessibilityHelpAttribute];
+ EXPECT_NSEQ(@"HelpText", helpText);
+}
+
+// Test querying for an invalid attribute to ensure it doesn't crash.
+TEST_F(BrowserAccessibilityTest, InvalidAttributeTest) {
+ NSString* shouldBeNil = [accessibility_
+ accessibilityAttributeValue:@"NSAnInvalidAttribute"];
+ EXPECT_TRUE(shouldBeNil == nil);
+}
+
+TEST_F(BrowserAccessibilityTest, RetainedDetachedObjectsReturnNil) {
+ // Get the first child.
+ BrowserAccessibilityCocoa* retainedFirstChild =
+ [accessibility_ accessibilityHitTest:NSMakePoint(50, 50)];
+ EXPECT_NSEQ(@"Child1", [retainedFirstChild
+ accessibilityAttributeValue:NSAccessibilityTitleAttribute]);
+
+ // Retain it. This simulates what the system might do with an
+ // accessibility object.
+ [retainedFirstChild retain];
+
+ // Rebuild the accessibility tree, which should detach |retainedFirstChild|.
+ RebuildAccessibilityTree();
+
+ // Now any attributes we query should return nil.
+ EXPECT_EQ(nil, [retainedFirstChild
+ accessibilityAttributeValue:NSAccessibilityTitleAttribute]);
+
+ // Don't leak memory in the test.
+ [retainedFirstChild release];
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.cc b/chromium/content/browser/accessibility/browser_accessibility_manager.cc
new file mode 100644
index 00000000000..7def57459f9
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager.cc
@@ -0,0 +1,419 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_manager.h"
+
+#include "base/logging.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+#include "content/common/accessibility_messages.h"
+
+namespace content {
+
+BrowserAccessibility* BrowserAccessibilityFactory::Create() {
+ return BrowserAccessibility::Create();
+}
+
+#if !defined(OS_MACOSX) && \
+ !defined(OS_WIN) && \
+ !defined(TOOLKIT_GTK) && \
+ !defined(OS_ANDROID) \
+// We have subclassess of BrowserAccessibilityManager on Mac, Linux/GTK,
+// and Win. For any other platform, instantiate the base class.
+// static
+BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory) {
+ return new BrowserAccessibilityManager(src, delegate, factory);
+}
+#endif
+
+BrowserAccessibilityManager::BrowserAccessibilityManager(
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory)
+ : delegate_(delegate),
+ factory_(factory),
+ root_(NULL),
+ focus_(NULL),
+ osk_state_(OSK_ALLOWED) {
+}
+
+BrowserAccessibilityManager::BrowserAccessibilityManager(
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory)
+ : delegate_(delegate),
+ factory_(factory),
+ root_(NULL),
+ focus_(NULL),
+ osk_state_(OSK_ALLOWED) {
+ Initialize(src);
+}
+
+BrowserAccessibilityManager::~BrowserAccessibilityManager() {
+ if (root_)
+ root_->Destroy();
+}
+
+void BrowserAccessibilityManager::Initialize(const AccessibilityNodeData src) {
+ std::vector<AccessibilityNodeData> nodes;
+ nodes.push_back(src);
+ if (!UpdateNodes(nodes))
+ return;
+ if (!focus_)
+ SetFocus(root_, false);
+}
+
+// static
+AccessibilityNodeData BrowserAccessibilityManager::GetEmptyDocument() {
+ AccessibilityNodeData empty_document;
+ empty_document.id = 0;
+ empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ return empty_document;
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
+ return root_;
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID(
+ int32 renderer_id) {
+ base::hash_map<int32, BrowserAccessibility*>::iterator iter =
+ renderer_id_map_.find(renderer_id);
+ if (iter != renderer_id_map_.end())
+ return iter->second;
+ return NULL;
+}
+
+void BrowserAccessibilityManager::GotFocus(bool touch_event_context) {
+ if (!touch_event_context)
+ osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED;
+
+ if (!focus_)
+ return;
+
+ NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_);
+}
+
+void BrowserAccessibilityManager::WasHidden() {
+ osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_HIDDEN;
+}
+
+void BrowserAccessibilityManager::GotMouseDown() {
+ osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
+ NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_);
+}
+
+bool BrowserAccessibilityManager::IsOSKAllowed(const gfx::Rect& bounds) {
+ if (!delegate_ || !delegate_->HasFocus())
+ return false;
+
+ gfx::Point touch_point = delegate_->GetLastTouchEventLocation();
+ return bounds.Contains(touch_point);
+}
+
+bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
+ return true;
+}
+
+void BrowserAccessibilityManager::RemoveNode(BrowserAccessibility* node) {
+ if (node == focus_)
+ SetFocus(root_, false);
+ int renderer_id = node->renderer_id();
+ renderer_id_map_.erase(renderer_id);
+}
+
+void BrowserAccessibilityManager::OnAccessibilityNotifications(
+ const std::vector<AccessibilityHostMsg_NotificationParams>& params) {
+ for (uint32 index = 0; index < params.size(); index++) {
+ const AccessibilityHostMsg_NotificationParams& param = params[index];
+
+ // Update nodes that changed.
+ if (!UpdateNodes(param.nodes))
+ return;
+
+ // Find the node corresponding to the id that's the target of the
+ // notification (which may not be the root of the update tree).
+ BrowserAccessibility* node = GetFromRendererID(param.id);
+ if (!node)
+ continue;
+
+ int notification_type = param.notification_type;
+ if (notification_type == AccessibilityNotificationFocusChanged ||
+ notification_type == AccessibilityNotificationBlur) {
+ SetFocus(node, false);
+
+ if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
+ osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED)
+ osk_state_ = OSK_ALLOWED;
+
+ // Don't send a native focus event if the window itself doesn't
+ // have focus.
+ if (delegate_ && !delegate_->HasFocus())
+ continue;
+ }
+
+ // Send the notification event to the operating system.
+ NotifyAccessibilityEvent(notification_type, node);
+
+ // Set initial focus when a page is loaded.
+ if (notification_type == AccessibilityNotificationLoadComplete) {
+ if (!focus_)
+ SetFocus(root_, false);
+ if (!delegate_ || delegate_->HasFocus())
+ NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_);
+ }
+ }
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
+ BrowserAccessibility* root) {
+ if (focus_ && (!root || focus_->IsDescendantOf(root)))
+ return focus_;
+
+ return NULL;
+}
+
+void BrowserAccessibilityManager::SetFocus(
+ BrowserAccessibility* node, bool notify) {
+ if (focus_ != node)
+ focus_ = node;
+
+ if (notify && node && delegate_)
+ delegate_->SetAccessibilityFocus(node->renderer_id());
+}
+
+void BrowserAccessibilityManager::SetRoot(BrowserAccessibility* node) {
+ root_ = node;
+ NotifyRootChanged();
+}
+
+void BrowserAccessibilityManager::DoDefaultAction(
+ const BrowserAccessibility& node) {
+ if (delegate_)
+ delegate_->AccessibilityDoDefaultAction(node.renderer_id());
+}
+
+void BrowserAccessibilityManager::ScrollToMakeVisible(
+ const BrowserAccessibility& node, gfx::Rect subfocus) {
+ if (delegate_) {
+ delegate_->AccessibilityScrollToMakeVisible(node.renderer_id(), subfocus);
+ }
+}
+
+void BrowserAccessibilityManager::ScrollToPoint(
+ const BrowserAccessibility& node, gfx::Point point) {
+ if (delegate_) {
+ delegate_->AccessibilityScrollToPoint(node.renderer_id(), point);
+ }
+}
+
+void BrowserAccessibilityManager::SetTextSelection(
+ const BrowserAccessibility& node, int start_offset, int end_offset) {
+ if (delegate_) {
+ delegate_->AccessibilitySetTextSelection(
+ node.renderer_id(), start_offset, end_offset);
+ }
+}
+
+gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
+ if (delegate_)
+ return delegate_->GetViewBounds();
+ return gfx::Rect();
+}
+
+void BrowserAccessibilityManager::UpdateNodesForTesting(
+ const AccessibilityNodeData& node1,
+ const AccessibilityNodeData& node2 /* = AccessibilityNodeData() */,
+ const AccessibilityNodeData& node3 /* = AccessibilityNodeData() */,
+ const AccessibilityNodeData& node4 /* = AccessibilityNodeData() */,
+ const AccessibilityNodeData& node5 /* = AccessibilityNodeData() */,
+ const AccessibilityNodeData& node6 /* = AccessibilityNodeData() */,
+ const AccessibilityNodeData& node7 /* = AccessibilityNodeData() */) {
+ std::vector<AccessibilityNodeData> nodes;
+ nodes.push_back(node1);
+ if (node2.id != AccessibilityNodeData().id)
+ nodes.push_back(node2);
+ if (node3.id != AccessibilityNodeData().id)
+ nodes.push_back(node3);
+ if (node4.id != AccessibilityNodeData().id)
+ nodes.push_back(node4);
+ if (node5.id != AccessibilityNodeData().id)
+ nodes.push_back(node5);
+ if (node6.id != AccessibilityNodeData().id)
+ nodes.push_back(node6);
+ if (node7.id != AccessibilityNodeData().id)
+ nodes.push_back(node7);
+ UpdateNodes(nodes);
+}
+
+bool BrowserAccessibilityManager::UpdateNodes(
+ const std::vector<AccessibilityNodeData>& nodes) {
+ bool success = true;
+
+ // First, update all of the nodes in the tree.
+ for (size_t i = 0; i < nodes.size() && success; i++) {
+ if (!UpdateNode(nodes[i]))
+ success = false;
+ }
+
+ // In a second pass, call PostInitialize on each one - this must
+ // be called after all of each node's children are initialized too.
+ for (size_t i = 0; i < nodes.size() && success; i++) {
+ // Note: it's not a bug for nodes[i].id to not be found in the tree.
+ // Consider this example:
+ // Before:
+ // A
+ // B
+ // C
+ // D
+ // E
+ // F
+ // After:
+ // A
+ // B
+ // C
+ // F
+ // D
+ // In this example, F is being reparented. The renderer scans the tree
+ // in order. If can't update "C" to add "F" as a child, when "F" is still
+ // a child of "E". So it first updates "E", to remove "F" as a child.
+ // Later, it ends up deleting "E". So when we get here, "E" was updated as
+ // part of this sequence but it no longer exists in the final tree, so
+ // there's nothing to postinitialize.
+ BrowserAccessibility* instance = GetFromRendererID(nodes[i].id);
+ if (instance)
+ instance->PostInitialize();
+ }
+
+ if (!success) {
+ // A bad accessibility tree could lead to memory corruption.
+ // Ask the delegate to crash the renderer, or if not available,
+ // crash the browser.
+ if (delegate_)
+ delegate_->FatalAccessibilityTreeError();
+ else
+ CHECK(false);
+ }
+
+ return success;
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::CreateNode(
+ BrowserAccessibility* parent,
+ int32 renderer_id,
+ int32 index_in_parent) {
+ BrowserAccessibility* node = factory_->Create();
+ node->InitializeTreeStructure(
+ this, parent, renderer_id, index_in_parent);
+ AddNodeToMap(node);
+ return node;
+}
+
+void BrowserAccessibilityManager::AddNodeToMap(BrowserAccessibility* node) {
+ renderer_id_map_[node->renderer_id()] = node;
+}
+
+bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) {
+ // This method updates one node in the tree based on serialized data
+ // received from the renderer.
+
+ // Create a set of child ids in |src| for fast lookup. If a duplicate id is
+ // found, exit now with a fatal error before changing anything else.
+ std::set<int32> new_child_ids;
+ for (size_t i = 0; i < src.child_ids.size(); ++i) {
+ if (new_child_ids.find(src.child_ids[i]) != new_child_ids.end())
+ return false;
+ new_child_ids.insert(src.child_ids[i]);
+ }
+
+ // Look up the node by id. If it's not found, then either the root
+ // of the tree is being swapped, or we're out of sync with the renderer
+ // and this is a serious error.
+ BrowserAccessibility* instance = GetFromRendererID(src.id);
+ if (!instance) {
+ if (src.role != AccessibilityNodeData::ROLE_ROOT_WEB_AREA)
+ return false;
+ instance = CreateNode(NULL, src.id, 0);
+ }
+
+ if (src.bool_attributes.find(
+ AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) !=
+ src.bool_attributes.end()) {
+ instance->SetLocation(src.location);
+ return true;
+ }
+
+ // Update all of the node-specific data, like its role, state, name, etc.
+ instance->InitializeData(src);
+
+ //
+ // Update the children in three steps:
+ //
+ // 1. Iterate over the old children and delete nodes that are no longer
+ // in the tree.
+ // 2. Build up a vector of new children, reusing children that haven't
+ // changed (but may have been reordered) and adding new empty
+ // objects for new children.
+ // 3. Swap in the new children vector for the old one.
+
+ // Delete any previous children of this instance that are no longer
+ // children first. We make a deletion-only pass first to prevent a
+ // node that's being reparented from being the child of both its old
+ // parent and new parent, which could lead to a double-free.
+ // If a node is reparented, the renderer will always send us a fresh
+ // copy of the node.
+ const std::vector<BrowserAccessibility*>& old_children = instance->children();
+ for (size_t i = 0; i < old_children.size(); ++i) {
+ int old_id = old_children[i]->renderer_id();
+ if (new_child_ids.find(old_id) == new_child_ids.end())
+ old_children[i]->Destroy();
+ }
+
+ // Now build a vector of new children, reusing objects that were already
+ // children of this node before.
+ std::vector<BrowserAccessibility*> new_children;
+ bool success = true;
+ for (size_t i = 0; i < src.child_ids.size(); i++) {
+ int32 child_renderer_id = src.child_ids[i];
+ int32 index_in_parent = static_cast<int32>(i);
+ BrowserAccessibility* child = GetFromRendererID(child_renderer_id);
+ if (child) {
+ if (child->parent() != instance) {
+ // This is a serious error - nodes should never be reparented.
+ // If this case occurs, continue so this node isn't left in an
+ // inconsistent state, but return failure at the end.
+ success = false;
+ continue;
+ }
+ child->UpdateParent(instance, index_in_parent);
+ } else {
+ child = CreateNode(instance, child_renderer_id, index_in_parent);
+ }
+ new_children.push_back(child);
+ }
+
+ // Finally, swap in the new children vector for the old.
+ instance->SwapChildren(new_children);
+
+ // Handle the case where this node is the new root of the tree.
+ if (src.role == AccessibilityNodeData::ROLE_ROOT_WEB_AREA &&
+ (!root_ || root_->renderer_id() != src.id)) {
+ if (root_)
+ root_->Destroy();
+ if (focus_ == root_)
+ SetFocus(instance, false);
+ SetRoot(instance);
+ }
+
+ // Keep track of what node is focused.
+ if (src.role != AccessibilityNodeData::ROLE_ROOT_WEB_AREA &&
+ src.role != AccessibilityNodeData::ROLE_WEB_AREA &&
+ (src.state >> AccessibilityNodeData::STATE_FOCUSED & 1)) {
+ SetFocus(instance, false);
+ }
+ return success;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.h b/chromium/content/browser/accessibility/browser_accessibility_manager.h
new file mode 100644
index 00000000000..5fac3e5e3f3
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager.h
@@ -0,0 +1,236 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_H_
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "content/common/accessibility_node_data.h"
+#include "content/common/content_export.h"
+#include "ui/gfx/native_widget_types.h"
+
+struct AccessibilityHostMsg_NotificationParams;
+
+namespace content {
+class BrowserAccessibility;
+#if defined(OS_WIN)
+class BrowserAccessibilityManagerWin;
+#endif
+
+// Class that can perform actions on behalf of the BrowserAccessibilityManager.
+class CONTENT_EXPORT BrowserAccessibilityDelegate {
+ public:
+ virtual ~BrowserAccessibilityDelegate() {}
+ virtual void SetAccessibilityFocus(int acc_obj_id) = 0;
+ virtual void AccessibilityDoDefaultAction(int acc_obj_id) = 0;
+ virtual void AccessibilityScrollToMakeVisible(
+ int acc_obj_id, gfx::Rect subfocus) = 0;
+ virtual void AccessibilityScrollToPoint(
+ int acc_obj_id, gfx::Point point) = 0;
+ virtual void AccessibilitySetTextSelection(
+ int acc_obj_id, int start_offset, int end_offset) = 0;
+ virtual bool HasFocus() const = 0;
+ virtual gfx::Rect GetViewBounds() const = 0;
+ virtual gfx::Point GetLastTouchEventLocation() const = 0;
+ virtual void FatalAccessibilityTreeError() = 0;
+};
+
+class CONTENT_EXPORT BrowserAccessibilityFactory {
+ public:
+ virtual ~BrowserAccessibilityFactory() {}
+
+ // Create an instance of BrowserAccessibility and return a new
+ // reference to it.
+ virtual BrowserAccessibility* Create();
+};
+
+// Manages a tree of BrowserAccessibility objects.
+class CONTENT_EXPORT BrowserAccessibilityManager {
+ public:
+ // Creates the platform-specific BrowserAccessibilityManager, but
+ // with no parent window pointer. Only useful for unit tests.
+ static BrowserAccessibilityManager* Create(
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
+
+ virtual ~BrowserAccessibilityManager();
+
+ void Initialize(const AccessibilityNodeData src);
+
+ static AccessibilityNodeData GetEmptyDocument();
+
+ // Type is enum AccessibilityNotification.
+ // We pass it as int so that we don't include the message declaration
+ // header here.
+ virtual void NotifyAccessibilityEvent(
+ int type,
+ BrowserAccessibility* node) { }
+
+ // Return a pointer to the root of the tree, does not make a new reference.
+ BrowserAccessibility* GetRoot();
+
+ // Removes a node from the manager.
+ virtual void RemoveNode(BrowserAccessibility* node);
+
+ // Return a pointer to the object corresponding to the given renderer_id,
+ // does not make a new reference.
+ BrowserAccessibility* GetFromRendererID(int32 renderer_id);
+
+ // Called to notify the accessibility manager that its associated native
+ // view got focused. This implies that it is shown (opposite of WasHidden,
+ // below).
+ // The touch_event_context parameter indicates that we were called in the
+ // context of a touch event.
+ void GotFocus(bool touch_event_context);
+
+ // Called to notify the accessibility manager that its associated native
+ // view was hidden. When it's no longer hidden, GotFocus will be called.
+ void WasHidden();
+
+ // Called to notify the accessibility manager that a mouse down event
+ // occurred in the tab.
+ void GotMouseDown();
+
+ // Update the focused node to |node|, which may be null.
+ // If |notify| is true, send a message to the renderer to set focus
+ // to this node.
+ void SetFocus(BrowserAccessibility* node, bool notify);
+
+ // Tell the renderer to do the default action for this node.
+ void DoDefaultAction(const BrowserAccessibility& node);
+
+ // Tell the renderer to scroll to make |node| visible.
+ // In addition, if it's not possible to make the entire object visible,
+ // scroll so that the |subfocus| rect is visible at least. The subfocus
+ // rect is in local coordinates of the object itself.
+ void ScrollToMakeVisible(
+ const BrowserAccessibility& node, gfx::Rect subfocus);
+
+ // Tell the renderer to scroll such that |node| is at |point|,
+ // where |point| is in global coordinates of the WebContents.
+ void ScrollToPoint(
+ const BrowserAccessibility& node, gfx::Point point);
+
+ // Tell the renderer to set the text selection on a node.
+ void SetTextSelection(
+ const BrowserAccessibility& node, int start_offset, int end_offset);
+
+ // Retrieve the bounds of the parent View in screen coordinates.
+ gfx::Rect GetViewBounds();
+
+ // Called when the renderer process has notified us of about tree changes.
+ // Send a notification to MSAA clients of the change.
+ void OnAccessibilityNotifications(
+ const std::vector<AccessibilityHostMsg_NotificationParams>& params);
+
+#if defined(OS_WIN)
+ BrowserAccessibilityManagerWin* ToBrowserAccessibilityManagerWin();
+#endif
+
+ // Return the object that has focus, if it's a descandant of the
+ // given root (inclusive). Does not make a new reference.
+ BrowserAccessibility* GetFocus(BrowserAccessibility* root);
+
+ // Is the on-screen keyboard allowed to be shown, in response to a
+ // focus event on a text box?
+ bool IsOSKAllowed(const gfx::Rect& bounds);
+
+ // True by default, but some platforms want to treat the root
+ // scroll offsets separately.
+ virtual bool UseRootScrollOffsetsWhenComputingBounds();
+
+ // For testing only: update the given nodes as if they were
+ // received from the renderer process in OnAccessibilityNotifications.
+ // Takes up to 7 nodes at once so tests don't need to create a vector
+ // each time.
+ void UpdateNodesForTesting(
+ const AccessibilityNodeData& node,
+ const AccessibilityNodeData& node2 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node3 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node4 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node5 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node6 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node7 = AccessibilityNodeData());
+
+ protected:
+ BrowserAccessibilityManager(
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory);
+
+ BrowserAccessibilityManager(
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory);
+
+ virtual void AddNodeToMap(BrowserAccessibility* node);
+
+ virtual void NotifyRootChanged() {}
+
+ private:
+ // The following states keep track of whether or not the
+ // on-screen keyboard is allowed to be shown.
+ enum OnScreenKeyboardState {
+ // Never show the on-screen keyboard because this tab is hidden.
+ OSK_DISALLOWED_BECAUSE_TAB_HIDDEN,
+
+ // This tab was just shown, so don't pop-up the on-screen keyboard if a
+ // text field gets focus that wasn't the result of an explicit touch.
+ OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED,
+
+ // A touch event has occurred within the window, but focus has not
+ // explicitly changed. Allow the on-screen keyboard to be shown if the
+ // touch event was within the bounds of the currently focused object.
+ // Otherwise we'll just wait to see if focus changes.
+ OSK_ALLOWED_WITHIN_FOCUSED_OBJECT,
+
+ // Focus has changed within a tab that's already visible. Allow the
+ // on-screen keyboard to show anytime that a touch event leads to an
+ // editable text control getting focus.
+ OSK_ALLOWED
+ };
+
+ // Update a set of nodes using data received from the renderer
+ // process.
+ bool UpdateNodes(const std::vector<AccessibilityNodeData>& nodes);
+
+ // Update one node from the tree using data received from the renderer
+ // process. Returns true on success, false on fatal error.
+ bool UpdateNode(const AccessibilityNodeData& src);
+
+ void SetRoot(BrowserAccessibility* root);
+
+ BrowserAccessibility* CreateNode(
+ BrowserAccessibility* parent,
+ int32 renderer_id,
+ int32 index_in_parent);
+
+ protected:
+ // The object that can perform actions on our behalf.
+ BrowserAccessibilityDelegate* delegate_;
+
+ // Factory to create BrowserAccessibility objects (for dependency injection).
+ scoped_ptr<BrowserAccessibilityFactory> factory_;
+
+ // The root of the tree of accessible objects and the element that
+ // currently has focus, if any.
+ BrowserAccessibility* root_;
+ BrowserAccessibility* focus_;
+
+ // The on-screen keyboard state.
+ OnScreenKeyboardState osk_state_;
+
+ // A mapping from renderer IDs to BrowserAccessibility objects.
+ base::hash_map<int32, BrowserAccessibility*> renderer_id_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc
new file mode 100644
index 00000000000..e14aa0bb977
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -0,0 +1,366 @@
+// Copyright 2013 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 "content/browser/accessibility/browser_accessibility_manager_android.h"
+
+#include <cmath>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/browser/accessibility/browser_accessibility_android.h"
+#include "content/common/accessibility_messages.h"
+#include "jni/BrowserAccessibilityManager_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace {
+
+// These are enums from android.view.accessibility.AccessibilityEvent in Java:
+enum {
+ ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_CHANGED = 16,
+ ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192
+};
+
+// Restricts |val| to the range [min, max].
+int Clamp(int val, int min, int max) {
+ return std::min(std::max(val, min), max);
+}
+
+} // anonymous namespace
+
+namespace content {
+
+namespace aria_strings {
+ const char kAriaLivePolite[] = "polite";
+ const char kAriaLiveAssertive[] = "assertive";
+}
+
+// static
+BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory) {
+ return new BrowserAccessibilityManagerAndroid(ScopedJavaLocalRef<jobject>(),
+ src, delegate, factory);
+}
+
+BrowserAccessibilityManagerAndroid::BrowserAccessibilityManagerAndroid(
+ ScopedJavaLocalRef<jobject> content_view_core,
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory)
+ : BrowserAccessibilityManager(src, delegate, factory) {
+ if (content_view_core.is_null())
+ return;
+
+ JNIEnv* env = AttachCurrentThread();
+ java_ref_ = JavaObjectWeakGlobalRef(
+ env, Java_BrowserAccessibilityManager_create(
+ env, reinterpret_cast<jint>(this), content_view_core.obj()).obj());
+}
+
+BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ Java_BrowserAccessibilityManager_onNativeObjectDestroyed(env, obj.obj());
+}
+
+// static
+AccessibilityNodeData BrowserAccessibilityManagerAndroid::GetEmptyDocument() {
+ AccessibilityNodeData empty_document;
+ empty_document.id = 0;
+ empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ empty_document.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ return empty_document;
+}
+
+void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent(
+ int type,
+ BrowserAccessibility* node) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ switch (type) {
+ case AccessibilityNotificationLoadComplete:
+ Java_BrowserAccessibilityManager_handlePageLoaded(
+ env, obj.obj(), focus_->renderer_id());
+ break;
+ case AccessibilityNotificationFocusChanged:
+ Java_BrowserAccessibilityManager_handleFocusChanged(
+ env, obj.obj(), node->renderer_id());
+ break;
+ case AccessibilityNotificationCheckStateChanged:
+ Java_BrowserAccessibilityManager_handleCheckStateChanged(
+ env, obj.obj(), node->renderer_id());
+ break;
+ case AccessibilityNotificationScrolledToAnchor:
+ Java_BrowserAccessibilityManager_handleScrolledToAnchor(
+ env, obj.obj(), node->renderer_id());
+ break;
+ case AccessibilityNotificationAlert:
+ // An alert is a special case of live region. Fall through to the
+ // next case to handle it.
+ case AccessibilityNotificationObjectShow: {
+ // This event is fired when an object appears in a live region.
+ // Speak its text.
+ BrowserAccessibilityAndroid* android_node =
+ static_cast<BrowserAccessibilityAndroid*>(node);
+ Java_BrowserAccessibilityManager_announceLiveRegionText(
+ env, obj.obj(),
+ base::android::ConvertUTF16ToJavaString(
+ env, android_node->GetText()).obj());
+ break;
+ }
+ case AccessibilityNotificationSelectedTextChanged:
+ Java_BrowserAccessibilityManager_handleTextSelectionChanged(
+ env, obj.obj(), node->renderer_id());
+ break;
+ case AccessibilityNotificationChildrenChanged:
+ case AccessibilityNotificationTextChanged:
+ case AccessibilityNotificationValueChanged:
+ if (node->IsEditableText()) {
+ Java_BrowserAccessibilityManager_handleEditableTextChanged(
+ env, obj.obj(), node->renderer_id());
+ } else {
+ Java_BrowserAccessibilityManager_handleContentChanged(
+ env, obj.obj(), node->renderer_id());
+ }
+ break;
+ default:
+ // There are some notifications that aren't meaningful on Android.
+ // It's okay to skip them.
+ break;
+ }
+}
+
+jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) {
+ return static_cast<jint>(root_->renderer_id());
+}
+
+jint BrowserAccessibilityManagerAndroid::HitTest(
+ JNIEnv* env, jobject obj, jint x, jint y) {
+ BrowserAccessibilityAndroid* result =
+ static_cast<BrowserAccessibilityAndroid*>(
+ root_->BrowserAccessibilityForPoint(gfx::Point(x, y)));
+
+ if (!result)
+ return root_->renderer_id();
+
+ if (result->IsFocusable())
+ return result->renderer_id();
+
+ // Examine the children of |result| to find the nearest accessibility focus
+ // candidate
+ BrowserAccessibility* nearest_node = FuzzyHitTest(x, y, result);
+ if (nearest_node)
+ return nearest_node->renderer_id();
+
+ return root_->renderer_id();
+}
+
+jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo(
+ JNIEnv* env, jobject obj, jobject info, jint id) {
+ BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>(
+ GetFromRendererID(id));
+ if (!node)
+ return false;
+
+ if (node->parent()) {
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoParent(
+ env, obj, info, node->parent()->renderer_id());
+ }
+ if (!node->IsLeaf()) {
+ for (unsigned i = 0; i < node->child_count(); ++i) {
+ Java_BrowserAccessibilityManager_addAccessibilityNodeInfoChild(
+ env, obj, info, node->children()[i]->renderer_id());
+ }
+ }
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoBooleanAttributes(
+ env, obj, info,
+ id,
+ node->IsCheckable(),
+ node->IsChecked(),
+ node->IsClickable(),
+ node->IsEnabled(),
+ node->IsFocusable(),
+ node->IsFocused(),
+ node->IsPassword(),
+ node->IsScrollable(),
+ node->IsSelected(),
+ node->IsVisibleToUser());
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoStringAttributes(
+ env, obj, info,
+ base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj(),
+ base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
+
+ gfx::Rect absolute_rect = node->GetLocalBoundsRect();
+ gfx::Rect parent_relative_rect = absolute_rect;
+ if (node->parent()) {
+ gfx::Rect parent_rect = node->parent()->GetLocalBoundsRect();
+ parent_relative_rect.Offset(-parent_rect.OffsetFromOrigin());
+ }
+ bool is_root = node->parent() == NULL;
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLocation(
+ env, obj, info,
+ absolute_rect.x(), absolute_rect.y(),
+ parent_relative_rect.x(), parent_relative_rect.y(),
+ absolute_rect.width(), absolute_rect.height(),
+ is_root);
+
+ return true;
+}
+
+jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityEvent(
+ JNIEnv* env, jobject obj, jobject event, jint id, jint event_type) {
+ BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>(
+ GetFromRendererID(id));
+ if (!node)
+ return false;
+
+ Java_BrowserAccessibilityManager_setAccessibilityEventBooleanAttributes(
+ env, obj, event,
+ node->IsChecked(),
+ node->IsEnabled(),
+ node->IsPassword(),
+ node->IsScrollable());
+ Java_BrowserAccessibilityManager_setAccessibilityEventClassName(
+ env, obj, event,
+ base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj());
+ Java_BrowserAccessibilityManager_setAccessibilityEventListAttributes(
+ env, obj, event,
+ node->GetItemIndex(),
+ node->GetItemCount());
+ Java_BrowserAccessibilityManager_setAccessibilityEventScrollAttributes(
+ env, obj, event,
+ node->GetScrollX(),
+ node->GetScrollY(),
+ node->GetMaxScrollX(),
+ node->GetMaxScrollY());
+
+ switch (event_type) {
+ case ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_CHANGED:
+ Java_BrowserAccessibilityManager_setAccessibilityEventTextChangedAttrs(
+ env, obj, event,
+ node->GetTextChangeFromIndex(),
+ node->GetTextChangeAddedCount(),
+ node->GetTextChangeRemovedCount(),
+ base::android::ConvertUTF16ToJavaString(
+ env, node->GetTextChangeBeforeText()).obj(),
+ base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
+ break;
+ case ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED:
+ Java_BrowserAccessibilityManager_setAccessibilityEventSelectionAttrs(
+ env, obj, event,
+ node->GetSelectionStart(),
+ node->GetSelectionEnd(),
+ node->GetEditableTextLength(),
+ base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void BrowserAccessibilityManagerAndroid::Click(
+ JNIEnv* env, jobject obj, jint id) {
+ BrowserAccessibility* node = GetFromRendererID(id);
+ if (node)
+ DoDefaultAction(*node);
+}
+
+void BrowserAccessibilityManagerAndroid::Focus(
+ JNIEnv* env, jobject obj, jint id) {
+ BrowserAccessibility* node = GetFromRendererID(id);
+ if (node)
+ SetFocus(node, true);
+}
+
+void BrowserAccessibilityManagerAndroid::Blur(JNIEnv* env, jobject obj) {
+ SetFocus(root_, true);
+}
+
+BrowserAccessibility* BrowserAccessibilityManagerAndroid::FuzzyHitTest(
+ int x, int y, BrowserAccessibility* start_node) {
+ BrowserAccessibility* nearest_node = NULL;
+ int min_distance = INT_MAX;
+ FuzzyHitTestImpl(x, y, start_node, &nearest_node, &min_distance);
+ return nearest_node;
+}
+
+// static
+void BrowserAccessibilityManagerAndroid::FuzzyHitTestImpl(
+ int x, int y, BrowserAccessibility* start_node,
+ BrowserAccessibility** nearest_candidate, int* nearest_distance) {
+ BrowserAccessibilityAndroid* node =
+ static_cast<BrowserAccessibilityAndroid*>(start_node);
+ int distance = CalculateDistanceSquared(x, y, node);
+
+ if (node->IsFocusable()) {
+ if (distance < *nearest_distance) {
+ *nearest_candidate = node;
+ *nearest_distance = distance;
+ }
+ // Don't examine any more children of focusable node
+ // TODO(aboxhall): what about focusable children?
+ return;
+ }
+
+ if (!node->GetText().empty()) {
+ if (distance < *nearest_distance) {
+ *nearest_candidate = node;
+ *nearest_distance = distance;
+ }
+ return;
+ }
+
+ if (!node->IsLeaf()) {
+ for (uint32 i = 0; i < node->child_count(); i++) {
+ BrowserAccessibility* child = node->GetChild(i);
+ FuzzyHitTestImpl(x, y, child, nearest_candidate, nearest_distance);
+ }
+ }
+}
+
+// static
+int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared(
+ int x, int y, BrowserAccessibility* node) {
+ gfx::Rect node_bounds = node->GetLocalBoundsRect();
+ int nearest_x = Clamp(x, node_bounds.x(), node_bounds.right());
+ int nearest_y = Clamp(y, node_bounds.y(), node_bounds.bottom());
+ int dx = std::abs(x - nearest_x);
+ int dy = std::abs(y - nearest_y);
+ return dx * dx + dy * dy;
+}
+
+void BrowserAccessibilityManagerAndroid::NotifyRootChanged() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ Java_BrowserAccessibilityManager_handleNavigate(env, obj.obj());
+}
+
+bool
+BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() {
+ // The Java layer handles the root scroll offset.
+ return false;
+}
+
+bool RegisterBrowserAccessibilityManager(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.h b/chromium/content/browser/accessibility/browser_accessibility_manager_android.h
new file mode 100644
index 00000000000..2a6c291bacb
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.h
@@ -0,0 +1,92 @@
+// Copyright 2013 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_ANDROID_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_ANDROID_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/android/content_view_core_impl.h"
+
+namespace content {
+
+namespace aria_strings {
+ extern const char kAriaLivePolite[];
+ extern const char kAriaLiveAssertive[];
+}
+
+class CONTENT_EXPORT BrowserAccessibilityManagerAndroid
+ : public BrowserAccessibilityManager {
+ public:
+ BrowserAccessibilityManagerAndroid(
+ base::android::ScopedJavaLocalRef<jobject> content_view_core,
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
+
+ virtual ~BrowserAccessibilityManagerAndroid();
+
+ static AccessibilityNodeData GetEmptyDocument();
+
+ // Implementation of BrowserAccessibilityManager.
+ virtual void NotifyAccessibilityEvent(int type,
+ BrowserAccessibility* node) OVERRIDE;
+
+ // --------------------------------------------------------------------------
+ // Methods called from Java via JNI
+ // --------------------------------------------------------------------------
+
+ // Tree methods.
+ jint GetRootId(JNIEnv* env, jobject obj);
+ jint HitTest(JNIEnv* env, jobject obj, jint x, jint y);
+
+ // Populate Java accessibility data structures with info about a node.
+ jboolean PopulateAccessibilityNodeInfo(
+ JNIEnv* env, jobject obj, jobject info, jint id);
+ jboolean PopulateAccessibilityEvent(
+ JNIEnv* env, jobject obj, jobject event, jint id, jint event_type);
+
+ // Perform actions.
+ void Click(JNIEnv* env, jobject obj, jint id);
+ void Focus(JNIEnv* env, jobject obj, jint id);
+ void Blur(JNIEnv* env, jobject obj);
+
+ protected:
+ virtual void NotifyRootChanged() OVERRIDE;
+
+ virtual bool UseRootScrollOffsetsWhenComputingBounds() OVERRIDE;
+
+ private:
+ // This gives BrowserAccessibilityManager::Create access to the class
+ // constructor.
+ friend class BrowserAccessibilityManager;
+
+ // A weak reference to the Java BrowserAccessibilityManager object.
+ // This avoids adding another reference to BrowserAccessibilityManager and
+ // preventing garbage collection.
+ // Premature garbage collection is prevented by the long-lived reference in
+ // ContentViewCore.
+ JavaObjectWeakGlobalRef java_ref_;
+
+ // Searches through the children of start_node to find the nearest
+ // accessibility focus candidate for a touch which has not landed directly on
+ // an accessibility focus candidate.
+ BrowserAccessibility* FuzzyHitTest(
+ int x, int y, BrowserAccessibility* start_node);
+
+ static void FuzzyHitTestImpl(int x, int y, BrowserAccessibility* start_node,
+ BrowserAccessibility** nearest_candidate, int* min_distance);
+
+ // Calculates the distance from the point (x, y) to the nearest point on the
+ // edge of |node|.
+ static int CalculateDistanceSquared(int x, int y, BrowserAccessibility* node);
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerAndroid);
+};
+
+bool RegisterBrowserAccessibilityManager(JNIEnv* env);
+
+}
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_ANDROID_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc
new file mode 100644
index 00000000000..15191c6a35b
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_manager_gtk.h"
+
+#include "content/browser/accessibility/browser_accessibility_gtk.h"
+#include "content/common/accessibility_messages.h"
+
+namespace content {
+
+// static
+BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory) {
+ return new BrowserAccessibilityManagerGtk(
+ NULL,
+ src,
+ delegate,
+ factory);
+}
+
+BrowserAccessibilityManagerGtk::BrowserAccessibilityManagerGtk(
+ GtkWidget* parent_widget,
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory)
+ : BrowserAccessibilityManager(delegate, factory),
+ parent_widget_(parent_widget) {
+ Initialize(src);
+}
+
+BrowserAccessibilityManagerGtk::~BrowserAccessibilityManagerGtk() {
+}
+
+// static
+AccessibilityNodeData BrowserAccessibilityManagerGtk::GetEmptyDocument() {
+ AccessibilityNodeData empty_document;
+ empty_document.id = 0;
+ empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ empty_document.state =
+ 1 << AccessibilityNodeData::STATE_READONLY;
+ return empty_document;
+}
+
+void BrowserAccessibilityManagerGtk::NotifyAccessibilityEvent(
+ int type,
+ BrowserAccessibility* node) {
+ if (!node->IsNative())
+ return;
+ AtkObject* atk_object = node->ToBrowserAccessibilityGtk()->GetAtkObject();
+
+ switch (type) {
+ case AccessibilityNotificationChildrenChanged:
+ RecursivelySendChildrenChanged(GetRoot()->ToBrowserAccessibilityGtk());
+ break;
+ case AccessibilityNotificationFocusChanged:
+ // Note: atk_focus_tracker_notify may be deprecated in the future;
+ // follow this bug for the replacement:
+ // https://bugzilla.gnome.org/show_bug.cgi?id=649575#c4
+ g_signal_emit_by_name(atk_object, "focus-event", true);
+ atk_focus_tracker_notify(atk_object);
+ break;
+ default:
+ break;
+ }
+}
+
+void BrowserAccessibilityManagerGtk::RecursivelySendChildrenChanged(
+ BrowserAccessibilityGtk* node) {
+ AtkObject* atkObject = node->ToBrowserAccessibilityGtk()->GetAtkObject();
+ for (unsigned int i = 0; i < node->children().size(); ++i) {
+ BrowserAccessibilityGtk* child =
+ node->children()[i]->ToBrowserAccessibilityGtk();
+ g_signal_emit_by_name(atkObject,
+ "children-changed::add",
+ i,
+ child->GetAtkObject());
+ RecursivelySendChildrenChanged(child);
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h
new file mode 100644
index 00000000000..eedae7ca121
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_GTK_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_GTK_H_
+
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+
+struct ViewHostMsg_AccessibilityNotification_Params;
+
+namespace content {
+class BrowserAccessibilityGtk;
+
+// Manages a tree of BrowserAccessibilityGtk objects.
+class CONTENT_EXPORT BrowserAccessibilityManagerGtk
+ : public BrowserAccessibilityManager {
+ public:
+ BrowserAccessibilityManagerGtk(
+ GtkWidget* parent_widget,
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
+
+ virtual ~BrowserAccessibilityManagerGtk();
+
+ static AccessibilityNodeData GetEmptyDocument();
+
+ // BrowserAccessibilityManager methods
+ virtual void NotifyAccessibilityEvent(int type, BrowserAccessibility* node)
+ OVERRIDE;
+
+ GtkWidget* parent_widget() { return parent_widget_; }
+
+ private:
+ void RecursivelySendChildrenChanged(BrowserAccessibilityGtk* node);
+
+ GtkWidget* parent_widget_;
+
+ // Give BrowserAccessibilityManager::Create access to our constructor.
+ friend class BrowserAccessibilityManager;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerGtk);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_GTK_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h
new file mode 100644
index 00000000000..d94c2c73df8
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_MAC_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+
+namespace content {
+
+class CONTENT_EXPORT BrowserAccessibilityManagerMac
+ : public BrowserAccessibilityManager {
+ public:
+ BrowserAccessibilityManagerMac(
+ NSView* parent_view,
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
+
+ static AccessibilityNodeData GetEmptyDocument();
+
+ // Implementation of BrowserAccessibilityManager.
+ virtual void NotifyAccessibilityEvent(int type,
+ BrowserAccessibility* node) OVERRIDE;
+
+ NSView* parent_view() { return parent_view_; }
+
+ private:
+ // This gives BrowserAccessibilityManager::Create access to the class
+ // constructor.
+ friend class BrowserAccessibilityManager;
+
+ NSView* parent_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerMac);
+};
+
+}
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_MAC_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm
new file mode 100644
index 00000000000..e3280997a25
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_manager_mac.h"
+
+#import "base/logging.h"
+#import "content/browser/accessibility/browser_accessibility_cocoa.h"
+#include "content/common/accessibility_messages.h"
+
+namespace content {
+
+// static
+BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory) {
+ return new BrowserAccessibilityManagerMac(NULL, src, delegate, factory);
+}
+
+BrowserAccessibilityManagerMac::BrowserAccessibilityManagerMac(
+ NSView* parent_view,
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory)
+ : BrowserAccessibilityManager(src, delegate, factory),
+ parent_view_(parent_view) {
+}
+
+// static
+AccessibilityNodeData BrowserAccessibilityManagerMac::GetEmptyDocument() {
+ AccessibilityNodeData empty_document;
+ empty_document.id = 0;
+ empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ empty_document.state =
+ 1 << AccessibilityNodeData::STATE_READONLY;
+ return empty_document;
+}
+
+void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
+ int type,
+ BrowserAccessibility* node) {
+ if (!node->IsNative())
+ return;
+
+ // Refer to AXObjectCache.mm (webkit).
+ NSString* event_id = @"";
+ switch (type) {
+ case AccessibilityNotificationActiveDescendantChanged:
+ if (node->role() == AccessibilityNodeData::ROLE_TREE)
+ event_id = NSAccessibilitySelectedRowsChangedNotification;
+ else
+ event_id = NSAccessibilityFocusedUIElementChangedNotification;
+ break;
+ case AccessibilityNotificationAlert:
+ // Not used on Mac.
+ return;
+ case AccessibilityNotificationBlur:
+ // A no-op on Mac.
+ return;
+ case AccessibilityNotificationCheckStateChanged:
+ // Not used on Mac.
+ return;
+ case AccessibilityNotificationChildrenChanged:
+ // TODO(dtseng): no clear equivalent on Mac.
+ return;
+ case AccessibilityNotificationFocusChanged:
+ event_id = NSAccessibilityFocusedUIElementChangedNotification;
+ break;
+ case AccessibilityNotificationLayoutComplete:
+ event_id = @"AXLayoutComplete";
+ break;
+ case AccessibilityNotificationLiveRegionChanged:
+ event_id = @"AXLiveRegionChanged";
+ break;
+ case AccessibilityNotificationLoadComplete:
+ event_id = @"AXLoadComplete";
+ break;
+ case AccessibilityNotificationMenuListValueChanged:
+ // Not used on Mac.
+ return;
+ case AccessibilityNotificationObjectShow:
+ // Not used on Mac.
+ return;
+ case AccessibilityNotificationObjectHide:
+ // Not used on Mac.
+ return;
+ case AccessibilityNotificationRowCountChanged:
+ event_id = NSAccessibilityRowCountChangedNotification;
+ break;
+ case AccessibilityNotificationRowCollapsed:
+ event_id = @"AXRowCollapsed";
+ break;
+ case AccessibilityNotificationRowExpanded:
+ event_id = @"AXRowExpanded";
+ break;
+ case AccessibilityNotificationScrolledToAnchor:
+ // Not used on Mac.
+ return;
+ case AccessibilityNotificationSelectedChildrenChanged:
+ event_id = NSAccessibilitySelectedChildrenChangedNotification;
+ break;
+ case AccessibilityNotificationSelectedTextChanged:
+ event_id = NSAccessibilitySelectedTextChangedNotification;
+ break;
+ case AccessibilityNotificationTextInserted:
+ // Not used on Mac.
+ return;
+ case AccessibilityNotificationTextRemoved:
+ // Not used on Mac.
+ return;
+ case AccessibilityNotificationValueChanged:
+ event_id = NSAccessibilityValueChangedNotification;
+ break;
+ }
+ BrowserAccessibilityCocoa* native_node = node->ToBrowserAccessibilityCocoa();
+ DCHECK(native_node);
+ NSAccessibilityPostNotification(native_node, event_id);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc
new file mode 100644
index 00000000000..5cb03cd25ce
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -0,0 +1,603 @@
+// Copyright (c) 2012 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 "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/common/accessibility_messages.h"
+#include "content/common/accessibility_node_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+// Subclass of BrowserAccessibility that counts the number of instances.
+class CountedBrowserAccessibility : public BrowserAccessibility {
+ public:
+ CountedBrowserAccessibility() {
+ global_obj_count_++;
+ native_ref_count_ = 1;
+ }
+ virtual ~CountedBrowserAccessibility() {
+ global_obj_count_--;
+ }
+
+ virtual void NativeAddReference() OVERRIDE {
+ native_ref_count_++;
+ }
+
+ virtual void NativeReleaseReference() OVERRIDE {
+ native_ref_count_--;
+ if (native_ref_count_ == 0)
+ delete this;
+ }
+
+ int native_ref_count_;
+ static int global_obj_count_;
+};
+
+int CountedBrowserAccessibility::global_obj_count_ = 0;
+
+// Factory that creates a CountedBrowserAccessibility.
+class CountedBrowserAccessibilityFactory
+ : public BrowserAccessibilityFactory {
+ public:
+ virtual ~CountedBrowserAccessibilityFactory() {}
+ virtual BrowserAccessibility* Create() OVERRIDE {
+ return new CountedBrowserAccessibility();
+ }
+};
+
+class TestBrowserAccessibilityDelegate
+ : public BrowserAccessibilityDelegate {
+ public:
+ TestBrowserAccessibilityDelegate()
+ : got_fatal_error_(false) {}
+
+ virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE {}
+ virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
+ virtual void AccessibilityScrollToMakeVisible(
+ int acc_obj_id, gfx::Rect subfocus) OVERRIDE {}
+ virtual void AccessibilityScrollToPoint(
+ int acc_obj_id, gfx::Point point) OVERRIDE {}
+ virtual void AccessibilitySetTextSelection(
+ int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
+ virtual bool HasFocus() const OVERRIDE {
+ return false;
+ }
+ virtual gfx::Rect GetViewBounds() const OVERRIDE {
+ return gfx::Rect();
+ }
+ virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE {
+ return gfx::Point();
+ }
+ virtual void FatalAccessibilityTreeError() OVERRIDE {
+ got_fatal_error_ = true;
+ }
+
+ bool got_fatal_error() const { return got_fatal_error_; }
+ void reset_got_fatal_error() { got_fatal_error_ = false; }
+
+private:
+ bool got_fatal_error_;
+};
+
+} // anonymous namespace
+
+TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
+ // Create AccessibilityNodeData objects for a simple document tree,
+ // representing the accessibility information used to initialize
+ // BrowserAccessibilityManager.
+ AccessibilityNodeData button;
+ button.id = 2;
+ button.name = UTF8ToUTF16("Button");
+ button.role = AccessibilityNodeData::ROLE_BUTTON;
+ button.state = 0;
+
+ AccessibilityNodeData checkbox;
+ checkbox.id = 3;
+ checkbox.name = UTF8ToUTF16("Checkbox");
+ checkbox.role = AccessibilityNodeData::ROLE_CHECKBOX;
+ checkbox.state = 0;
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.name = UTF8ToUTF16("Document");
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.state = 0;
+ root.child_ids.push_back(2);
+ root.child_ids.push_back(3);
+
+ // Construct a BrowserAccessibilityManager with this
+ // AccessibilityNodeData tree and a factory for an instance-counting
+ // BrowserAccessibility, and ensure that exactly 3 instances were
+ // created. Note that the manager takes ownership of the factory.
+ CountedBrowserAccessibility::global_obj_count_ = 0;
+ BrowserAccessibilityManager* manager =
+ BrowserAccessibilityManager::Create(
+ root,
+ NULL,
+ new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(button, checkbox);
+
+ ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
+
+ // Delete the manager and test that all 3 instances are deleted.
+ delete manager;
+ ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
+
+ // Construct a manager again, and this time save references to two of
+ // the three nodes in the tree.
+ manager =
+ BrowserAccessibilityManager::Create(
+ root,
+ NULL,
+ new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(button, checkbox);
+ ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
+
+ CountedBrowserAccessibility* root_accessible =
+ static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
+ root_accessible->NativeAddReference();
+ CountedBrowserAccessibility* child1_accessible =
+ static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(1));
+ child1_accessible->NativeAddReference();
+
+ // Now delete the manager, and only one of the three nodes in the tree
+ // should be released.
+ delete manager;
+ ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
+
+ // Release each of our references and make sure that each one results in
+ // the instance being deleted as its reference count hits zero.
+ root_accessible->NativeReleaseReference();
+ ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
+ child1_accessible->NativeReleaseReference();
+ ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
+}
+
+TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
+ // Make sure that changes to a subtree reuse as many objects as possible.
+
+ // Tree 1:
+ //
+ // root
+ // child1
+ // child2
+ // child3
+
+ AccessibilityNodeData tree1_child1;
+ tree1_child1.id = 2;
+ tree1_child1.name = UTF8ToUTF16("Child1");
+ tree1_child1.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_child1.state = 0;
+
+ AccessibilityNodeData tree1_child2;
+ tree1_child2.id = 3;
+ tree1_child2.name = UTF8ToUTF16("Child2");
+ tree1_child2.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_child2.state = 0;
+
+ AccessibilityNodeData tree1_child3;
+ tree1_child3.id = 4;
+ tree1_child3.name = UTF8ToUTF16("Child3");
+ tree1_child3.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_child3.state = 0;
+
+ AccessibilityNodeData tree1_root;
+ tree1_root.id = 1;
+ tree1_root.name = UTF8ToUTF16("Document");
+ tree1_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ tree1_root.state = 0;
+ tree1_root.child_ids.push_back(2);
+ tree1_root.child_ids.push_back(3);
+ tree1_root.child_ids.push_back(4);
+
+ // Tree 2:
+ //
+ // root
+ // child0 <-- inserted
+ // child1
+ // child2
+ // <-- child3 deleted
+
+ AccessibilityNodeData tree2_child0;
+ tree2_child0.id = 5;
+ tree2_child0.name = UTF8ToUTF16("Child0");
+ tree2_child0.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree2_child0.state = 0;
+
+ AccessibilityNodeData tree2_root;
+ tree2_root.id = 1;
+ tree2_root.name = UTF8ToUTF16("DocumentChanged");
+ tree2_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ tree2_root.state = 0;
+ tree2_root.child_ids.push_back(5);
+ tree2_root.child_ids.push_back(2);
+ tree2_root.child_ids.push_back(3);
+
+ // Construct a BrowserAccessibilityManager with tree1.
+ CountedBrowserAccessibility::global_obj_count_ = 0;
+ BrowserAccessibilityManager* manager =
+ BrowserAccessibilityManager::Create(
+ tree1_root,
+ NULL,
+ new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(tree1_child1, tree1_child2, tree1_child3);
+ ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
+
+ // Save references to all of the objects.
+ CountedBrowserAccessibility* root_accessible =
+ static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
+ root_accessible->NativeAddReference();
+ CountedBrowserAccessibility* child1_accessible =
+ static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(0));
+ child1_accessible->NativeAddReference();
+ CountedBrowserAccessibility* child2_accessible =
+ static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(1));
+ child2_accessible->NativeAddReference();
+ CountedBrowserAccessibility* child3_accessible =
+ static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(2));
+ child3_accessible->NativeAddReference();
+
+ // Check the index in parent.
+ EXPECT_EQ(0, child1_accessible->index_in_parent());
+ EXPECT_EQ(1, child2_accessible->index_in_parent());
+ EXPECT_EQ(2, child3_accessible->index_in_parent());
+
+ // Process a notification containing the changed subtree.
+ std::vector<AccessibilityHostMsg_NotificationParams> params;
+ params.push_back(AccessibilityHostMsg_NotificationParams());
+ AccessibilityHostMsg_NotificationParams* msg = &params[0];
+ msg->notification_type = AccessibilityNotificationChildrenChanged;
+ msg->nodes.push_back(tree2_root);
+ msg->nodes.push_back(tree2_child0);
+ msg->id = tree2_root.id;
+ manager->OnAccessibilityNotifications(params);
+
+ // There should be 5 objects now: the 4 from the new tree, plus the
+ // reference to child3 we kept.
+ EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
+
+ // Check that our references to the root, child1, and child2 are still valid,
+ // but that the reference to child3 is now invalid.
+ EXPECT_TRUE(root_accessible->instance_active());
+ EXPECT_TRUE(child1_accessible->instance_active());
+ EXPECT_TRUE(child2_accessible->instance_active());
+ EXPECT_FALSE(child3_accessible->instance_active());
+
+ // Check that the index in parent has been updated.
+ EXPECT_EQ(1, child1_accessible->index_in_parent());
+ EXPECT_EQ(2, child2_accessible->index_in_parent());
+
+ // Release our references. The object count should only decrease by 1
+ // for child3.
+ root_accessible->NativeReleaseReference();
+ child1_accessible->NativeReleaseReference();
+ child2_accessible->NativeReleaseReference();
+ child3_accessible->NativeReleaseReference();
+
+ EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
+
+ // Delete the manager and make sure all memory is cleaned up.
+ delete manager;
+ ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
+}
+
+TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
+ // Similar to the test above, but with a more complicated tree.
+
+ // Tree 1:
+ //
+ // root
+ // container
+ // child1
+ // grandchild1
+ // child2
+ // grandchild2
+ // child3
+ // grandchild3
+
+ AccessibilityNodeData tree1_grandchild1;
+ tree1_grandchild1.id = 4;
+ tree1_grandchild1.name = UTF8ToUTF16("GrandChild1");
+ tree1_grandchild1.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_grandchild1.state = 0;
+
+ AccessibilityNodeData tree1_child1;
+ tree1_child1.id = 3;
+ tree1_child1.name = UTF8ToUTF16("Child1");
+ tree1_child1.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_child1.state = 0;
+ tree1_child1.child_ids.push_back(4);
+
+ AccessibilityNodeData tree1_grandchild2;
+ tree1_grandchild2.id = 6;
+ tree1_grandchild2.name = UTF8ToUTF16("GrandChild1");
+ tree1_grandchild2.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_grandchild2.state = 0;
+
+ AccessibilityNodeData tree1_child2;
+ tree1_child2.id = 5;
+ tree1_child2.name = UTF8ToUTF16("Child2");
+ tree1_child2.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_child2.state = 0;
+ tree1_child2.child_ids.push_back(6);
+
+ AccessibilityNodeData tree1_grandchild3;
+ tree1_grandchild3.id = 8;
+ tree1_grandchild3.name = UTF8ToUTF16("GrandChild3");
+ tree1_grandchild3.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_grandchild3.state = 0;
+
+ AccessibilityNodeData tree1_child3;
+ tree1_child3.id = 7;
+ tree1_child3.name = UTF8ToUTF16("Child3");
+ tree1_child3.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree1_child3.state = 0;
+ tree1_child3.child_ids.push_back(8);
+
+ AccessibilityNodeData tree1_container;
+ tree1_container.id = 2;
+ tree1_container.name = UTF8ToUTF16("Container");
+ tree1_container.role = AccessibilityNodeData::ROLE_GROUP;
+ tree1_container.state = 0;
+ tree1_container.child_ids.push_back(3);
+ tree1_container.child_ids.push_back(5);
+ tree1_container.child_ids.push_back(7);
+
+ AccessibilityNodeData tree1_root;
+ tree1_root.id = 1;
+ tree1_root.name = UTF8ToUTF16("Document");
+ tree1_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ tree1_root.state = 0;
+ tree1_root.child_ids.push_back(2);
+
+ // Tree 2:
+ //
+ // root
+ // container
+ // child0 <-- inserted
+ // grandchild0 <--
+ // child1
+ // grandchild1
+ // child2
+ // grandchild2
+ // <-- child3 (and grandchild3) deleted
+
+ AccessibilityNodeData tree2_grandchild0;
+ tree2_grandchild0.id = 9;
+ tree2_grandchild0.name = UTF8ToUTF16("GrandChild0");
+ tree2_grandchild0.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree2_grandchild0.state = 0;
+
+ AccessibilityNodeData tree2_child0;
+ tree2_child0.id = 10;
+ tree2_child0.name = UTF8ToUTF16("Child0");
+ tree2_child0.role = AccessibilityNodeData::ROLE_BUTTON;
+ tree2_child0.state = 0;
+ tree2_child0.child_ids.push_back(9);
+
+ AccessibilityNodeData tree2_container;
+ tree2_container.id = 2;
+ tree2_container.name = UTF8ToUTF16("Container");
+ tree2_container.role = AccessibilityNodeData::ROLE_GROUP;
+ tree2_container.state = 0;
+ tree2_container.child_ids.push_back(10);
+ tree2_container.child_ids.push_back(3);
+ tree2_container.child_ids.push_back(5);
+
+ // Construct a BrowserAccessibilityManager with tree1.
+ CountedBrowserAccessibility::global_obj_count_ = 0;
+ BrowserAccessibilityManager* manager =
+ BrowserAccessibilityManager::Create(
+ tree1_root,
+ NULL,
+ new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(tree1_container,
+ tree1_child1, tree1_grandchild1,
+ tree1_child2, tree1_grandchild2,
+ tree1_child3, tree1_grandchild3);
+ ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
+
+ // Save references to some objects.
+ CountedBrowserAccessibility* root_accessible =
+ static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
+ root_accessible->NativeAddReference();
+ CountedBrowserAccessibility* container_accessible =
+ static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(0));
+ container_accessible->NativeAddReference();
+ CountedBrowserAccessibility* child2_accessible =
+ static_cast<CountedBrowserAccessibility*>(
+ container_accessible->GetChild(1));
+ child2_accessible->NativeAddReference();
+ CountedBrowserAccessibility* child3_accessible =
+ static_cast<CountedBrowserAccessibility*>(
+ container_accessible->GetChild(2));
+ child3_accessible->NativeAddReference();
+
+ // Check the index in parent.
+ EXPECT_EQ(1, child2_accessible->index_in_parent());
+ EXPECT_EQ(2, child3_accessible->index_in_parent());
+
+ // Process a notification containing the changed subtree rooted at
+ // the container.
+ std::vector<AccessibilityHostMsg_NotificationParams> params;
+ params.push_back(AccessibilityHostMsg_NotificationParams());
+ AccessibilityHostMsg_NotificationParams* msg = &params[0];
+ msg->notification_type = AccessibilityNotificationChildrenChanged;
+ msg->nodes.push_back(tree2_container);
+ msg->nodes.push_back(tree2_child0);
+ msg->nodes.push_back(tree2_grandchild0);
+ msg->id = tree2_container.id;
+ manager->OnAccessibilityNotifications(params);
+
+ // There should be 9 objects now: the 8 from the new tree, plus the
+ // reference to child3 we kept.
+ EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
+
+ // Check that our references to the root and container and child2 are
+ // still valid, but that the reference to child3 is now invalid.
+ EXPECT_TRUE(root_accessible->instance_active());
+ EXPECT_TRUE(container_accessible->instance_active());
+ EXPECT_TRUE(child2_accessible->instance_active());
+ EXPECT_FALSE(child3_accessible->instance_active());
+
+ // Ensure that we retain the parent of the detached subtree.
+ EXPECT_EQ(root_accessible, container_accessible->parent());
+ EXPECT_EQ(0, container_accessible->index_in_parent());
+
+ // Check that the index in parent has been updated.
+ EXPECT_EQ(2, child2_accessible->index_in_parent());
+
+ // Release our references. The object count should only decrease by 1
+ // for child3.
+ root_accessible->NativeReleaseReference();
+ container_accessible->NativeReleaseReference();
+ child2_accessible->NativeReleaseReference();
+ child3_accessible->NativeReleaseReference();
+
+ EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
+
+ // Delete the manager and make sure all memory is cleaned up.
+ delete manager;
+ ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
+}
+
+TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
+ // Tree 1:
+ //
+ // 1
+ // 2
+ // 3
+ // 4
+
+ AccessibilityNodeData tree1_4;
+ tree1_4.id = 4;
+ tree1_4.state = 0;
+
+ AccessibilityNodeData tree1_3;
+ tree1_3.id = 3;
+ tree1_3.state = 0;
+ tree1_3.child_ids.push_back(4);
+
+ AccessibilityNodeData tree1_2;
+ tree1_2.id = 2;
+ tree1_2.state = 0;
+
+ AccessibilityNodeData tree1_1;
+ tree1_1.id = 1;
+ tree1_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ tree1_1.state = 0;
+ tree1_1.child_ids.push_back(2);
+ tree1_1.child_ids.push_back(3);
+
+ // Tree 2:
+ //
+ // 1
+ // 4 <-- moves up a level and gains child
+ // 6 <-- new
+ // 5 <-- new
+
+ AccessibilityNodeData tree2_6;
+ tree2_6.id = 6;
+ tree2_6.state = 0;
+
+ AccessibilityNodeData tree2_5;
+ tree2_5.id = 5;
+ tree2_5.state = 0;
+
+ AccessibilityNodeData tree2_4;
+ tree2_4.id = 4;
+ tree2_4.state = 0;
+ tree2_4.child_ids.push_back(6);
+
+ AccessibilityNodeData tree2_1;
+ tree2_1.id = 1;
+ tree2_1.state = 0;
+ tree2_1.child_ids.push_back(4);
+ tree2_1.child_ids.push_back(5);
+
+ // Construct a BrowserAccessibilityManager with tree1.
+ CountedBrowserAccessibility::global_obj_count_ = 0;
+ BrowserAccessibilityManager* manager =
+ BrowserAccessibilityManager::Create(
+ tree1_1,
+ NULL,
+ new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(tree1_2, tree1_3, tree1_4);
+ ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
+
+ // Process a notification containing the changed subtree.
+ std::vector<AccessibilityHostMsg_NotificationParams> params;
+ params.push_back(AccessibilityHostMsg_NotificationParams());
+ AccessibilityHostMsg_NotificationParams* msg = &params[0];
+ msg->notification_type = AccessibilityNotificationChildrenChanged;
+ msg->nodes.push_back(tree2_1);
+ msg->nodes.push_back(tree2_4);
+ msg->nodes.push_back(tree2_5);
+ msg->nodes.push_back(tree2_6);
+ msg->id = tree2_1.id;
+ manager->OnAccessibilityNotifications(params);
+
+ // There should be 4 objects now.
+ EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
+
+ // Delete the manager and make sure all memory is cleaned up.
+ delete manager;
+ ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
+}
+
+TEST(BrowserAccessibilityManagerTest, TestFatalError) {
+ // Test that BrowserAccessibilityManager raises a fatal error
+ // (which will crash the renderer) if the same id is used in
+ // two places in the tree.
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.child_ids.push_back(2);
+ root.child_ids.push_back(2);
+
+ CountedBrowserAccessibilityFactory* factory =
+ new CountedBrowserAccessibilityFactory();
+ scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
+ new TestBrowserAccessibilityDelegate());
+ scoped_ptr<BrowserAccessibilityManager> manager;
+ ASSERT_FALSE(delegate->got_fatal_error());
+ manager.reset(BrowserAccessibilityManager::Create(
+ root,
+ delegate.get(),
+ factory));
+ ASSERT_TRUE(delegate->got_fatal_error());
+
+ AccessibilityNodeData root2;
+ root2.id = 1;
+ root2.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root2.child_ids.push_back(2);
+ root2.child_ids.push_back(3);
+
+ AccessibilityNodeData child1;
+ child1.id = 2;
+ child1.child_ids.push_back(4);
+ child1.child_ids.push_back(5);
+
+ AccessibilityNodeData child2;
+ child2.id = 3;
+ child2.child_ids.push_back(6);
+ child2.child_ids.push_back(5); // Duplicate
+
+ delegate->reset_got_fatal_error();
+ factory = new CountedBrowserAccessibilityFactory();
+ manager.reset(BrowserAccessibilityManager::Create(
+ root2,
+ delegate.get(),
+ factory));
+ ASSERT_FALSE(delegate->got_fatal_error());
+ manager->UpdateNodesForTesting(child1, child2);
+ ASSERT_TRUE(delegate->got_fatal_error());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc
new file mode 100644
index 00000000000..b956d218d33
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_manager_win.h"
+
+#include "content/browser/accessibility/browser_accessibility_win.h"
+#include "content/common/accessibility_messages.h"
+
+namespace content {
+
+// static
+BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory) {
+ return new BrowserAccessibilityManagerWin(
+ GetDesktopWindow(), NULL, src, delegate, factory);
+}
+
+BrowserAccessibilityManagerWin*
+BrowserAccessibilityManager::ToBrowserAccessibilityManagerWin() {
+ return static_cast<BrowserAccessibilityManagerWin*>(this);
+}
+
+BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin(
+ HWND parent_hwnd,
+ IAccessible* parent_iaccessible,
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory)
+ : BrowserAccessibilityManager(src, delegate, factory),
+ parent_hwnd_(parent_hwnd),
+ parent_iaccessible_(parent_iaccessible),
+ tracked_scroll_object_(NULL) {
+}
+
+BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
+ if (tracked_scroll_object_) {
+ tracked_scroll_object_->Release();
+ tracked_scroll_object_ = NULL;
+ }
+}
+
+// static
+AccessibilityNodeData BrowserAccessibilityManagerWin::GetEmptyDocument() {
+ AccessibilityNodeData empty_document;
+ empty_document.id = 0;
+ empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ empty_document.state =
+ (1 << AccessibilityNodeData::STATE_READONLY) |
+ (1 << AccessibilityNodeData::STATE_BUSY);
+ return empty_document;
+}
+
+void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event,
+ LONG child_id) {
+ if (parent_iaccessible())
+ ::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id);
+}
+
+void BrowserAccessibilityManagerWin::AddNodeToMap(BrowserAccessibility* node) {
+ BrowserAccessibilityManager::AddNodeToMap(node);
+ LONG unique_id_win = node->ToBrowserAccessibilityWin()->unique_id_win();
+ unique_id_to_renderer_id_map_[unique_id_win] = node->renderer_id();
+}
+
+void BrowserAccessibilityManagerWin::RemoveNode(BrowserAccessibility* node) {
+ unique_id_to_renderer_id_map_.erase(
+ node->ToBrowserAccessibilityWin()->unique_id_win());
+ BrowserAccessibilityManager::RemoveNode(node);
+ if (node == tracked_scroll_object_) {
+ tracked_scroll_object_->Release();
+ tracked_scroll_object_ = NULL;
+ }
+}
+
+void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
+ int type,
+ BrowserAccessibility* node) {
+ LONG event_id = EVENT_MIN;
+ switch (type) {
+ case AccessibilityNotificationActiveDescendantChanged:
+ event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED;
+ break;
+ case AccessibilityNotificationAlert:
+ event_id = EVENT_SYSTEM_ALERT;
+ break;
+ case AccessibilityNotificationAriaAttributeChanged:
+ event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
+ break;
+ case AccessibilityNotificationAutocorrectionOccurred:
+ event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
+ break;
+ case AccessibilityNotificationBlur:
+ // Equivalent to focus on the root.
+ event_id = EVENT_OBJECT_FOCUS;
+ node = GetRoot();
+ break;
+ case AccessibilityNotificationCheckStateChanged:
+ event_id = EVENT_OBJECT_STATECHANGE;
+ break;
+ case AccessibilityNotificationChildrenChanged:
+ event_id = EVENT_OBJECT_REORDER;
+ break;
+ case AccessibilityNotificationFocusChanged:
+ event_id = EVENT_OBJECT_FOCUS;
+ break;
+ case AccessibilityNotificationInvalidStatusChanged:
+ event_id = EVENT_OBJECT_STATECHANGE;
+ break;
+ case AccessibilityNotificationLiveRegionChanged:
+ // TODO: try not firing a native notification at all, since
+ // on Windows, each individual item in a live region that changes
+ // already gets its own notification.
+ event_id = EVENT_OBJECT_REORDER;
+ break;
+ case AccessibilityNotificationLoadComplete:
+ event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
+ break;
+ case AccessibilityNotificationMenuListItemSelected:
+ event_id = EVENT_OBJECT_FOCUS;
+ break;
+ case AccessibilityNotificationMenuListValueChanged:
+ event_id = EVENT_OBJECT_VALUECHANGE;
+ break;
+ case AccessibilityNotificationObjectHide:
+ event_id = EVENT_OBJECT_HIDE;
+ break;
+ case AccessibilityNotificationObjectShow:
+ event_id = EVENT_OBJECT_SHOW;
+ break;
+ case AccessibilityNotificationScrolledToAnchor:
+ event_id = EVENT_SYSTEM_SCROLLINGSTART;
+ break;
+ case AccessibilityNotificationSelectedChildrenChanged:
+ event_id = EVENT_OBJECT_SELECTIONWITHIN;
+ break;
+ case AccessibilityNotificationSelectedTextChanged:
+ event_id = IA2_EVENT_TEXT_CARET_MOVED;
+ break;
+ case AccessibilityNotificationTextChanged:
+ event_id = EVENT_OBJECT_NAMECHANGE;
+ break;
+ case AccessibilityNotificationTextInserted:
+ event_id = IA2_EVENT_TEXT_INSERTED;
+ break;
+ case AccessibilityNotificationTextRemoved:
+ event_id = IA2_EVENT_TEXT_REMOVED;
+ break;
+ case AccessibilityNotificationValueChanged:
+ event_id = EVENT_OBJECT_VALUECHANGE;
+ break;
+ default:
+ // Not all WebKit accessibility events result in a Windows
+ // accessibility notification.
+ break;
+ }
+
+ if (event_id != EVENT_MIN) {
+ // Pass the node's unique id in the |child_id| argument to NotifyWinEvent;
+ // the AT client will then call get_accChild on the HWND's accessibility
+ // object and pass it that same id, which we can use to retrieve the
+ // IAccessible for this node.
+ LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win();
+ MaybeCallNotifyWinEvent(event_id, child_id);
+ }
+
+ // If this is a layout complete notification (sent when a container scrolls)
+ // and there is a descendant tracked object, send a notification on it.
+ // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
+ if (type == AccessibilityNotificationLayoutComplete &&
+ tracked_scroll_object_ &&
+ tracked_scroll_object_->IsDescendantOf(node)) {
+ MaybeCallNotifyWinEvent(
+ IA2_EVENT_VISIBLE_DATA_CHANGED,
+ tracked_scroll_object_->ToBrowserAccessibilityWin()->unique_id_win());
+ tracked_scroll_object_->Release();
+ tracked_scroll_object_ = NULL;
+ }
+}
+
+void BrowserAccessibilityManagerWin::TrackScrollingObject(
+ BrowserAccessibilityWin* node) {
+ if (tracked_scroll_object_)
+ tracked_scroll_object_->Release();
+ tracked_scroll_object_ = node;
+ tracked_scroll_object_->AddRef();
+}
+
+BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
+ LONG unique_id_win) {
+ base::hash_map<LONG, int32>::iterator iter =
+ unique_id_to_renderer_id_map_.find(unique_id_win);
+ if (iter != unique_id_to_renderer_id_map_.end()) {
+ BrowserAccessibility* result = GetFromRendererID(iter->second);
+ if (result)
+ return result->ToBrowserAccessibilityWin();
+ }
+ return NULL;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h
new file mode 100644
index 00000000000..7aec2e1c5e9
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_WIN_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_WIN_H_
+
+#include <oleacc.h>
+
+#include "base/win/scoped_comptr.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+
+namespace content {
+class BrowserAccessibilityWin;
+
+// Manages a tree of BrowserAccessibilityWin objects.
+class CONTENT_EXPORT BrowserAccessibilityManagerWin
+ : public BrowserAccessibilityManager {
+ public:
+ BrowserAccessibilityManagerWin(
+ HWND parent_hwnd,
+ IAccessible* parent_iaccessible,
+ const AccessibilityNodeData& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
+
+ virtual ~BrowserAccessibilityManagerWin();
+
+ static AccessibilityNodeData GetEmptyDocument();
+
+ // Get the closest containing HWND.
+ HWND parent_hwnd() { return parent_hwnd_; }
+
+ // The IAccessible for the parent window.
+ IAccessible* parent_iaccessible() { return parent_iaccessible_; }
+ void set_parent_iaccessible(IAccessible* parent_iaccessible) {
+ parent_iaccessible_ = parent_iaccessible;
+ }
+
+ // Calls NotifyWinEvent if the parent window's IAccessible pointer is known.
+ void MaybeCallNotifyWinEvent(DWORD event, LONG child_id);
+
+ // BrowserAccessibilityManager methods
+ virtual void AddNodeToMap(BrowserAccessibility* node);
+ virtual void RemoveNode(BrowserAccessibility* node) OVERRIDE;
+ virtual void NotifyAccessibilityEvent(int type, BrowserAccessibility* node)
+ OVERRIDE;
+
+ // Track this object and post a VISIBLE_DATA_CHANGED notification when
+ // its container scrolls.
+ // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
+ void TrackScrollingObject(BrowserAccessibilityWin* node);
+
+ // Return a pointer to the object corresponding to the given windows-specific
+ // unique id, does not make a new reference.
+ BrowserAccessibilityWin* GetFromUniqueIdWin(LONG unique_id_win);
+
+ private:
+ // The closest ancestor HWND.
+ HWND parent_hwnd_;
+
+ // The accessibility instance for the parent window.
+ IAccessible* parent_iaccessible_;
+
+ // Give BrowserAccessibilityManager::Create access to our constructor.
+ friend class BrowserAccessibilityManager;
+
+ // Track the most recent object that has been asked to scroll and
+ // post a notification directly on it when it reaches its destination.
+ // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
+ BrowserAccessibilityWin* tracked_scroll_object_;
+
+ // A mapping from the Windows-specific unique IDs (unique within the
+ // browser process) to renderer ids within this page.
+ base::hash_map<long, int32> unique_id_to_renderer_id_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerWin);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_WIN_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc
new file mode 100644
index 00000000000..befd717bc0c
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_state_impl.h"
+
+#include "base/command_line.h"
+#include "base/metrics/histogram.h"
+#include "base/timer/timer.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_switches.h"
+#include "ui/gfx/sys_color_change_listener.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+namespace content {
+
+// Update the accessibility histogram 45 seconds after initialization.
+static const int kAccessibilityHistogramDelaySecs = 45;
+
+// static
+BrowserAccessibilityState* BrowserAccessibilityState::GetInstance() {
+ return BrowserAccessibilityStateImpl::GetInstance();
+}
+
+// static
+BrowserAccessibilityStateImpl* BrowserAccessibilityStateImpl::GetInstance() {
+ return Singleton<BrowserAccessibilityStateImpl,
+ LeakySingletonTraits<BrowserAccessibilityStateImpl> >::get();
+}
+
+BrowserAccessibilityStateImpl::BrowserAccessibilityStateImpl()
+ : BrowserAccessibilityState(),
+ accessibility_mode_(AccessibilityModeOff) {
+#if defined(OS_WIN)
+ // On Windows 8, always enable accessibility for editable text controls
+ // so we can show the virtual keyboard when one is enabled.
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableRendererAccessibility)) {
+ accessibility_mode_ = AccessibilityModeEditableTextOnly;
+ }
+#endif // defined(OS_WIN)
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kForceRendererAccessibility)) {
+ accessibility_mode_ = AccessibilityModeComplete;
+ }
+
+#if defined(OS_WIN)
+ // On Windows, UpdateHistograms calls some system functions with unknown
+ // runtime, so call it on the file thread to ensure there's no jank.
+ // Everything in that method must be safe to call on another thread.
+ BrowserThread::ID update_histogram_thread = BrowserThread::FILE;
+#else
+ // On all other platforms, UpdateHistograms should be called on the main
+ // thread.
+ BrowserThread::ID update_histogram_thread = BrowserThread::UI;
+#endif
+
+ // We need to AddRef() the leaky singleton so that Bind doesn't
+ // delete it prematurely.
+ AddRef();
+ BrowserThread::PostDelayedTask(
+ update_histogram_thread, FROM_HERE,
+ base::Bind(&BrowserAccessibilityStateImpl::UpdateHistograms, this),
+ base::TimeDelta::FromSeconds(kAccessibilityHistogramDelaySecs));
+}
+
+BrowserAccessibilityStateImpl::~BrowserAccessibilityStateImpl() {
+}
+
+void BrowserAccessibilityStateImpl::OnScreenReaderDetected() {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableRendererAccessibility)) {
+ return;
+ }
+ SetAccessibilityMode(AccessibilityModeComplete);
+}
+
+void BrowserAccessibilityStateImpl::EnableAccessibility() {
+ // We may want to do something different with this later.
+ SetAccessibilityMode(AccessibilityModeComplete);
+}
+
+void BrowserAccessibilityStateImpl::DisableAccessibility() {
+ SetAccessibilityMode(AccessibilityModeOff);
+}
+
+bool BrowserAccessibilityStateImpl::IsAccessibleBrowser() {
+ return (accessibility_mode_ == AccessibilityModeComplete);
+}
+
+void BrowserAccessibilityStateImpl::AddHistogramCallback(
+ base::Closure callback) {
+ histogram_callbacks_.push_back(callback);
+}
+
+void BrowserAccessibilityStateImpl::UpdateHistogramsForTesting() {
+ UpdateHistograms();
+}
+
+void BrowserAccessibilityStateImpl::UpdateHistograms() {
+ UpdatePlatformSpecificHistograms();
+
+ for (size_t i = 0; i < histogram_callbacks_.size(); ++i)
+ histogram_callbacks_[i].Run();
+
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.State", IsAccessibleBrowser());
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.InvertedColors",
+ gfx::IsInvertedColorScheme());
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.ManuallyEnabled",
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kForceRendererAccessibility));
+}
+
+#if !defined(OS_WIN)
+void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() {
+}
+#endif
+
+void BrowserAccessibilityStateImpl::SetAccessibilityMode(
+ AccessibilityMode mode) {
+ if (accessibility_mode_ == mode)
+ return;
+ accessibility_mode_ = mode;
+
+ // Iterate over all RenderWidgetHosts, even swapped out ones in case
+ // they become active again.
+ RenderWidgetHost::List widgets =
+ RenderWidgetHostImpl::GetAllRenderWidgetHosts();
+ for (size_t i = 0; i < widgets.size(); ++i) {
+ // Ignore processes that don't have a connection, such as crashed tabs.
+ if (!widgets[i]->GetProcess()->HasConnection())
+ continue;
+ if (!widgets[i]->IsRenderView())
+ continue;
+
+ RenderWidgetHostImpl::From(widgets[i])->SetAccessibilityMode(mode);
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl.h b/chromium/content/browser/accessibility/browser_accessibility_state_impl.h
new file mode 100644
index 00000000000..3098341ef25
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/singleton.h"
+#include "content/common/view_message_enums.h"
+#include "content/public/browser/browser_accessibility_state.h"
+
+namespace content {
+
+// The BrowserAccessibilityState class is used to determine if Chrome should be
+// customized for users with assistive technology, such as screen readers. We
+// modify the behavior of certain user interfaces to provide a better experience
+// for screen reader users. The way we detect a screen reader program is
+// different for each platform.
+//
+// Screen Reader Detection
+// (1) On windows many screen reader detection mechinisms will give false
+// positives like relying on the SPI_GETSCREENREADER system parameter. In Chrome
+// we attempt to dynamically detect a MSAA client screen reader by calling
+// NotifiyWinEvent in NativeWidgetWin with a custom ID and wait to see if the ID
+// is requested by a subsequent call to WM_GETOBJECT.
+// (2) On mac we detect dynamically if VoiceOver is running. We rely upon the
+// undocumented accessibility attribute @"AXEnhancedUserInterface" which is set
+// when VoiceOver is launched and unset when VoiceOver is closed. This is an
+// improvement over reading defaults preference values (which has no callback
+// mechanism).
+class CONTENT_EXPORT BrowserAccessibilityStateImpl
+ : public base::RefCountedThreadSafe<BrowserAccessibilityStateImpl>,
+ public BrowserAccessibilityState {
+ public:
+ BrowserAccessibilityStateImpl();
+
+ static BrowserAccessibilityStateImpl* GetInstance();
+
+ virtual void EnableAccessibility() OVERRIDE;
+ virtual void DisableAccessibility() OVERRIDE;
+ virtual void OnScreenReaderDetected() OVERRIDE;
+ virtual bool IsAccessibleBrowser() OVERRIDE;
+ virtual void AddHistogramCallback(base::Closure callback) OVERRIDE;
+
+ virtual void UpdateHistogramsForTesting() OVERRIDE;
+
+ AccessibilityMode accessibility_mode() const { return accessibility_mode_; };
+ void SetAccessibilityMode(AccessibilityMode mode);
+
+ private:
+ friend class base::RefCountedThreadSafe<BrowserAccessibilityStateImpl>;
+ friend struct DefaultSingletonTraits<BrowserAccessibilityStateImpl>;
+
+ // Called a short while after startup to allow time for the accessibility
+ // state to be determined. Updates histograms with the current state.
+ void UpdateHistograms();
+
+ // Leaky singleton, destructor generally won't be called.
+ virtual ~BrowserAccessibilityStateImpl();
+
+ void UpdatePlatformSpecificHistograms();
+
+ AccessibilityMode accessibility_mode_;
+
+ std::vector<base::Closure> histogram_callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityStateImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc b/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc
new file mode 100644
index 00000000000..824055f6e5d
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_state_impl.h"
+
+#include <windows.h>
+#include <psapi.h>
+
+#include "base/files/file_path.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_util.h"
+
+namespace content {
+
+void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() {
+ // NOTE: this method is run from the file thread to reduce jank, since
+ // there's no guarantee these system calls will return quickly. Be careful
+ // not to add any code that isn't safe to run from a non-main thread!
+
+ AUDIODESCRIPTION audio_description = {0};
+ audio_description.cbSize = sizeof(AUDIODESCRIPTION);
+ SystemParametersInfo(SPI_GETAUDIODESCRIPTION, 0, &audio_description, 0);
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.WinAudioDescription",
+ !!audio_description.Enabled);
+
+ BOOL win_screen_reader = FALSE;
+ SystemParametersInfo(SPI_GETSCREENREADER, 0, &win_screen_reader, 0);
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.WinScreenReader",
+ !!win_screen_reader);
+
+ STICKYKEYS sticky_keys = {0};
+ sticky_keys.cbSize = sizeof(STICKYKEYS);
+ SystemParametersInfo(SPI_GETSTICKYKEYS, 0, &sticky_keys, 0);
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.WinStickyKeys",
+ 0 != (sticky_keys.dwFlags & SKF_STICKYKEYSON));
+
+ // Get the file paths of all DLLs loaded.
+ HANDLE process = GetCurrentProcess();
+ HMODULE* modules = NULL;
+ DWORD bytes_required;
+ if (!EnumProcessModules(process, modules, 0, &bytes_required))
+ return;
+
+ scoped_ptr<char[]> buffer(new char[bytes_required]);
+ modules = reinterpret_cast<HMODULE*>(buffer.get());
+ DWORD ignore;
+ if (!EnumProcessModules(process, modules, bytes_required, &ignore))
+ return;
+
+ // Look for DLLs of assistive technology known to work with Chrome.
+ bool jaws = false;
+ bool nvda = false;
+ bool satogo = false;
+ bool zoomtext = false;
+ size_t module_count = bytes_required / sizeof(HMODULE);
+ for (size_t i = 0; i < module_count; i++) {
+ TCHAR filename[MAX_PATH];
+ GetModuleFileName(modules[i], filename, sizeof(filename));
+ string16 module_name(base::FilePath(filename).BaseName().value());
+ if (LowerCaseEqualsASCII(module_name, "fsdomsrv.dll"))
+ jaws = true;
+ if (LowerCaseEqualsASCII(module_name, "vbufbackend_gecko_ia2.dll"))
+ nvda = true;
+ if (LowerCaseEqualsASCII(module_name, "stsaw32.dll"))
+ satogo = true;
+ if (LowerCaseEqualsASCII(module_name, "zslhook.dll"))
+ zoomtext = true;
+ }
+
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.WinJAWS", jaws);
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.WinNVDA", nvda);
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.WinSAToGo", satogo);
+ UMA_HISTOGRAM_BOOLEAN("Accessibility.WinZoomText", zoomtext);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_win.cc b/chromium/content/browser/accessibility/browser_accessibility_win.cc
new file mode 100644
index 00000000000..792adc4440d
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_win.cc
@@ -0,0 +1,3626 @@
+// Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_win.h"
+
+#include <UIAutomationClient.h>
+#include <UIAutomationCoreApi.h>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/enum_variant.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/windows_version.h"
+#include "content/browser/accessibility/browser_accessibility_manager_win.h"
+#include "content/common/accessibility_messages.h"
+#include "content/public/common/content_client.h"
+#include "ui/base/accessibility/accessible_text_utils.h"
+#include "ui/base/win/accessibility_ids_win.h"
+#include "ui/base/win/accessibility_misc_utils.h"
+
+namespace content {
+
+// These nonstandard GUIDs are taken directly from the Mozilla sources
+// (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here:
+// http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA
+const GUID GUID_ISimpleDOM = {
+ 0x0c539790, 0x12e4, 0x11cf,
+ 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
+const GUID GUID_IAccessibleContentDocument = {
+ 0xa5d8e1f3, 0x3571, 0x4d8f,
+ 0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e};
+
+const char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc";
+
+// static
+LONG BrowserAccessibilityWin::next_unique_id_win_ =
+ base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
+
+//
+// BrowserAccessibilityRelation
+//
+// A simple implementation of IAccessibleRelation, used to represent
+// a relationship between two accessible nodes in the tree.
+//
+
+class BrowserAccessibilityRelation
+ : public CComObjectRootEx<CComMultiThreadModel>,
+ public IAccessibleRelation {
+ BEGIN_COM_MAP(BrowserAccessibilityRelation)
+ COM_INTERFACE_ENTRY(IAccessibleRelation)
+ END_COM_MAP()
+
+ CONTENT_EXPORT BrowserAccessibilityRelation() {}
+ CONTENT_EXPORT virtual ~BrowserAccessibilityRelation() {}
+
+ CONTENT_EXPORT void Initialize(BrowserAccessibilityWin* owner,
+ const string16& type);
+ CONTENT_EXPORT void AddTarget(int target_id);
+
+ // IAccessibleRelation methods.
+ CONTENT_EXPORT STDMETHODIMP get_relationType(BSTR* relation_type);
+ CONTENT_EXPORT STDMETHODIMP get_nTargets(long* n_targets);
+ CONTENT_EXPORT STDMETHODIMP get_target(long target_index, IUnknown** target);
+ CONTENT_EXPORT STDMETHODIMP get_targets(long max_targets,
+ IUnknown** targets,
+ long* n_targets);
+
+ // IAccessibleRelation methods not implemented.
+ CONTENT_EXPORT STDMETHODIMP get_localizedRelationType(BSTR* relation_type) {
+ return E_NOTIMPL;
+ }
+
+ private:
+ string16 type_;
+ base::win::ScopedComPtr<BrowserAccessibilityWin> owner_;
+ std::vector<int> target_ids_;
+};
+
+void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin* owner,
+ const string16& type) {
+ owner_ = owner;
+ type_ = type;
+}
+
+void BrowserAccessibilityRelation::AddTarget(int target_id) {
+ target_ids_.push_back(target_id);
+}
+
+STDMETHODIMP BrowserAccessibilityRelation::get_relationType(
+ BSTR* relation_type) {
+ if (!relation_type)
+ return E_INVALIDARG;
+
+ if (!owner_->instance_active())
+ return E_FAIL;
+
+ *relation_type = SysAllocString(type_.c_str());
+ DCHECK(*relation_type);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) {
+ if (!n_targets)
+ return E_INVALIDARG;
+
+ if (!owner_->instance_active())
+ return E_FAIL;
+
+ *n_targets = static_cast<long>(target_ids_.size());
+
+ BrowserAccessibilityManager* manager = owner_->manager();
+ for (long i = *n_targets - 1; i >= 0; --i) {
+ BrowserAccessibility* result = manager->GetFromRendererID(target_ids_[i]);
+ if (!result || !result->instance_active()) {
+ *n_targets = 0;
+ break;
+ }
+ }
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index,
+ IUnknown** target) {
+ if (!target)
+ return E_INVALIDARG;
+
+ if (!owner_->instance_active())
+ return E_FAIL;
+
+ if (target_index < 0 ||
+ target_index >= static_cast<long>(target_ids_.size())) {
+ return E_INVALIDARG;
+ }
+
+ BrowserAccessibilityManager* manager = owner_->manager();
+ BrowserAccessibility* result =
+ manager->GetFromRendererID(target_ids_[target_index]);
+ if (!result || !result->instance_active())
+ return E_FAIL;
+
+ *target = static_cast<IAccessible*>(
+ result->ToBrowserAccessibilityWin()->NewReference());
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityRelation::get_targets(long max_targets,
+ IUnknown** targets,
+ long* n_targets) {
+ if (!targets || !n_targets)
+ return E_INVALIDARG;
+
+ if (!owner_->instance_active())
+ return E_FAIL;
+
+ long count = static_cast<long>(target_ids_.size());
+ if (count > max_targets)
+ count = max_targets;
+
+ *n_targets = count;
+ if (count == 0)
+ return S_FALSE;
+
+ for (long i = 0; i < count; ++i) {
+ HRESULT result = get_target(i, &targets[i]);
+ if (result != S_OK)
+ return result;
+ }
+
+ return S_OK;
+}
+
+//
+// BrowserAccessibilityWin
+//
+
+// static
+BrowserAccessibility* BrowserAccessibility::Create() {
+ CComObject<BrowserAccessibilityWin>* instance;
+ HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance);
+ DCHECK(SUCCEEDED(hr));
+ return instance->NewReference();
+}
+
+BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() {
+ return static_cast<BrowserAccessibilityWin*>(this);
+}
+
+BrowserAccessibilityWin::BrowserAccessibilityWin()
+ : ia_role_(0),
+ ia_state_(0),
+ ia2_role_(0),
+ ia2_state_(0),
+ first_time_(true),
+ old_ia_state_(0) {
+ // Start unique IDs at -1 and decrement each time, because get_accChild
+ // uses positive IDs to enumerate children, so we use negative IDs to
+ // clearly distinguish between indices and unique IDs.
+ unique_id_win_ = next_unique_id_win_;
+ if (next_unique_id_win_ ==
+ base::win::kLastBrowserAccessibilityManagerAccessibilityId) {
+ next_unique_id_win_ =
+ base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
+ }
+ next_unique_id_win_--;
+}
+
+BrowserAccessibilityWin::~BrowserAccessibilityWin() {
+ for (size_t i = 0; i < relations_.size(); ++i)
+ relations_[i]->Release();
+}
+
+//
+// IAccessible methods.
+//
+// Conventions:
+// * Always test for instance_active_ first and return E_FAIL if it's false.
+// * Always check for invalid arguments first, even if they're unused.
+// * Return S_FALSE if the only output is a string argument and it's empty.
+//
+
+HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ manager_->DoDefaultAction(*target);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left,
+ LONG y_top,
+ VARIANT* child) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!child)
+ return E_INVALIDARG;
+
+ gfx::Point point(x_left, y_top);
+ if (!GetGlobalBoundsRect().Contains(point)) {
+ // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
+ child->vt = VT_EMPTY;
+ return S_FALSE;
+ }
+
+ BrowserAccessibility* result = BrowserAccessibilityForPoint(point);
+ if (result == this) {
+ // Point is within this object.
+ child->vt = VT_I4;
+ child->lVal = CHILDID_SELF;
+ } else {
+ child->vt = VT_DISPATCH;
+ child->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
+ }
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left,
+ LONG* y_top,
+ LONG* width,
+ LONG* height,
+ VARIANT var_id) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!x_left || !y_top || !width || !height)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ gfx::Rect bounds = target->GetGlobalBoundsRect();
+ *x_left = bounds.x();
+ *y_top = bounds.y();
+ *width = bounds.width();
+ *height = bounds.height();
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir,
+ VARIANT start,
+ VARIANT* end) {
+ BrowserAccessibilityWin* target = GetTargetFromChildID(start);
+ if (!target)
+ return E_INVALIDARG;
+
+ if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) &&
+ start.lVal != CHILDID_SELF) {
+ // MSAA states that navigating to first/last child can only be from self.
+ return E_INVALIDARG;
+ }
+
+ BrowserAccessibility* result = NULL;
+ switch (nav_dir) {
+ case NAVDIR_DOWN:
+ case NAVDIR_UP:
+ case NAVDIR_LEFT:
+ case NAVDIR_RIGHT:
+ // These directions are not implemented, matching Mozilla and IE.
+ return E_NOTIMPL;
+ case NAVDIR_FIRSTCHILD:
+ if (!target->children_.empty())
+ result = target->children_.front();
+ break;
+ case NAVDIR_LASTCHILD:
+ if (!target->children_.empty())
+ result = target->children_.back();
+ break;
+ case NAVDIR_NEXT:
+ result = target->GetNextSibling();
+ break;
+ case NAVDIR_PREVIOUS:
+ result = target->GetPreviousSibling();
+ break;
+ }
+
+ if (!result) {
+ end->vt = VT_EMPTY;
+ return S_FALSE;
+ }
+
+ end->vt = VT_DISPATCH;
+ end->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child,
+ IDispatch** disp_child) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!disp_child)
+ return E_INVALIDARG;
+
+ *disp_child = NULL;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_child);
+ if (!target)
+ return E_INVALIDARG;
+
+ (*disp_child) = target->NewReference();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!child_count)
+ return E_INVALIDARG;
+
+ *child_count = children_.size();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id,
+ BSTR* def_action) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!def_action)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ return target->GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_SHORTCUT, def_action);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
+ BSTR* desc) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!desc)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ return target->GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_DESCRIPTION, desc);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!focus_child)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>(
+ manager_->GetFocus(this));
+ if (focus == this) {
+ focus_child->vt = VT_I4;
+ focus_child->lVal = CHILDID_SELF;
+ } else if (focus == NULL) {
+ focus_child->vt = VT_EMPTY;
+ } else {
+ focus_child->vt = VT_DISPATCH;
+ focus_child->pdispVal = focus->NewReference();
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!help)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ return target->GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_HELP, help);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
+ BSTR* acc_key) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!acc_key)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ return target->GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_SHORTCUT, acc_key);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!name)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ string16 name_str = target->name_;
+
+ // If the name is empty, see if it's labeled by another element.
+ if (name_str.empty()) {
+ int title_elem_id;
+ if (target->GetIntAttribute(AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT,
+ &title_elem_id)) {
+ BrowserAccessibility* title_elem =
+ manager_->GetFromRendererID(title_elem_id);
+ if (title_elem)
+ name_str = title_elem->GetTextRecursive();
+ }
+ }
+
+ if (name_str.empty())
+ return S_FALSE;
+
+ *name = SysAllocString(name_str.c_str());
+
+ DCHECK(*name);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!disp_parent)
+ return E_INVALIDARG;
+
+ IAccessible* parent = parent_->ToBrowserAccessibilityWin();
+ if (parent == NULL) {
+ // This happens if we're the root of the tree;
+ // return the IAccessible for the window.
+ parent = manager_->ToBrowserAccessibilityManagerWin()->parent_iaccessible();
+ // |parent| can only be NULL if the manager was created before the parent
+ // IAccessible was known and it wasn't subsequently set before a client
+ // requested it. Crash hard if this happens so that we get crash reports.
+ CHECK(parent);
+ }
+
+ parent->AddRef();
+ *disp_parent = parent;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id,
+ VARIANT* role) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!role)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ if (!target->role_name_.empty()) {
+ role->vt = VT_BSTR;
+ role->bstrVal = SysAllocString(target->role_name_.c_str());
+ } else {
+ role->vt = VT_I4;
+ role->lVal = target->ia_role_;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
+ VARIANT* state) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!state)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ state->vt = VT_I4;
+ state->lVal = target->ia_state_;
+ if (manager_->GetFocus(NULL) == this)
+ state->lVal |= STATE_SYSTEM_FOCUSED;
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
+ BSTR* value) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!value)
+ return E_INVALIDARG;
+
+ BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
+ if (!target)
+ return E_INVALIDARG;
+
+ *value = SysAllocString(target->value_.c_str());
+
+ DCHECK(*value);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file,
+ VARIANT var_id,
+ LONG* topic_id) {
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (role_ != AccessibilityNodeData::ROLE_LISTBOX)
+ return E_NOTIMPL;
+
+ unsigned long selected_count = 0;
+ for (size_t i = 0; i < children_.size(); ++i) {
+ if (children_[i]->HasState(AccessibilityNodeData::STATE_SELECTED))
+ ++selected_count;
+ }
+
+ if (selected_count == 0) {
+ selected->vt = VT_EMPTY;
+ return S_OK;
+ }
+
+ if (selected_count == 1) {
+ for (size_t i = 0; i < children_.size(); ++i) {
+ if (children_[i]->HasState(AccessibilityNodeData::STATE_SELECTED)) {
+ selected->vt = VT_DISPATCH;
+ selected->pdispVal =
+ children_[i]->ToBrowserAccessibilityWin()->NewReference();
+ return S_OK;
+ }
+ }
+ }
+
+ // Multiple items are selected.
+ base::win::EnumVariant* enum_variant =
+ new base::win::EnumVariant(selected_count);
+ enum_variant->AddRef();
+ unsigned long index = 0;
+ for (size_t i = 0; i < children_.size(); ++i) {
+ if (children_[i]->HasState(AccessibilityNodeData::STATE_SELECTED)) {
+ enum_variant->ItemAt(index)->vt = VT_DISPATCH;
+ enum_variant->ItemAt(index)->pdispVal =
+ children_[i]->ToBrowserAccessibilityWin()->NewReference();
+ ++index;
+ }
+ }
+ selected->vt = VT_UNKNOWN;
+ selected->punkVal = static_cast<IUnknown*>(
+ static_cast<base::win::IUnknownImpl*>(enum_variant));
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::accSelect(
+ LONG flags_sel, VARIANT var_id) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (flags_sel & SELFLAG_TAKEFOCUS) {
+ manager_->SetFocus(this, true);
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+//
+// IAccessible2 methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!role)
+ return E_INVALIDARG;
+
+ *role = ia2_role_;
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!attributes)
+ return E_INVALIDARG;
+
+ // The iaccessible2 attributes are a set of key-value pairs
+ // separated by semicolons, with a colon between the key and the value.
+ string16 str;
+ for (unsigned int i = 0; i < ia2_attributes_.size(); ++i) {
+ if (i != 0)
+ str += L';';
+ str += ia2_attributes_[i];
+ }
+
+ if (str.empty())
+ return S_FALSE;
+
+ *attributes = SysAllocString(str.c_str());
+ DCHECK(*attributes);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!states)
+ return E_INVALIDARG;
+
+ *states = ia2_state_;
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!unique_id)
+ return E_INVALIDARG;
+
+ *unique_id = unique_id_win_;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!window_handle)
+ return E_INVALIDARG;
+
+ *window_handle = manager_->ToBrowserAccessibilityManagerWin()->parent_hwnd();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!index_in_parent)
+ return E_INVALIDARG;
+
+ *index_in_parent = index_in_parent_;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!n_relations)
+ return E_INVALIDARG;
+
+ *n_relations = relations_.size();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_relation(
+ LONG relation_index,
+ IAccessibleRelation** relation) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (relation_index < 0 ||
+ relation_index >= static_cast<long>(relations_.size())) {
+ return E_INVALIDARG;
+ }
+
+ if (!relation)
+ return E_INVALIDARG;
+
+ relations_[relation_index]->AddRef();
+ *relation = relations_[relation_index];
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_relations(
+ LONG max_relations,
+ IAccessibleRelation** relations,
+ LONG* n_relations) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!relations || !n_relations)
+ return E_INVALIDARG;
+
+ long count = static_cast<long>(relations_.size());
+ *n_relations = count;
+ if (count == 0)
+ return S_FALSE;
+
+ for (long i = 0; i < count; ++i) {
+ relations_[i]->AddRef();
+ relations[i] = relations_[i];
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ gfx::Rect r = location_;
+ switch(scroll_type) {
+ case IA2_SCROLL_TYPE_TOP_LEFT:
+ manager_->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
+ break;
+ case IA2_SCROLL_TYPE_BOTTOM_RIGHT:
+ manager_->ScrollToMakeVisible(
+ *this, gfx::Rect(r.right(), r.bottom(), 0, 0));
+ break;
+ case IA2_SCROLL_TYPE_TOP_EDGE:
+ manager_->ScrollToMakeVisible(
+ *this, gfx::Rect(r.x(), r.y(), r.width(), 0));
+ break;
+ case IA2_SCROLL_TYPE_BOTTOM_EDGE:
+ manager_->ScrollToMakeVisible(
+ *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0));
+ break;
+ case IA2_SCROLL_TYPE_LEFT_EDGE:
+ manager_->ScrollToMakeVisible(
+ *this, gfx::Rect(r.x(), r.y(), 0, r.height()));
+ break;
+ case IA2_SCROLL_TYPE_RIGHT_EDGE:
+ manager_->ScrollToMakeVisible(
+ *this, gfx::Rect(r.right(), r.y(), 0, r.height()));
+ break;
+ case IA2_SCROLL_TYPE_ANYWHERE:
+ default:
+ manager_->ScrollToMakeVisible(*this, r);
+ break;
+ }
+
+ static_cast<BrowserAccessibilityManagerWin*>(manager_)
+ ->TrackScrollingObject(this);
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
+ enum IA2CoordinateType coordinate_type,
+ LONG x,
+ LONG y) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ gfx::Point scroll_to(x, y);
+
+ if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
+ scroll_to -= manager_->GetViewBounds().OffsetFromOrigin();
+ } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
+ if (parent_)
+ scroll_to += parent_->location().OffsetFromOrigin();
+ } else {
+ return E_INVALIDARG;
+ }
+
+ manager_->ScrollToPoint(*this, scroll_to);
+
+ static_cast<BrowserAccessibilityManagerWin*>(manager_)
+ ->TrackScrollingObject(this);
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
+ LONG* group_level,
+ LONG* similar_items_in_group,
+ LONG* position_in_group) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!group_level || !similar_items_in_group || !position_in_group)
+ return E_INVALIDARG;
+
+ if (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION &&
+ parent_ &&
+ parent_->role() == AccessibilityNodeData::ROLE_LISTBOX) {
+ *group_level = 0;
+ *similar_items_in_group = parent_->child_count();
+ *position_in_group = index_in_parent_ + 1;
+ return S_OK;
+ }
+
+ return E_NOTIMPL;
+}
+
+//
+// IAccessibleApplication methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
+ // No need to check |instance_active_| because this interface is
+ // global, and doesn't depend on any local state.
+
+ if (!app_name)
+ return E_INVALIDARG;
+
+ // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
+ // the part before the "/".
+ std::vector<std::string> product_components;
+ base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
+ DCHECK_EQ(2U, product_components.size());
+ if (product_components.size() != 2)
+ return E_FAIL;
+ *app_name = SysAllocString(UTF8ToUTF16(product_components[0]).c_str());
+ DCHECK(*app_name);
+ return *app_name ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
+ // No need to check |instance_active_| because this interface is
+ // global, and doesn't depend on any local state.
+
+ if (!app_version)
+ return E_INVALIDARG;
+
+ // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
+ // the part after the "/".
+ std::vector<std::string> product_components;
+ base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
+ DCHECK_EQ(2U, product_components.size());
+ if (product_components.size() != 2)
+ return E_FAIL;
+ *app_version = SysAllocString(UTF8ToUTF16(product_components[1]).c_str());
+ DCHECK(*app_version);
+ return *app_version ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) {
+ // No need to check |instance_active_| because this interface is
+ // global, and doesn't depend on any local state.
+
+ if (!toolkit_name)
+ return E_INVALIDARG;
+
+ // This is hard-coded; all products based on the Chromium engine
+ // will have the same toolkit name, so that assistive technology can
+ // detect any Chrome-based product.
+ *toolkit_name = SysAllocString(L"Chrome");
+ DCHECK(*toolkit_name);
+ return *toolkit_name ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
+ BSTR* toolkit_version) {
+ // No need to check |instance_active_| because this interface is
+ // global, and doesn't depend on any local state.
+
+ if (!toolkit_version)
+ return E_INVALIDARG;
+
+ std::string user_agent = GetContentClient()->GetUserAgent();
+ *toolkit_version = SysAllocString(UTF8ToUTF16(user_agent).c_str());
+ DCHECK(*toolkit_version);
+ return *toolkit_version ? S_OK : E_FAIL;
+}
+
+//
+// IAccessibleImage methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!desc)
+ return E_INVALIDARG;
+
+ return GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_DESCRIPTION, desc);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
+ enum IA2CoordinateType coordinate_type,
+ LONG* x,
+ LONG* y) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!x || !y)
+ return E_INVALIDARG;
+
+ if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
+ HWND parent_hwnd =
+ manager_->ToBrowserAccessibilityManagerWin()->parent_hwnd();
+ POINT top_left = {0, 0};
+ ::ClientToScreen(parent_hwnd, &top_left);
+ *x = location_.x() + top_left.x;
+ *y = location_.y() + top_left.y;
+ } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
+ *x = location_.x();
+ *y = location_.y();
+ if (parent_) {
+ *x -= parent_->location().x();
+ *y -= parent_->location().y();
+ }
+ } else {
+ return E_INVALIDARG;
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!height || !width)
+ return E_INVALIDARG;
+
+ *height = location_.height();
+ *width = location_.width();
+ return S_OK;
+}
+
+//
+// IAccessibleTable methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
+ long row,
+ long column,
+ IUnknown** accessible) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!accessible)
+ return E_INVALIDARG;
+
+ int columns;
+ int rows;
+ if (!GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ columns <= 0 ||
+ rows <= 0) {
+ return S_FALSE;
+ }
+
+ if (row < 0 || row >= rows || column < 0 || column >= columns)
+ return E_INVALIDARG;
+
+ DCHECK_EQ(columns * rows, static_cast<int>(cell_ids_.size()));
+
+ int cell_id = cell_ids_[row * columns + column];
+ BrowserAccessibilityWin* cell = GetFromRendererID(cell_id);
+ if (cell) {
+ *accessible = static_cast<IAccessible*>(cell->NewReference());
+ return S_OK;
+ }
+
+ *accessible = NULL;
+ return E_INVALIDARG;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!accessible)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): implement
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
+ long column,
+ long* cell_index) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!cell_index)
+ return E_INVALIDARG;
+
+ int columns;
+ int rows;
+ if (!GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ columns <= 0 ||
+ rows <= 0) {
+ return S_FALSE;
+ }
+
+ if (row < 0 || row >= rows || column < 0 || column >= columns)
+ return E_INVALIDARG;
+
+ DCHECK_EQ(columns * rows, static_cast<int>(cell_ids_.size()));
+ int cell_id = cell_ids_[row * columns + column];
+ for (size_t i = 0; i < unique_cell_ids_.size(); ++i) {
+ if (unique_cell_ids_[i] == cell_id) {
+ *cell_index = (long)i;
+ return S_OK;
+ }
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
+ BSTR* description) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!description)
+ return E_INVALIDARG;
+
+ int columns;
+ int rows;
+ if (!GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ columns <= 0 ||
+ rows <= 0) {
+ return S_FALSE;
+ }
+
+ if (column < 0 || column >= columns)
+ return E_INVALIDARG;
+
+ for (int i = 0; i < rows; ++i) {
+ int cell_id = cell_ids_[i * columns + column];
+ BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
+ manager_->GetFromRendererID(cell_id));
+ if (cell && cell->role_ == AccessibilityNodeData::ROLE_COLUMN_HEADER) {
+ if (cell->name_.size() > 0) {
+ *description = SysAllocString(cell->name_.c_str());
+ return S_OK;
+ }
+
+ return cell->GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_DESCRIPTION, description);
+ }
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
+ long row,
+ long column,
+ long* n_columns_spanned) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!n_columns_spanned)
+ return E_INVALIDARG;
+
+ int columns;
+ int rows;
+ if (!GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ columns <= 0 ||
+ rows <= 0) {
+ return S_FALSE;
+ }
+
+ if (row < 0 || row >= rows || column < 0 || column >= columns)
+ return E_INVALIDARG;
+
+ int cell_id = cell_ids_[row * columns + column];
+ BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
+ manager_->GetFromRendererID(cell_id));
+ int colspan;
+ if (cell &&
+ cell->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
+ colspan >= 1) {
+ *n_columns_spanned = colspan;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_columnHeader(
+ IAccessibleTable** accessible_table,
+ long* starting_row_index) {
+ // TODO(dmazzoni): implement
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
+ long* column_index) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!column_index)
+ return E_INVALIDARG;
+
+ int cell_id_count = static_cast<int>(unique_cell_ids_.size());
+ if (cell_index < 0)
+ return E_INVALIDARG;
+ if (cell_index >= cell_id_count)
+ return S_FALSE;
+
+ int cell_id = unique_cell_ids_[cell_index];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ int col_index;
+ if (cell &&
+ cell->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
+ *column_index = col_index;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!column_count)
+ return E_INVALIDARG;
+
+ int columns;
+ if (GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns)) {
+ *column_count = columns;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!row_count)
+ return E_INVALIDARG;
+
+ int rows;
+ if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
+ *row_count = rows;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!cell_count)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
+ *cell_count = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!column_count)
+ return E_INVALIDARG;
+
+ *column_count = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!row_count)
+ return E_INVALIDARG;
+
+ *row_count = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
+ BSTR* description) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!description)
+ return E_INVALIDARG;
+
+ int columns;
+ int rows;
+ if (!GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ columns <= 0 ||
+ rows <= 0) {
+ return S_FALSE;
+ }
+
+ if (row < 0 || row >= rows)
+ return E_INVALIDARG;
+
+ for (int i = 0; i < columns; ++i) {
+ int cell_id = cell_ids_[row * columns + i];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ if (cell && cell->role_ == AccessibilityNodeData::ROLE_ROW_HEADER) {
+ if (cell->name_.size() > 0) {
+ *description = SysAllocString(cell->name_.c_str());
+ return S_OK;
+ }
+
+ return cell->GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_DESCRIPTION, description);
+ }
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
+ long column,
+ long* n_rows_spanned) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!n_rows_spanned)
+ return E_INVALIDARG;
+
+ int columns;
+ int rows;
+ if (!GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ columns <= 0 ||
+ rows <= 0) {
+ return S_FALSE;
+ }
+
+ if (row < 0 || row >= rows || column < 0 || column >= columns)
+ return E_INVALIDARG;
+
+ int cell_id = cell_ids_[row * columns + column];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ int rowspan;
+ if (cell &&
+ cell->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
+ rowspan >= 1) {
+ *n_rows_spanned = rowspan;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowHeader(
+ IAccessibleTable** accessible_table,
+ long* starting_column_index) {
+ // TODO(dmazzoni): implement
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
+ long* row_index) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!row_index)
+ return E_INVALIDARG;
+
+ int cell_id_count = static_cast<int>(unique_cell_ids_.size());
+ if (cell_index < 0)
+ return E_INVALIDARG;
+ if (cell_index >= cell_id_count)
+ return S_FALSE;
+
+ int cell_id = unique_cell_ids_[cell_index];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ int cell_row_index;
+ if (cell &&
+ cell->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
+ *row_index = cell_row_index;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children,
+ long** children,
+ long* n_children) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!children || !n_children)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *n_children = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns,
+ long** columns,
+ long* n_columns) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!columns || !n_columns)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *n_columns = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows,
+ long** rows,
+ long* n_rows) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!rows || !n_rows)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *n_rows = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!accessible)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): implement
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected(
+ long column,
+ boolean* is_selected) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!is_selected)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *is_selected = false;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row,
+ boolean* is_selected) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!is_selected)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *is_selected = false;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row,
+ long column,
+ boolean* is_selected) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!is_selected)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *is_selected = false;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
+ long index,
+ long* row,
+ long* column,
+ long* row_extents,
+ long* column_extents,
+ boolean* is_selected) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!row || !column || !row_extents || !column_extents || !is_selected)
+ return E_INVALIDARG;
+
+ int cell_id_count = static_cast<int>(unique_cell_ids_.size());
+ if (index < 0)
+ return E_INVALIDARG;
+ if (index >= cell_id_count)
+ return S_FALSE;
+
+ int cell_id = unique_cell_ids_[index];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ int rowspan;
+ int colspan;
+ if (cell &&
+ cell->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
+ cell->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
+ rowspan >= 1 &&
+ colspan >= 1) {
+ *row_extents = rowspan;
+ *column_extents = colspan;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+//
+// IAccessibleTable2 methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row,
+ long column,
+ IUnknown** cell) {
+ return get_accessibleAt(row, column, cell);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) {
+ return get_nSelectedChildren(cell_count);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_selectedCells(
+ IUnknown*** cells,
+ long* n_selected_cells) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!cells || !n_selected_cells)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *n_selected_cells = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns,
+ long* n_columns) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!columns || !n_columns)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *n_columns = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows,
+ long* n_rows) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!rows || !n_rows)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): Implement this.
+ *n_rows = 0;
+ return S_OK;
+}
+
+
+//
+// IAccessibleTableCell methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
+ long* n_columns_spanned) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!n_columns_spanned)
+ return E_INVALIDARG;
+
+ int colspan;
+ if (GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
+ colspan >= 1) {
+ *n_columns_spanned = colspan;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
+ IUnknown*** cell_accessibles,
+ long* n_column_header_cells) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!cell_accessibles || !n_column_header_cells)
+ return E_INVALIDARG;
+
+ *n_column_header_cells = 0;
+
+ int column;
+ if (!GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
+ return S_FALSE;
+ }
+
+ BrowserAccessibility* table = parent();
+ while (table && table->role() != AccessibilityNodeData::ROLE_TABLE)
+ table = table->parent();
+ if (!table) {
+ NOTREACHED();
+ return S_FALSE;
+ }
+
+ int columns;
+ int rows;
+ if (!table->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !table->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
+ return S_FALSE;
+ }
+ if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
+ return S_FALSE;
+
+ for (int i = 0; i < rows; ++i) {
+ int cell_id = table->cell_ids()[i * columns + column];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ if (cell && cell->role_ == AccessibilityNodeData::ROLE_COLUMN_HEADER)
+ (*n_column_header_cells)++;
+ }
+
+ *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
+ (*n_column_header_cells) * sizeof(cell_accessibles[0])));
+ int index = 0;
+ for (int i = 0; i < rows; ++i) {
+ int cell_id = table->cell_ids()[i * columns + column];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ if (cell && cell->role_ == AccessibilityNodeData::ROLE_COLUMN_HEADER) {
+ (*cell_accessibles)[index] =
+ static_cast<IAccessible*>(cell->NewReference());
+ ++index;
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!column_index)
+ return E_INVALIDARG;
+
+ int column;
+ if (GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
+ *column_index = column;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!n_rows_spanned)
+ return E_INVALIDARG;
+
+ int rowspan;
+ if (GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
+ rowspan >= 1) {
+ *n_rows_spanned = rowspan;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
+ IUnknown*** cell_accessibles,
+ long* n_row_header_cells) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!cell_accessibles || !n_row_header_cells)
+ return E_INVALIDARG;
+
+ *n_row_header_cells = 0;
+
+ int row;
+ if (!GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row)) {
+ return S_FALSE;
+ }
+
+ BrowserAccessibility* table = parent();
+ while (table && table->role() != AccessibilityNodeData::ROLE_TABLE)
+ table = table->parent();
+ if (!table) {
+ NOTREACHED();
+ return S_FALSE;
+ }
+
+ int columns;
+ int rows;
+ if (!table->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !table->GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
+ return S_FALSE;
+ }
+ if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
+ return S_FALSE;
+
+ for (int i = 0; i < columns; ++i) {
+ int cell_id = table->cell_ids()[row * columns + i];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ if (cell && cell->role_ == AccessibilityNodeData::ROLE_ROW_HEADER)
+ (*n_row_header_cells)++;
+ }
+
+ *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
+ (*n_row_header_cells) * sizeof(cell_accessibles[0])));
+ int index = 0;
+ for (int i = 0; i < columns; ++i) {
+ int cell_id = table->cell_ids()[row * columns + i];
+ BrowserAccessibilityWin* cell =
+ manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ if (cell && cell->role_ == AccessibilityNodeData::ROLE_ROW_HEADER) {
+ (*cell_accessibles)[index] =
+ static_cast<IAccessible*>(cell->NewReference());
+ ++index;
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!row_index)
+ return E_INVALIDARG;
+
+ int row;
+ if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row)) {
+ *row_index = row;
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!is_selected)
+ return E_INVALIDARG;
+
+ *is_selected = false;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
+ long* row_index,
+ long* column_index,
+ long* row_extents,
+ long* column_extents,
+ boolean* is_selected) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!row_index ||
+ !column_index ||
+ !row_extents ||
+ !column_extents ||
+ !is_selected) {
+ return E_INVALIDARG;
+ }
+
+ int row;
+ int column;
+ int rowspan;
+ int colspan;
+ if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row) &&
+ GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
+ GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
+ GetIntAttribute(
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
+ *row_index = row;
+ *column_index = column;
+ *row_extents = rowspan;
+ *column_extents = colspan;
+ *is_selected = false;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!table)
+ return E_INVALIDARG;
+
+
+ int row;
+ int column;
+ GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row);
+ GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column);
+
+ BrowserAccessibility* find_table = parent();
+ while (find_table && find_table->role() != AccessibilityNodeData::ROLE_TABLE)
+ find_table = find_table->parent();
+ if (!find_table) {
+ NOTREACHED();
+ return S_FALSE;
+ }
+
+ *table = static_cast<IAccessibleTable*>(
+ find_table->ToBrowserAccessibilityWin()->NewReference());
+
+ return S_OK;
+}
+
+//
+// IAccessibleText methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!n_characters)
+ return E_INVALIDARG;
+
+ *n_characters = TextForIAccessibleText().length();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!offset)
+ return E_INVALIDARG;
+
+ *offset = 0;
+ if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD ||
+ role_ == AccessibilityNodeData::ROLE_TEXTAREA) {
+ int sel_start = 0;
+ if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START,
+ &sel_start))
+ *offset = sel_start;
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!n_selections)
+ return E_INVALIDARG;
+
+ *n_selections = 0;
+ if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD ||
+ role_ == AccessibilityNodeData::ROLE_TEXTAREA) {
+ int sel_start = 0;
+ int sel_end = 0;
+ if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START,
+ &sel_start) &&
+ GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end) &&
+ sel_start != sel_end)
+ *n_selections = 1;
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
+ LONG* start_offset,
+ LONG* end_offset) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!start_offset || !end_offset || selection_index != 0)
+ return E_INVALIDARG;
+
+ *start_offset = 0;
+ *end_offset = 0;
+ if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD ||
+ role_ == AccessibilityNodeData::ROLE_TEXTAREA) {
+ int sel_start = 0;
+ int sel_end = 0;
+ if (GetIntAttribute(
+ AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start) &&
+ GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end)) {
+ *start_offset = sel_start;
+ *end_offset = sel_end;
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset,
+ LONG end_offset,
+ BSTR* text) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!text)
+ return E_INVALIDARG;
+
+ const string16& text_str = TextForIAccessibleText();
+
+ // Handle special text offsets.
+ HandleSpecialTextOffset(text_str, &start_offset);
+ HandleSpecialTextOffset(text_str, &end_offset);
+
+ // The spec allows the arguments to be reversed.
+ if (start_offset > end_offset) {
+ LONG tmp = start_offset;
+ start_offset = end_offset;
+ end_offset = tmp;
+ }
+
+ // The spec does not allow the start or end offsets to be out or range;
+ // we must return an error if so.
+ LONG len = text_str.length();
+ if (start_offset < 0)
+ return E_INVALIDARG;
+ if (end_offset > len)
+ return E_INVALIDARG;
+
+ string16 substr = text_str.substr(start_offset, end_offset - start_offset);
+ if (substr.empty())
+ return S_FALSE;
+
+ *text = SysAllocString(substr.c_str());
+ DCHECK(*text);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
+ LONG offset,
+ enum IA2TextBoundaryType boundary_type,
+ LONG* start_offset,
+ LONG* end_offset,
+ BSTR* text) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!start_offset || !end_offset || !text)
+ return E_INVALIDARG;
+
+ // The IAccessible2 spec says we don't have to implement the "sentence"
+ // boundary type, we can just let the screenreader handle it.
+ if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
+ *start_offset = 0;
+ *end_offset = 0;
+ *text = NULL;
+ return S_FALSE;
+ }
+
+ const string16& text_str = TextForIAccessibleText();
+
+ *start_offset = FindBoundary(
+ text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
+ *end_offset = FindBoundary(
+ text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
+ return get_text(*start_offset, *end_offset, text);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
+ LONG offset,
+ enum IA2TextBoundaryType boundary_type,
+ LONG* start_offset,
+ LONG* end_offset,
+ BSTR* text) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!start_offset || !end_offset || !text)
+ return E_INVALIDARG;
+
+ // The IAccessible2 spec says we don't have to implement the "sentence"
+ // boundary type, we can just let the screenreader handle it.
+ if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
+ *start_offset = 0;
+ *end_offset = 0;
+ *text = NULL;
+ return S_FALSE;
+ }
+
+ const string16& text_str = TextForIAccessibleText();
+
+ *start_offset = FindBoundary(
+ text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
+ *end_offset = offset;
+ return get_text(*start_offset, *end_offset, text);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
+ LONG offset,
+ enum IA2TextBoundaryType boundary_type,
+ LONG* start_offset,
+ LONG* end_offset,
+ BSTR* text) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!start_offset || !end_offset || !text)
+ return E_INVALIDARG;
+
+ // The IAccessible2 spec says we don't have to implement the "sentence"
+ // boundary type, we can just let the screenreader handle it.
+ if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
+ *start_offset = 0;
+ *end_offset = 0;
+ *text = NULL;
+ return S_FALSE;
+ }
+
+ const string16& text_str = TextForIAccessibleText();
+
+ *start_offset = offset;
+ *end_offset = FindBoundary(
+ text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
+ return get_text(*start_offset, *end_offset, text);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!new_text)
+ return E_INVALIDARG;
+
+ string16 text = TextForIAccessibleText();
+
+ new_text->text = SysAllocString(text.c_str());
+ new_text->start = 0;
+ new_text->end = static_cast<long>(text.size());
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!old_text)
+ return E_INVALIDARG;
+
+ old_text->text = SysAllocString(old_text_.c_str());
+ old_text->start = 0;
+ old_text->end = static_cast<long>(old_text_.size());
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint(
+ LONG x,
+ LONG y,
+ enum IA2CoordinateType coord_type,
+ LONG* offset) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!offset)
+ return E_INVALIDARG;
+
+ // TODO(dmazzoni): implement this. We're returning S_OK for now so that
+ // screen readers still return partially accurate results rather than
+ // completely failing.
+ *offset = 0;
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo(
+ LONG start_index,
+ LONG end_index,
+ enum IA2ScrollType scroll_type) {
+ // TODO(dmazzoni): adjust this for the start and end index, too.
+ return scrollTo(scroll_type);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint(
+ LONG start_index,
+ LONG end_index,
+ enum IA2CoordinateType coordinate_type,
+ LONG x, LONG y) {
+ // TODO(dmazzoni): adjust this for the start and end index, too.
+ return scrollToPoint(coordinate_type, x, y);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset,
+ LONG end_offset) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ const string16& text_str = TextForIAccessibleText();
+ HandleSpecialTextOffset(text_str, &start_offset);
+ HandleSpecialTextOffset(text_str, &end_offset);
+
+ manager_->SetTextSelection(*this, start_offset, end_offset);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (selection_index != 0)
+ return E_INVALIDARG;
+
+ manager_->SetTextSelection(*this, 0, 0);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ const string16& text_str = TextForIAccessibleText();
+ HandleSpecialTextOffset(text_str, &offset);
+ manager_->SetTextSelection(*this, offset, offset);
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index,
+ LONG start_offset,
+ LONG end_offset) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (selection_index != 0)
+ return E_INVALIDARG;
+
+ const string16& text_str = TextForIAccessibleText();
+ HandleSpecialTextOffset(text_str, &start_offset);
+ HandleSpecialTextOffset(text_str, &end_offset);
+
+ manager_->SetTextSelection(*this, start_offset, end_offset);
+ return S_OK;
+}
+
+//
+// IAccessibleHypertext methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!hyperlink_count)
+ return E_INVALIDARG;
+
+ *hyperlink_count = hyperlink_offset_to_index_.size();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
+ long index,
+ IAccessibleHyperlink** hyperlink) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!hyperlink ||
+ index < 0 ||
+ index >= static_cast<long>(hyperlinks_.size())) {
+ return E_INVALIDARG;
+ }
+
+ BrowserAccessibilityWin* child =
+ children_[hyperlinks_[index]]->ToBrowserAccessibilityWin();
+ *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex(
+ long char_index,
+ long* hyperlink_index) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!hyperlink_index)
+ return E_INVALIDARG;
+
+ *hyperlink_index = -1;
+
+ if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size()))
+ return E_INVALIDARG;
+
+ std::map<int32, int32>::iterator it =
+ hyperlink_offset_to_index_.find(char_index);
+ if (it == hyperlink_offset_to_index_.end())
+ return E_FAIL;
+
+ *hyperlink_index = it->second;
+ return S_OK;
+}
+
+//
+// IAccessibleValue methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!value)
+ return E_INVALIDARG;
+
+ float float_val;
+ if (GetFloatAttribute(
+ AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &float_val)) {
+ value->vt = VT_R8;
+ value->dblVal = float_val;
+ return S_OK;
+ }
+
+ value->vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!value)
+ return E_INVALIDARG;
+
+ float float_val;
+ if (GetFloatAttribute(AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE,
+ &float_val)) {
+ value->vt = VT_R8;
+ value->dblVal = float_val;
+ return S_OK;
+ }
+
+ value->vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!value)
+ return E_INVALIDARG;
+
+ float float_val;
+ if (GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
+ &float_val)) {
+ value->vt = VT_R8;
+ value->dblVal = float_val;
+ return S_OK;
+ }
+
+ value->vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) {
+ // TODO(dmazzoni): Implement this.
+ return E_NOTIMPL;
+}
+
+//
+// ISimpleDOMDocument methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!url)
+ return E_INVALIDARG;
+
+ return GetStringAttributeAsBstr(AccessibilityNodeData::ATTR_DOC_URL, url);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!title)
+ return E_INVALIDARG;
+
+ return GetStringAttributeAsBstr(AccessibilityNodeData::ATTR_DOC_TITLE, title);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!mime_type)
+ return E_INVALIDARG;
+
+ return GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_DOC_MIMETYPE, mime_type);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!doc_type)
+ return E_INVALIDARG;
+
+ return GetStringAttributeAsBstr(
+ AccessibilityNodeData::ATTR_DOC_DOCTYPE, doc_type);
+}
+
+//
+// ISimpleDOMNode methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
+ BSTR* node_name,
+ short* name_space_id,
+ BSTR* node_value,
+ unsigned int* num_children,
+ unsigned int* unique_id,
+ unsigned short* node_type) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!node_name || !name_space_id || !node_value || !num_children ||
+ !unique_id || !node_type) {
+ return E_INVALIDARG;
+ }
+
+ string16 tag;
+ if (GetStringAttribute(AccessibilityNodeData::ATTR_HTML_TAG, &tag))
+ *node_name = SysAllocString(tag.c_str());
+ else
+ *node_name = NULL;
+
+ *name_space_id = 0;
+ *node_value = SysAllocString(value_.c_str());
+ *num_children = children_.size();
+ *unique_id = unique_id_win_;
+
+ if (ia_role_ == ROLE_SYSTEM_DOCUMENT) {
+ *node_type = NODETYPE_DOCUMENT;
+ } else if (ia_role_ == ROLE_SYSTEM_TEXT &&
+ ((ia2_state_ & IA2_STATE_EDITABLE) == 0)) {
+ *node_type = NODETYPE_TEXT;
+ } else {
+ *node_type = NODETYPE_ELEMENT;
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_attributes(
+ unsigned short max_attribs,
+ BSTR* attrib_names,
+ short* name_space_id,
+ BSTR* attrib_values,
+ unsigned short* num_attribs) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
+ return E_INVALIDARG;
+
+ *num_attribs = max_attribs;
+ if (*num_attribs > html_attributes_.size())
+ *num_attribs = html_attributes_.size();
+
+ for (unsigned short i = 0; i < *num_attribs; ++i) {
+ attrib_names[i] = SysAllocString(html_attributes_[i].first.c_str());
+ name_space_id[i] = 0;
+ attrib_values[i] = SysAllocString(html_attributes_[i].second.c_str());
+ }
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
+ unsigned short num_attribs,
+ BSTR* attrib_names,
+ short* name_space_id,
+ BSTR* attrib_values) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!attrib_names || !name_space_id || !attrib_values)
+ return E_INVALIDARG;
+
+ for (unsigned short i = 0; i < num_attribs; ++i) {
+ name_space_id[i] = 0;
+ bool found = false;
+ string16 name = (LPCWSTR)attrib_names[i];
+ for (unsigned int j = 0; j < html_attributes_.size(); ++j) {
+ if (html_attributes_[j].first == name) {
+ attrib_values[i] = SysAllocString(html_attributes_[j].second.c_str());
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ attrib_values[i] = NULL;
+ }
+ }
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
+ unsigned short max_style_properties,
+ boolean use_alternate_view,
+ BSTR* style_properties,
+ BSTR* style_values,
+ unsigned short *num_style_properties) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!style_properties || !style_values)
+ return E_INVALIDARG;
+
+ // We only cache a single style property for now: DISPLAY
+
+ string16 display;
+ if (max_style_properties == 0 ||
+ !GetStringAttribute(AccessibilityNodeData::ATTR_DISPLAY, &display)) {
+ *num_style_properties = 0;
+ return S_OK;
+ }
+
+ *num_style_properties = 1;
+ style_properties[0] = SysAllocString(L"display");
+ style_values[0] = SysAllocString(display.c_str());
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
+ unsigned short num_style_properties,
+ boolean use_alternate_view,
+ BSTR* style_properties,
+ BSTR* style_values) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!style_properties || !style_values)
+ return E_INVALIDARG;
+
+ // We only cache a single style property for now: DISPLAY
+
+ for (unsigned short i = 0; i < num_style_properties; ++i) {
+ string16 name = (LPCWSTR)style_properties[i];
+ StringToLowerASCII(&name);
+ if (name == L"display") {
+ string16 display;
+ GetStringAttribute(AccessibilityNodeData::ATTR_DISPLAY, &display);
+ style_values[i] = SysAllocString(display.c_str());
+ } else {
+ style_values[i] = NULL;
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
+ return scrollTo(placeTopLeft ?
+ IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE);
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!node)
+ return E_INVALIDARG;
+
+ *node = parent_->ToBrowserAccessibilityWin()->NewReference();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!node)
+ return E_INVALIDARG;
+
+ if (children_.empty()) {
+ *node = NULL;
+ return S_FALSE;
+ }
+
+ *node = children_[0]->ToBrowserAccessibilityWin()->NewReference();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!node)
+ return E_INVALIDARG;
+
+ if (children_.empty()) {
+ *node = NULL;
+ return S_FALSE;
+ }
+
+ *node = (*children_.rbegin())->ToBrowserAccessibilityWin()->NewReference();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
+ ISimpleDOMNode** node) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!node)
+ return E_INVALIDARG;
+
+ if (!parent_ || index_in_parent_ <= 0) {
+ *node = NULL;
+ return S_FALSE;
+ }
+
+ *node = parent_->children()[index_in_parent_ - 1]->
+ ToBrowserAccessibilityWin()->NewReference();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!node)
+ return E_INVALIDARG;
+
+ if (!parent_ ||
+ index_in_parent_ < 0 ||
+ index_in_parent_ >= static_cast<int>(parent_->children().size()) - 1) {
+ *node = NULL;
+ return S_FALSE;
+ }
+
+ *node = parent_->children()[index_in_parent_ + 1]->
+ ToBrowserAccessibilityWin()->NewReference();
+ return S_OK;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::get_childAt(
+ unsigned int child_index,
+ ISimpleDOMNode** node) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!node)
+ return E_INVALIDARG;
+
+ if (child_index < children_.size()) {
+ *node = NULL;
+ return S_FALSE;
+ }
+
+ *node = children_[child_index]->ToBrowserAccessibilityWin()->NewReference();
+ return S_OK;
+}
+
+//
+// ISimpleDOMText methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (!dom_text)
+ return E_INVALIDARG;
+
+ if (name_.empty())
+ return S_FALSE;
+
+ *dom_text = SysAllocString(name_.c_str());
+ DCHECK(*dom_text);
+ return S_OK;
+}
+
+//
+// IServiceProvider methods.
+//
+
+STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService,
+ REFIID riid,
+ void** object) {
+ if (!instance_active_)
+ return E_FAIL;
+
+ if (guidService == GUID_IAccessibleContentDocument) {
+ // Special Mozilla extension: return the accessible for the root document.
+ // Screen readers use this to distinguish between a document loaded event
+ // on the root document vs on an iframe.
+ return manager_->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
+ IID_IAccessible2, object);
+ }
+
+ if (guidService == IID_IAccessible ||
+ guidService == IID_IAccessible2 ||
+ guidService == IID_IAccessibleAction ||
+ guidService == IID_IAccessibleApplication ||
+ guidService == IID_IAccessibleHyperlink ||
+ guidService == IID_IAccessibleHypertext ||
+ guidService == IID_IAccessibleImage ||
+ guidService == IID_IAccessibleTable ||
+ guidService == IID_IAccessibleTable2 ||
+ guidService == IID_IAccessibleTableCell ||
+ guidService == IID_IAccessibleText ||
+ guidService == IID_IAccessibleValue ||
+ guidService == IID_ISimpleDOMDocument ||
+ guidService == IID_ISimpleDOMNode ||
+ guidService == IID_ISimpleDOMText ||
+ guidService == GUID_ISimpleDOM) {
+ return QueryInterface(riid, object);
+ }
+
+ // We only support the IAccessibleEx interface on Windows 8 and above. This
+ // is needed for the on-screen Keyboard to show up in metro mode, when the
+ // user taps an editable portion on the page.
+ // All methods in the IAccessibleEx interface are unimplemented.
+ if (riid == IID_IAccessibleEx &&
+ base::win::GetVersion() >= base::win::VERSION_WIN8) {
+ return QueryInterface(riid, object);
+ }
+
+ *object = NULL;
+ return E_FAIL;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
+ IUnknown** provider) {
+ DVLOG(1) << "In Function: "
+ << __FUNCTION__
+ << " for pattern id: "
+ << id;
+ if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
+ if (IsEditableText()) {
+ // The BrowserAccessibilityManager keeps track of instances when
+ // we don't want to show the on-screen keyboard.
+ if (!manager_->IsOSKAllowed(GetGlobalBoundsRect()))
+ return E_NOTIMPL;
+
+ DVLOG(1) << "Returning UIA text provider";
+ base::win::UIATextProvider::CreateTextProvider(true, provider);
+ return S_OK;
+ }
+ }
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id,
+ VARIANT* ret) {
+ DVLOG(1) << "In Function: "
+ << __FUNCTION__
+ << " for property id: "
+ << id;
+ V_VT(ret) = VT_EMPTY;
+ if (id == UIA_ControlTypePropertyId) {
+ if (IsEditableText()) {
+ V_VT(ret) = VT_I4;
+ ret->lVal = UIA_EditControlTypeId;
+ DVLOG(1) << "Returning Edit control type";
+ } else {
+ DVLOG(1) << "Returning empty control type";
+ }
+ }
+ return S_OK;
+}
+
+//
+// CComObjectRootEx methods.
+//
+
+HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
+ void* this_ptr,
+ const _ATL_INTMAP_ENTRY* entries,
+ REFIID iid,
+ void** object) {
+ if (iid == IID_IAccessibleImage) {
+ if (ia_role_ != ROLE_SYSTEM_GRAPHIC) {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) {
+ if (ia_role_ != ROLE_SYSTEM_TABLE) {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ } else if (iid == IID_IAccessibleTableCell) {
+ if (ia_role_ != ROLE_SYSTEM_CELL) {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ } else if (iid == IID_IAccessibleValue) {
+ if (ia_role_ != ROLE_SYSTEM_PROGRESSBAR &&
+ ia_role_ != ROLE_SYSTEM_SCROLLBAR &&
+ ia_role_ != ROLE_SYSTEM_SLIDER) {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ } else if (iid == IID_ISimpleDOMDocument) {
+ if (ia_role_ != ROLE_SYSTEM_DOCUMENT) {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ }
+
+ return CComObjectRootBase::InternalQueryInterface(
+ this_ptr, entries, iid, object);
+}
+
+//
+// Private methods.
+//
+
+// Initialize this object and mark it as active.
+void BrowserAccessibilityWin::PreInitialize() {
+ BrowserAccessibility::PreInitialize();
+
+ InitRoleAndState();
+
+ // Expose the "display" and "tag" attributes.
+ StringAttributeToIA2(AccessibilityNodeData::ATTR_DISPLAY, "display");
+ StringAttributeToIA2(AccessibilityNodeData::ATTR_HTML_TAG, "tag");
+ StringAttributeToIA2(AccessibilityNodeData::ATTR_ROLE, "xml-roles");
+
+ // Expose "level" attribute for headings, trees, etc.
+ IntAttributeToIA2(AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, "level");
+
+ // Expose the set size and position in set for listbox options.
+ if (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION &&
+ parent_ &&
+ parent_->role() == AccessibilityNodeData::ROLE_LISTBOX) {
+ ia2_attributes_.push_back(
+ L"setsize:" + base::IntToString16(parent_->child_count()));
+ ia2_attributes_.push_back(
+ L"setsize:" + base::IntToString16(index_in_parent_ + 1));
+ }
+
+ if (ia_role_ == ROLE_SYSTEM_CHECKBUTTON ||
+ ia_role_ == ROLE_SYSTEM_RADIOBUTTON ||
+ ia2_role_ == IA2_ROLE_TOGGLE_BUTTON) {
+ ia2_attributes_.push_back(L"checkable:true");
+ }
+
+ // Expose live region attributes.
+ StringAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_STATUS, "live");
+ StringAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_RELEVANT, "relevant");
+ BoolAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_ATOMIC, "atomic");
+ BoolAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_BUSY, "busy");
+
+ // Expose container live region attributes.
+ StringAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS,
+ "container-live");
+ StringAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_RELEVANT,
+ "container-relevant");
+ BoolAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_ATOMIC,
+ "container-atomic");
+ BoolAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_BUSY,
+ "container-busy");
+
+ // Expose slider value.
+ if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR ||
+ ia_role_ == ROLE_SYSTEM_SCROLLBAR ||
+ ia_role_ == ROLE_SYSTEM_SLIDER) {
+ float fval;
+ if (value_.empty() &&
+ GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &fval)) {
+ // TODO(dmazzoni): Use ICU to localize this?
+ value_ = UTF8ToUTF16(base::DoubleToString(fval));
+ }
+ ia2_attributes_.push_back(L"valuetext:" + value_);
+ }
+
+ // Expose color well value.
+ if (ia2_role_ == IA2_ROLE_COLOR_CHOOSER) {
+ int r, g, b;
+ GetIntAttribute(AccessibilityNodeData::ATTR_COLOR_VALUE_RED, &r);
+ GetIntAttribute(AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN, &g);
+ GetIntAttribute(AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE, &b);
+ value_ = base::IntToString16((r * 100) / 255) + L"% red " +
+ base::IntToString16((g * 100) / 255) + L"% green " +
+ base::IntToString16((b * 100) / 255) + L"% blue";
+ }
+
+ // Expose table cell index.
+ if (ia_role_ == ROLE_SYSTEM_CELL) {
+ BrowserAccessibility* table = parent();
+ while (table && table->role() != AccessibilityNodeData::ROLE_TABLE)
+ table = table->parent();
+ if (table) {
+ const std::vector<int32>& unique_cell_ids = table->unique_cell_ids();
+ for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
+ if (unique_cell_ids[i] == renderer_id_) {
+ ia2_attributes_.push_back(
+ string16(L"table-cell-index:") + base::IntToString16(i));
+ }
+ }
+ }
+ }
+
+ // The calculation of the accessible name of an element has been
+ // standardized in the HTML to Platform Accessibility APIs Implementation
+ // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
+ // appropriate accessible name on Windows, we need to apply some logic
+ // to the fields we get from WebKit.
+ //
+ // TODO(dmazzoni): move most of this logic into WebKit.
+ //
+ // WebKit gives us:
+ //
+ // name: the default name, e.g. inner text
+ // title ui element: a reference to a <label> element on the same
+ // page that labels this node.
+ // description: accessible labels that override the default name:
+ // aria-label or aria-labelledby or aria-describedby
+ // help: the value of the "title" attribute
+ //
+ // On Windows, the logic we apply lets some fields take precedence and
+ // always returns the primary name in "name" and the secondary name,
+ // if any, in "description".
+
+ string16 description, help, title_attr;
+ int title_elem_id = 0;
+ GetIntAttribute(AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, &title_elem_id);
+ GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description);
+ GetStringAttribute(AccessibilityNodeData::ATTR_HELP, &help);
+
+ // WebKit annoyingly puts the title in the description if there's no other
+ // description, which just confuses the rest of the logic. Put it back.
+ // Now "help" is always the value of the "title" attribute, if present.
+ if (GetHtmlAttribute("title", &title_attr) &&
+ description == title_attr &&
+ help.empty()) {
+ help = description;
+ description.clear();
+ string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION].clear();
+ string_attributes_[AccessibilityNodeData::ATTR_HELP] = help;
+ }
+
+ // Now implement the main logic: the descripion should become the name if
+ // it's nonempty, and the help should become the description if
+ // there's no description - or the name if there's no name or description.
+ if (!description.empty()) {
+ name_ = description;
+ description.clear();
+ string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = description;
+ }
+ if (!help.empty() && description.empty()) {
+ description = help;
+ string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = help;
+ string_attributes_[AccessibilityNodeData::ATTR_HELP].clear();
+ }
+ if (!description.empty() && name_.empty() && !title_elem_id) {
+ name_ = description;
+ description.clear();
+ string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION].clear();
+ }
+
+ // If it's a text field, also consider the placeholder.
+ string16 placeholder;
+ if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD &&
+ HasState(AccessibilityNodeData::STATE_FOCUSABLE) &&
+ GetHtmlAttribute("placeholder", &placeholder)) {
+ if (name_.empty() && !title_elem_id) {
+ name_ = placeholder;
+ } else if (description.empty()) {
+ description = placeholder;
+ string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = description;
+ }
+ }
+
+ // On Windows, the value of a document should be its url.
+ if (role_ == AccessibilityNodeData::ROLE_ROOT_WEB_AREA ||
+ role_ == AccessibilityNodeData::ROLE_WEB_AREA) {
+ GetStringAttribute(AccessibilityNodeData::ATTR_DOC_URL, &value_);
+ }
+
+ // For certain roles (listbox option, static text, and list marker)
+ // WebKit stores the main accessible text in the "value" - swap it so
+ // that it's the "name".
+ if (name_.empty() &&
+ (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION ||
+ role_ == AccessibilityNodeData::ROLE_STATIC_TEXT ||
+ role_ == AccessibilityNodeData::ROLE_LIST_MARKER)) {
+ name_.swap(value_);
+ }
+
+ // If this doesn't have a value and is linked then set its value to the url
+ // attribute. This allows screen readers to read an empty link's destination.
+ string16 url;
+ if (value_.empty() && (ia_state_ & STATE_SYSTEM_LINKED))
+ GetStringAttribute(AccessibilityNodeData::ATTR_URL, &value_);
+
+ // Clear any old relationships between this node and other nodes.
+ for (size_t i = 0; i < relations_.size(); ++i)
+ relations_[i]->Release();
+ relations_.clear();
+
+ // Handle title UI element.
+ if (title_elem_id) {
+ // Add a labelled by relationship.
+ CComObject<BrowserAccessibilityRelation>* relation;
+ HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance(
+ &relation);
+ DCHECK(SUCCEEDED(hr));
+ relation->AddRef();
+ relation->Initialize(this, IA2_RELATION_LABELLED_BY);
+ relation->AddTarget(title_elem_id);
+ relations_.push_back(relation);
+ }
+}
+
+void BrowserAccessibilityWin::PostInitialize() {
+ BrowserAccessibility::PostInitialize();
+
+ // Construct the hypertext for this node.
+ hyperlink_offset_to_index_.clear();
+ hyperlinks_.clear();
+ hypertext_.clear();
+ for (unsigned int i = 0; i < children().size(); ++i) {
+ BrowserAccessibility* child = children()[i];
+ if (child->role() == AccessibilityNodeData::ROLE_STATIC_TEXT) {
+ hypertext_ += child->name();
+ } else {
+ hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size();
+ hypertext_ += kEmbeddedCharacter;
+ hyperlinks_.push_back(i);
+ }
+ }
+ DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size());
+
+ // Fire an event when an alert first appears.
+ if (role_ == AccessibilityNodeData::ROLE_ALERT && first_time_)
+ manager_->NotifyAccessibilityEvent(AccessibilityNotificationAlert, this);
+
+ // Fire events if text has changed.
+ string16 text = TextForIAccessibleText();
+ if (previous_text_ != text) {
+ if (!previous_text_.empty() && !text.empty()) {
+ manager_->NotifyAccessibilityEvent(
+ AccessibilityNotificationObjectShow, this);
+ }
+
+ // TODO(dmazzoni): Look into HIDE events, too.
+
+ old_text_ = previous_text_;
+ previous_text_ = text;
+ }
+
+ // Fire events if the state has changed.
+ if (!first_time_ && ia_state_ != old_ia_state_) {
+ BrowserAccessibilityManagerWin* manager =
+ manager_->ToBrowserAccessibilityManagerWin();
+
+ // Normally focus events are handled elsewhere, however
+ // focus for managed descendants is platform-specific.
+ // Fire a focus event if the focused descendant in a multi-select
+ // list box changes.
+ if (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION &&
+ (ia_state_ & STATE_SYSTEM_FOCUSABLE) &&
+ (ia_state_ & STATE_SYSTEM_SELECTABLE) &&
+ (ia_state_ & STATE_SYSTEM_FOCUSED) &&
+ !(old_ia_state_ & STATE_SYSTEM_FOCUSED)) {
+ manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS, unique_id_win());
+ }
+
+ if ((ia_state_ & STATE_SYSTEM_SELECTED) &&
+ !(old_ia_state_ & STATE_SYSTEM_SELECTED)) {
+ manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD,
+ unique_id_win());
+ } else if (!(ia_state_ & STATE_SYSTEM_SELECTED) &&
+ (old_ia_state_ & STATE_SYSTEM_SELECTED)) {
+ manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE,
+ unique_id_win());
+ }
+
+ old_ia_state_ = ia_state_;
+ }
+
+ first_time_ = false;
+}
+
+void BrowserAccessibilityWin::NativeAddReference() {
+ AddRef();
+}
+
+void BrowserAccessibilityWin::NativeReleaseReference() {
+ Release();
+}
+
+bool BrowserAccessibilityWin::IsNative() const {
+ return true;
+}
+
+void BrowserAccessibilityWin::SetLocation(const gfx::Rect& new_location) {
+ BrowserAccessibility::SetLocation(new_location);
+ manager_->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
+ EVENT_OBJECT_LOCATIONCHANGE, unique_id_win());
+}
+
+BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
+ AddRef();
+ return this;
+}
+
+BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
+ const VARIANT& var_id) {
+ if (var_id.vt != VT_I4)
+ return NULL;
+
+ LONG child_id = var_id.lVal;
+ if (child_id == CHILDID_SELF)
+ return this;
+
+ if (child_id >= 1 && child_id <= static_cast<LONG>(children_.size()))
+ return children_[child_id - 1]->ToBrowserAccessibilityWin();
+
+ return manager_->ToBrowserAccessibilityManagerWin()->
+ GetFromUniqueIdWin(child_id);
+}
+
+HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
+ AccessibilityNodeData::StringAttribute attribute,
+ BSTR* value_bstr) {
+ string16 str;
+
+ if (!GetStringAttribute(attribute, &str))
+ return S_FALSE;
+
+ if (str.empty())
+ return S_FALSE;
+
+ *value_bstr = SysAllocString(str.c_str());
+ DCHECK(*value_bstr);
+
+ return S_OK;
+}
+
+void BrowserAccessibilityWin::StringAttributeToIA2(
+ AccessibilityNodeData::StringAttribute attribute,
+ const char* ia2_attr) {
+ string16 value;
+ if (GetStringAttribute(attribute, &value))
+ ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" + value);
+}
+
+void BrowserAccessibilityWin::BoolAttributeToIA2(
+ AccessibilityNodeData::BoolAttribute attribute,
+ const char* ia2_attr) {
+ bool value;
+ if (GetBoolAttribute(attribute, &value)) {
+ ia2_attributes_.push_back((ASCIIToUTF16(ia2_attr) + L":") +
+ (value ? L"true" : L"false"));
+ }
+}
+
+void BrowserAccessibilityWin::IntAttributeToIA2(
+ AccessibilityNodeData::IntAttribute attribute,
+ const char* ia2_attr) {
+ int value;
+ if (GetIntAttribute(attribute, &value))
+ ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" +
+ base::IntToString16(value));
+}
+
+const string16& BrowserAccessibilityWin::TextForIAccessibleText() {
+ if (IsEditableText())
+ return value_;
+ return (role_ == AccessibilityNodeData::ROLE_STATIC_TEXT) ?
+ name_ : hypertext_;
+}
+
+void BrowserAccessibilityWin::HandleSpecialTextOffset(const string16& text,
+ LONG* offset) {
+ if (*offset == IA2_TEXT_OFFSET_LENGTH)
+ *offset = static_cast<LONG>(text.size());
+ else if (*offset == IA2_TEXT_OFFSET_CARET)
+ get_caretOffset(offset);
+}
+
+ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
+ IA2TextBoundaryType ia2_boundary) {
+ switch(ia2_boundary) {
+ case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY;
+ case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY;
+ case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY;
+ case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY;
+ case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY;
+ case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY;
+ default:
+ NOTREACHED();
+ return ui::CHAR_BOUNDARY;
+ }
+}
+
+LONG BrowserAccessibilityWin::FindBoundary(
+ const string16& text,
+ IA2TextBoundaryType ia2_boundary,
+ LONG start_offset,
+ ui::TextBoundaryDirection direction) {
+ HandleSpecialTextOffset(text, &start_offset);
+ ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
+ return ui::FindAccessibleTextBoundary(
+ text, line_breaks_, boundary, start_offset, direction);
+}
+
+BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromRendererID(
+ int32 renderer_id) {
+ return manager_->GetFromRendererID(renderer_id)->ToBrowserAccessibilityWin();
+}
+
+void BrowserAccessibilityWin::InitRoleAndState() {
+ ia_state_ = 0;
+ ia2_state_ = IA2_STATE_OPAQUE;
+ ia2_attributes_.clear();
+
+ if (HasState(AccessibilityNodeData::STATE_BUSY))
+ ia_state_ |= STATE_SYSTEM_BUSY;
+ if (HasState(AccessibilityNodeData::STATE_CHECKED))
+ ia_state_ |= STATE_SYSTEM_CHECKED;
+ if (HasState(AccessibilityNodeData::STATE_COLLAPSED))
+ ia_state_ |= STATE_SYSTEM_COLLAPSED;
+ if (HasState(AccessibilityNodeData::STATE_EXPANDED))
+ ia_state_ |= STATE_SYSTEM_EXPANDED;
+ if (HasState(AccessibilityNodeData::STATE_FOCUSABLE))
+ ia_state_ |= STATE_SYSTEM_FOCUSABLE;
+ if (HasState(AccessibilityNodeData::STATE_HASPOPUP))
+ ia_state_ |= STATE_SYSTEM_HASPOPUP;
+ if (HasState(AccessibilityNodeData::STATE_HOTTRACKED))
+ ia_state_ |= STATE_SYSTEM_HOTTRACKED;
+ if (HasState(AccessibilityNodeData::STATE_INDETERMINATE))
+ ia_state_ |= STATE_SYSTEM_INDETERMINATE;
+ if (HasState(AccessibilityNodeData::STATE_INVISIBLE))
+ ia_state_ |= STATE_SYSTEM_INVISIBLE;
+ if (HasState(AccessibilityNodeData::STATE_LINKED))
+ ia_state_ |= STATE_SYSTEM_LINKED;
+ if (HasState(AccessibilityNodeData::STATE_MULTISELECTABLE)) {
+ ia_state_ |= STATE_SYSTEM_EXTSELECTABLE;
+ ia_state_ |= STATE_SYSTEM_MULTISELECTABLE;
+ }
+ // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
+ if (HasState(AccessibilityNodeData::STATE_OFFSCREEN))
+ ia_state_ |= STATE_SYSTEM_OFFSCREEN;
+ if (HasState(AccessibilityNodeData::STATE_PRESSED))
+ ia_state_ |= STATE_SYSTEM_PRESSED;
+ if (HasState(AccessibilityNodeData::STATE_PROTECTED))
+ ia_state_ |= STATE_SYSTEM_PROTECTED;
+ if (HasState(AccessibilityNodeData::STATE_REQUIRED))
+ ia2_state_ |= IA2_STATE_REQUIRED;
+ if (HasState(AccessibilityNodeData::STATE_SELECTABLE))
+ ia_state_ |= STATE_SYSTEM_SELECTABLE;
+ if (HasState(AccessibilityNodeData::STATE_SELECTED))
+ ia_state_ |= STATE_SYSTEM_SELECTED;
+ if (HasState(AccessibilityNodeData::STATE_TRAVERSED))
+ ia_state_ |= STATE_SYSTEM_TRAVERSED;
+ if (HasState(AccessibilityNodeData::STATE_UNAVAILABLE))
+ ia_state_ |= STATE_SYSTEM_UNAVAILABLE;
+ if (HasState(AccessibilityNodeData::STATE_VERTICAL)) {
+ ia2_state_ |= IA2_STATE_VERTICAL;
+ } else {
+ ia2_state_ |= IA2_STATE_HORIZONTAL;
+ }
+ if (HasState(AccessibilityNodeData::STATE_VISITED))
+ ia_state_ |= STATE_SYSTEM_TRAVERSED;
+
+ // WebKit marks everything as readonly unless it's editable text, so if it's
+ // not readonly, mark it as editable now. The final computation of the
+ // READONLY state for MSAA is below, after the switch.
+ if (!HasState(AccessibilityNodeData::STATE_READONLY))
+ ia2_state_ |= IA2_STATE_EDITABLE;
+
+ string16 invalid;
+ if (GetHtmlAttribute("aria-invalid", &invalid))
+ ia2_state_ |= IA2_STATE_INVALID_ENTRY;
+
+ bool mixed = false;
+ GetBoolAttribute(AccessibilityNodeData::ATTR_BUTTON_MIXED, &mixed);
+ if (mixed)
+ ia_state_ |= STATE_SYSTEM_MIXED;
+
+ bool editable = false;
+ GetBoolAttribute(AccessibilityNodeData::ATTR_CAN_SET_VALUE, &editable);
+ if (editable)
+ ia2_state_ |= IA2_STATE_EDITABLE;
+
+ string16 html_tag;
+ GetStringAttribute(AccessibilityNodeData::ATTR_HTML_TAG, &html_tag);
+ ia_role_ = 0;
+ ia2_role_ = 0;
+ switch (role_) {
+ case AccessibilityNodeData::ROLE_ALERT:
+ ia_role_ = ROLE_SYSTEM_ALERT;
+ break;
+ case AccessibilityNodeData::ROLE_ALERT_DIALOG:
+ ia_role_ = ROLE_SYSTEM_DIALOG;
+ break;
+ case AccessibilityNodeData::ROLE_APPLICATION:
+ ia_role_ = ROLE_SYSTEM_APPLICATION;
+ break;
+ case AccessibilityNodeData::ROLE_ARTICLE:
+ ia_role_ = ROLE_SYSTEM_GROUPING;
+ ia2_role_ = IA2_ROLE_SECTION;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_BUSY_INDICATOR:
+ ia_role_ = ROLE_SYSTEM_ANIMATION;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_BUTTON:
+ ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
+ bool is_aria_pressed_defined;
+ bool is_mixed;
+ if (GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed))
+ ia_state_ |= STATE_SYSTEM_PRESSED;
+ if (is_aria_pressed_defined)
+ ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
+ if (is_mixed)
+ ia_state_ |= STATE_SYSTEM_MIXED;
+ break;
+ case AccessibilityNodeData::ROLE_CANVAS:
+ ia_role_ = ROLE_SYSTEM_GRAPHIC;
+ break;
+ case AccessibilityNodeData::ROLE_CANVAS_WITH_FALLBACK_CONTENT:
+ role_name_ = L"canvas";
+ ia2_role_ = IA2_ROLE_CANVAS;
+ break;
+ case AccessibilityNodeData::ROLE_CELL:
+ ia_role_ = ROLE_SYSTEM_CELL;
+ break;
+ case AccessibilityNodeData::ROLE_CHECKBOX:
+ ia_role_ = ROLE_SYSTEM_CHECKBUTTON;
+ break;
+ case AccessibilityNodeData::ROLE_COLOR_WELL:
+ ia_role_ = ROLE_SYSTEM_CLIENT;
+ ia2_role_ = IA2_ROLE_COLOR_CHOOSER;
+ break;
+ case AccessibilityNodeData::ROLE_COLUMN:
+ ia_role_ = ROLE_SYSTEM_COLUMN;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_COLUMN_HEADER:
+ ia_role_ = ROLE_SYSTEM_COLUMNHEADER;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_COMBO_BOX:
+ ia_role_ = ROLE_SYSTEM_COMBOBOX;
+ break;
+ case AccessibilityNodeData::ROLE_DIV:
+ role_name_ = L"div";
+ ia2_role_ = IA2_ROLE_SECTION;
+ break;
+ case AccessibilityNodeData::ROLE_DEFINITION:
+ role_name_ = html_tag;
+ ia2_role_ = IA2_ROLE_PARAGRAPH;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_DESCRIPTION_LIST_DETAIL:
+ role_name_ = html_tag;
+ ia2_role_ = IA2_ROLE_PARAGRAPH;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_DESCRIPTION_LIST_TERM:
+ ia_role_ = ROLE_SYSTEM_LISTITEM;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_DIALOG:
+ ia_role_ = ROLE_SYSTEM_DIALOG;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE:
+ ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_DOCUMENT:
+ case AccessibilityNodeData::ROLE_ROOT_WEB_AREA:
+ case AccessibilityNodeData::ROLE_WEB_AREA:
+ ia_role_ = ROLE_SYSTEM_DOCUMENT;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ ia_state_ |= STATE_SYSTEM_FOCUSABLE;
+ break;
+ case AccessibilityNodeData::ROLE_EDITABLE_TEXT:
+ ia_role_ = ROLE_SYSTEM_TEXT;
+ ia2_state_ |= IA2_STATE_SINGLE_LINE;
+ ia2_state_ |= IA2_STATE_EDITABLE;
+ break;
+ case AccessibilityNodeData::ROLE_FORM:
+ role_name_ = L"form";
+ ia2_role_ = IA2_ROLE_FORM;
+ break;
+ case AccessibilityNodeData::ROLE_FOOTER:
+ ia_role_ = IA2_ROLE_FOOTER;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_GRID:
+ ia_role_ = ROLE_SYSTEM_TABLE;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_GROUP: {
+ string16 aria_role;
+ GetStringAttribute(AccessibilityNodeData::ATTR_ROLE, &aria_role);
+ if (aria_role == L"group" || html_tag == L"fieldset") {
+ ia_role_ = ROLE_SYSTEM_GROUPING;
+ } else if (html_tag == L"li") {
+ ia_role_ = ROLE_SYSTEM_LISTITEM;
+ } else {
+ if (html_tag.empty())
+ role_name_ = L"div";
+ else
+ role_name_ = html_tag;
+ ia2_role_ = IA2_ROLE_SECTION;
+ }
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ }
+ case AccessibilityNodeData::ROLE_GROW_AREA:
+ ia_role_ = ROLE_SYSTEM_GRIP;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_HEADING:
+ role_name_ = html_tag;
+ ia2_role_ = IA2_ROLE_HEADING;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_HORIZONTAL_RULE:
+ ia_role_ = ROLE_SYSTEM_SEPARATOR;
+ break;
+ case AccessibilityNodeData::ROLE_IMAGE:
+ ia_role_ = ROLE_SYSTEM_GRAPHIC;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_IMAGE_MAP:
+ role_name_ = html_tag;
+ ia2_role_ = IA2_ROLE_IMAGE_MAP;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_IMAGE_MAP_LINK:
+ ia_role_ = ROLE_SYSTEM_LINK;
+ ia_state_ |= STATE_SYSTEM_LINKED;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_LABEL:
+ ia_role_ = ROLE_SYSTEM_TEXT;
+ ia2_role_ = IA2_ROLE_LABEL;
+ break;
+ case AccessibilityNodeData::ROLE_LANDMARK_APPLICATION:
+ case AccessibilityNodeData::ROLE_LANDMARK_BANNER:
+ case AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY:
+ case AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO:
+ case AccessibilityNodeData::ROLE_LANDMARK_MAIN:
+ case AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION:
+ case AccessibilityNodeData::ROLE_LANDMARK_SEARCH:
+ ia_role_ = ROLE_SYSTEM_GROUPING;
+ ia2_role_ = IA2_ROLE_SECTION;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_LINK:
+ case AccessibilityNodeData::ROLE_WEBCORE_LINK:
+ ia_role_ = ROLE_SYSTEM_LINK;
+ ia_state_ |= STATE_SYSTEM_LINKED;
+ break;
+ case AccessibilityNodeData::ROLE_LIST:
+ ia_role_ = ROLE_SYSTEM_LIST;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_LISTBOX:
+ ia_role_ = ROLE_SYSTEM_LIST;
+ break;
+ case AccessibilityNodeData::ROLE_LISTBOX_OPTION:
+ ia_role_ = ROLE_SYSTEM_LISTITEM;
+ if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
+ ia_state_ |= STATE_SYSTEM_FOCUSABLE;
+ if (HasState(AccessibilityNodeData::STATE_FOCUSED))
+ ia_state_ |= STATE_SYSTEM_FOCUSED;
+ }
+ break;
+ case AccessibilityNodeData::ROLE_LIST_ITEM:
+ ia_role_ = ROLE_SYSTEM_LISTITEM;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_LIST_MARKER:
+ ia_role_ = ROLE_SYSTEM_TEXT;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_MATH:
+ ia_role_ = ROLE_SYSTEM_EQUATION;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_MENU:
+ case AccessibilityNodeData::ROLE_MENU_BUTTON:
+ ia_role_ = ROLE_SYSTEM_MENUPOPUP;
+ break;
+ case AccessibilityNodeData::ROLE_MENU_BAR:
+ ia_role_ = ROLE_SYSTEM_MENUBAR;
+ break;
+ case AccessibilityNodeData::ROLE_MENU_ITEM:
+ ia_role_ = ROLE_SYSTEM_MENUITEM;
+ break;
+ case AccessibilityNodeData::ROLE_MENU_LIST_POPUP:
+ ia_role_ = ROLE_SYSTEM_CLIENT;
+ break;
+ case AccessibilityNodeData::ROLE_MENU_LIST_OPTION:
+ ia_role_ = ROLE_SYSTEM_LISTITEM;
+ if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
+ ia_state_ |= STATE_SYSTEM_FOCUSABLE;
+ if (HasState(AccessibilityNodeData::STATE_FOCUSED))
+ ia_state_ |= STATE_SYSTEM_FOCUSED;
+ }
+ break;
+ case AccessibilityNodeData::ROLE_NOTE:
+ ia_role_ = ROLE_SYSTEM_GROUPING;
+ ia2_role_ = IA2_ROLE_NOTE;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_OUTLINE:
+ ia_role_ = ROLE_SYSTEM_OUTLINE;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_PARAGRAPH:
+ role_name_ = L"P";
+ ia2_role_ = IA2_ROLE_PARAGRAPH;
+ break;
+ case AccessibilityNodeData::ROLE_POPUP_BUTTON:
+ if (html_tag == L"select") {
+ ia_role_ = ROLE_SYSTEM_COMBOBOX;
+ } else {
+ ia_role_ = ROLE_SYSTEM_BUTTONMENU;
+ }
+ break;
+ case AccessibilityNodeData::ROLE_PROGRESS_INDICATOR:
+ ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_RADIO_BUTTON:
+ ia_role_ = ROLE_SYSTEM_RADIOBUTTON;
+ break;
+ case AccessibilityNodeData::ROLE_RADIO_GROUP:
+ ia_role_ = ROLE_SYSTEM_GROUPING;
+ ia2_role_ = IA2_ROLE_SECTION;
+ break;
+ case AccessibilityNodeData::ROLE_REGION:
+ ia_role_ = ROLE_SYSTEM_GROUPING;
+ ia2_role_ = IA2_ROLE_SECTION;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_ROW:
+ ia_role_ = ROLE_SYSTEM_ROW;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_ROW_HEADER:
+ ia_role_ = ROLE_SYSTEM_ROWHEADER;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_RULER:
+ ia_role_ = ROLE_SYSTEM_CLIENT;
+ ia2_role_ = IA2_ROLE_RULER;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_SCROLLAREA:
+ ia_role_ = ROLE_SYSTEM_CLIENT;
+ ia2_role_ = IA2_ROLE_SCROLL_PANE;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_SCROLLBAR:
+ ia_role_ = ROLE_SYSTEM_SCROLLBAR;
+ break;
+ case AccessibilityNodeData::ROLE_SLIDER:
+ ia_role_ = ROLE_SYSTEM_SLIDER;
+ break;
+ case AccessibilityNodeData::ROLE_SPIN_BUTTON:
+ ia_role_ = ROLE_SYSTEM_SPINBUTTON;
+ break;
+ case AccessibilityNodeData::ROLE_SPIN_BUTTON_PART:
+ ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
+ break;
+ case AccessibilityNodeData::ROLE_SPLIT_GROUP:
+ ia_role_ = ROLE_SYSTEM_CLIENT;
+ ia2_role_ = IA2_ROLE_SPLIT_PANE;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_ANNOTATION:
+ case AccessibilityNodeData::ROLE_STATIC_TEXT:
+ ia_role_ = ROLE_SYSTEM_TEXT;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_STATUS:
+ ia_role_ = ROLE_SYSTEM_STATUSBAR;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_SPLITTER:
+ ia_role_ = ROLE_SYSTEM_SEPARATOR;
+ break;
+ case AccessibilityNodeData::ROLE_SVG_ROOT:
+ ia_role_ = ROLE_SYSTEM_GRAPHIC;
+ break;
+ case AccessibilityNodeData::ROLE_TAB:
+ ia_role_ = ROLE_SYSTEM_PAGETAB;
+ break;
+ case AccessibilityNodeData::ROLE_TABLE: {
+ string16 aria_role;
+ GetStringAttribute(AccessibilityNodeData::ATTR_ROLE, &aria_role);
+ if (aria_role == L"treegrid") {
+ ia_role_ = ROLE_SYSTEM_OUTLINE;
+ } else {
+ ia_role_ = ROLE_SYSTEM_TABLE;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ }
+ break;
+ }
+ case AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER:
+ ia_role_ = ROLE_SYSTEM_GROUPING;
+ ia2_role_ = IA2_ROLE_SECTION;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED:
+ NOTREACHED();
+ ia_role_ = ROLE_SYSTEM_PAGETABLIST;
+ break;
+ case AccessibilityNodeData::ROLE_TAB_LIST:
+ ia_role_ = ROLE_SYSTEM_PAGETABLIST;
+ break;
+ case AccessibilityNodeData::ROLE_TAB_PANEL:
+ ia_role_ = ROLE_SYSTEM_PROPERTYPAGE;
+ break;
+ case AccessibilityNodeData::ROLE_TOGGLE_BUTTON:
+ ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
+ ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
+ break;
+ case AccessibilityNodeData::ROLE_TEXTAREA:
+ ia_role_ = ROLE_SYSTEM_TEXT;
+ ia2_state_ |= IA2_STATE_MULTI_LINE;
+ ia2_state_ |= IA2_STATE_EDITABLE;
+ ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
+ break;
+ case AccessibilityNodeData::ROLE_TEXT_FIELD:
+ ia_role_ = ROLE_SYSTEM_TEXT;
+ ia2_state_ |= IA2_STATE_SINGLE_LINE;
+ ia2_state_ |= IA2_STATE_EDITABLE;
+ ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
+ break;
+ case AccessibilityNodeData::ROLE_TIMER:
+ ia_role_ = ROLE_SYSTEM_CLOCK;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_TOOLBAR:
+ ia_role_ = ROLE_SYSTEM_TOOLBAR;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_TOOLTIP:
+ ia_role_ = ROLE_SYSTEM_TOOLTIP;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_TREE:
+ ia_role_ = ROLE_SYSTEM_OUTLINE;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_TREE_GRID:
+ ia_role_ = ROLE_SYSTEM_OUTLINE;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_TREE_ITEM:
+ ia_role_ = ROLE_SYSTEM_OUTLINEITEM;
+ ia_state_ |= STATE_SYSTEM_READONLY;
+ break;
+ case AccessibilityNodeData::ROLE_WINDOW:
+ ia_role_ = ROLE_SYSTEM_WINDOW;
+ break;
+
+ // TODO(dmazzoni): figure out the proper MSAA role for all of these.
+ case AccessibilityNodeData::ROLE_BROWSER:
+ case AccessibilityNodeData::ROLE_DIRECTORY:
+ case AccessibilityNodeData::ROLE_DRAWER:
+ case AccessibilityNodeData::ROLE_HELP_TAG:
+ case AccessibilityNodeData::ROLE_IGNORED:
+ case AccessibilityNodeData::ROLE_INCREMENTOR:
+ case AccessibilityNodeData::ROLE_LOG:
+ case AccessibilityNodeData::ROLE_MARQUEE:
+ case AccessibilityNodeData::ROLE_MATTE:
+ case AccessibilityNodeData::ROLE_PRESENTATIONAL:
+ case AccessibilityNodeData::ROLE_RULER_MARKER:
+ case AccessibilityNodeData::ROLE_SHEET:
+ case AccessibilityNodeData::ROLE_SLIDER_THUMB:
+ case AccessibilityNodeData::ROLE_SYSTEM_WIDE:
+ case AccessibilityNodeData::ROLE_VALUE_INDICATOR:
+ default:
+ ia_role_ = ROLE_SYSTEM_CLIENT;
+ break;
+ }
+
+ // Compute the final value of READONLY for MSAA.
+ //
+ // We always set the READONLY state for elements that have the
+ // aria-readonly attribute and for a few roles (in the switch above).
+ // We clear the READONLY state on focusable controls and on a document.
+ // Everything else, the majority of objects, do not have this state set.
+ if (HasState(AccessibilityNodeData::STATE_FOCUSABLE) &&
+ ia_role_ != ROLE_SYSTEM_DOCUMENT) {
+ ia_state_ &= ~(STATE_SYSTEM_READONLY);
+ }
+ if (!HasState(AccessibilityNodeData::STATE_READONLY))
+ ia_state_ &= ~(STATE_SYSTEM_READONLY);
+ bool aria_readonly = false;
+ GetBoolAttribute(AccessibilityNodeData::ATTR_ARIA_READONLY, &aria_readonly);
+ if (aria_readonly)
+ ia_state_ |= STATE_SYSTEM_READONLY;
+
+ // The role should always be set.
+ DCHECK(!role_name_.empty() || ia_role_);
+
+ // If we didn't explicitly set the IAccessible2 role, make it the same
+ // as the MSAA role.
+ if (!ia2_role_)
+ ia2_role_ = ia_role_;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_win.h b/chromium/content/browser/accessibility/browser_accessibility_win.h
new file mode 100644
index 00000000000..b6599325da8
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_win.h
@@ -0,0 +1,899 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_WIN_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_WIN_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <oleacc.h>
+#include <UIAutomationCore.h>
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+#include "content/common/content_export.h"
+#include "third_party/iaccessible2/ia2_api_all.h"
+#include "third_party/isimpledom/ISimpleDOMDocument.h"
+#include "third_party/isimpledom/ISimpleDOMNode.h"
+#include "third_party/isimpledom/ISimpleDOMText.h"
+
+namespace ui {
+enum TextBoundaryDirection;
+enum TextBoundaryType;
+}
+
+namespace content {
+class BrowserAccessibilityRelation;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// BrowserAccessibilityWin
+//
+// Class implementing the windows accessible interface for the Browser-Renderer
+// communication of accessibility information, providing accessibility
+// to be used by screen readers and other assistive technology (AT).
+//
+////////////////////////////////////////////////////////////////////////////////
+class __declspec(uuid("562072fe-3390-43b1-9e2c-dd4118f5ac79"))
+BrowserAccessibilityWin
+ : public BrowserAccessibility,
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public IDispatchImpl<IAccessible2, &IID_IAccessible2,
+ &LIBID_IAccessible2Lib>,
+ public IAccessibleApplication,
+ public IAccessibleHyperlink,
+ public IAccessibleHypertext,
+ public IAccessibleImage,
+ public IAccessibleTable,
+ public IAccessibleTable2,
+ public IAccessibleTableCell,
+ public IAccessibleValue,
+ public IServiceProvider,
+ public ISimpleDOMDocument,
+ public ISimpleDOMNode,
+ public ISimpleDOMText,
+ public IAccessibleEx,
+ public IRawElementProviderSimple {
+ public:
+ BEGIN_COM_MAP(BrowserAccessibilityWin)
+ COM_INTERFACE_ENTRY2(IDispatch, IAccessible2)
+ COM_INTERFACE_ENTRY2(IAccessible, IAccessible2)
+ COM_INTERFACE_ENTRY2(IAccessibleText, IAccessibleHypertext)
+ COM_INTERFACE_ENTRY(IAccessible2)
+ COM_INTERFACE_ENTRY(IAccessibleApplication)
+ COM_INTERFACE_ENTRY(IAccessibleHyperlink)
+ COM_INTERFACE_ENTRY(IAccessibleHypertext)
+ COM_INTERFACE_ENTRY(IAccessibleImage)
+ COM_INTERFACE_ENTRY(IAccessibleTable)
+ COM_INTERFACE_ENTRY(IAccessibleTable2)
+ COM_INTERFACE_ENTRY(IAccessibleTableCell)
+ COM_INTERFACE_ENTRY(IAccessibleValue)
+ COM_INTERFACE_ENTRY(IServiceProvider)
+ COM_INTERFACE_ENTRY(ISimpleDOMDocument)
+ COM_INTERFACE_ENTRY(ISimpleDOMNode)
+ COM_INTERFACE_ENTRY(ISimpleDOMText)
+ COM_INTERFACE_ENTRY(IAccessibleEx)
+ COM_INTERFACE_ENTRY(IRawElementProviderSimple)
+ END_COM_MAP()
+
+ // Represents a non-static text node in IAccessibleHypertext. This character
+ // is embedded in the response to IAccessibleText::get_text, indicating the
+ // position where a non-static text child object appears.
+ CONTENT_EXPORT static const char16 kEmbeddedCharacter[];
+
+ // Mappings from roles and states to human readable strings. Initialize
+ // with |InitializeStringMaps|.
+ static std::map<int32, string16> role_string_map;
+ static std::map<int32, string16> state_string_map;
+
+ CONTENT_EXPORT BrowserAccessibilityWin();
+
+ CONTENT_EXPORT virtual ~BrowserAccessibilityWin();
+
+ // The Windows-specific unique ID, used as the child ID for MSAA methods
+ // like NotifyWinEvent, and as the unique ID for IAccessible2 and ISimpleDOM.
+ LONG unique_id_win() const { return unique_id_win_; }
+
+ //
+ // BrowserAccessibility methods.
+ //
+ CONTENT_EXPORT virtual void PreInitialize() OVERRIDE;
+ CONTENT_EXPORT virtual void PostInitialize() OVERRIDE;
+ CONTENT_EXPORT virtual void NativeAddReference() OVERRIDE;
+ CONTENT_EXPORT virtual void NativeReleaseReference() OVERRIDE;
+ CONTENT_EXPORT virtual bool IsNative() const OVERRIDE;
+ CONTENT_EXPORT virtual void SetLocation(const gfx::Rect& new_location)
+ OVERRIDE;
+
+ //
+ // IAccessible methods.
+ //
+
+ // Performs the default action on a given object.
+ CONTENT_EXPORT STDMETHODIMP accDoDefaultAction(VARIANT var_id);
+
+ // Retrieves the child element or child object at a given point on the screen.
+ CONTENT_EXPORT STDMETHODIMP accHitTest(LONG x_left, LONG y_top,
+ VARIANT* child);
+
+ // Retrieves the specified object's current screen location.
+ CONTENT_EXPORT STDMETHODIMP accLocation(LONG* x_left,
+ LONG* y_top,
+ LONG* width,
+ LONG* height,
+ VARIANT var_id);
+
+ // Traverses to another UI element and retrieves the object.
+ CONTENT_EXPORT STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start,
+ VARIANT* end);
+
+ // Retrieves an IDispatch interface pointer for the specified child.
+ CONTENT_EXPORT STDMETHODIMP get_accChild(VARIANT var_child,
+ IDispatch** disp_child);
+
+ // Retrieves the number of accessible children.
+ CONTENT_EXPORT STDMETHODIMP get_accChildCount(LONG* child_count);
+
+ // Retrieves a string that describes the object's default action.
+ CONTENT_EXPORT STDMETHODIMP get_accDefaultAction(VARIANT var_id,
+ BSTR* default_action);
+
+ // Retrieves the object's description.
+ CONTENT_EXPORT STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc);
+
+ // Retrieves the object that has the keyboard focus.
+ CONTENT_EXPORT STDMETHODIMP get_accFocus(VARIANT* focus_child);
+
+ // Retrieves the help information associated with the object.
+ CONTENT_EXPORT STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* heflp);
+
+ // Retrieves the specified object's shortcut.
+ CONTENT_EXPORT STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id,
+ BSTR* access_key);
+
+ // Retrieves the name of the specified object.
+ CONTENT_EXPORT STDMETHODIMP get_accName(VARIANT var_id, BSTR* name);
+
+ // Retrieves the IDispatch interface of the object's parent.
+ CONTENT_EXPORT STDMETHODIMP get_accParent(IDispatch** disp_parent);
+
+ // Retrieves information describing the role of the specified object.
+ CONTENT_EXPORT STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role);
+
+ // Retrieves the current state of the specified object.
+ CONTENT_EXPORT STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state);
+
+ // Returns the value associated with the object.
+ CONTENT_EXPORT STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value);
+
+ // Make an object take focus or extend the selection.
+ CONTENT_EXPORT STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id);
+
+ CONTENT_EXPORT STDMETHODIMP get_accHelpTopic(BSTR* help_file,
+ VARIANT var_id,
+ LONG* topic_id);
+
+ CONTENT_EXPORT STDMETHODIMP get_accSelection(VARIANT* selected);
+
+ // Deprecated methods, not implemented.
+ CONTENT_EXPORT STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP put_accValue(VARIANT var_id, BSTR put_val) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // IAccessible2 methods.
+ //
+
+ // Returns role from a longer list of possible roles.
+ CONTENT_EXPORT STDMETHODIMP role(LONG* role);
+
+ // Returns the state bitmask from a larger set of possible states.
+ CONTENT_EXPORT STDMETHODIMP get_states(AccessibleStates* states);
+
+ // Returns the attributes specific to this IAccessible2 object,
+ // such as a cell's formula.
+ CONTENT_EXPORT STDMETHODIMP get_attributes(BSTR* attributes);
+
+ // Get the unique ID of this object so that the client knows if it's
+ // been encountered previously.
+ CONTENT_EXPORT STDMETHODIMP get_uniqueID(LONG* unique_id);
+
+ // Get the window handle of the enclosing window.
+ CONTENT_EXPORT STDMETHODIMP get_windowHandle(HWND* window_handle);
+
+ // Get this object's index in its parent object.
+ CONTENT_EXPORT STDMETHODIMP get_indexInParent(LONG* index_in_parent);
+
+ CONTENT_EXPORT STDMETHODIMP get_nRelations(LONG* n_relations);
+
+ CONTENT_EXPORT STDMETHODIMP get_relation(LONG relation_index,
+ IAccessibleRelation** relation);
+
+ CONTENT_EXPORT STDMETHODIMP get_relations(LONG max_relations,
+ IAccessibleRelation** relations,
+ LONG* n_relations);
+
+ CONTENT_EXPORT STDMETHODIMP scrollTo(enum IA2ScrollType scroll_type);
+
+ CONTENT_EXPORT STDMETHODIMP scrollToPoint(
+ enum IA2CoordinateType coordinate_type,
+ LONG x,
+ LONG y);
+
+ CONTENT_EXPORT STDMETHODIMP get_groupPosition(LONG* group_level,
+ LONG* similar_items_in_group,
+ LONG* position_in_group);
+
+ //
+ // IAccessibleEx methods not implemented.
+ //
+ CONTENT_EXPORT STDMETHODIMP get_extendedRole(BSTR* extended_role) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_localizedExtendedRole(
+ BSTR* localized_extended_role) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_nExtendedStates(LONG* n_extended_states) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_extendedStates(LONG max_extended_states,
+ BSTR** extended_states,
+ LONG* n_extended_states) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_localizedExtendedStates(
+ LONG max_localized_extended_states,
+ BSTR** localized_extended_states,
+ LONG* n_localized_extended_states) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_locale(IA2Locale* locale) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // IAccessibleApplication methods.
+ //
+ CONTENT_EXPORT STDMETHODIMP get_appName(BSTR* app_name);
+
+ CONTENT_EXPORT STDMETHODIMP get_appVersion(BSTR* app_version);
+
+ CONTENT_EXPORT STDMETHODIMP get_toolkitName(BSTR* toolkit_name);
+
+ CONTENT_EXPORT STDMETHODIMP get_toolkitVersion(BSTR* toolkit_version);
+
+ //
+ // IAccessibleImage methods.
+ //
+ CONTENT_EXPORT STDMETHODIMP get_description(BSTR* description);
+
+ CONTENT_EXPORT STDMETHODIMP get_imagePosition(
+ enum IA2CoordinateType coordinate_type,
+ LONG* x,
+ LONG* y);
+
+ CONTENT_EXPORT STDMETHODIMP get_imageSize(LONG* height, LONG* width);
+
+ //
+ // IAccessibleTable methods.
+ //
+
+ // get_description - also used by IAccessibleImage
+
+ CONTENT_EXPORT STDMETHODIMP get_accessibleAt(long row,
+ long column,
+ IUnknown** accessible);
+
+ CONTENT_EXPORT STDMETHODIMP get_caption(IUnknown** accessible);
+
+ CONTENT_EXPORT STDMETHODIMP get_childIndex(long row_index,
+ long column_index,
+ long* cell_index);
+
+ CONTENT_EXPORT STDMETHODIMP get_columnDescription(long column,
+ BSTR* description);
+
+ CONTENT_EXPORT STDMETHODIMP get_columnExtentAt(long row,
+ long column,
+ long* n_columns_spanned);
+
+ CONTENT_EXPORT STDMETHODIMP get_columnHeader(
+ IAccessibleTable** accessible_table,
+ long* starting_row_index);
+
+ CONTENT_EXPORT STDMETHODIMP get_columnIndex(long cell_index,
+ long* column_index);
+
+ CONTENT_EXPORT STDMETHODIMP get_nColumns(long* column_count);
+
+ CONTENT_EXPORT STDMETHODIMP get_nRows(long* row_count);
+
+ CONTENT_EXPORT STDMETHODIMP get_nSelectedChildren(long* cell_count);
+
+ CONTENT_EXPORT STDMETHODIMP get_nSelectedColumns(long* column_count);
+
+ CONTENT_EXPORT STDMETHODIMP get_nSelectedRows(long *row_count);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowDescription(long row,
+ BSTR* description);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowExtentAt(long row,
+ long column,
+ long* n_rows_spanned);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowHeader(IAccessibleTable** accessible_table,
+ long* starting_column_index);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowIndex(long cell_index,
+ long* row_index);
+
+ CONTENT_EXPORT STDMETHODIMP get_selectedChildren(long max_children,
+ long** children,
+ long* n_children);
+
+ CONTENT_EXPORT STDMETHODIMP get_selectedColumns(long max_columns,
+ long** columns,
+ long* n_columns);
+
+ CONTENT_EXPORT STDMETHODIMP get_selectedRows(long max_rows,
+ long** rows,
+ long* n_rows);
+
+ CONTENT_EXPORT STDMETHODIMP get_summary(IUnknown** accessible);
+
+ CONTENT_EXPORT STDMETHODIMP get_isColumnSelected(long column,
+ boolean* is_selected);
+
+ CONTENT_EXPORT STDMETHODIMP get_isRowSelected(long row,
+ boolean* is_selected);
+
+ CONTENT_EXPORT STDMETHODIMP get_isSelected(long row,
+ long column,
+ boolean* is_selected);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowColumnExtentsAtIndex(long index,
+ long* row,
+ long* column,
+ long* row_extents,
+ long* column_extents,
+ boolean* is_selected);
+
+ CONTENT_EXPORT STDMETHODIMP selectRow(long row) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP selectColumn(long column) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP unselectRow(long row) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP unselectColumn(long column) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP get_modelChange(
+ IA2TableModelChange* model_change) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // IAccessibleTable2 methods.
+ //
+ // (Most of these are duplicates of IAccessibleTable methods, only the
+ // unique ones are included here.)
+ //
+
+ CONTENT_EXPORT STDMETHODIMP get_cellAt(long row,
+ long column,
+ IUnknown** cell);
+
+ CONTENT_EXPORT STDMETHODIMP get_nSelectedCells(long* cell_count);
+
+ CONTENT_EXPORT STDMETHODIMP get_selectedCells(IUnknown*** cells,
+ long* n_selected_cells);
+
+ CONTENT_EXPORT STDMETHODIMP get_selectedColumns(long** columns,
+ long* n_columns);
+
+ CONTENT_EXPORT STDMETHODIMP get_selectedRows(long** rows,
+ long* n_rows);
+
+ //
+ // IAccessibleTableCell methods.
+ //
+
+ CONTENT_EXPORT STDMETHODIMP get_columnExtent(long* n_columns_spanned);
+
+ CONTENT_EXPORT STDMETHODIMP get_columnHeaderCells(
+ IUnknown*** cell_accessibles,
+ long* n_column_header_cells);
+
+ CONTENT_EXPORT STDMETHODIMP get_columnIndex(long* column_index);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowExtent(long* n_rows_spanned);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowHeaderCells(IUnknown*** cell_accessibles,
+ long* n_row_header_cells);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowIndex(long* row_index);
+
+ CONTENT_EXPORT STDMETHODIMP get_isSelected(boolean* is_selected);
+
+ CONTENT_EXPORT STDMETHODIMP get_rowColumnExtents(long* row,
+ long* column,
+ long* row_extents,
+ long* column_extents,
+ boolean* is_selected);
+
+ CONTENT_EXPORT STDMETHODIMP get_table(IUnknown** table);
+
+ //
+ // IAccessibleText methods.
+ //
+
+ CONTENT_EXPORT STDMETHODIMP get_nCharacters(LONG* n_characters);
+
+ CONTENT_EXPORT STDMETHODIMP get_caretOffset(LONG* offset);
+
+ CONTENT_EXPORT STDMETHODIMP get_nSelections(LONG* n_selections);
+
+ CONTENT_EXPORT STDMETHODIMP get_selection(LONG selection_index,
+ LONG* start_offset,
+ LONG* end_offset);
+
+ CONTENT_EXPORT STDMETHODIMP get_text(LONG start_offset,
+ LONG end_offset,
+ BSTR* text);
+
+ CONTENT_EXPORT STDMETHODIMP get_textAtOffset(
+ LONG offset,
+ enum IA2TextBoundaryType boundary_type,
+ LONG* start_offset,
+ LONG* end_offset,
+ BSTR* text);
+
+ CONTENT_EXPORT STDMETHODIMP get_textBeforeOffset(
+ LONG offset,
+ enum IA2TextBoundaryType boundary_type,
+ LONG* start_offset,
+ LONG* end_offset,
+ BSTR* text);
+
+ CONTENT_EXPORT STDMETHODIMP get_textAfterOffset(
+ LONG offset,
+ enum IA2TextBoundaryType boundary_type,
+ LONG* start_offset,
+ LONG* end_offset,
+ BSTR* text);
+
+ CONTENT_EXPORT STDMETHODIMP get_newText(IA2TextSegment* new_text);
+
+ CONTENT_EXPORT STDMETHODIMP get_oldText(IA2TextSegment* old_text);
+
+ CONTENT_EXPORT STDMETHODIMP get_offsetAtPoint(
+ LONG x,
+ LONG y,
+ enum IA2CoordinateType coord_type,
+ LONG* offset);
+
+ CONTENT_EXPORT STDMETHODIMP scrollSubstringTo(
+ LONG start_index,
+ LONG end_index,
+ enum IA2ScrollType scroll_type);
+
+ CONTENT_EXPORT STDMETHODIMP scrollSubstringToPoint(
+ LONG start_index,
+ LONG end_index,
+ enum IA2CoordinateType coordinate_type,
+ LONG x, LONG y);
+
+ CONTENT_EXPORT STDMETHODIMP addSelection(LONG start_offset, LONG end_offset);
+
+ CONTENT_EXPORT STDMETHODIMP removeSelection(LONG selection_index);
+
+ CONTENT_EXPORT STDMETHODIMP setCaretOffset(LONG offset);
+
+ CONTENT_EXPORT STDMETHODIMP setSelection(LONG selection_index,
+ LONG start_offset,
+ LONG end_offset);
+
+ // IAccessibleText methods not implemented.
+ CONTENT_EXPORT STDMETHODIMP get_attributes(LONG offset, LONG* start_offset,
+ LONG* end_offset,
+ BSTR* text_attributes) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_characterExtents(LONG offset,
+ enum IA2CoordinateType coord_type,
+ LONG* x,
+ LONG* y,
+ LONG* width,
+ LONG* height) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // IAccessibleHypertext methods.
+ //
+
+ CONTENT_EXPORT STDMETHODIMP get_nHyperlinks(long* hyperlink_count);
+
+ CONTENT_EXPORT STDMETHODIMP get_hyperlink(long index,
+ IAccessibleHyperlink** hyperlink);
+
+ CONTENT_EXPORT STDMETHODIMP get_hyperlinkIndex(long char_index,
+ long* hyperlink_index);
+
+ // IAccessibleHyperlink not implemented.
+ CONTENT_EXPORT STDMETHODIMP get_anchor(long index, VARIANT* anchor) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_anchorTarget(long index,
+ VARIANT* anchor_target) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_startIndex( long* index) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_endIndex( long* index) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_valid(boolean* valid) {
+ return E_NOTIMPL;
+ }
+
+ // IAccessibleAction not implemented.
+ CONTENT_EXPORT STDMETHODIMP nActions(long* n_actions) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP doAction(long action_index) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_description(long action_index,
+ BSTR* description) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_keyBinding(long action_index,
+ long n_max_bindings,
+ BSTR** key_bindings,
+ long* n_bindings) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_name(long action_index, BSTR* name) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP get_localizedName(long action_index,
+ BSTR* localized_name) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // IAccessibleValue methods.
+ //
+
+ CONTENT_EXPORT STDMETHODIMP get_currentValue(VARIANT* value);
+
+ CONTENT_EXPORT STDMETHODIMP get_minimumValue(VARIANT* value);
+
+ CONTENT_EXPORT STDMETHODIMP get_maximumValue(VARIANT* value);
+
+ CONTENT_EXPORT STDMETHODIMP setCurrentValue(VARIANT new_value);
+
+ //
+ // ISimpleDOMDocument methods.
+ //
+
+ CONTENT_EXPORT STDMETHODIMP get_URL(BSTR* url);
+
+ CONTENT_EXPORT STDMETHODIMP get_title(BSTR* title);
+
+ CONTENT_EXPORT STDMETHODIMP get_mimeType(BSTR* mime_type);
+
+ CONTENT_EXPORT STDMETHODIMP get_docType(BSTR* doc_type);
+
+ CONTENT_EXPORT STDMETHODIMP get_nameSpaceURIForID(short name_space_id,
+ BSTR* name_space_uri) {
+ return E_NOTIMPL;
+ }
+ CONTENT_EXPORT STDMETHODIMP put_alternateViewMediaTypes(
+ BSTR* comma_separated_media_types) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // ISimpleDOMNode methods.
+ //
+
+ CONTENT_EXPORT STDMETHODIMP get_nodeInfo(BSTR* node_name,
+ short* name_space_id,
+ BSTR* node_value,
+ unsigned int* num_children,
+ unsigned int* unique_id,
+ unsigned short* node_type);
+
+ CONTENT_EXPORT STDMETHODIMP get_attributes(unsigned short max_attribs,
+ BSTR* attrib_names,
+ short* name_space_id,
+ BSTR* attrib_values,
+ unsigned short* num_attribs);
+
+ CONTENT_EXPORT STDMETHODIMP get_attributesForNames(
+ unsigned short num_attribs,
+ BSTR* attrib_names,
+ short* name_space_id,
+ BSTR* attrib_values);
+
+ CONTENT_EXPORT STDMETHODIMP get_computedStyle(
+ unsigned short max_style_properties,
+ boolean use_alternate_view,
+ BSTR *style_properties,
+ BSTR *style_values,
+ unsigned short *num_style_properties);
+
+ CONTENT_EXPORT STDMETHODIMP get_computedStyleForProperties(
+ unsigned short num_style_properties,
+ boolean use_alternate_view,
+ BSTR* style_properties,
+ BSTR* style_values);
+
+ CONTENT_EXPORT STDMETHODIMP scrollTo(boolean placeTopLeft);
+
+ CONTENT_EXPORT STDMETHODIMP get_parentNode(ISimpleDOMNode** node);
+
+ CONTENT_EXPORT STDMETHODIMP get_firstChild(ISimpleDOMNode** node);
+
+ CONTENT_EXPORT STDMETHODIMP get_lastChild(ISimpleDOMNode** node);
+
+ CONTENT_EXPORT STDMETHODIMP get_previousSibling(ISimpleDOMNode** node);
+
+ CONTENT_EXPORT STDMETHODIMP get_nextSibling(ISimpleDOMNode** node);
+
+ CONTENT_EXPORT STDMETHODIMP get_childAt(unsigned int child_index,
+ ISimpleDOMNode** node);
+
+ CONTENT_EXPORT STDMETHODIMP get_innerHTML(BSTR* innerHTML) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP get_localInterface(void** local_interface) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP get_language(BSTR* language) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // ISimpleDOMText methods.
+ //
+
+ CONTENT_EXPORT STDMETHODIMP get_domText(BSTR* dom_text);
+
+ CONTENT_EXPORT STDMETHODIMP get_clippedSubstringBounds(
+ unsigned int start_index,
+ unsigned int end_index,
+ int* x,
+ int* y,
+ int* width,
+ int* height) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP get_unclippedSubstringBounds(
+ unsigned int start_index,
+ unsigned int end_index,
+ int* x,
+ int* y,
+ int* width,
+ int* height) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP scrollToSubstring(unsigned int start_index,
+ unsigned int end_index) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP get_fontFamily(BSTR *font_family) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // IServiceProvider methods.
+ //
+
+ CONTENT_EXPORT STDMETHODIMP QueryService(REFGUID guidService,
+ REFIID riid,
+ void** object);
+
+ // IAccessibleEx methods not implemented.
+ CONTENT_EXPORT STDMETHODIMP GetObjectForChild(long child_id,
+ IAccessibleEx** ret) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP GetIAccessiblePair(IAccessible** acc,
+ long* child_id) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP GetRuntimeId(SAFEARRAY** runtime_id) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP ConvertReturnedElement(
+ IRawElementProviderSimple* element,
+ IAccessibleEx** acc) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // IRawElementProviderSimple methods.
+ //
+ // The GetPatternProvider/GetPropertyValue methods need to be implemented for
+ // the on-screen keyboard to show up in Windows 8 metro.
+ CONTENT_EXPORT STDMETHODIMP GetPatternProvider(PATTERNID id,
+ IUnknown** provider);
+ CONTENT_EXPORT STDMETHODIMP GetPropertyValue(PROPERTYID id, VARIANT* ret);
+
+ //
+ // IRawElementProviderSimple methods not implemented
+ //
+ CONTENT_EXPORT STDMETHODIMP get_ProviderOptions(enum ProviderOptions* ret) {
+ return E_NOTIMPL;
+ }
+
+ CONTENT_EXPORT STDMETHODIMP get_HostRawElementProvider(
+ IRawElementProviderSimple** provider) {
+ return E_NOTIMPL;
+ }
+
+ //
+ // CComObjectRootEx methods.
+ //
+
+ CONTENT_EXPORT HRESULT WINAPI InternalQueryInterface(
+ void* this_ptr,
+ const _ATL_INTMAP_ENTRY* entries,
+ REFIID iid,
+ void** object);
+
+ // Accessors.
+ int32 ia_role() const { return ia_role_; }
+ int32 ia_state() const { return ia_state_; }
+ int32 ia2_role() const { return ia2_role_; }
+ int32 ia2_state() const { return ia2_state_; }
+ const std::vector<string16>& ia2_attributes() const {
+ return ia2_attributes_;
+ }
+
+ private:
+ // Add one to the reference count and return the same object. Always
+ // use this method when returning a BrowserAccessibilityWin object as
+ // an output parameter to a COM interface, never use it otherwise.
+ BrowserAccessibilityWin* NewReference();
+
+ // Many MSAA methods take a var_id parameter indicating that the operation
+ // should be performed on a particular child ID, rather than this object.
+ // This method tries to figure out the target object from |var_id| and
+ // returns a pointer to the target object if it exists, otherwise NULL.
+ // Does not return a new reference.
+ BrowserAccessibilityWin* GetTargetFromChildID(const VARIANT& var_id);
+
+ // Initialize the role and state metadata from the role enum and state
+ // bitmasks defined in AccessibilityNodeData.
+ void InitRoleAndState();
+
+ // Retrieve the value of an attribute from the string attribute map and
+ // if found and nonempty, allocate a new BSTR (with SysAllocString)
+ // and return S_OK. If not found or empty, return S_FALSE.
+ HRESULT GetStringAttributeAsBstr(
+ AccessibilityNodeData::StringAttribute attribute,
+ BSTR* value_bstr);
+
+ // If the string attribute |attribute| is present, add its value as an
+ // IAccessible2 attribute with the name |ia2_attr|.
+ void StringAttributeToIA2(AccessibilityNodeData::StringAttribute attribute,
+ const char* ia2_attr);
+
+ // If the bool attribute |attribute| is present, add its value as an
+ // IAccessible2 attribute with the name |ia2_attr|.
+ void BoolAttributeToIA2(AccessibilityNodeData::BoolAttribute attribute,
+ const char* ia2_attr);
+
+ // If the int attribute |attribute| is present, add its value as an
+ // IAccessible2 attribute with the name |ia2_attr|.
+ void IntAttributeToIA2(AccessibilityNodeData::IntAttribute attribute,
+ const char* ia2_attr);
+
+ // Get the text of this node for the purposes of IAccessibleText - it may
+ // be the name, it may be the value, etc. depending on the role.
+ const string16& TextForIAccessibleText();
+
+ // If offset is a member of IA2TextSpecialOffsets this function updates the
+ // value of offset and returns, otherwise offset remains unchanged.
+ void HandleSpecialTextOffset(const string16& text, LONG* offset);
+
+ // Convert from a IA2TextBoundaryType to a ui::TextBoundaryType.
+ ui::TextBoundaryType IA2TextBoundaryToTextBoundary(IA2TextBoundaryType type);
+
+ // Search forwards (direction == 1) or backwards (direction == -1)
+ // from the given offset until the given boundary is found, and
+ // return the offset of that boundary.
+ LONG FindBoundary(const string16& text,
+ IA2TextBoundaryType ia2_boundary,
+ LONG start_offset,
+ ui::TextBoundaryDirection direction);
+
+ // Return a pointer to the object corresponding to the given renderer_id,
+ // does not make a new reference.
+ BrowserAccessibilityWin* GetFromRendererID(int32 renderer_id);
+
+ // Windows-specific unique ID (unique within the browser process),
+ // used for get_accChild, NotifyWinEvent, and as the unique ID for
+ // IAccessible2 and ISimpleDOM.
+ LONG unique_id_win_;
+
+ // IAccessible role and state.
+ int32 ia_role_;
+ int32 ia_state_;
+
+ // IAccessible2 role and state.
+ int32 ia2_role_;
+ int32 ia2_state_;
+
+ // IAccessible2 attributes.
+ std::vector<string16> ia2_attributes_;
+
+ // True in Initialize when the object is first created, and false
+ // subsequent times.
+ bool first_time_;
+
+ // The previous text, before the last update to this object.
+ string16 previous_text_;
+
+ // The old text to return in IAccessibleText::get_oldText - this is like
+ // previous_text_ except that it's NOT updated when the object
+ // is initialized again but the text doesn't change.
+ string16 old_text_;
+
+ // The previous state, used to see if there was a state change.
+ int32 old_ia_state_;
+
+ // Relationships between this node and other nodes.
+ std::vector<BrowserAccessibilityRelation*> relations_;
+
+ // The text of this node including embedded hyperlink characters.
+ string16 hypertext_;
+
+ // Maps the |hypertext_| embedded character offset to an index in
+ // |hyperlinks_|.
+ std::map<int32, int32> hyperlink_offset_to_index_;
+
+ // Collection of non-static text child indicies, each of which corresponds to
+ // a hyperlink.
+ std::vector<int32> hyperlinks_;
+
+ // The next unique id to use.
+ static LONG next_unique_id_win_;
+
+ // Give BrowserAccessibility::Create access to our constructor.
+ friend class BrowserAccessibility;
+ friend class BrowserAccessibilityRelation;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityWin);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_WIN_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc
new file mode 100644
index 00000000000..98a5d404a43
--- /dev/null
+++ b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -0,0 +1,660 @@
+// Copyright (c) 2012 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 "base/memory/scoped_ptr.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_variant.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/accessibility/browser_accessibility_manager_win.h"
+#include "content/browser/accessibility/browser_accessibility_win.h"
+#include "content/common/accessibility_messages.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/win/atl_module.h"
+
+namespace content {
+namespace {
+
+
+// CountedBrowserAccessibility ------------------------------------------------
+
+// Subclass of BrowserAccessibilityWin that counts the number of instances.
+class CountedBrowserAccessibility : public BrowserAccessibilityWin {
+ public:
+ CountedBrowserAccessibility();
+ virtual ~CountedBrowserAccessibility();
+
+ static void reset() { num_instances_ = 0; }
+ static int num_instances() { return num_instances_; }
+
+ private:
+ static int num_instances_;
+
+ DISALLOW_COPY_AND_ASSIGN(CountedBrowserAccessibility);
+};
+
+// static
+int CountedBrowserAccessibility::num_instances_ = 0;
+
+CountedBrowserAccessibility::CountedBrowserAccessibility() {
+ ++num_instances_;
+}
+
+CountedBrowserAccessibility::~CountedBrowserAccessibility() {
+ --num_instances_;
+}
+
+
+// CountedBrowserAccessibilityFactory -----------------------------------------
+
+// Factory that creates a CountedBrowserAccessibility.
+class CountedBrowserAccessibilityFactory : public BrowserAccessibilityFactory {
+ public:
+ CountedBrowserAccessibilityFactory();
+
+ private:
+ virtual ~CountedBrowserAccessibilityFactory();
+
+ virtual BrowserAccessibility* Create() OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(CountedBrowserAccessibilityFactory);
+};
+
+CountedBrowserAccessibilityFactory::CountedBrowserAccessibilityFactory() {
+}
+
+CountedBrowserAccessibilityFactory::~CountedBrowserAccessibilityFactory() {
+}
+
+BrowserAccessibility* CountedBrowserAccessibilityFactory::Create() {
+ CComObject<CountedBrowserAccessibility>* instance;
+ HRESULT hr = CComObject<CountedBrowserAccessibility>::CreateInstance(
+ &instance);
+ DCHECK(SUCCEEDED(hr));
+ instance->AddRef();
+ return instance;
+}
+
+} // namespace
+
+
+// BrowserAccessibilityTest ---------------------------------------------------
+
+class BrowserAccessibilityTest : public testing::Test {
+ public:
+ BrowserAccessibilityTest();
+ virtual ~BrowserAccessibilityTest();
+
+ private:
+ virtual void SetUp() OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityTest);
+};
+
+BrowserAccessibilityTest::BrowserAccessibilityTest() {
+}
+
+BrowserAccessibilityTest::~BrowserAccessibilityTest() {
+}
+
+void BrowserAccessibilityTest::SetUp() {
+ ui::win::CreateATLModuleIfNeeded();
+}
+
+
+// Actual tests ---------------------------------------------------------------
+
+// Test that BrowserAccessibilityManager correctly releases the tree of
+// BrowserAccessibility instances upon delete.
+TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
+ // Create AccessibilityNodeData objects for a simple document tree,
+ // representing the accessibility information used to initialize
+ // BrowserAccessibilityManager.
+ AccessibilityNodeData button;
+ button.id = 2;
+ button.name = L"Button";
+ button.role = AccessibilityNodeData::ROLE_BUTTON;
+ button.state = 0;
+
+ AccessibilityNodeData checkbox;
+ checkbox.id = 3;
+ checkbox.name = L"Checkbox";
+ checkbox.role = AccessibilityNodeData::ROLE_CHECKBOX;
+ checkbox.state = 0;
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.name = L"Document";
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.state = 0;
+ root.child_ids.push_back(2);
+ root.child_ids.push_back(3);
+
+ // Construct a BrowserAccessibilityManager with this
+ // AccessibilityNodeData tree and a factory for an instance-counting
+ // BrowserAccessibility, and ensure that exactly 3 instances were
+ // created. Note that the manager takes ownership of the factory.
+ CountedBrowserAccessibility::reset();
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ root, NULL, new CountedBrowserAccessibilityFactory()));
+ manager->UpdateNodesForTesting(button, checkbox);
+ ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
+
+ // Delete the manager and test that all 3 instances are deleted.
+ manager.reset();
+ ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
+
+ // Construct a manager again, and this time use the IAccessible interface
+ // to get new references to two of the three nodes in the tree.
+ manager.reset(BrowserAccessibilityManager::Create(
+ root, NULL, new CountedBrowserAccessibilityFactory()));
+ manager->UpdateNodesForTesting(button, checkbox);
+ ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
+ IAccessible* root_accessible =
+ manager->GetRoot()->ToBrowserAccessibilityWin();
+ IDispatch* root_iaccessible = NULL;
+ IDispatch* child1_iaccessible = NULL;
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ HRESULT hr = root_accessible->get_accChild(childid_self, &root_iaccessible);
+ ASSERT_EQ(S_OK, hr);
+ base::win::ScopedVariant one(1);
+ hr = root_accessible->get_accChild(one, &child1_iaccessible);
+ ASSERT_EQ(S_OK, hr);
+
+ // Now delete the manager, and only one of the three nodes in the tree
+ // should be released.
+ manager.reset();
+ ASSERT_EQ(2, CountedBrowserAccessibility::num_instances());
+
+ // Release each of our references and make sure that each one results in
+ // the instance being deleted as its reference count hits zero.
+ root_iaccessible->Release();
+ ASSERT_EQ(1, CountedBrowserAccessibility::num_instances());
+ child1_iaccessible->Release();
+ ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
+}
+
+TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
+ // Create AccessibilityNodeData objects for a simple document tree,
+ // representing the accessibility information used to initialize
+ // BrowserAccessibilityManager.
+ AccessibilityNodeData text;
+ text.id = 2;
+ text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text.name = L"old text";
+ text.state = 0;
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.name = L"Document";
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.state = 0;
+ root.child_ids.push_back(2);
+
+ // Construct a BrowserAccessibilityManager with this
+ // AccessibilityNodeData tree and a factory for an instance-counting
+ // BrowserAccessibility.
+ CountedBrowserAccessibility::reset();
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ root, NULL, new CountedBrowserAccessibilityFactory()));
+ manager->UpdateNodesForTesting(text);
+
+ // Query for the text IAccessible and verify that it returns "old text" as its
+ // value.
+ base::win::ScopedVariant one(1);
+ base::win::ScopedComPtr<IDispatch> text_dispatch;
+ HRESULT hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild(
+ one, text_dispatch.Receive());
+ ASSERT_EQ(S_OK, hr);
+
+ base::win::ScopedComPtr<IAccessible> text_accessible;
+ hr = text_dispatch.QueryInterface(text_accessible.Receive());
+ ASSERT_EQ(S_OK, hr);
+
+ base::win::ScopedVariant childid_self(CHILDID_SELF);
+ base::win::ScopedBstr name;
+ hr = text_accessible->get_accName(childid_self, name.Receive());
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_EQ(L"old text", string16(name));
+ name.Reset();
+
+ text_dispatch.Release();
+ text_accessible.Release();
+
+ // Notify the BrowserAccessibilityManager that the text child has changed.
+ text.name = L"new text";
+ AccessibilityHostMsg_NotificationParams param;
+ param.notification_type = AccessibilityNotificationChildrenChanged;
+ param.nodes.push_back(text);
+ param.id = text.id;
+ std::vector<AccessibilityHostMsg_NotificationParams> notifications;
+ notifications.push_back(param);
+ manager->OnAccessibilityNotifications(notifications);
+
+ // Query for the text IAccessible and verify that it now returns "new text"
+ // as its value.
+ hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild(
+ one, text_dispatch.Receive());
+ ASSERT_EQ(S_OK, hr);
+
+ hr = text_dispatch.QueryInterface(text_accessible.Receive());
+ ASSERT_EQ(S_OK, hr);
+
+ hr = text_accessible->get_accName(childid_self, name.Receive());
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_EQ(L"new text", string16(name));
+
+ text_dispatch.Release();
+ text_accessible.Release();
+
+ // Delete the manager and test that all BrowserAccessibility instances are
+ // deleted.
+ manager.reset();
+ ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
+}
+
+TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
+ // Create AccessibilityNodeData objects for a simple document tree,
+ // representing the accessibility information used to initialize
+ // BrowserAccessibilityManager.
+ AccessibilityNodeData div;
+ div.id = 2;
+ div.role = AccessibilityNodeData::ROLE_GROUP;
+ div.state = 0;
+
+ AccessibilityNodeData text3;
+ text3.id = 3;
+ text3.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text3.state = 0;
+
+ AccessibilityNodeData text4;
+ text4.id = 4;
+ text4.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text4.state = 0;
+
+ div.child_ids.push_back(3);
+ div.child_ids.push_back(4);
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.state = 0;
+ root.child_ids.push_back(2);
+
+ // Construct a BrowserAccessibilityManager with this
+ // AccessibilityNodeData tree and a factory for an instance-counting
+ // BrowserAccessibility and ensure that exactly 4 instances were
+ // created. Note that the manager takes ownership of the factory.
+ CountedBrowserAccessibility::reset();
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ root, NULL, new CountedBrowserAccessibilityFactory()));
+ manager->UpdateNodesForTesting(div, text3, text4);
+ ASSERT_EQ(4, CountedBrowserAccessibility::num_instances());
+
+ // Notify the BrowserAccessibilityManager that the div node and its children
+ // were removed and ensure that only one BrowserAccessibility instance exists.
+ root.child_ids.clear();
+ AccessibilityHostMsg_NotificationParams param;
+ param.notification_type = AccessibilityNotificationChildrenChanged;
+ param.nodes.push_back(root);
+ param.id = root.id;
+ std::vector<AccessibilityHostMsg_NotificationParams> notifications;
+ notifications.push_back(param);
+ manager->OnAccessibilityNotifications(notifications);
+ ASSERT_EQ(1, CountedBrowserAccessibility::num_instances());
+
+ // Delete the manager and test that all BrowserAccessibility instances are
+ // deleted.
+ manager.reset();
+ ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
+}
+
+TEST_F(BrowserAccessibilityTest, TestTextBoundaries) {
+ AccessibilityNodeData text1;
+ text1.id = 11;
+ text1.role = AccessibilityNodeData::ROLE_TEXT_FIELD;
+ text1.state = 0;
+ text1.value = L"One two three.\nFour five six.";
+ text1.line_breaks.push_back(15);
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.state = 0;
+ root.child_ids.push_back(11);
+
+ CountedBrowserAccessibility::reset();
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ root, NULL, new CountedBrowserAccessibilityFactory()));
+ manager->UpdateNodesForTesting(text1);
+ ASSERT_EQ(2, CountedBrowserAccessibility::num_instances());
+
+ BrowserAccessibilityWin* root_obj =
+ manager->GetRoot()->ToBrowserAccessibilityWin();
+ BrowserAccessibilityWin* text1_obj =
+ root_obj->GetChild(0)->ToBrowserAccessibilityWin();
+
+ long text1_len;
+ ASSERT_EQ(S_OK, text1_obj->get_nCharacters(&text1_len));
+
+ base::win::ScopedBstr text;
+ ASSERT_EQ(S_OK, text1_obj->get_text(0, text1_len, text.Receive()));
+ ASSERT_EQ(text1.value, string16(text));
+ text.Reset();
+
+ ASSERT_EQ(S_OK, text1_obj->get_text(0, 4, text.Receive()));
+ ASSERT_STREQ(L"One ", text);
+ text.Reset();
+
+ long start;
+ long end;
+ ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
+ 1, IA2_TEXT_BOUNDARY_CHAR, &start, &end, text.Receive()));
+ ASSERT_EQ(1, start);
+ ASSERT_EQ(2, end);
+ ASSERT_STREQ(L"n", text);
+ text.Reset();
+
+ ASSERT_EQ(S_FALSE, text1_obj->get_textAtOffset(
+ text1_len, IA2_TEXT_BOUNDARY_CHAR, &start, &end, text.Receive()));
+ ASSERT_EQ(text1_len, start);
+ ASSERT_EQ(text1_len, end);
+ text.Reset();
+
+ ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
+ 1, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive()));
+ ASSERT_EQ(0, start);
+ ASSERT_EQ(3, end);
+ ASSERT_STREQ(L"One", text);
+ text.Reset();
+
+ ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
+ 6, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive()));
+ ASSERT_EQ(4, start);
+ ASSERT_EQ(7, end);
+ ASSERT_STREQ(L"two", text);
+ text.Reset();
+
+ ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
+ text1_len, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive()));
+ ASSERT_EQ(25, start);
+ ASSERT_EQ(29, end);
+ ASSERT_STREQ(L"six.", text);
+ text.Reset();
+
+ ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
+ 1, IA2_TEXT_BOUNDARY_LINE, &start, &end, text.Receive()));
+ ASSERT_EQ(0, start);
+ ASSERT_EQ(15, end);
+ ASSERT_STREQ(L"One two three.\n", text);
+ text.Reset();
+
+ ASSERT_EQ(S_OK,
+ text1_obj->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
+ ASSERT_STREQ(L"One two three.\nFour five six.", text);
+
+ // Delete the manager and test that all BrowserAccessibility instances are
+ // deleted.
+ manager.reset();
+ ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
+}
+
+TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) {
+ AccessibilityNodeData text1;
+ text1.id = 11;
+ text1.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text1.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ text1.name = L"One two three.";
+
+ AccessibilityNodeData text2;
+ text2.id = 12;
+ text2.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text2.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ text2.name = L" Four five six.";
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ root.child_ids.push_back(11);
+ root.child_ids.push_back(12);
+
+ CountedBrowserAccessibility::reset();
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ root, NULL, new CountedBrowserAccessibilityFactory()));
+ manager->UpdateNodesForTesting(root, text1, text2);
+ ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
+
+ BrowserAccessibilityWin* root_obj =
+ manager->GetRoot()->ToBrowserAccessibilityWin();
+
+ long text_len;
+ ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
+
+ base::win::ScopedBstr text;
+ ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive()));
+ EXPECT_EQ(text1.name + text2.name, string16(text));
+
+ long hyperlink_count;
+ ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
+ EXPECT_EQ(0, hyperlink_count);
+
+ base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink;
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, hyperlink.Receive()));
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(0, hyperlink.Receive()));
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(28, hyperlink.Receive()));
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(29, hyperlink.Receive()));
+
+ long hyperlink_index;
+ EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(0, &hyperlink_index));
+ EXPECT_EQ(-1, hyperlink_index);
+ EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(28, &hyperlink_index));
+ EXPECT_EQ(-1, hyperlink_index);
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlinkIndex(-1, &hyperlink_index));
+ EXPECT_EQ(-1, hyperlink_index);
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlinkIndex(29, &hyperlink_index));
+ EXPECT_EQ(-1, hyperlink_index);
+
+ // Delete the manager and test that all BrowserAccessibility instances are
+ // deleted.
+ manager.reset();
+ ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
+}
+
+TEST_F(BrowserAccessibilityTest, TestComplexHypertext) {
+ AccessibilityNodeData text1;
+ text1.id = 11;
+ text1.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text1.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ text1.name = L"One two three.";
+
+ AccessibilityNodeData text2;
+ text2.id = 12;
+ text2.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text2.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ text2.name = L" Four five six.";
+
+ AccessibilityNodeData button1, button1_text;
+ button1.id = 13;
+ button1_text.id = 15;
+ button1_text.name = L"red";
+ button1.role = AccessibilityNodeData::ROLE_BUTTON;
+ button1_text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ button1.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ button1_text.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ button1.child_ids.push_back(15);
+
+ AccessibilityNodeData link1, link1_text;
+ link1.id = 14;
+ link1_text.id = 16;
+ link1_text.name = L"blue";
+ link1.role = AccessibilityNodeData::ROLE_LINK;
+ link1_text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ link1.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ link1_text.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ link1.child_ids.push_back(16);
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.state = 1 << AccessibilityNodeData::STATE_READONLY;
+ root.child_ids.push_back(11);
+ root.child_ids.push_back(13);
+ root.child_ids.push_back(12);
+ root.child_ids.push_back(14);
+
+ CountedBrowserAccessibility::reset();
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ root, NULL, new CountedBrowserAccessibilityFactory()));
+ manager->UpdateNodesForTesting(root,
+ text1, button1, button1_text,
+ text2, link1, link1_text);
+
+ ASSERT_EQ(7, CountedBrowserAccessibility::num_instances());
+
+ BrowserAccessibilityWin* root_obj =
+ manager->GetRoot()->ToBrowserAccessibilityWin();
+
+ long text_len;
+ ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
+
+ base::win::ScopedBstr text;
+ ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive()));
+ const string16 embed = BrowserAccessibilityWin::kEmbeddedCharacter;
+ EXPECT_EQ(text1.name + embed + text2.name + embed, string16(text));
+ text.Reset();
+
+ long hyperlink_count;
+ ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
+ EXPECT_EQ(2, hyperlink_count);
+
+ base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink;
+ base::win::ScopedComPtr<IAccessibleText> hypertext;
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, hyperlink.Receive()));
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(2, hyperlink.Receive()));
+ EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(28, hyperlink.Receive()));
+
+ EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, hyperlink.Receive()));
+ EXPECT_EQ(S_OK,
+ hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive()));
+ EXPECT_EQ(S_OK, hypertext->get_text(0, 3, text.Receive()));
+ EXPECT_STREQ(L"red", text);
+ text.Reset();
+ hyperlink.Release();
+ hypertext.Release();
+
+ EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, hyperlink.Receive()));
+ EXPECT_EQ(S_OK,
+ hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive()));
+ EXPECT_EQ(S_OK, hypertext->get_text(0, 4, text.Receive()));
+ EXPECT_STREQ(L"blue", text);
+ text.Reset();
+ hyperlink.Release();
+ hypertext.Release();
+
+ long hyperlink_index;
+ EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(0, &hyperlink_index));
+ EXPECT_EQ(-1, hyperlink_index);
+ EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(28, &hyperlink_index));
+ EXPECT_EQ(-1, hyperlink_index);
+ EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(14, &hyperlink_index));
+ EXPECT_EQ(0, hyperlink_index);
+ EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(30, &hyperlink_index));
+ EXPECT_EQ(1, hyperlink_index);
+
+ // Delete the manager and test that all BrowserAccessibility instances are
+ // deleted.
+ manager.reset();
+ ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
+}
+
+TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) {
+ // Try creating an empty document with busy state. Readonly is
+ // set automatically.
+ CountedBrowserAccessibility::reset();
+ const int32 busy_state = 1 << AccessibilityNodeData::STATE_BUSY;
+ const int32 readonly_state = 1 << AccessibilityNodeData::STATE_READONLY;
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ new BrowserAccessibilityManagerWin(
+ GetDesktopWindow(),
+ NULL,
+ BrowserAccessibilityManagerWin::GetEmptyDocument(),
+ NULL,
+ new CountedBrowserAccessibilityFactory()));
+
+ // Verify the root is as we expect by default.
+ BrowserAccessibility* root = manager->GetRoot();
+ EXPECT_EQ(0, root->renderer_id());
+ EXPECT_EQ(AccessibilityNodeData::ROLE_ROOT_WEB_AREA, root->role());
+ EXPECT_EQ(busy_state | readonly_state, root->state());
+
+ // Tree with a child textfield.
+ AccessibilityNodeData tree1_1;
+ tree1_1.id = 1;
+ tree1_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ tree1_1.child_ids.push_back(2);
+
+ AccessibilityNodeData tree1_2;
+ tree1_2.id = 2;
+ tree1_2.role = AccessibilityNodeData::ROLE_TEXT_FIELD;
+
+ // Process a load complete.
+ std::vector<AccessibilityHostMsg_NotificationParams> params;
+ params.push_back(AccessibilityHostMsg_NotificationParams());
+ AccessibilityHostMsg_NotificationParams* msg = &params[0];
+ msg->notification_type = AccessibilityNotificationLoadComplete;
+ msg->nodes.push_back(tree1_1);
+ msg->nodes.push_back(tree1_2);
+ msg->id = tree1_1.id;
+ manager->OnAccessibilityNotifications(params);
+
+ // Save for later comparison.
+ BrowserAccessibility* acc1_2 = manager->GetFromRendererID(2);
+
+ // Verify the root has changed.
+ EXPECT_NE(root, manager->GetRoot());
+
+ // And the proper child remains.
+ EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, acc1_2->role());
+ EXPECT_EQ(2, acc1_2->renderer_id());
+
+ // Tree with a child button.
+ AccessibilityNodeData tree2_1;
+ tree2_1.id = 1;
+ tree2_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ tree2_1.child_ids.push_back(3);
+
+ AccessibilityNodeData tree2_2;
+ tree2_2.id = 3;
+ tree2_2.role = AccessibilityNodeData::ROLE_BUTTON;
+
+ msg->nodes.clear();
+ msg->nodes.push_back(tree2_1);
+ msg->nodes.push_back(tree2_2);
+ msg->id = tree2_1.id;
+
+ // Fire another load complete.
+ manager->OnAccessibilityNotifications(params);
+
+ BrowserAccessibility* acc2_2 = manager->GetFromRendererID(3);
+
+ // Verify the root has changed.
+ EXPECT_NE(root, manager->GetRoot());
+
+ // And the new child exists.
+ EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, acc2_2->role());
+ EXPECT_EQ(3, acc2_2->renderer_id());
+
+ // Ensure we properly cleaned up.
+ manager.reset();
+ ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
new file mode 100644
index 00000000000..5e3effe3c2a
--- /dev/null
+++ b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -0,0 +1,465 @@
+// Copyright (c) 2012 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 <string>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/shell/shell.h"
+#include "content/test/accessibility_browser_test_utils.h"
+#include "content/test/content_browser_test.h"
+#include "content/test/content_browser_test_utils.h"
+
+#if defined(OS_WIN)
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/win/scoped_com_initializer.h"
+#include "ui/base/win/atl_module.h"
+#endif
+
+// TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
+#if defined(OS_WIN) && defined(ARCH_CPU_X86_64)
+#define MAYBE_TableSpan DISABLED_TableSpan
+#else
+#define MAYBE_TableSpan TableSpan
+#endif
+
+namespace content {
+
+class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest {
+ public:
+ CrossPlatformAccessibilityBrowserTest() {}
+
+ // Tell the renderer to send an accessibility tree, then wait for the
+ // notification that it's been received.
+ const AccessibilityNodeDataTreeNode& GetAccessibilityNodeDataTree(
+ AccessibilityMode accessibility_mode = AccessibilityModeComplete) {
+ AccessibilityNotificationWaiter waiter(
+ shell(), accessibility_mode, AccessibilityNotificationLayoutComplete);
+ waiter.WaitForNotification();
+ return waiter.GetAccessibilityNodeDataTree();
+ }
+
+ // Make sure each node in the tree has an unique id.
+ void RecursiveAssertUniqueIds(
+ const AccessibilityNodeDataTreeNode& node, base::hash_set<int>* ids) {
+ ASSERT_TRUE(ids->find(node.id) == ids->end());
+ ids->insert(node.id);
+ for (size_t i = 0; i < node.children.size(); i++)
+ RecursiveAssertUniqueIds(node.children[i], ids);
+ }
+
+ // ContentBrowserTest
+ virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
+ virtual void TearDownInProcessBrowserTestFixture() OVERRIDE;
+
+ protected:
+ std::string GetAttr(const AccessibilityNodeData& node,
+ const AccessibilityNodeData::StringAttribute attr);
+ int GetIntAttr(const AccessibilityNodeData& node,
+ const AccessibilityNodeData::IntAttribute attr);
+ bool GetBoolAttr(const AccessibilityNodeData& node,
+ const AccessibilityNodeData::BoolAttribute attr);
+
+ private:
+#if defined(OS_WIN)
+ scoped_ptr<base::win::ScopedCOMInitializer> com_initializer_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(CrossPlatformAccessibilityBrowserTest);
+};
+
+void CrossPlatformAccessibilityBrowserTest::SetUpInProcessBrowserTestFixture() {
+#if defined(OS_WIN)
+ ui::win::CreateATLModuleIfNeeded();
+ com_initializer_.reset(new base::win::ScopedCOMInitializer());
+#endif
+}
+
+void
+CrossPlatformAccessibilityBrowserTest::TearDownInProcessBrowserTestFixture() {
+#if defined(OS_WIN)
+ com_initializer_.reset();
+#endif
+}
+
+// Convenience method to get the value of a particular AccessibilityNodeData
+// node attribute as a UTF-8 const char*.
+std::string CrossPlatformAccessibilityBrowserTest::GetAttr(
+ const AccessibilityNodeData& node,
+ const AccessibilityNodeData::StringAttribute attr) {
+ std::map<AccessibilityNodeData::StringAttribute, string16>::const_iterator
+ iter = node.string_attributes.find(attr);
+ if (iter != node.string_attributes.end())
+ return UTF16ToUTF8(iter->second);
+ else
+ return std::string();
+}
+
+// Convenience method to get the value of a particular AccessibilityNodeData
+// node integer attribute.
+int CrossPlatformAccessibilityBrowserTest::GetIntAttr(
+ const AccessibilityNodeData& node,
+ const AccessibilityNodeData::IntAttribute attr) {
+ std::map<AccessibilityNodeData::IntAttribute, int32>::const_iterator iter =
+ node.int_attributes.find(attr);
+ if (iter != node.int_attributes.end())
+ return iter->second;
+ else
+ return -1;
+}
+
+// Convenience method to get the value of a particular AccessibilityNodeData
+// node boolean attribute.
+bool CrossPlatformAccessibilityBrowserTest::GetBoolAttr(
+ const AccessibilityNodeData& node,
+ const AccessibilityNodeData::BoolAttribute attr) {
+ std::map<AccessibilityNodeData::BoolAttribute, bool>::const_iterator iter =
+ node.bool_attributes.find(attr);
+ if (iter != node.bool_attributes.end())
+ return iter->second;
+ else
+ return false;
+}
+
+// Marked flaky per http://crbug.com/101984
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ DISABLED_WebpageAccessibility) {
+ // Create a data url and load it.
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html>"
+ "<html><head><title>Accessibility Test</title></head>"
+ "<body><input type='button' value='push' /><input type='checkbox' />"
+ "</body></html>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+
+ // Check properties of the root element of the tree.
+ EXPECT_STREQ(url_str,
+ GetAttr(tree, AccessibilityNodeData::ATTR_DOC_URL).c_str());
+ EXPECT_STREQ(
+ "Accessibility Test",
+ GetAttr(tree, AccessibilityNodeData::ATTR_DOC_TITLE).c_str());
+ EXPECT_STREQ(
+ "html", GetAttr(tree, AccessibilityNodeData::ATTR_DOC_DOCTYPE).c_str());
+ EXPECT_STREQ(
+ "text/html",
+ GetAttr(tree, AccessibilityNodeData::ATTR_DOC_MIMETYPE).c_str());
+ EXPECT_STREQ("Accessibility Test", UTF16ToUTF8(tree.name).c_str());
+ EXPECT_EQ(AccessibilityNodeData::ROLE_ROOT_WEB_AREA, tree.role);
+
+ // Check properites of the BODY element.
+ ASSERT_EQ(1U, tree.children.size());
+ const AccessibilityNodeDataTreeNode& body = tree.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_GROUP, body.role);
+ EXPECT_STREQ("body",
+ GetAttr(body, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ EXPECT_STREQ("block",
+ GetAttr(body, AccessibilityNodeData::ATTR_DISPLAY).c_str());
+
+ // Check properties of the two children of the BODY element.
+ ASSERT_EQ(2U, body.children.size());
+
+ const AccessibilityNodeDataTreeNode& button = body.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button.role);
+ EXPECT_STREQ(
+ "input", GetAttr(button, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ EXPECT_STREQ("push", UTF16ToUTF8(button.name).c_str());
+ EXPECT_STREQ(
+ "inline-block",
+ GetAttr(button, AccessibilityNodeData::ATTR_DISPLAY).c_str());
+ ASSERT_EQ(2U, button.html_attributes.size());
+ EXPECT_STREQ("type", UTF16ToUTF8(button.html_attributes[0].first).c_str());
+ EXPECT_STREQ("button", UTF16ToUTF8(button.html_attributes[0].second).c_str());
+ EXPECT_STREQ("value", UTF16ToUTF8(button.html_attributes[1].first).c_str());
+ EXPECT_STREQ("push", UTF16ToUTF8(button.html_attributes[1].second).c_str());
+
+ const AccessibilityNodeDataTreeNode& checkbox = body.children[1];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_CHECKBOX, checkbox.role);
+ EXPECT_STREQ(
+ "input", GetAttr(checkbox, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ EXPECT_STREQ(
+ "inline-block",
+ GetAttr(checkbox, AccessibilityNodeData::ATTR_DISPLAY).c_str());
+ ASSERT_EQ(1U, checkbox.html_attributes.size());
+ EXPECT_STREQ(
+ "type", UTF16ToUTF8(checkbox.html_attributes[0].first).c_str());
+ EXPECT_STREQ(
+ "checkbox", UTF16ToUTF8(checkbox.html_attributes[0].second).c_str());
+}
+
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ UnselectedEditableTextAccessibility) {
+ // Create a data url and load it.
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html>"
+ "<body>"
+ "<input value=\"Hello, world.\"/>"
+ "</body></html>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ ASSERT_EQ(1U, tree.children.size());
+ const AccessibilityNodeDataTreeNode& body = tree.children[0];
+ ASSERT_EQ(1U, body.children.size());
+ const AccessibilityNodeDataTreeNode& text = body.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, text.role);
+ EXPECT_STREQ(
+ "input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_START));
+ EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_END));
+ EXPECT_STREQ("Hello, world.", UTF16ToUTF8(text.value).c_str());
+
+ // TODO(dmazzoni): as soon as more accessibility code is cross-platform,
+ // this code should test that the accessible info is dynamically updated
+ // if the selection or value changes.
+}
+
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ SelectedEditableTextAccessibility) {
+ // Create a data url and load it.
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html>"
+ "<body onload=\"document.body.children[0].select();\">"
+ "<input value=\"Hello, world.\"/>"
+ "</body></html>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ ASSERT_EQ(1U, tree.children.size());
+ const AccessibilityNodeDataTreeNode& body = tree.children[0];
+ ASSERT_EQ(1U, body.children.size());
+ const AccessibilityNodeDataTreeNode& text = body.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, text.role);
+ EXPECT_STREQ(
+ "input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_START));
+ EXPECT_EQ(13, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_END));
+ EXPECT_STREQ("Hello, world.", UTF16ToUTF8(text.value).c_str());
+}
+
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ MultipleInheritanceAccessibility) {
+ // In a WebKit accessibility render tree for a table, each cell is a
+ // child of both a row and a column, so it appears to use multiple
+ // inheritance. Make sure that the AccessibilityNodeDataObject tree only
+ // keeps one copy of each cell, and uses an indirect child id for the
+ // additional reference to it.
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html>"
+ "<table border=1><tr><td>1</td><td>2</td></tr></table>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ ASSERT_EQ(1U, tree.children.size());
+ const AccessibilityNodeDataTreeNode& table = tree.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_TABLE, table.role);
+ const AccessibilityNodeDataTreeNode& row = table.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_ROW, row.role);
+ const AccessibilityNodeDataTreeNode& cell1 = row.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_CELL, cell1.role);
+ const AccessibilityNodeDataTreeNode& cell2 = row.children[1];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_CELL, cell2.role);
+ const AccessibilityNodeDataTreeNode& column1 = table.children[1];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, column1.role);
+ EXPECT_EQ(0U, column1.children.size());
+ EXPECT_EQ(1U, column1.indirect_child_ids.size());
+ EXPECT_EQ(cell1.id, column1.indirect_child_ids[0]);
+ const AccessibilityNodeDataTreeNode& column2 = table.children[2];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, column2.role);
+ EXPECT_EQ(0U, column2.children.size());
+ EXPECT_EQ(1U, column2.indirect_child_ids.size());
+ EXPECT_EQ(cell2.id, column2.indirect_child_ids[0]);
+}
+
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ MultipleInheritanceAccessibility2) {
+ // Here's another html snippet where WebKit puts the same node as a child
+ // of two different parents. Instead of checking the exact output, just
+ // make sure that no id is reused in the resulting tree.
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html>"
+ "<script>\n"
+ " document.writeln('<q><section></section></q><q><li>');\n"
+ " setTimeout(function() {\n"
+ " document.close();\n"
+ " }, 1);\n"
+ "</script>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ base::hash_set<int> ids;
+ RecursiveAssertUniqueIds(tree, &ids);
+}
+
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ IframeAccessibility) {
+ // Create a data url and load it.
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html><html><body>"
+ "<button>Button 1</button>"
+ "<iframe src='data:text/html,"
+ "<!doctype html><html><body><button>Button 2</button></body></html>"
+ "'></iframe>"
+ "<button>Button 3</button>"
+ "</body></html>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ ASSERT_EQ(1U, tree.children.size());
+ const AccessibilityNodeDataTreeNode& body = tree.children[0];
+ ASSERT_EQ(3U, body.children.size());
+
+ const AccessibilityNodeDataTreeNode& button1 = body.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button1.role);
+ EXPECT_STREQ("Button 1", UTF16ToUTF8(button1.name).c_str());
+
+ const AccessibilityNodeDataTreeNode& iframe = body.children[1];
+ EXPECT_STREQ("iframe",
+ GetAttr(iframe, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ ASSERT_EQ(1U, iframe.children.size());
+
+ const AccessibilityNodeDataTreeNode& scroll_area = iframe.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_SCROLLAREA, scroll_area.role);
+ ASSERT_EQ(1U, scroll_area.children.size());
+
+ const AccessibilityNodeDataTreeNode& sub_document = scroll_area.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_WEB_AREA, sub_document.role);
+ ASSERT_EQ(1U, sub_document.children.size());
+
+ const AccessibilityNodeDataTreeNode& sub_body = sub_document.children[0];
+ ASSERT_EQ(1U, sub_body.children.size());
+
+ const AccessibilityNodeDataTreeNode& button2 = sub_body.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button2.role);
+ EXPECT_STREQ("Button 2", UTF16ToUTF8(button2.name).c_str());
+
+ const AccessibilityNodeDataTreeNode& button3 = body.children[2];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button3.role);
+ EXPECT_STREQ("Button 3", UTF16ToUTF8(button3.name).c_str());
+}
+
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ DuplicateChildrenAccessibility) {
+ // Here's another html snippet where WebKit has a parent node containing
+ // two duplicate child nodes. Instead of checking the exact output, just
+ // make sure that no id is reused in the resulting tree.
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html>"
+ "<em><code ><h4 ></em>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ base::hash_set<int> ids;
+ RecursiveAssertUniqueIds(tree, &ids);
+}
+
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ MAYBE_TableSpan) {
+ // +---+---+---+
+ // | 1 | 2 |
+ // +---+---+---+
+ // | 3 | 4 |
+ // +---+---+---+
+
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html>"
+ "<table border=1>"
+ " <tr>"
+ " <td colspan=2>1</td><td>2</td>"
+ " </tr>"
+ " <tr>"
+ " <td>3</td><td colspan=2>4</td>"
+ " </tr>"
+ "</table>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& table = tree.children[0];
+ EXPECT_EQ(AccessibilityNodeData::ROLE_TABLE, table.role);
+ ASSERT_GE(table.children.size(), 5U);
+ EXPECT_EQ(AccessibilityNodeData::ROLE_ROW, table.children[0].role);
+ EXPECT_EQ(AccessibilityNodeData::ROLE_ROW, table.children[1].role);
+ EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, table.children[2].role);
+ EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, table.children[3].role);
+ EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, table.children[4].role);
+ EXPECT_EQ(3,
+ GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT));
+ EXPECT_EQ(2, GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_ROW_COUNT));
+
+ const AccessibilityNodeDataTreeNode& cell1 = table.children[0].children[0];
+ const AccessibilityNodeDataTreeNode& cell2 = table.children[0].children[1];
+ const AccessibilityNodeDataTreeNode& cell3 = table.children[1].children[0];
+ const AccessibilityNodeDataTreeNode& cell4 = table.children[1].children[1];
+
+ ASSERT_EQ(6U, table.cell_ids.size());
+ EXPECT_EQ(cell1.id, table.cell_ids[0]);
+ EXPECT_EQ(cell1.id, table.cell_ids[1]);
+ EXPECT_EQ(cell2.id, table.cell_ids[2]);
+ EXPECT_EQ(cell3.id, table.cell_ids[3]);
+ EXPECT_EQ(cell4.id, table.cell_ids[4]);
+ EXPECT_EQ(cell4.id, table.cell_ids[5]);
+
+ EXPECT_EQ(0, GetIntAttr(cell1,
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
+ EXPECT_EQ(0, GetIntAttr(cell1,
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX));
+ EXPECT_EQ(2, GetIntAttr(cell1,
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
+ EXPECT_EQ(1, GetIntAttr(cell1,
+ AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN));
+ EXPECT_EQ(2, GetIntAttr(cell2,
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
+ EXPECT_EQ(1, GetIntAttr(cell2,
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
+ EXPECT_EQ(0, GetIntAttr(cell3,
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
+ EXPECT_EQ(1, GetIntAttr(cell3,
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
+ EXPECT_EQ(1, GetIntAttr(cell4,
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
+ EXPECT_EQ(2, GetIntAttr(cell4,
+ AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
+}
+
+IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
+ WritableElement) {
+ const char url_str[] =
+ "data:text/html,"
+ "<!doctype html>"
+ "<div role='textbox' tabindex=0>"
+ " Some text"
+ "</div>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+
+ ASSERT_EQ(1U, tree.children.size());
+ const AccessibilityNodeDataTreeNode& textbox = tree.children[0];
+
+ EXPECT_EQ(
+ true, GetBoolAttr(textbox, AccessibilityNodeData::ATTR_CAN_SET_VALUE));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
new file mode 100644
index 00000000000..d0ff119fd24
--- /dev/null
+++ b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -0,0 +1,456 @@
+// Copyright (c) 2012 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 <set>
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_paths.h"
+#include "content/public/common/url_constants.h"
+#include "content/shell/shell.h"
+#include "content/test/accessibility_browser_test_utils.h"
+#include "content/test/content_browser_test.h"
+#include "content/test/content_browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
+#if defined(OS_WIN) && defined(ARCH_CPU_X86_64)
+#define MAYBE(x) DISABLED_##x
+#else
+#define MAYBE(x) x
+#endif
+
+namespace content {
+
+namespace {
+
+const char kCommentToken = '#';
+const char kMarkSkipFile[] = "#<skip";
+const char kMarkEndOfFile[] = "<-- End-of-file -->";
+const char kSignalDiff[] = "*";
+
+} // namespace
+
+typedef AccessibilityTreeFormatter::Filter Filter;
+
+// This test takes a snapshot of the platform BrowserAccessibility tree and
+// tests it against an expected baseline.
+//
+// The flow of the test is as outlined below.
+// 1. Load an html file from chrome/test/data/accessibility.
+// 2. Read the expectation.
+// 3. Browse to the page and serialize the platform specific tree into a human
+// readable string.
+// 4. Perform a comparison between actual and expected and fail if they do not
+// exactly match.
+class DumpAccessibilityTreeTest : public ContentBrowserTest {
+ public:
+ // Utility helper that does a comment aware equality check.
+ // Returns array of lines from expected file which are different.
+ std::vector<int> DiffLines(const std::vector<std::string>& expected_lines,
+ const std::vector<std::string>& actual_lines) {
+ int actual_lines_count = actual_lines.size();
+ int expected_lines_count = expected_lines.size();
+ std::vector<int> diff_lines;
+ int i = 0, j = 0;
+ while (i < actual_lines_count && j < expected_lines_count) {
+ if (expected_lines[j].size() == 0 ||
+ expected_lines[j][0] == kCommentToken) {
+ // Skip comment lines and blank lines in expected output.
+ ++j;
+ continue;
+ }
+
+ if (actual_lines[i] != expected_lines[j])
+ diff_lines.push_back(j);
+ ++i;
+ ++j;
+ }
+
+ // Actual file has been fully checked.
+ return diff_lines;
+ }
+
+ void AddDefaultFilters(std::vector<Filter>* filters) {
+ filters->push_back(Filter(ASCIIToUTF16("FOCUSABLE"), Filter::ALLOW));
+ filters->push_back(Filter(ASCIIToUTF16("READONLY"), Filter::ALLOW));
+ filters->push_back(Filter(ASCIIToUTF16("*=''"), Filter::DENY));
+ }
+
+ void ParseFilters(const std::string& test_html,
+ std::vector<Filter>* filters) {
+ std::vector<std::string> lines;
+ base::SplitString(test_html, '\n', &lines);
+ for (std::vector<std::string>::const_iterator iter = lines.begin();
+ iter != lines.end();
+ ++iter) {
+ const std::string& line = *iter;
+ const std::string& allow_empty_str =
+ AccessibilityTreeFormatter::GetAllowEmptyString();
+ const std::string& allow_str =
+ AccessibilityTreeFormatter::GetAllowString();
+ const std::string& deny_str =
+ AccessibilityTreeFormatter::GetDenyString();
+ if (StartsWithASCII(line, allow_empty_str, true)) {
+ filters->push_back(
+ Filter(UTF8ToUTF16(line.substr(allow_empty_str.size())),
+ Filter::ALLOW_EMPTY));
+ } else if (StartsWithASCII(line, allow_str, true)) {
+ filters->push_back(Filter(UTF8ToUTF16(line.substr(allow_str.size())),
+ Filter::ALLOW));
+ } else if (StartsWithASCII(line, deny_str, true)) {
+ filters->push_back(Filter(UTF8ToUTF16(line.substr(deny_str.size())),
+ Filter::DENY));
+ }
+ }
+ }
+
+ void RunTest(const base::FilePath::CharType* file_path);
+};
+
+void DumpAccessibilityTreeTest::RunTest(
+ const base::FilePath::CharType* file_path) {
+ NavigateToURL(shell(), GURL(kAboutBlankURL));
+
+ // Setup test paths.
+ base::FilePath dir_test_data;
+ ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data));
+ base::FilePath test_path(
+ dir_test_data.Append(FILE_PATH_LITERAL("accessibility")));
+ ASSERT_TRUE(base::PathExists(test_path))
+ << test_path.LossyDisplayName();
+
+ base::FilePath html_file = test_path.Append(base::FilePath(file_path));
+ // Output the test path to help anyone who encounters a failure and needs
+ // to know where to look.
+ printf("Testing: %s\n", html_file.MaybeAsASCII().c_str());
+
+ std::string html_contents;
+ file_util::ReadFileToString(html_file, &html_contents);
+
+ // Read the expected file.
+ std::string expected_contents_raw;
+ base::FilePath expected_file =
+ base::FilePath(html_file.RemoveExtension().value() +
+ AccessibilityTreeFormatter::GetExpectedFileSuffix());
+ file_util::ReadFileToString(expected_file, &expected_contents_raw);
+
+ // Tolerate Windows-style line endings (\r\n) in the expected file:
+ // normalize by deleting all \r from the file (if any) to leave only \n.
+ std::string expected_contents;
+ RemoveChars(expected_contents_raw, "\r", &expected_contents);
+
+ if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) {
+ printf("Skipping this test on this platform.\n");
+ return;
+ }
+
+ // Load the page.
+ string16 html_contents16;
+ html_contents16 = UTF8ToUTF16(html_contents);
+ GURL url = GetTestUrl("accessibility",
+ html_file.BaseName().MaybeAsASCII().c_str());
+ AccessibilityNotificationWaiter waiter(
+ shell(), AccessibilityModeComplete,
+ AccessibilityNotificationLoadComplete);
+ NavigateToURL(shell(), url);
+ waiter.WaitForNotification();
+
+ RenderWidgetHostViewPort* host_view = RenderWidgetHostViewPort::FromRWHV(
+ shell()->web_contents()->GetRenderWidgetHostView());
+ AccessibilityTreeFormatter formatter(
+ host_view->GetBrowserAccessibilityManager()->GetRoot());
+
+ // Parse filters in the test file.
+ std::vector<Filter> filters;
+ AddDefaultFilters(&filters);
+ ParseFilters(html_contents, &filters);
+ formatter.SetFilters(filters);
+
+ // Perform a diff (or write the initial baseline).
+ string16 actual_contents_utf16;
+ formatter.FormatAccessibilityTree(&actual_contents_utf16);
+ std::string actual_contents = UTF16ToUTF8(actual_contents_utf16);
+ std::vector<std::string> actual_lines, expected_lines;
+ Tokenize(actual_contents, "\n", &actual_lines);
+ Tokenize(expected_contents, "\n", &expected_lines);
+ // Marking the end of the file with a line of text ensures that
+ // file length differences are found.
+ expected_lines.push_back(kMarkEndOfFile);
+ actual_lines.push_back(kMarkEndOfFile);
+
+ std::vector<int> diff_lines = DiffLines(expected_lines, actual_lines);
+ bool is_different = diff_lines.size() > 0;
+ EXPECT_FALSE(is_different);
+ if (is_different) {
+ // Mark the expected lines which did not match actual output with a *.
+ printf("* Line Expected\n");
+ printf("- ---- --------\n");
+ for (int line = 0, diff_index = 0;
+ line < static_cast<int>(expected_lines.size());
+ ++line) {
+ bool is_diff = false;
+ if (diff_index < static_cast<int>(diff_lines.size()) &&
+ diff_lines[diff_index] == line) {
+ is_diff = true;
+ ++diff_index;
+ }
+ printf("%1s %4d %s\n", is_diff? kSignalDiff : "", line + 1,
+ expected_lines[line].c_str());
+ }
+ printf("\nActual\n");
+ printf("------\n");
+ printf("%s\n", actual_contents.c_str());
+ }
+
+ if (!base::PathExists(expected_file)) {
+ base::FilePath actual_file =
+ base::FilePath(html_file.RemoveExtension().value() +
+ AccessibilityTreeFormatter::GetActualFileSuffix());
+
+ EXPECT_TRUE(file_util::WriteFile(
+ actual_file, actual_contents.c_str(), actual_contents.size()));
+
+ ADD_FAILURE() << "No expectation found. Create it by doing:\n"
+ << "mv " << actual_file.LossyDisplayName() << " "
+ << expected_file.LossyDisplayName();
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityA) {
+ RunTest(FILE_PATH_LITERAL("a.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAddress) {
+ RunTest(FILE_PATH_LITERAL("address.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAName) {
+ RunTest(FILE_PATH_LITERAL("a-name.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAOnclick) {
+ RunTest(FILE_PATH_LITERAL("a-onclick.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaApplication) {
+ RunTest(FILE_PATH_LITERAL("aria-application.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaAutocomplete) {
+ RunTest(FILE_PATH_LITERAL("aria-autocomplete.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaCombobox) {
+ RunTest(FILE_PATH_LITERAL("aria-combobox.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaInvalid) {
+ RunTest(FILE_PATH_LITERAL("aria-invalid.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaLevel) {
+ RunTest(FILE_PATH_LITERAL("aria-level.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaMenu) {
+ RunTest(FILE_PATH_LITERAL("aria-menu.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaMenuitemradio) {
+ RunTest(FILE_PATH_LITERAL("aria-menuitemradio.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaPressed) {
+ RunTest(FILE_PATH_LITERAL("aria-pressed.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaProgressbar) {
+ RunTest(FILE_PATH_LITERAL("aria-progressbar.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaToolbar) {
+ RunTest(FILE_PATH_LITERAL("toolbar.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaValueMin) {
+ RunTest(FILE_PATH_LITERAL("aria-valuemin.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaValueMax) {
+ RunTest(FILE_PATH_LITERAL("aria-valuemax.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityArticle) {
+ RunTest(FILE_PATH_LITERAL("article.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAWithImg) {
+ RunTest(FILE_PATH_LITERAL("a-with-img.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityBdo) {
+ RunTest(FILE_PATH_LITERAL("bdo.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityBR) {
+ RunTest(FILE_PATH_LITERAL("br.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityButtonNameCalc) {
+ RunTest(FILE_PATH_LITERAL("button-name-calc.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityCanvas) {
+ RunTest(FILE_PATH_LITERAL("canvas.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityCheckboxNameCalc) {
+ RunTest(FILE_PATH_LITERAL("checkbox-name-calc.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDiv) {
+ RunTest(FILE_PATH_LITERAL("div.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDl) {
+ RunTest(FILE_PATH_LITERAL("dl.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityContenteditableDescendants) {
+ RunTest(FILE_PATH_LITERAL("contenteditable-descendants.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityEm) {
+ RunTest(FILE_PATH_LITERAL("em.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityFooter) {
+ RunTest(FILE_PATH_LITERAL("footer.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityForm) {
+ RunTest(FILE_PATH_LITERAL("form.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityHeading) {
+ RunTest(FILE_PATH_LITERAL("heading.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityHR) {
+ RunTest(FILE_PATH_LITERAL("hr.html"));
+}
+
+// crbug.com/179717 and crbug.com/224659
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ DISABLED_AccessibilityIframeCoordinates) {
+ RunTest(FILE_PATH_LITERAL("iframe-coordinates.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputButton) {
+ RunTest(FILE_PATH_LITERAL("input-button.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityInputButtonInMenu) {
+ RunTest(FILE_PATH_LITERAL("input-button-in-menu.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputColor) {
+ RunTest(FILE_PATH_LITERAL("input-color.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityInputImageButtonInMenu) {
+ RunTest(FILE_PATH_LITERAL("input-image-button-in-menu.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputRange) {
+ RunTest(FILE_PATH_LITERAL("input-range.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityInputTextNameCalc) {
+ RunTest(FILE_PATH_LITERAL("input-text-name-calc.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityLabel) {
+ RunTest(FILE_PATH_LITERAL("label.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityListMarkers) {
+ RunTest(FILE_PATH_LITERAL("list-markers.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityP) {
+ RunTest(FILE_PATH_LITERAL("p.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySelect) {
+ RunTest(FILE_PATH_LITERAL("select.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySpan) {
+ RunTest(FILE_PATH_LITERAL("span.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySpinButton) {
+ RunTest(FILE_PATH_LITERAL("spinbutton.html"));
+}
+
+// TODO(dmazzoni): Rebaseline this test after Blink rolls past r155083.
+// See http://crbug.com/265619
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, DISABLED_AccessibilitySvg) {
+ RunTest(FILE_PATH_LITERAL("svg.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTab) {
+ RunTest(FILE_PATH_LITERAL("tab.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTableSimple) {
+ RunTest(FILE_PATH_LITERAL("table-simple.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTableSpans) {
+ RunTest(FILE_PATH_LITERAL("table-spans.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityToggleButton) {
+ RunTest(FILE_PATH_LITERAL("togglebutton.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityUl) {
+ RunTest(FILE_PATH_LITERAL("ul.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityWbr) {
+ RunTest(FILE_PATH_LITERAL("wbr.html"));
+}
+
+} // namespace content