summaryrefslogtreecommitdiff
path: root/chromium/ui/accessibility
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/ui/accessibility
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
downloadqtwebengine-chromium-ab0a50979b9eb4dfa3320eff7e187e41efedf7a9.tar.gz
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/ui/accessibility')
-rw-r--r--chromium/ui/accessibility/BUILD.gn61
-rw-r--r--chromium/ui/accessibility/accessibility.gyp46
-rw-r--r--chromium/ui/accessibility/ax_enums.h124
-rw-r--r--chromium/ui/accessibility/ax_enums.idl356
-rw-r--r--chromium/ui/accessibility/ax_generated_tree_unittest.cc243
-rw-r--r--chromium/ui/accessibility/ax_node.cc13
-rw-r--r--chromium/ui/accessibility/ax_node.h15
-rw-r--r--chromium/ui/accessibility/ax_node_data.cc356
-rw-r--r--chromium/ui/accessibility/ax_node_data.h167
-rw-r--r--chromium/ui/accessibility/ax_serializable_tree.cc29
-rw-r--r--chromium/ui/accessibility/ax_serializable_tree.h2
-rw-r--r--chromium/ui/accessibility/ax_text_utils.cc88
-rw-r--r--chromium/ui/accessibility/ax_text_utils.h54
-rw-r--r--chromium/ui/accessibility/ax_tree.cc122
-rw-r--r--chromium/ui/accessibility/ax_tree.h78
-rw-r--r--chromium/ui/accessibility/ax_tree_serializer.h149
-rw-r--r--chromium/ui/accessibility/ax_tree_serializer_unittest.cc8
-rw-r--r--chromium/ui/accessibility/ax_tree_source.h44
-rw-r--r--chromium/ui/accessibility/ax_tree_unittest.cc142
-rw-r--r--chromium/ui/accessibility/ax_tree_update.cc27
-rw-r--r--chromium/ui/accessibility/ax_tree_update.h3
-rw-r--r--chromium/ui/accessibility/ax_view_state.cc27
-rw-r--r--chromium/ui/accessibility/ax_view_state.h81
23 files changed, 1796 insertions, 439 deletions
diff --git a/chromium/ui/accessibility/BUILD.gn b/chromium/ui/accessibility/BUILD.gn
new file mode 100644
index 00000000000..66a04da74f9
--- /dev/null
+++ b/chromium/ui/accessibility/BUILD.gn
@@ -0,0 +1,61 @@
+# Copyright 2014 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("//extensions/generated_extensions_api.gni")
+
+component("accessibility") {
+ sources = [
+ "ax_node.cc",
+ "ax_node.h",
+ "ax_node_data.cc",
+ "ax_node_data.h",
+ "ax_serializable_tree.cc",
+ "ax_serializable_tree.h",
+ "ax_text_utils.cc",
+ "ax_text_utils.h",
+ "ax_tree.cc",
+ "ax_tree.h",
+ "ax_tree_serializer.cc",
+ "ax_tree_serializer.h",
+ "ax_tree_source.h",
+ "ax_tree_update.cc",
+ "ax_tree_update.h",
+ "ax_view_state.cc",
+ "ax_view_state.h",
+ ]
+
+ defines = [ "ACCESSIBILITY_IMPLEMENTATION" ]
+
+ forward_dependent_configs_from = [ ":ax_gen" ]
+
+ deps = [
+ ":ax_gen",
+ "//base",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ ]
+}
+
+test("accessibility_unittests") {
+ sources = [
+ "ax_generated_tree_unittest.cc",
+ "ax_tree_serializer_unittest.cc",
+ "ax_tree_unittest.cc",
+ ]
+
+ deps = [
+ ":accessibility",
+ "//base",
+ "//base/test:run_all_unittests",
+ "//testing/gtest",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ ]
+}
+
+generated_extensions_api("ax_gen") {
+ sources = [ "ax_enums.idl" ]
+ root_namespace = ""
+ impl_dir = "."
+}
diff --git a/chromium/ui/accessibility/accessibility.gyp b/chromium/ui/accessibility/accessibility.gyp
index 1402d3b9758..bdc42128dc3 100644
--- a/chromium/ui/accessibility/accessibility.gyp
+++ b/chromium/ui/accessibility/accessibility.gyp
@@ -11,22 +11,29 @@
{
'target_name': 'accessibility',
'type': '<(component)',
+ 'export_dependent_settings': [
+ 'ax_gen',
+ ],
+ 'hard_dependency': 1,
'dependencies': [
'../../base/base.gyp:base',
'../gfx/gfx.gyp:gfx',
+ '../gfx/gfx.gyp:gfx_geometry',
+ 'ax_gen',
],
'defines': [
'ACCESSIBILITY_IMPLEMENTATION',
],
'sources': [
# All .cc, .h under accessibility, except unittests
- 'ax_enums.h',
'ax_node.cc',
+ 'ax_node.h',
'ax_node_data.cc',
'ax_node_data.h',
- 'ax_node.h',
'ax_serializable_tree.cc',
'ax_serializable_tree.h',
+ 'ax_text_utils.cc',
+ 'ax_text_utils.h',
'ax_tree.cc',
'ax_tree.h',
'ax_tree_serializer.cc',
@@ -34,6 +41,8 @@
'ax_tree_source.h',
'ax_tree_update.cc',
'ax_tree_update.h',
+ 'ax_view_state.cc',
+ 'ax_view_state.h',
]
},
{
@@ -43,12 +52,45 @@
'../../base/base.gyp:base',
'../../base/base.gyp:run_all_unittests',
'../../testing/gtest.gyp:gtest',
+ '../gfx/gfx.gyp:gfx',
+ '../gfx/gfx.gyp:gfx_geometry',
'accessibility',
+ 'ax_gen',
],
'sources': [
+ 'ax_generated_tree_unittest.cc',
'ax_tree_serializer_unittest.cc',
'ax_tree_unittest.cc',
]
},
+ {
+ 'target_name': 'ax_gen',
+ 'type': 'static_library',
+ # This target exports a hard dependency because dependent targets may
+ # include ax_enums.h, a generated header.
+ 'hard_dependency': 1,
+ 'dependencies': [
+ '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations'
+ ],
+ 'sources': [
+ '<@(schema_files)',
+ ],
+ 'msvs_disabled_warnings': [ 4267 ],
+ 'includes': [
+ '../../build/json_schema_bundle_compile.gypi',
+ '../../build/json_schema_compile.gypi',
+ ],
+ 'variables': {
+ 'chromium_code': 1,
+ 'schema_files': [
+ 'ax_enums.idl',
+ ],
+ 'non_compiled_schema_files': [],
+ 'cc_dir': 'ui/accessibility',
+ # TODO(dtseng): Change this once all files under ui/accessibility
+ # namespaced under ui::ax.
+ 'root_namespace': '',
+ },
+ },
],
}
diff --git a/chromium/ui/accessibility/ax_enums.h b/chromium/ui/accessibility/ax_enums.h
deleted file mode 100644
index 7f543ed4aab..00000000000
--- a/chromium/ui/accessibility/ax_enums.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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 UI_ACCESSIBILITY_AX_ROLES_H_
-#define UI_ACCESSIBILITY_AX_ROLES_H_
-
-// These should be kept in sync with third_party/WebKit/public/web/WebAXEnums.h
-// until the Chromium and Blink trees are merged.
-enum AXRole {
- AX_ROLE_ALERT_DIALOG = 1,
- AX_ROLE_ALERT,
- AX_ROLE_ANNOTATION,
- AX_ROLE_APPLICATION,
- AX_ROLE_ARTICLE,
- AX_ROLE_BANNER,
- AX_ROLE_BROWSER,
- AX_ROLE_BUSY_INDICATOR,
- AX_ROLE_BUTTON,
- AX_ROLE_CANVAS,
- AX_ROLE_CELL,
- AX_ROLE_CHECK_BOX,
- AX_ROLE_COLOR_WELL,
- AX_ROLE_COLUMN_HEADER,
- AX_ROLE_COLUMN,
- AX_ROLE_COMBO_BOX,
- AX_ROLE_COMPLEMENTARY,
- AX_ROLE_CONTENT_INFO,
- AX_ROLE_DEFINITION,
- AX_ROLE_DESCRIPTION_LIST_DETAIL,
- AX_ROLE_DESCRIPTION_LIST_TERM,
- AX_ROLE_DIALOG,
- AX_ROLE_DIRECTORY,
- AX_ROLE_DISCLOSURE_TRIANGLE,
- AX_ROLE_DIV,
- AX_ROLE_DOCUMENT,
- AX_ROLE_DRAWER,
- AX_ROLE_EDITABLE_TEXT,
- AX_ROLE_FOOTER,
- AX_ROLE_FORM,
- AX_ROLE_GRID,
- AX_ROLE_GROUP,
- AX_ROLE_GROW_AREA,
- AX_ROLE_HEADING,
- AX_ROLE_HELP_TAG,
- AX_ROLE_HORIZONTAL_RULE,
- AX_ROLE_IGNORED,
- AX_ROLE_IMAGE_MAP_LINK,
- AX_ROLE_IMAGE_MAP,
- AX_ROLE_IMAGE,
- AX_ROLE_INCREMENTOR,
- AX_ROLE_INLINE_TEXT_BOX,
- AX_ROLE_LABEL,
- AX_ROLE_LEGEND,
- AX_ROLE_LINK,
- AX_ROLE_LIST_BOX_OPTION,
- AX_ROLE_LIST_BOX,
- AX_ROLE_LIST_ITEM,
- AX_ROLE_LIST_MARKER,
- AX_ROLE_LIST,
- AX_ROLE_LOG,
- AX_ROLE_MAIN,
- AX_ROLE_MARQUEE,
- AX_ROLE_MATH_ELEMENT,
- AX_ROLE_MATH,
- AX_ROLE_MATTE,
- AX_ROLE_MENU_BAR,
- AX_ROLE_MENU_BUTTON,
- AX_ROLE_MENU_ITEM,
- AX_ROLE_MENU_LIST_OPTION,
- AX_ROLE_MENU_LIST_POPUP,
- AX_ROLE_MENU,
- AX_ROLE_NAVIGATION,
- AX_ROLE_NOTE,
- AX_ROLE_OUTLINE,
- AX_ROLE_PARAGRAPH,
- AX_ROLE_POP_UP_BUTTON,
- AX_ROLE_PRESENTATIONAL,
- AX_ROLE_PROGRESS_INDICATOR,
- AX_ROLE_RADIO_BUTTON,
- AX_ROLE_RADIO_GROUP,
- AX_ROLE_REGION,
- AX_ROLE_ROOT_WEB_AREA,
- AX_ROLE_ROW_HEADER,
- AX_ROLE_ROW,
- AX_ROLE_RULER_MARKER,
- AX_ROLE_RULER,
- AX_ROLE_SVG_ROOT,
- AX_ROLE_SCROLL_AREA,
- AX_ROLE_SCROLL_BAR,
- AX_ROLE_SEAMLESS_WEB_AREA,
- AX_ROLE_SEARCH,
- AX_ROLE_SHEET,
- AX_ROLE_SLIDER,
- AX_ROLE_SLIDER_THUMB,
- AX_ROLE_SPIN_BUTTON_PART,
- AX_ROLE_SPIN_BUTTON,
- AX_ROLE_SPLIT_GROUP,
- AX_ROLE_SPLITTER,
- AX_ROLE_STATIC_TEXT,
- AX_ROLE_STATUS,
- AX_ROLE_SYSTEM_WIDE,
- AX_ROLE_TAB_GROUP,
- AX_ROLE_TAB_LIST,
- AX_ROLE_TAB_PANEL,
- AX_ROLE_TAB,
- AX_ROLE_TABLE_HEADER_CONTAINER,
- AX_ROLE_TABLE,
- AX_ROLE_TEXT_AREA,
- AX_ROLE_TEXT_FIELD,
- AX_ROLE_TIMER,
- AX_ROLE_TOGGLE_BUTTON,
- AX_ROLE_TOOLBAR,
- AX_ROLE_TREE_GRID,
- AX_ROLE_TREE_ITEM,
- AX_ROLE_TREE,
- AX_ROLE_UNKNOWN,
- AX_ROLE_USER_INTERFACE_TOOLTIP,
- AX_ROLE_VALUE_INDICATOR,
- AX_ROLE_WEB_AREA,
- AX_ROLE_WINDOW
-};
-
-#endif // UI_ACCESSIBILITY_AX_ROLES_H_
diff --git a/chromium/ui/accessibility/ax_enums.idl b/chromium/ui/accessibility/ax_enums.idl
new file mode 100644
index 00000000000..ef3b5e6c895
--- /dev/null
+++ b/chromium/ui/accessibility/ax_enums.idl
@@ -0,0 +1,356 @@
+// Copyright 2014 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.
+
+// These should be kept in sync with third_party/WebKit/public/web/WebAXEnums.h
+// until the Chromium and Blink trees are merged.
+[camel_case_enum_to_string=true] namespace ui {
+
+ // For new entries to the following three enums, also add to
+ // chrome/common/extensions/apis/automation.idl.
+ enum AXEvent {
+ activedescendantchanged,
+ alert,
+ aria_attribute_changed,
+ autocorrection_occured,
+ blur,
+ checked_state_changed,
+ children_changed,
+ focus,
+ hide,
+ hover,
+ invalid_status_changed,
+ layout_complete,
+ live_region_changed,
+ load_complete,
+ location_changed,
+ menu_end,
+ menu_list_item_selected,
+ menu_list_value_changed,
+ menu_popup_end,
+ menu_popup_start,
+ menu_start,
+ row_collapsed,
+ row_count_changed,
+ row_expanded,
+ scroll_position_changed,
+ scrolled_to_anchor,
+ selected_children_changed,
+ selected_text_changed,
+ selection_changed,
+ show,
+ text_changed,
+ text_inserted,
+ text_removed,
+ value_changed
+ };
+
+ enum AXRole {
+ alert_dialog,
+ alert,
+ annotation,
+ application,
+ article,
+ banner,
+ browser,
+ busy_indicator,
+ button,
+ button_drop_down,
+ canvas,
+ cell,
+ check_box,
+ client,
+ color_well,
+ column_header,
+ column,
+ combo_box,
+ complementary,
+ content_info,
+ definition,
+ description_list_detail,
+ description_list_term,
+ desktop,
+ dialog,
+ directory,
+ disclosure_triangle,
+ div,
+ document,
+ drawer,
+ editable_text,
+ embedded_object,
+ footer,
+ form,
+ grid,
+ group,
+ grow_area,
+ heading,
+ help_tag,
+ horizontal_rule,
+ iframe,
+ ignored,
+ image_map_link,
+ image_map,
+ image,
+ incrementor,
+ inline_text_box,
+ label_text,
+ legend,
+ link,
+ list_box_option,
+ list_box,
+ list_item,
+ list_marker,
+ list,
+ location_bar,
+ log,
+ main,
+ marquee,
+ math_element,
+ math,
+ matte,
+ menu_bar,
+ menu_button,
+ menu_item,
+ menu_list_option,
+ menu_list_popup,
+ menu,
+ navigation,
+ note,
+ outline,
+ pane,
+ paragraph,
+ pop_up_button,
+ presentational,
+ progress_indicator,
+ radio_button,
+ radio_group,
+ region,
+ root_web_area,
+ row_header,
+ row,
+ ruler_marker,
+ ruler,
+ svg_root,
+ scroll_area,
+ scroll_bar,
+ seamless_web_area,
+ search,
+ sheet,
+ slider,
+ slider_thumb,
+ spin_button_part,
+ spin_button,
+ split_group,
+ splitter,
+ static_text,
+ status,
+ system_wide,
+ tab_group,
+ tab_list,
+ tab_panel,
+ tab,
+ table_header_container,
+ table,
+ text_area,
+ text_field,
+ timer,
+ title_bar,
+ toggle_button,
+ toolbar,
+ tree_grid,
+ tree_item,
+ tree,
+ unknown,
+ tooltip,
+ value_indicator,
+ web_area,
+ window
+ };
+
+ // TODO(dmazzoni): switch content/ to use AX_STATE_DISABLED instead of
+ // !AX_STATE_ENABLED, and AX_STATE_EDITABLE instead of !AX_STATE_READONLY.
+ enum AXState {
+ busy,
+ checked,
+ collapsed,
+ default,
+ disabled, // ui/views only
+ editable, // ui/views only
+ enabled, // content only
+ expanded,
+ focusable,
+ focused,
+ haspopup,
+ hovered,
+ indeterminate,
+ invisible,
+ linked,
+ multiselectable,
+ offscreen,
+ pressed,
+ protected,
+ read_only,
+ required,
+ selectable,
+ selected,
+ vertical,
+ visited
+ };
+
+ [cpp_enum_prefix_override="ax_attr"] enum AXStringAttribute {
+ // Document attributes.
+ doc_url,
+ doc_title,
+ doc_mimetype,
+ doc_doctype,
+
+ // Attributes that could apply to any node.
+ access_key,
+ action,
+ container_live_relevant,
+ container_live_status,
+ description,
+ display,
+ help,
+ html_tag,
+ name,
+ live_relevant,
+ live_status,
+ role,
+ shortcut,
+ url,
+ value
+ };
+
+ [cpp_enum_prefix_override="ax_attr"] enum AXIntAttribute {
+ // Scrollable container attributes.
+ scroll_x,
+ scroll_x_min,
+ scroll_x_max,
+ scroll_y,
+ scroll_y_min,
+ scroll_y_max,
+
+ // Editable text attributes.
+ text_sel_start,
+ text_sel_end,
+
+ // Table attributes.
+ table_row_count,
+ table_column_count,
+ table_header_id,
+
+ // Table row attributes.
+ table_row_index,
+ table_row_header_id,
+
+ // Table column attributes.
+ table_column_index,
+ table_column_header_id,
+
+ // Table cell attributes.
+ table_cell_column_index,
+ table_cell_column_span,
+ table_cell_row_index,
+ table_cell_row_span,
+
+ // Tree control attributes.
+ hierarchical_level,
+
+ // Relationships between this element and other elements.
+ title_ui_element,
+ activedescendant_id,
+
+ // Color value for AX_ROLE_COLOR_WELL, each component is 0..255
+ color_value_red,
+ color_value_green,
+ color_value_blue,
+
+ // Inline text attributes.
+ text_direction
+ };
+
+ [cpp_enum_prefix_override="ax_attr"] enum AXFloatAttribute {
+ // Document attributes.
+ doc_loading_progress,
+
+ // Range attributes.
+ value_for_range,
+ min_value_for_range,
+ max_value_for_range
+ };
+
+ [cpp_enum_prefix_override="ax_attr"] enum AXBoolAttribute {
+ // Document attributes.
+ doc_loaded,
+
+ // True if a checkbox or radio button is in the "mixed" state.
+ button_mixed,
+
+ // Live region attributes.
+ container_live_atomic,
+ container_live_busy,
+ live_atomic,
+ live_busy,
+
+ // ARIA readonly flag.
+ aria_readonly,
+
+ // Writeable attributes
+ can_set_value,
+
+ // If this is set, all of the other fields in this struct should
+ // be ignored and only the locations should change.
+ update_location_only,
+
+ // Set on a canvas element if it has fallback content.
+ canvas_has_fallback
+ };
+
+ [cpp_enum_prefix_override="ax_attr"] enum AXIntListAttribute {
+ // Ids of nodes that are children of this node logically, but are
+ // not children of this node in the tree structure. As an example,
+ // a table cell is a child of a row, and an 'indirect' child of a
+ // column.
+ indirect_child_ids,
+
+ // Relationships between this element and other elements.
+ controls_ids,
+ describedby_ids,
+ flowto_ids,
+ labelledby_ids,
+ owns_ids,
+
+ // Character indices where line breaks occur.
+ line_breaks,
+
+ // For a table, the cell ids in row-major order, with duplicate entries
+ // when there's a rowspan or colspan, and with -1 for missing cells.
+ // There are always exactly rows * columns entries.
+ cell_ids,
+
+ // For a table, the unique cell ids in row-major order of their first
+ // occurrence.
+ unique_cell_ids,
+
+ // For inline text. This is the pixel position of the end of this
+ // character within the bounding rectangle of this object, in the
+ // direction given by AX_ATTR_TEXT_DIRECTION. For example, for left-to-right
+ // text, the first offset is the right coordinate of the first character
+ // within the object's bounds, the second offset is the right coordinate
+ // of the second character, and so on.
+ character_offsets,
+
+ // For inline text. These int lists must be the same size; they represent
+ // the start and end character index of each word within this text.
+ word_starts,
+ word_ends
+ };
+
+ [cpp_enum_prefix_override="ax"] enum AXTextDirection {
+ text_direction_lr,
+ text_direction_rl,
+ text_direction_tb,
+ text_direction_bt
+ };
+};
diff --git a/chromium/ui/accessibility/ax_generated_tree_unittest.cc b/chromium/ui/accessibility/ax_generated_tree_unittest.cc
new file mode 100644
index 00000000000..835200cb27a
--- /dev/null
+++ b/chromium/ui/accessibility/ax_generated_tree_unittest.cc
@@ -0,0 +1,243 @@
+// Copyright 2014 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/strings/string_number_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/ax_node.h"
+#include "ui/accessibility/ax_serializable_tree.h"
+#include "ui/accessibility/ax_tree.h"
+#include "ui/accessibility/ax_tree_serializer.h"
+
+namespace ui {
+namespace {
+
+// A function to turn a tree into a string, capturing only the node ids
+// and their relationship to one another.
+//
+// The string format is kind of like an S-expression, with each expression
+// being either a node id, or a node id followed by a subexpression
+// representing its children.
+//
+// Examples:
+//
+// (1) is a tree with a single node with id 1.
+// (1 (2 3)) is a tree with 1 as the root, and 2 and 3 as its children.
+// (1 (2 (3))) has 1 as the root, 2 as its child, and then 3 as the child of 2.
+void TreeToStringHelper(const AXNode* node, std::string* out_result) {
+ *out_result += base::IntToString(node->id());
+ if (node->child_count() != 0) {
+ *out_result += " (";
+ for (int i = 0; i < node->child_count(); ++i) {
+ if (i != 0)
+ *out_result += " ";
+ TreeToStringHelper(node->ChildAtIndex(i), out_result);
+ }
+ *out_result += ")";
+ }
+}
+
+std::string TreeToString(const AXTree& tree) {
+ std::string result;
+ TreeToStringHelper(tree.GetRoot(), &result);
+ return "(" + result + ")";
+}
+
+} // anonymous namespace
+
+// A class to create all possible trees with <n> nodes and the ids [1...n].
+//
+// There are two parts to the algorithm:
+//
+// The tree structure is formed as follows: without loss of generality,
+// the first node becomes the root and the second node becomes its
+// child. Thereafter, choose every possible parent for every other node.
+//
+// So for node i in (3...n), there are (i - 1) possible choices for its
+// parent, for a total of (n-1)! (n minus 1 factorial) possible trees.
+//
+// The second part is the assignment of ids to the nodes in the tree.
+// There are exactly n! (n factorial) permutations of the sequence 1...n,
+// and each of these is assigned to every node in every possible tree.
+//
+// The total number of trees returned for a given <n>, then, is
+// n! * (n-1)!
+//
+// n = 2: 2 trees
+// n = 3: 12 trees
+// n = 4: 144 trees
+// n = 5: 2880 trees
+//
+// This grows really fast! Luckily it's very unlikely that there'd be
+// bugs that affect trees with >4 nodes that wouldn't affect a smaller tree
+// too.
+class TreeGenerator {
+ public:
+ TreeGenerator(int node_count)
+ : node_count_(node_count),
+ unique_tree_count_(1) {
+ // (n-1)! for the possible trees.
+ for (int i = 2; i < node_count_; i++)
+ unique_tree_count_ *= i;
+ // n! for the permutations of ids.
+ for (int i = 2; i <= node_count_; i++)
+ unique_tree_count_ *= i;
+ }
+
+ int UniqueTreeCount() {
+ return unique_tree_count_;
+ }
+
+ void BuildUniqueTree(int tree_index, AXTree* out_tree) {
+ std::vector<int> indices;
+ std::vector<int> permuted;
+ CHECK(tree_index <= unique_tree_count_);
+
+ // Use the first few bits of |tree_index| to permute
+ // the indices.
+ for (int i = 0; i < node_count_; i++)
+ indices.push_back(i + 1);
+ for (int i = 0; i < node_count_; i++) {
+ int p = (node_count_ - i);
+ int index = tree_index % p;
+ tree_index /= p;
+ permuted.push_back(indices[index]);
+ indices.erase(indices.begin() + index);
+ }
+
+ // Build an AXTreeUpdate. The first two nodes of the tree always
+ // go in the same place.
+ AXTreeUpdate update;
+ update.nodes.resize(node_count_);
+ update.nodes[0].id = permuted[0];
+ update.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
+ update.nodes[0].child_ids.push_back(permuted[1]);
+ update.nodes[1].id = permuted[1];
+
+ // The remaining nodes are assigned based on their parent
+ // selected from the next bits from |tree_index|.
+ for (int i = 2; i < node_count_; i++) {
+ update.nodes[i].id = permuted[i];
+ int parent_index = (tree_index % i);
+ tree_index /= i;
+ update.nodes[parent_index].child_ids.push_back(permuted[i]);
+ }
+
+ // Unserialize the tree update into the destination tree.
+ CHECK(out_tree->Unserialize(update));
+ }
+
+ private:
+ int node_count_;
+ int unique_tree_count_;
+};
+
+// Test the TreeGenerator class by building all possible trees with
+// 3 nodes and the ids [1...3].
+TEST(AXGeneratedTreeTest, TestTreeGenerator) {
+ int tree_size = 3;
+ TreeGenerator generator(tree_size);
+ const char* EXPECTED_TREES[] = {
+ "(1 (2 3))",
+ "(2 (1 3))",
+ "(3 (1 2))",
+ "(1 (3 2))",
+ "(2 (3 1))",
+ "(3 (2 1))",
+ "(1 (2 (3)))",
+ "(2 (1 (3)))",
+ "(3 (1 (2)))",
+ "(1 (3 (2)))",
+ "(2 (3 (1)))",
+ "(3 (2 (1)))",
+ };
+
+ int n = generator.UniqueTreeCount();
+ ASSERT_EQ(static_cast<int>(arraysize(EXPECTED_TREES)), n);
+
+ for (int i = 0; i < n; i++) {
+ AXTree tree;
+ generator.BuildUniqueTree(i, &tree);
+ std::string str = TreeToString(tree);
+ EXPECT_EQ(EXPECTED_TREES[i], str);
+ }
+}
+
+// Test mutating every possible tree with <n> nodes to every other possible
+// tree with <n> nodes, where <n> is 4 in release mode and 3 in debug mode
+// (for speed). For each possible combination of trees, we also vary which
+// node we serialize first.
+//
+// For every possible scenario, we check that the AXTreeUpdate is valid,
+// that the destination tree can unserialize it and create a valid tree,
+// and that after updating all nodes the resulting tree now matches the
+// intended tree.
+TEST(AXGeneratedTreeTest, SerializeGeneratedTrees) {
+ // Do a more exhaustive test in release mode. If you're modifying
+ // the algorithm you may want to try even larger tree sizes if you
+ // can afford the time.
+#ifdef NDEBUG
+ int tree_size = 4;
+#else
+ LOG(WARNING) << "Debug build, only testing trees with 3 nodes and not 4.";
+ int tree_size = 3;
+#endif
+
+ TreeGenerator generator(tree_size);
+ int n = generator.UniqueTreeCount();
+
+ for (int i = 0; i < n; i++) {
+ // Build the first tree, tree0.
+ AXSerializableTree tree0;
+ generator.BuildUniqueTree(i, &tree0);
+ SCOPED_TRACE("tree0 is " + TreeToString(tree0));
+
+ for (int j = 0; j < n; j++) {
+ // Build the second tree, tree1.
+ AXSerializableTree tree1;
+ generator.BuildUniqueTree(j, &tree1);
+ SCOPED_TRACE("tree1 is " + TreeToString(tree0));
+
+ // Now iterate over which node to update first, |k|.
+ for (int k = 0; k < tree_size; k++) {
+ SCOPED_TRACE("i=" + base::IntToString(i) +
+ " j=" + base::IntToString(j) +
+ " k=" + base::IntToString(k));
+
+ // Start by serializing tree0 and unserializing it into a new
+ // empty tree |dst_tree|.
+ scoped_ptr<AXTreeSource<const AXNode*> > tree0_source(
+ tree0.CreateTreeSource());
+ AXTreeSerializer<const AXNode*> serializer(tree0_source.get());
+ AXTreeUpdate update0;
+ serializer.SerializeChanges(tree0.GetRoot(), &update0);
+
+ AXTree dst_tree;
+ ASSERT_TRUE(dst_tree.Unserialize(update0));
+
+ // At this point, |dst_tree| should now be identical to |tree0|.
+ EXPECT_EQ(TreeToString(tree0), TreeToString(dst_tree));
+
+ // Next, pretend that tree0 turned into tree1, and serialize
+ // a sequence of updates to |dst_tree| to match.
+ scoped_ptr<AXTreeSource<const AXNode*> > tree1_source(
+ tree1.CreateTreeSource());
+ serializer.ChangeTreeSourceForTesting(tree1_source.get());
+
+ for (int k_index = 0; k_index < tree_size; ++k_index) {
+ int id = 1 + (k + k_index) % tree_size;
+ AXTreeUpdate update;
+ serializer.SerializeChanges(tree1.GetFromId(id), &update);
+ ASSERT_TRUE(dst_tree.Unserialize(update));
+ }
+
+ // After the sequence of updates, |dst_tree| should now be
+ // identical to |tree1|.
+ EXPECT_EQ(TreeToString(tree1), TreeToString(dst_tree));
+ }
+ }
+ }
+}
+
+} // namespace ui
diff --git a/chromium/ui/accessibility/ax_node.cc b/chromium/ui/accessibility/ax_node.cc
index 8adb9409051..587f7375d74 100644
--- a/chromium/ui/accessibility/ax_node.cc
+++ b/chromium/ui/accessibility/ax_node.cc
@@ -19,6 +19,10 @@ void AXNode::SetData(const AXNodeData& src) {
data_ = src;
}
+void AXNode::SetLocation(const gfx::Rect& new_location) {
+ data_.location = new_location;
+}
+
void AXNode::SetIndexInParent(int index_in_parent) {
index_in_parent_ = index_in_parent;
}
@@ -31,4 +35,13 @@ void AXNode::Destroy() {
delete this;
}
+bool AXNode::IsDescendantOf(AXNode* ancestor) {
+ if (this == ancestor)
+ return true;
+ else if (parent())
+ return parent()->IsDescendantOf(ancestor);
+
+ return false;
+}
+
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_node.h b/chromium/ui/accessibility/ax_node.h
index b7e31257b96..5da73401c53 100644
--- a/chromium/ui/accessibility/ax_node.h
+++ b/chromium/ui/accessibility/ax_node.h
@@ -24,27 +24,36 @@ class AX_EXPORT AXNode {
int child_count() const { return static_cast<int>(children_.size()); }
const AXNodeData& data() const { return data_; }
const std::vector<AXNode*>& children() const { return children_; }
+ int index_in_parent() const { return index_in_parent_; }
// Get the child at the given index.
AXNode* ChildAtIndex(int index) const { return children_[index]; }
// Set the node's accessibility data. This may be done during initial
// initialization or later when the node data changes.
- virtual void SetData(const AXNodeData& src);
+ void SetData(const AXNodeData& src);
+
+ // Update this node's location. This is separate from SetData just because
+ // changing only the location is common and should be more efficient than
+ // re-copying all of the data.
+ void SetLocation(const gfx::Rect& new_location);
// Set the index in parent, for example if siblings were inserted or deleted.
void SetIndexInParent(int index_in_parent);
// Swap the internal children vector with |children|. This instance
// now owns all of the passed children.
- virtual void SwapChildren(std::vector<AXNode*>& children);
+ void SwapChildren(std::vector<AXNode*>& children);
// This is called when the AXTree no longer includes this node in the
// tree. Reference counting is used on some platforms because the
// operating system may hold onto a reference to an AXNode
// object even after we're through with it, so this may decrement the
// reference count and clear out the object's data.
- virtual void Destroy();
+ void Destroy();
+
+ // Return true if this object is equal to or a descendant of |ancestor|.
+ bool IsDescendantOf(AXNode* ancestor);
private:
int index_in_parent_;
diff --git a/chromium/ui/accessibility/ax_node_data.cc b/chromium/ui/accessibility/ax_node_data.cc
index 1655fe84524..78987ef6e18 100644
--- a/chromium/ui/accessibility/ax_node_data.cc
+++ b/chromium/ui/accessibility/ax_node_data.cc
@@ -6,7 +6,6 @@
#include <set>
-#include "base/containers/hash_tables.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -16,6 +15,20 @@ using base::IntToString;
namespace ui {
+namespace {
+
+std::string IntVectorToString(const std::vector<int>& items) {
+ std::string str;
+ for (size_t i = 0; i < items.size(); ++i) {
+ if (i > 0)
+ str += ",";
+ str += IntToString(items[i]);
+ }
+ return str;
+}
+
+} // Anonymous namespace
+
AXNodeData::AXNodeData()
: id(-1),
role(AX_ROLE_UNKNOWN),
@@ -26,36 +39,363 @@ AXNodeData::~AXNodeData() {
}
void AXNodeData::AddStringAttribute(
- StringAttribute attribute, const std::string& value) {
+ AXStringAttribute attribute, const std::string& value) {
string_attributes.push_back(std::make_pair(attribute, value));
}
void AXNodeData::AddIntAttribute(
- IntAttribute attribute, int value) {
+ AXIntAttribute attribute, int value) {
int_attributes.push_back(std::make_pair(attribute, value));
}
void AXNodeData::AddFloatAttribute(
- FloatAttribute attribute, float value) {
+ AXFloatAttribute attribute, float value) {
float_attributes.push_back(std::make_pair(attribute, value));
}
void AXNodeData::AddBoolAttribute(
- BoolAttribute attribute, bool value) {
+ AXBoolAttribute attribute, bool value) {
bool_attributes.push_back(std::make_pair(attribute, value));
}
void AXNodeData::AddIntListAttribute(
- IntListAttribute attribute, const std::vector<int32>& value) {
+ AXIntListAttribute attribute, const std::vector<int32>& value) {
intlist_attributes.push_back(std::make_pair(attribute, value));
}
void AXNodeData::SetName(std::string name) {
- string_attributes.push_back(std::make_pair(ATTR_NAME, name));
+ string_attributes.push_back(std::make_pair(AX_ATTR_NAME, name));
}
void AXNodeData::SetValue(std::string value) {
- string_attributes.push_back(std::make_pair(ATTR_VALUE, value));
+ string_attributes.push_back(std::make_pair(AX_ATTR_VALUE, value));
+}
+
+std::string AXNodeData::ToString() const {
+ std::string result;
+
+ result += "id=" + IntToString(id);
+ result += " " + ui::ToString(role);
+
+ if (state & (1 << ui::AX_STATE_BUSY))
+ result += " BUSY";
+ if (state & (1 << ui::AX_STATE_CHECKED))
+ result += " CHECKED";
+ if (state & (1 << ui::AX_STATE_COLLAPSED))
+ result += " COLLAPSED";
+ if (state & (1 << ui::AX_STATE_EXPANDED))
+ result += " EXPANDED";
+ if (state & (1 << ui::AX_STATE_FOCUSABLE))
+ result += " FOCUSABLE";
+ if (state & (1 << ui::AX_STATE_FOCUSED))
+ result += " FOCUSED";
+ if (state & (1 << ui::AX_STATE_HASPOPUP))
+ result += " HASPOPUP";
+ if (state & (1 << ui::AX_STATE_HOVERED))
+ result += " HOVERED";
+ if (state & (1 << ui::AX_STATE_INDETERMINATE))
+ result += " INDETERMINATE";
+ if (state & (1 << ui::AX_STATE_INVISIBLE))
+ result += " INVISIBLE";
+ if (state & (1 << ui::AX_STATE_LINKED))
+ result += " LINKED";
+ if (state & (1 << ui::AX_STATE_MULTISELECTABLE))
+ result += " MULTISELECTABLE";
+ if (state & (1 << ui::AX_STATE_OFFSCREEN))
+ result += " OFFSCREEN";
+ if (state & (1 << ui::AX_STATE_PRESSED))
+ result += " PRESSED";
+ if (state & (1 << ui::AX_STATE_PROTECTED))
+ result += " PROTECTED";
+ if (state & (1 << ui::AX_STATE_READ_ONLY))
+ result += " READONLY";
+ if (state & (1 << ui::AX_STATE_REQUIRED))
+ result += " REQUIRED";
+ if (state & (1 << ui::AX_STATE_SELECTABLE))
+ result += " SELECTABLE";
+ if (state & (1 << ui::AX_STATE_SELECTED))
+ result += " SELECTED";
+ if (state & (1 << ui::AX_STATE_VERTICAL))
+ result += " VERTICAL";
+ if (state & (1 << ui::AX_STATE_VISITED))
+ result += " VISITED";
+
+ result += " (" + IntToString(location.x()) + ", " +
+ IntToString(location.y()) + ")-(" +
+ IntToString(location.width()) + ", " +
+ IntToString(location.height()) + ")";
+
+ for (size_t i = 0; i < int_attributes.size(); ++i) {
+ std::string value = IntToString(int_attributes[i].second);
+ switch (int_attributes[i].first) {
+ case AX_ATTR_SCROLL_X:
+ result += " scroll_x=" + value;
+ break;
+ case AX_ATTR_SCROLL_X_MIN:
+ result += " scroll_x_min=" + value;
+ break;
+ case AX_ATTR_SCROLL_X_MAX:
+ result += " scroll_x_max=" + value;
+ break;
+ case AX_ATTR_SCROLL_Y:
+ result += " scroll_y=" + value;
+ break;
+ case AX_ATTR_SCROLL_Y_MIN:
+ result += " scroll_y_min=" + value;
+ break;
+ case AX_ATTR_SCROLL_Y_MAX:
+ result += " scroll_y_max=" + value;
+ break;
+ case AX_ATTR_HIERARCHICAL_LEVEL:
+ result += " level=" + value;
+ break;
+ case AX_ATTR_TEXT_SEL_START:
+ result += " sel_start=" + value;
+ break;
+ case AX_ATTR_TEXT_SEL_END:
+ result += " sel_end=" + value;
+ break;
+ case AX_ATTR_TABLE_ROW_COUNT:
+ result += " rows=" + value;
+ break;
+ case AX_ATTR_TABLE_COLUMN_COUNT:
+ result += " cols=" + value;
+ break;
+ case AX_ATTR_TABLE_CELL_COLUMN_INDEX:
+ result += " col=" + value;
+ break;
+ case AX_ATTR_TABLE_CELL_ROW_INDEX:
+ result += " row=" + value;
+ break;
+ case AX_ATTR_TABLE_CELL_COLUMN_SPAN:
+ result += " colspan=" + value;
+ break;
+ case AX_ATTR_TABLE_CELL_ROW_SPAN:
+ result += " rowspan=" + value;
+ break;
+ case AX_ATTR_TABLE_COLUMN_HEADER_ID:
+ result += " column_header_id=" + value;
+ break;
+ case AX_ATTR_TABLE_COLUMN_INDEX:
+ result += " column_index=" + value;
+ break;
+ case AX_ATTR_TABLE_HEADER_ID:
+ result += " header_id=" + value;
+ break;
+ case AX_ATTR_TABLE_ROW_HEADER_ID:
+ result += " row_header_id=" + value;
+ break;
+ case AX_ATTR_TABLE_ROW_INDEX:
+ result += " row_index=" + value;
+ break;
+ case AX_ATTR_TITLE_UI_ELEMENT:
+ result += " title_elem=" + value;
+ break;
+ case AX_ATTR_ACTIVEDESCENDANT_ID:
+ result += " activedescendant=" + value;
+ break;
+ case AX_ATTR_COLOR_VALUE_RED:
+ result += " color_value_red=" + value;
+ break;
+ case AX_ATTR_COLOR_VALUE_GREEN:
+ result += " color_value_green=" + value;
+ break;
+ case AX_ATTR_COLOR_VALUE_BLUE:
+ result += " color_value_blue=" + value;
+ break;
+ case AX_ATTR_TEXT_DIRECTION:
+ switch (int_attributes[i].second) {
+ case AX_TEXT_DIRECTION_LR:
+ default:
+ result += " text_direction=lr";
+ break;
+ case AX_TEXT_DIRECTION_RL:
+ result += " text_direction=rl";
+ break;
+ case AX_TEXT_DIRECTION_TB:
+ result += " text_direction=tb";
+ break;
+ case AX_TEXT_DIRECTION_BT:
+ result += " text_direction=bt";
+ break;
+ }
+ break;
+ case AX_INT_ATTRIBUTE_NONE:
+ break;
+ }
+ }
+
+ for (size_t i = 0; i < string_attributes.size(); ++i) {
+ std::string value = string_attributes[i].second;
+ switch (string_attributes[i].first) {
+ case AX_ATTR_DOC_URL:
+ result += " doc_url=" + value;
+ break;
+ case AX_ATTR_DOC_TITLE:
+ result += " doc_title=" + value;
+ break;
+ case AX_ATTR_DOC_MIMETYPE:
+ result += " doc_mimetype=" + value;
+ break;
+ case AX_ATTR_DOC_DOCTYPE:
+ result += " doc_doctype=" + value;
+ break;
+ case AX_ATTR_ACCESS_KEY:
+ result += " access_key=" + value;
+ break;
+ case AX_ATTR_ACTION:
+ result += " action=" + value;
+ break;
+ case AX_ATTR_DESCRIPTION:
+ result += " description=" + value;
+ break;
+ case AX_ATTR_DISPLAY:
+ result += " display=" + value;
+ break;
+ case AX_ATTR_HELP:
+ result += " help=" + value;
+ break;
+ case AX_ATTR_HTML_TAG:
+ result += " html_tag=" + value;
+ break;
+ case AX_ATTR_LIVE_RELEVANT:
+ result += " relevant=" + value;
+ break;
+ case AX_ATTR_LIVE_STATUS:
+ result += " live=" + value;
+ break;
+ case AX_ATTR_CONTAINER_LIVE_RELEVANT:
+ result += " container_relevant=" + value;
+ break;
+ case AX_ATTR_CONTAINER_LIVE_STATUS:
+ result += " container_live=" + value;
+ break;
+ case AX_ATTR_ROLE:
+ result += " role=" + value;
+ break;
+ case AX_ATTR_SHORTCUT:
+ result += " shortcut=" + value;
+ break;
+ case AX_ATTR_URL:
+ result += " url=" + value;
+ break;
+ case AX_ATTR_NAME:
+ result += " name=" + value;
+ break;
+ case AX_ATTR_VALUE:
+ result += " value=" + value;
+ break;
+ case AX_STRING_ATTRIBUTE_NONE:
+ break;
+ }
+ }
+
+ for (size_t i = 0; i < float_attributes.size(); ++i) {
+ std::string value = DoubleToString(float_attributes[i].second);
+ switch (float_attributes[i].first) {
+ case AX_ATTR_DOC_LOADING_PROGRESS:
+ result += " doc_progress=" + value;
+ break;
+ case AX_ATTR_VALUE_FOR_RANGE:
+ result += " value_for_range=" + value;
+ break;
+ case AX_ATTR_MAX_VALUE_FOR_RANGE:
+ result += " max_value=" + value;
+ break;
+ case AX_ATTR_MIN_VALUE_FOR_RANGE:
+ result += " min_value=" + value;
+ break;
+ case AX_FLOAT_ATTRIBUTE_NONE:
+ break;
+ }
+ }
+
+ for (size_t i = 0; i < bool_attributes.size(); ++i) {
+ std::string value = bool_attributes[i].second ? "true" : "false";
+ switch (bool_attributes[i].first) {
+ case AX_ATTR_DOC_LOADED:
+ result += " doc_loaded=" + value;
+ break;
+ case AX_ATTR_BUTTON_MIXED:
+ result += " mixed=" + value;
+ break;
+ case AX_ATTR_LIVE_ATOMIC:
+ result += " atomic=" + value;
+ break;
+ case AX_ATTR_LIVE_BUSY:
+ result += " busy=" + value;
+ break;
+ case AX_ATTR_CONTAINER_LIVE_ATOMIC:
+ result += " container_atomic=" + value;
+ break;
+ case AX_ATTR_CONTAINER_LIVE_BUSY:
+ result += " container_busy=" + value;
+ break;
+ case AX_ATTR_ARIA_READONLY:
+ result += " aria_readonly=" + value;
+ break;
+ case AX_ATTR_CAN_SET_VALUE:
+ result += " can_set_value=" + value;
+ break;
+ case AX_ATTR_UPDATE_LOCATION_ONLY:
+ result += " update_location_only=" + value;
+ break;
+ case AX_ATTR_CANVAS_HAS_FALLBACK:
+ result += " has_fallback=" + value;
+ break;
+ case AX_BOOL_ATTRIBUTE_NONE:
+ break;
+ }
+ }
+
+ for (size_t i = 0; i < intlist_attributes.size(); ++i) {
+ const std::vector<int32>& values = intlist_attributes[i].second;
+ switch (intlist_attributes[i].first) {
+ case AX_ATTR_INDIRECT_CHILD_IDS:
+ result += " indirect_child_ids=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_CONTROLS_IDS:
+ result += " controls_ids=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_DESCRIBEDBY_IDS:
+ result += " describedby_ids=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_FLOWTO_IDS:
+ result += " flowto_ids=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_LABELLEDBY_IDS:
+ result += " labelledby_ids=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_OWNS_IDS:
+ result += " owns_ids=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_LINE_BREAKS:
+ result += " line_breaks=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_CELL_IDS:
+ result += " cell_ids=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_UNIQUE_CELL_IDS:
+ result += " unique_cell_ids=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_CHARACTER_OFFSETS:
+ result += " character_offsets=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_WORD_STARTS:
+ result += " word_starts=" + IntVectorToString(values);
+ break;
+ case AX_ATTR_WORD_ENDS:
+ result += " word_ends=" + IntVectorToString(values);
+ break;
+ case AX_INT_LIST_ATTRIBUTE_NONE:
+ break;
+ }
+ }
+
+ if (!child_ids.empty())
+ result += " child_ids=" + IntVectorToString(child_ids);
+
+ return result;
}
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_node_data.h b/chromium/ui/accessibility/ax_node_data.h
index 1c5defb8b48..cd30fbb2c06 100644
--- a/chromium/ui/accessibility/ax_node_data.h
+++ b/chromium/ui/accessibility/ax_node_data.h
@@ -20,159 +20,15 @@ namespace ui {
// single web object, in a form that can be serialized and sent from
// one process to another.
struct AX_EXPORT AXNodeData {
- // Additional optional attributes that can be optionally attached to
- // a node.
- enum StringAttribute {
- // Document attributes.
- ATTR_DOC_URL,
- ATTR_DOC_TITLE,
- ATTR_DOC_MIMETYPE,
- ATTR_DOC_DOCTYPE,
-
- // Attributes that could apply to any node.
- ATTR_ACCESS_KEY,
- ATTR_ACTION,
- ATTR_CONTAINER_LIVE_RELEVANT,
- ATTR_CONTAINER_LIVE_STATUS,
- ATTR_DESCRIPTION,
- ATTR_DISPLAY,
- ATTR_HELP,
- ATTR_HTML_TAG,
- ATTR_NAME,
- ATTR_LIVE_RELEVANT,
- ATTR_LIVE_STATUS,
- ATTR_ROLE,
- ATTR_SHORTCUT,
- ATTR_URL,
- ATTR_VALUE,
- };
-
- enum IntAttribute {
- // Scrollable container attributes.
- ATTR_SCROLL_X,
- ATTR_SCROLL_X_MIN,
- ATTR_SCROLL_X_MAX,
- ATTR_SCROLL_Y,
- ATTR_SCROLL_Y_MIN,
- ATTR_SCROLL_Y_MAX,
-
- // Editable text attributes.
- ATTR_TEXT_SEL_START,
- ATTR_TEXT_SEL_END,
-
- // Table attributes.
- ATTR_TABLE_ROW_COUNT,
- ATTR_TABLE_COLUMN_COUNT,
- ATTR_TABLE_HEADER_ID,
-
- // Table row attributes.
- ATTR_TABLE_ROW_INDEX,
- ATTR_TABLE_ROW_HEADER_ID,
-
- // Table column attributes.
- ATTR_TABLE_COLUMN_INDEX,
- ATTR_TABLE_COLUMN_HEADER_ID,
-
- // Table cell attributes.
- ATTR_TABLE_CELL_COLUMN_INDEX,
- ATTR_TABLE_CELL_COLUMN_SPAN,
- ATTR_TABLE_CELL_ROW_INDEX,
- ATTR_TABLE_CELL_ROW_SPAN,
-
- // Tree control attributes.
- ATTR_HIERARCHICAL_LEVEL,
-
- // Relationships between this element and other elements.
- ATTR_TITLE_UI_ELEMENT,
-
- // Color value for AX_ROLE_COLOR_WELL, each component is 0..255
- ATTR_COLOR_VALUE_RED,
- ATTR_COLOR_VALUE_GREEN,
- ATTR_COLOR_VALUE_BLUE,
-
- // Inline text attributes.
- ATTR_TEXT_DIRECTION
- };
-
- enum FloatAttribute {
- // Document attributes.
- ATTR_DOC_LOADING_PROGRESS,
-
- // Range attributes.
- ATTR_VALUE_FOR_RANGE,
- ATTR_MIN_VALUE_FOR_RANGE,
- ATTR_MAX_VALUE_FOR_RANGE,
- };
-
- enum BoolAttribute {
- // Document attributes.
- ATTR_DOC_LOADED,
-
- // True if a checkbox or radio button is in the "mixed" state.
- ATTR_BUTTON_MIXED,
-
- // Live region attributes.
- ATTR_CONTAINER_LIVE_ATOMIC,
- ATTR_CONTAINER_LIVE_BUSY,
- ATTR_LIVE_ATOMIC,
- ATTR_LIVE_BUSY,
-
- // ARIA readonly flag.
- ATTR_ARIA_READONLY,
-
- // Writeable attributes
- ATTR_CAN_SET_VALUE,
-
- // If this is set, all of the other fields in this struct should
- // be ignored and only the locations should change.
- ATTR_UPDATE_LOCATION_ONLY,
-
- // Set on a canvas element if it has fallback content.
- ATTR_CANVAS_HAS_FALLBACK,
- };
-
- enum IntListAttribute {
- // Ids of nodes that are children of this node logically, but are
- // not children of this node in the tree structure. As an example,
- // a table cell is a child of a row, and an 'indirect' child of a
- // column.
- ATTR_INDIRECT_CHILD_IDS,
-
- // Character indices where line breaks occur.
- ATTR_LINE_BREAKS,
-
- // For a table, the cell ids in row-major order, with duplicate entries
- // when there's a rowspan or colspan, and with -1 for missing cells.
- // There are always exactly rows * columns entries.
- ATTR_CELL_IDS,
-
- // For a table, the unique cell ids in row-major order of their first
- // occurrence.
- ATTR_UNIQUE_CELL_IDS,
-
- // For inline text. This is the pixel position of the end of this
- // character within the bounding rectangle of this object, in the
- // direction given by ATTR_TEXT_DIRECTION. For example, for left-to-right
- // text, the first offset is the right coordinate of the first character
- // within the object's bounds, the second offset is the right coordinate
- // of the second character, and so on.
- ATTR_CHARACTER_OFFSETS,
-
- // For inline text. These int lists must be the same size; they represent
- // the start and end character index of each word within this text.
- ATTR_WORD_STARTS,
- ATTR_WORD_ENDS,
- };
-
AXNodeData();
virtual ~AXNodeData();
- void AddStringAttribute(StringAttribute attribute,
+ void AddStringAttribute(AXStringAttribute attribute,
const std::string& value);
- void AddIntAttribute(IntAttribute attribute, int value);
- void AddFloatAttribute(FloatAttribute attribute, float value);
- void AddBoolAttribute(BoolAttribute attribute, bool value);
- void AddIntListAttribute(IntListAttribute attribute,
+ void AddIntAttribute(AXIntAttribute attribute, int value);
+ void AddFloatAttribute(AXFloatAttribute attribute, float value);
+ void AddBoolAttribute(AXBoolAttribute attribute, bool value);
+ void AddIntListAttribute(AXIntListAttribute attribute,
const std::vector<int32>& value);
// Convenience functions, mainly for writing unit tests.
@@ -181,17 +37,20 @@ struct AX_EXPORT AXNodeData {
// Equivalent to AddStringAttribute(ATTR_VALUE, value).
void SetValue(std::string value);
+ // Return a string representation of this data, for debugging.
+ std::string ToString() const;
+
// This is a simple serializable struct. All member variables should be
// public and copyable.
int32 id;
AXRole role;
uint32 state;
gfx::Rect location;
- std::vector<std::pair<StringAttribute, std::string> > string_attributes;
- std::vector<std::pair<IntAttribute, int32> > int_attributes;
- std::vector<std::pair<FloatAttribute, float> > float_attributes;
- std::vector<std::pair<BoolAttribute, bool> > bool_attributes;
- std::vector<std::pair<IntListAttribute, std::vector<int32> > >
+ std::vector<std::pair<AXStringAttribute, std::string> > string_attributes;
+ std::vector<std::pair<AXIntAttribute, int32> > int_attributes;
+ std::vector<std::pair<AXFloatAttribute, float> > float_attributes;
+ std::vector<std::pair<AXBoolAttribute, bool> > bool_attributes;
+ std::vector<std::pair<AXIntListAttribute, std::vector<int32> > >
intlist_attributes;
std::vector<std::pair<std::string, std::string> > html_attributes;
std::vector<int32> child_ids;
diff --git a/chromium/ui/accessibility/ax_serializable_tree.cc b/chromium/ui/accessibility/ax_serializable_tree.cc
index ca52ab39a23..4e4da834d80 100644
--- a/chromium/ui/accessibility/ax_serializable_tree.cc
+++ b/chromium/ui/accessibility/ax_serializable_tree.cc
@@ -14,7 +14,7 @@ namespace ui {
// AXTreeSource abstraction and doesn't need to actually know about
// AXTree directly. Another AXTreeSource is used to abstract the Blink
// accessibility tree.
-class AX_EXPORT AXTreeSourceAdapter : public AXTreeSource<AXNode> {
+class AX_EXPORT AXTreeSourceAdapter : public AXTreeSource<const AXNode*> {
public:
AXTreeSourceAdapter(AXTree* tree) : tree_(tree) {}
virtual ~AXTreeSourceAdapter() {}
@@ -32,19 +32,30 @@ class AX_EXPORT AXTreeSourceAdapter : public AXTreeSource<AXNode> {
return node->id();
}
- virtual int GetChildCount(const AXNode* node) const OVERRIDE {
- return node->child_count();
- }
-
- virtual AXNode* GetChildAtIndex(const AXNode* node, int index)
- const OVERRIDE {
- return node->ChildAtIndex(index);
+ virtual void GetChildren(
+ const AXNode* node,
+ std::vector<const AXNode*>* out_children) const OVERRIDE {
+ for (int i = 0; i < node->child_count(); ++i)
+ out_children->push_back(node->ChildAtIndex(i));
}
virtual AXNode* GetParent(const AXNode* node) const OVERRIDE {
return node->parent();
}
+ virtual bool IsValid(const AXNode* node) const OVERRIDE {
+ return node != NULL;
+ }
+
+ virtual bool IsEqual(const AXNode* node1,
+ const AXNode* node2) const OVERRIDE {
+ return node1 == node2;
+ }
+
+ virtual const AXNode* GetNull() const OVERRIDE {
+ return NULL;
+ }
+
virtual void SerializeNode(
const AXNode* node, AXNodeData* out_data) const OVERRIDE {
*out_data = node->data();
@@ -64,7 +75,7 @@ AXSerializableTree::AXSerializableTree(const AXTreeUpdate& initial_state)
AXSerializableTree::~AXSerializableTree() {
}
-AXTreeSource<AXNode>* AXSerializableTree::CreateTreeSource() {
+AXTreeSource<const AXNode*>* AXSerializableTree::CreateTreeSource() {
return new AXTreeSourceAdapter(this);
}
diff --git a/chromium/ui/accessibility/ax_serializable_tree.h b/chromium/ui/accessibility/ax_serializable_tree.h
index c070565a8d1..994be7d1c28 100644
--- a/chromium/ui/accessibility/ax_serializable_tree.h
+++ b/chromium/ui/accessibility/ax_serializable_tree.h
@@ -18,7 +18,7 @@ class AX_EXPORT AXSerializableTree : public AXTree {
// Create a TreeSource adapter for this tree. The client gets ownership
// of the return value and should delete it when done.
- virtual AXTreeSource<AXNode>* CreateTreeSource();
+ virtual AXTreeSource<const AXNode*>* CreateTreeSource();
};
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_text_utils.cc b/chromium/ui/accessibility/ax_text_utils.cc
new file mode 100644
index 00000000000..08cd562ecd0
--- /dev/null
+++ b/chromium/ui/accessibility/ax_text_utils.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/accessibility/ax_text_utils.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+
+namespace ui {
+
+size_t FindAccessibleTextBoundary(const base::string16& text,
+ const std::vector<int>& line_breaks,
+ TextBoundaryType boundary,
+ size_t start_offset,
+ TextBoundaryDirection direction) {
+ size_t text_size = text.size();
+ DCHECK(start_offset <= text_size);
+
+ if (boundary == CHAR_BOUNDARY) {
+ if (direction == FORWARDS_DIRECTION && start_offset < text_size)
+ return start_offset + 1;
+ else
+ return start_offset;
+ } else if (boundary == LINE_BOUNDARY) {
+ if (direction == FORWARDS_DIRECTION) {
+ for (size_t j = 0; j < line_breaks.size(); ++j) {
+ size_t line_break = line_breaks[j] >= 0 ? line_breaks[j] : 0;
+ if (line_break > start_offset)
+ return line_break;
+ }
+ return text_size;
+ } else {
+ for (size_t j = line_breaks.size(); j != 0; --j) {
+ size_t line_break = line_breaks[j - 1] >= 0 ? line_breaks[j - 1] : 0;
+ if (line_break <= start_offset)
+ return line_break;
+ }
+ return 0;
+ }
+ }
+
+ size_t result = start_offset;
+ for (;;) {
+ size_t pos;
+ if (direction == FORWARDS_DIRECTION) {
+ if (result >= text_size)
+ return text_size;
+ pos = result;
+ } else {
+ if (result == 0)
+ return 0;
+ pos = result - 1;
+ }
+
+ switch (boundary) {
+ case CHAR_BOUNDARY:
+ case LINE_BOUNDARY:
+ NOTREACHED(); // These are handled above.
+ break;
+ case WORD_BOUNDARY:
+ if (IsWhitespace(text[pos]))
+ return result;
+ break;
+ case PARAGRAPH_BOUNDARY:
+ if (text[pos] == '\n')
+ return result;
+ break;
+ case SENTENCE_BOUNDARY:
+ if ((text[pos] == '.' || text[pos] == '!' || text[pos] == '?') &&
+ (pos == text_size - 1 || IsWhitespace(text[pos + 1]))) {
+ return result;
+ }
+ break;
+ case ALL_BOUNDARY:
+ default:
+ break;
+ }
+
+ if (direction == FORWARDS_DIRECTION) {
+ result++;
+ } else {
+ result--;
+ }
+ }
+}
+
+} // Namespace ui
diff --git a/chromium/ui/accessibility/ax_text_utils.h b/chromium/ui/accessibility/ax_text_utils.h
new file mode 100644
index 00000000000..2dc2d2f03ee
--- /dev/null
+++ b/chromium/ui/accessibility/ax_text_utils.h
@@ -0,0 +1,54 @@
+// 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 UI_ACCESSIBILITY_AX_TEXT_UTILS_H_
+#define UI_ACCESSIBILITY_AX_TEXT_UTILS_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "ui/accessibility/ax_export.h"
+
+namespace ui {
+
+// Boundaries that can be passed to FindAccessibleTextBoundary,
+// representing various visual boundaries in (potentially multi-line)
+// text. This is used by assistive technology in order to, for example,
+// retrieve the nearest word to the cursor, or retrieve all of the
+// text from the current cursor position to the end of the line.
+// These should be self-explanatory; "line" here refers to the visual
+// line as currently displayed (possibly affected by wrapping).
+enum TextBoundaryType {
+ CHAR_BOUNDARY,
+ WORD_BOUNDARY,
+ LINE_BOUNDARY,
+ SENTENCE_BOUNDARY,
+ PARAGRAPH_BOUNDARY,
+ ALL_BOUNDARY
+};
+
+// A direction when searching for the next boundary.
+enum TextBoundaryDirection {
+ // Search forwards for the next boundary past the starting position.
+ FORWARDS_DIRECTION,
+ // Search backwards for the previous boundary before the starting position.
+ BACKWARDS_DIRECTION
+};
+
+// Convenience method needed to implement platform-specific text
+// accessibility APIs like IAccessible2. Search forwards or backwards
+// (depending on |direction|) from the given |start_offset| until the
+// given boundary is found, and return the offset of that boundary,
+// using the vector of line break character offsets in |line_breaks|.
+size_t AX_EXPORT
+ FindAccessibleTextBoundary(const base::string16& text,
+ const std::vector<int>& line_breaks,
+ TextBoundaryType boundary,
+ size_t start_offset,
+ TextBoundaryDirection direction);
+
+} // namespace ui
+
+#endif // UI_ACCESSIBILITY_AX_TEXT_UTILS_H_
diff --git a/chromium/ui/accessibility/ax_tree.cc b/chromium/ui/accessibility/ax_tree.cc
index 5edb649ae85..359c8e2fe1d 100644
--- a/chromium/ui/accessibility/ax_tree.cc
+++ b/chromium/ui/accessibility/ax_tree.cc
@@ -12,10 +12,40 @@
namespace ui {
+namespace {
+
+std::string TreeToStringHelper(AXNode* node, int indent) {
+ std::string result = std::string(2 * indent, ' ');
+ result += node->data().ToString() + "\n";
+ for (int i = 0; i < node->child_count(); ++i)
+ result += TreeToStringHelper(node->ChildAtIndex(i), indent + 1);
+ return result;
+}
+
+} // anonymous namespace
+
+// Intermediate state to keep track of during a tree update.
+struct AXTreeUpdateState {
+ // During an update, this keeps track of all nodes that have been
+ // implicitly referenced as part of this update, but haven't been
+ // updated yet. It's an error if there are any pending nodes at the
+ // end of Unserialize.
+ std::set<AXNode*> pending_nodes;
+
+ // Keeps track of new nodes created during this update.
+ std::set<AXNode*> new_nodes;
+};
+
+AXTreeDelegate::AXTreeDelegate() {
+}
+
+AXTreeDelegate::~AXTreeDelegate() {
+}
+
AXTree::AXTree()
- : root_(NULL) {
+ : delegate_(NULL), root_(NULL) {
AXNodeData root;
- root.id = 0;
+ root.id = -1;
root.role = AX_ROLE_ROOT_WEB_AREA;
AXTreeUpdate initial_state;
@@ -24,7 +54,7 @@ AXTree::AXTree()
}
AXTree::AXTree(const AXTreeUpdate& initial_state)
- : root_(NULL) {
+ : delegate_(NULL), root_(NULL) {
CHECK(Unserialize(initial_state)) << error();
}
@@ -33,6 +63,10 @@ AXTree::~AXTree() {
DestroyNodeAndSubtree(root_);
}
+void AXTree::SetDelegate(AXTreeDelegate* delegate) {
+ delegate_ = delegate;
+}
+
AXNode* AXTree::GetRoot() const {
return root_;
}
@@ -43,7 +77,8 @@ AXNode* AXTree::GetFromId(int32 id) const {
}
bool AXTree::Unserialize(const AXTreeUpdate& update) {
- std::set<AXNode*> pending_nodes;
+ AXTreeUpdateState update_state;
+ int32 old_root_id = root_ ? root_->id() : 0;
if (update.node_id_to_clear != 0) {
AXNode* node = GetFromId(update.node_id_to_clear);
@@ -60,33 +95,56 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) {
DestroyNodeAndSubtree(node->ChildAtIndex(i));
std::vector<AXNode*> children;
node->SwapChildren(children);
- pending_nodes.insert(node);
+ update_state.pending_nodes.insert(node);
}
}
for (size_t i = 0; i < update.nodes.size(); ++i) {
- if (!UpdateNode(update.nodes[i], &pending_nodes))
+ if (!UpdateNode(update.nodes[i], &update_state))
return false;
}
- if (!pending_nodes.empty()) {
+ if (!update_state.pending_nodes.empty()) {
error_ = "Nodes left pending by the update:";
- for (std::set<AXNode*>::iterator iter = pending_nodes.begin();
- iter != pending_nodes.end(); ++iter) {
+ for (std::set<AXNode*>::iterator iter = update_state.pending_nodes.begin();
+ iter != update_state.pending_nodes.end(); ++iter) {
error_ += base::StringPrintf(" %d", (*iter)->id());
}
return false;
}
+ if (delegate_) {
+ for (size_t i = 0; i < update.nodes.size(); ++i) {
+ AXNode* node = GetFromId(update.nodes[i].id);
+ if (update_state.new_nodes.find(node) != update_state.new_nodes.end()) {
+ delegate_->OnNodeCreationFinished(node);
+ update_state.new_nodes.erase(node);
+ } else {
+ delegate_->OnNodeChangeFinished(node);
+ }
+ }
+ if (root_->id() != old_root_id)
+ delegate_->OnRootChanged(root_);
+ }
+
return true;
}
-AXNode* AXTree::CreateNode(AXNode* parent, int32 id, int32 index_in_parent) {
- return new AXNode(parent, id, index_in_parent);
+std::string AXTree::ToString() const {
+ return TreeToStringHelper(root_, 0);
+}
+
+AXNode* AXTree::CreateNode(
+ AXNode* parent, int32 id, int32 index_in_parent) {
+ AXNode* new_node = new AXNode(parent, id, index_in_parent);
+ id_map_[new_node->id()] = new_node;
+ if (delegate_)
+ delegate_->OnNodeCreated(new_node);
+ return new_node;
}
bool AXTree::UpdateNode(
- const AXNodeData& src, std::set<AXNode*>* pending_nodes) {
+ const AXNodeData& src, AXTreeUpdateState* update_state) {
// This method updates one node in the tree based on serialized data
// received in an AXTreeUpdate. See AXTreeUpdate for pre and post
// conditions.
@@ -95,30 +153,38 @@ bool AXTree::UpdateNode(
// of the tree is being swapped, or we're out of sync with the source
// and this is a serious error.
AXNode* node = GetFromId(src.id);
+ AXNode* new_node = NULL;
if (node) {
- pending_nodes->erase(node);
+ update_state->pending_nodes.erase(node);
+ node->SetData(src);
} else {
if (src.role != AX_ROLE_ROOT_WEB_AREA) {
error_ = base::StringPrintf(
"%d is not in the tree and not the new root", src.id);
return false;
}
- node = CreateAndInitializeNode(NULL, src.id, 0);
+ new_node = CreateNode(NULL, src.id, 0);
+ node = new_node;
+ update_state->new_nodes.insert(node);
+ node->SetData(src);
}
- // Set the node's data.
- node->SetData(src);
+ if (delegate_)
+ delegate_->OnNodeChanged(node);
// First, delete nodes that used to be children of this node but aren't
// anymore.
- if (!DeleteOldChildren(node, src.child_ids))
+ if (!DeleteOldChildren(node, src.child_ids)) {
+ if (new_node)
+ DestroyNodeAndSubtree(new_node);
return false;
+ }
// Now build a new children vector, reusing nodes when possible,
// and swap it in.
std::vector<AXNode*> new_children;
bool success = CreateNewChildVector(
- node, src.child_ids, &new_children, pending_nodes);
+ node, src.child_ids, &new_children, update_state);
node->SwapChildren(new_children);
// Update the root of the tree if needed.
@@ -127,26 +193,17 @@ bool AXTree::UpdateNode(
if (root_)
DestroyNodeAndSubtree(root_);
root_ = node;
- OnRootChanged();
}
return success;
}
-void AXTree::OnRootChanged() {
-}
-
-AXNode* AXTree::CreateAndInitializeNode(
- AXNode* parent, int32 id, int32 index_in_parent) {
- AXNode* node = CreateNode(parent, id, index_in_parent);
- id_map_[node->id()] = node;
- return node;
-}
-
void AXTree::DestroyNodeAndSubtree(AXNode* node) {
id_map_.erase(node->id());
for (int i = 0; i < node->child_count(); ++i)
DestroyNodeAndSubtree(node->ChildAtIndex(i));
+ if (delegate_)
+ delegate_->OnNodeWillBeDeleted(node);
node->Destroy();
}
@@ -178,7 +235,7 @@ bool AXTree::DeleteOldChildren(AXNode* node,
bool AXTree::CreateNewChildVector(AXNode* node,
const std::vector<int32> new_child_ids,
std::vector<AXNode*>* new_children,
- std::set<AXNode*>* pending_nodes) {
+ AXTreeUpdateState* update_state) {
bool success = true;
for (size_t i = 0; i < new_child_ids.size(); ++i) {
int32 child_id = new_child_ids[i];
@@ -199,8 +256,9 @@ bool AXTree::CreateNewChildVector(AXNode* node,
}
child->SetIndexInParent(index_in_parent);
} else {
- child = CreateAndInitializeNode(node, child_id, index_in_parent);
- pending_nodes->insert(child);
+ child = CreateNode(node, child_id, index_in_parent);
+ update_state->pending_nodes.insert(child);
+ update_state->new_nodes.insert(child);
}
new_children->push_back(child);
}
diff --git a/chromium/ui/accessibility/ax_tree.h b/chromium/ui/accessibility/ax_tree.h
index 773a773adf5..b4baac460cc 100644
--- a/chromium/ui/accessibility/ax_tree.h
+++ b/chromium/ui/accessibility/ax_tree.h
@@ -9,12 +9,57 @@
#include "base/containers/hash_tables.h"
#include "ui/accessibility/ax_export.h"
-#include "ui/accessibility/ax_tree.h"
#include "ui/accessibility/ax_tree_update.h"
namespace ui {
class AXNode;
+struct AXTreeUpdateState;
+
+// Used when you want to be notified when changes happen to the tree.
+//
+// Some of the notifications are called in the middle of an update operation.
+// Be careful, as the tree may be in an inconsistent state at this time;
+// don't walk the parents and children at this time:
+// OnNodeWillBeDeleted
+// OnNodeCreated
+// OnNodeChanged
+//
+// Other notifications are called at the end of an atomic update operation.
+// From these, it's safe to walk the tree and do any initialization that
+// assumes the tree is in a consistent state.
+// OnNodeCreationFinished
+// OnNodeChangeFinished
+// OnRootChanged
+class AX_EXPORT AXTreeDelegate {
+ public:
+ AXTreeDelegate();
+ virtual ~AXTreeDelegate();
+
+ // Called just before a node is deleted. Its id and data will be valid,
+ // but its links to parents and children are invalid. This is called
+ // in the middle of an update, the tree may be in an invalid state!
+ virtual void OnNodeWillBeDeleted(AXNode* node) = 0;
+
+ // Called immediately after a new node is created. The tree may be in
+ // the middle of an update, don't walk the parents and children now.
+ virtual void OnNodeCreated(AXNode* node) = 0;
+
+ // Called when a node changes its data or children. The tree may be in
+ // the middle of an update, don't walk the parents and children now.
+ virtual void OnNodeChanged(AXNode* node) = 0;
+
+ // Called for each new node at the end of an update operation,
+ // when the tree is in a consistent state.
+ virtual void OnNodeCreationFinished(AXNode* node) = 0;
+
+ // Called for each existing node that changed at the end of an update
+ // operation, when the tree is in a consistent state.
+ virtual void OnNodeChangeFinished(AXNode* node) = 0;
+
+ // Called at the end of an update operation when the root node changes.
+ virtual void OnRootChanged(AXNode* new_root) = 0;
+};
// AXTree is a live, managed tree of AXNode objects that can receive
// updates from another AXTreeSource via AXTreeUpdates, and it can be
@@ -27,6 +72,8 @@ class AX_EXPORT AXTree {
explicit AXTree(const AXTreeUpdate& initial_state);
virtual ~AXTree();
+ virtual void SetDelegate(AXTreeDelegate* delegate);
+
virtual AXNode* GetRoot() const;
virtual AXNode* GetFromId(int32 id) const;
@@ -35,29 +82,20 @@ class AX_EXPORT AXTree {
// should not be trusted any longer.
virtual bool Unserialize(const AXTreeUpdate& update);
+ // Return a multi-line indented string representation, for logging.
+ std::string ToString() const;
+
// A string describing the error from an unsuccessful Unserialize,
// for testing and debugging.
const std::string& error() { return error_; }
- protected:
- // Subclasses can override this to use a subclass of AXNode.
- virtual AXNode* CreateNode(AXNode* parent, int32 id, int32 index_in_parent);
+ private:
+ AXNode* CreateNode(AXNode* parent, int32 id, int32 index_in_parent);
// This is called from within Unserialize(), it returns true on success.
- // Subclasses can override this to do additional processing. |pending_nodes|
- // is updated to contain all nodes that have been implicitly referenced
- // as part of this update, but haven't been updated yet. It's an error if
- // there are any pending nodes at the end of Unserialize.
- virtual bool UpdateNode(const AXNodeData& src,
- std::set<AXNode*>* pending_nodes);
-
- // Subclasses can override this to do special behavior when the root changes.
- virtual void OnRootChanged();
+ bool UpdateNode(const AXNodeData& src, AXTreeUpdateState* update_state);
- private:
- // Convenience function to create a node and call Initialize on it.
- AXNode* CreateAndInitializeNode(
- AXNode* parent, int32 id, int32 index_in_parent);
+ void OnRootChanged();
// Call Destroy() on |node|, and delete it from the id map, and then
// call recursively on all nodes in its subtree.
@@ -73,13 +111,13 @@ class AX_EXPORT AXTree {
// pointers to child nodes, reusing existing nodes already in the tree
// if they exist, and creating otherwise. Reparenting is disallowed, so
// if the id already exists as the child of another node, that's an
- // error. Returns true on success, false on fatal error. See
- // UpdateNode, above, for an explanation of |pending_nodes|.
+ // error. Returns true on success, false on fatal error.
bool CreateNewChildVector(AXNode* node,
const std::vector<int32> new_child_ids,
std::vector<AXNode*>* new_children,
- std::set<AXNode*>* pending_nodes);
+ AXTreeUpdateState* update_state);
+ AXTreeDelegate* delegate_;
AXNode* root_;
base::hash_map<int32, AXNode*> id_map_;
std::string error_;
diff --git a/chromium/ui/accessibility/ax_tree_serializer.h b/chromium/ui/accessibility/ax_tree_serializer.h
index e6049964c76..f1cbfcea105 100644
--- a/chromium/ui/accessibility/ax_tree_serializer.h
+++ b/chromium/ui/accessibility/ax_tree_serializer.h
@@ -51,7 +51,7 @@ struct ClientTreeNode;
// because AXTreeSerializer always keeps track of what updates it's sent,
// it will never send an invalid update and the client tree will not break,
// it just may not contain all of the changes.
-template<class AXSourceNode>
+template<typename AXSourceNode>
class AXTreeSerializer {
public:
explicit AXTreeSerializer(AXTreeSource<AXSourceNode>* tree);
@@ -63,9 +63,13 @@ class AXTreeSerializer {
void Reset();
// Serialize all changes to |node| and append them to |out_update|.
- void SerializeChanges(const AXSourceNode* node,
+ void SerializeChanges(AXSourceNode node,
AXTreeUpdate* out_update);
+ // Delete the client subtree for this node, ensuring that the subtree
+ // is re-serialized.
+ void DeleteClientSubtree(AXSourceNode node);
+
// Only for unit testing. Normally this class relies on getting a call
// to SerializeChanges() every time the source tree changes. For unit
// testing, it's convenient to create a static AXTree for the initial
@@ -107,21 +111,21 @@ class AXTreeSerializer {
// LCA(source node 5, client node 5) is node 1.
// It's not node 5, because the two trees disagree on the parent of
// node 4, so the LCA is the first ancestor both trees agree on.
- const AXSourceNode* LeastCommonAncestor(const AXSourceNode* node,
- ClientTreeNode* client_node);
+ AXSourceNode LeastCommonAncestor(AXSourceNode node,
+ ClientTreeNode* client_node);
// Return the least common ancestor of |node| that's in the client tree.
// This just walks up the ancestors of |node| until it finds a node that's
// also in the client tree, and then calls LeastCommonAncestor on the
// source node and client node.
- const AXSourceNode* LeastCommonAncestor(const AXSourceNode* node);
+ AXSourceNode LeastCommonAncestor(AXSourceNode node);
// Walk the subtree rooted at |node| and return true if any nodes that
- // would be updated are being reparented. If so, update |lca| to point
+ // would be updated are being reparented. If so, update |out_lca| to point
// to the least common ancestor of the previous LCA and the previous
// parent of the node being reparented.
- bool AnyDescendantWasReparented(const AXSourceNode* node,
- const AXSourceNode** lca);
+ bool AnyDescendantWasReparented(AXSourceNode node,
+ AXSourceNode* out_lca);
ClientTreeNode* ClientTreeNodeById(int32 id);
@@ -130,7 +134,7 @@ class AXTreeSerializer {
void DeleteClientSubtree(ClientTreeNode* client_node);
// Helper function, called recursively with each new node to serialize.
- void SerializeChangedNodes(const AXSourceNode* node,
+ void SerializeChangedNodes(AXSourceNode node,
AXTreeUpdate* out_update);
// The tree source.
@@ -154,40 +158,43 @@ struct AX_EXPORT ClientTreeNode {
std::vector<ClientTreeNode*> children;
};
-template<class AXSourceNode>
+template<typename AXSourceNode>
AXTreeSerializer<AXSourceNode>::AXTreeSerializer(
AXTreeSource<AXSourceNode>* tree)
: tree_(tree),
client_root_(NULL) {
}
-template<class AXSourceNode>
+template<typename AXSourceNode>
AXTreeSerializer<AXSourceNode>::~AXTreeSerializer() {
Reset();
}
-template<class AXSourceNode>
+template<typename AXSourceNode>
void AXTreeSerializer<AXSourceNode>::Reset() {
- if (client_root_) {
- DeleteClientSubtree(client_root_);
- client_root_ = NULL;
- }
+ if (!client_root_)
+ return;
+
+ DeleteClientSubtree(client_root_);
+ client_id_map_.erase(client_root_->id);
+ delete client_root_;
+ client_root_ = NULL;
}
-template<class AXSourceNode>
+template<typename AXSourceNode>
void AXTreeSerializer<AXSourceNode>::ChangeTreeSourceForTesting(
AXTreeSource<AXSourceNode>* new_tree) {
tree_ = new_tree;
}
-template<class AXSourceNode>
-const AXSourceNode* AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
- const AXSourceNode* node, ClientTreeNode* client_node) {
- if (node == NULL || client_node == NULL)
- return NULL;
+template<typename AXSourceNode>
+AXSourceNode AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
+ AXSourceNode node, ClientTreeNode* client_node) {
+ if (!tree_->IsValid(node) || client_node == NULL)
+ return tree_->GetNull();
- std::vector<const AXSourceNode*> ancestors;
- while (node) {
+ std::vector<AXSourceNode> ancestors;
+ while (tree_->IsValid(node)) {
ancestors.push_back(node);
node = tree_->GetParent(node);
}
@@ -201,7 +208,7 @@ const AXSourceNode* AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
// Start at the root. Keep going until the source ancestor chain and
// client ancestor chain disagree. The last node before they disagree
// is the LCA.
- const AXSourceNode* lca = NULL;
+ AXSourceNode lca = tree_->GetNull();
int source_index = static_cast<int>(ancestors.size() - 1);
int client_index = static_cast<int>(client_ancestors.size() - 1);
while (source_index >= 0 && client_index >= 0) {
@@ -216,40 +223,41 @@ const AXSourceNode* AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
return lca;
}
-template<class AXSourceNode>
-const AXSourceNode* AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
- const AXSourceNode* node) {
+template<typename AXSourceNode>
+AXSourceNode AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
+ AXSourceNode node) {
// Walk up the tree until the source node's id also exists in the
// client tree, then call LeastCommonAncestor on those two nodes.
ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
- while (node && !client_node) {
+ while (tree_->IsValid(node) && !client_node) {
node = tree_->GetParent(node);
- if (node)
+ if (tree_->IsValid(node))
client_node = ClientTreeNodeById(tree_->GetId(node));
}
return LeastCommonAncestor(node, client_node);
}
-template<class AXSourceNode>
+template<typename AXSourceNode>
bool AXTreeSerializer<AXSourceNode>::AnyDescendantWasReparented(
- const AXSourceNode* node, const AXSourceNode** lca) {
+ AXSourceNode node, AXSourceNode* out_lca) {
bool result = false;
int id = tree_->GetId(node);
- int child_count = tree_->GetChildCount(node);
- for (int i = 0; i < child_count; ++i) {
- const AXSourceNode* child = tree_->GetChildAtIndex(node, i);
+ std::vector<AXSourceNode> children;
+ tree_->GetChildren(node, &children);
+ for (size_t i = 0; i < children.size(); ++i) {
+ AXSourceNode& child = children[i];
int child_id = tree_->GetId(child);
ClientTreeNode* client_child = ClientTreeNodeById(child_id);
if (client_child) {
if (!client_child->parent) {
// If the client child has no parent, it must have been the
// previous root node, so there is no LCA and we can exit early.
- *lca = NULL;
+ *out_lca = tree_->GetNull();
return true;
} else if (client_child->parent->id != id) {
// If the client child's parent is not this node, update the LCA
// and return true (reparenting was found).
- *lca = LeastCommonAncestor(*lca, client_child);
+ *out_lca = LeastCommonAncestor(*out_lca, client_child);
result = true;
} else {
// This child is already in the client tree, we won't
@@ -260,13 +268,13 @@ bool AXTreeSerializer<AXSourceNode>::AnyDescendantWasReparented(
}
// This is a new child or reparented child, check it recursively.
- if (AnyDescendantWasReparented(child, lca))
+ if (AnyDescendantWasReparented(child, out_lca))
result = true;
}
return result;
}
-template<class AXSourceNode>
+template<typename AXSourceNode>
ClientTreeNode* AXTreeSerializer<AXSourceNode>::ClientTreeNodeById(int32 id) {
base::hash_map<int32, ClientTreeNode*>::iterator iter =
client_id_map_.find(id);
@@ -276,19 +284,17 @@ ClientTreeNode* AXTreeSerializer<AXSourceNode>::ClientTreeNodeById(int32 id) {
return NULL;
}
-template<class AXSourceNode>
+template<typename AXSourceNode>
void AXTreeSerializer<AXSourceNode>::SerializeChanges(
- const AXSourceNode* node,
+ AXSourceNode node,
AXTreeUpdate* out_update) {
// If the node isn't in the client tree, we need to serialize starting
// with the LCA.
- const AXSourceNode* lca = LeastCommonAncestor(node);
+ AXSourceNode lca = LeastCommonAncestor(node);
if (client_root_) {
- // If the LCA is anything other than the node itself, tell the
- // client to first delete the subtree rooted at the LCA.
- bool need_delete = (lca != node);
- if (lca) {
+ bool need_delete = false;
+ if (tree_->IsValid(lca)) {
// Check for any reparenting within this subtree - if there is
// any, we need to delete and reserialize the whole subtree
// that contains the old and new parents of the reparented node.
@@ -296,13 +302,11 @@ void AXTreeSerializer<AXSourceNode>::SerializeChanges(
need_delete = true;
}
- if (lca == NULL) {
+ if (!tree_->IsValid(lca)) {
// If there's no LCA, just tell the client to destroy the whole
// tree and then we'll serialize everything from the new root.
out_update->node_id_to_clear = client_root_->id;
- DeleteClientSubtree(client_root_);
- client_id_map_.erase(client_root_->id);
- client_root_ = NULL;
+ Reset();
} else if (need_delete) {
// Otherwise, if we need to reserialize a subtree, first we need
// to delete those nodes in our client tree so that
@@ -313,30 +317,39 @@ void AXTreeSerializer<AXSourceNode>::SerializeChanges(
for (size_t i = 0; i < client_lca->children.size(); ++i) {
client_id_map_.erase(client_lca->children[i]->id);
DeleteClientSubtree(client_lca->children[i]);
+ delete client_lca->children[i];
}
client_lca->children.clear();
}
}
// Serialize from the LCA, or from the root if there isn't one.
- if (!lca)
+ if (!tree_->IsValid(lca))
lca = tree_->GetRoot();
SerializeChangedNodes(lca, out_update);
}
-template<class AXSourceNode>
+template<typename AXSourceNode>
+void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree(AXSourceNode node) {
+ ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
+ if (client_node)
+ DeleteClientSubtree(client_node);
+}
+
+template<typename AXSourceNode>
void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree(
ClientTreeNode* client_node) {
for (size_t i = 0; i < client_node->children.size(); ++i) {
client_id_map_.erase(client_node->children[i]->id);
DeleteClientSubtree(client_node->children[i]);
+ delete client_node->children[i];
}
client_node->children.clear();
}
-template<class AXSourceNode>
+template<typename AXSourceNode>
void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
- const AXSourceNode* node,
+ AXSourceNode node,
AXTreeUpdate* out_update) {
// This method has three responsibilities:
// 1. Serialize |node| into an AXNodeData, and append it to
@@ -353,10 +366,7 @@ void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
int id = tree_->GetId(node);
ClientTreeNode* client_node = ClientTreeNodeById(id);
if (!client_node) {
- if (client_root_) {
- client_id_map_.erase(client_root_->id);
- DeleteClientSubtree(client_root_);
- }
+ Reset();
client_root_ = new ClientTreeNode();
client_node = client_root_;
client_node->id = id;
@@ -368,9 +378,10 @@ void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
// Create a set of the child ids so we can quickly look
// up which children are new and which ones were there before.
base::hash_set<int32> new_child_ids;
- int child_count = tree_->GetChildCount(node);
- for (int i = 0; i < child_count; ++i) {
- AXSourceNode* child = tree_->GetChildAtIndex(node, i);
+ std::vector<AXSourceNode> children;
+ tree_->GetChildren(node, &children);
+ for (size_t i = 0; i < children.size(); ++i) {
+ AXSourceNode& child = children[i];
int new_child_id = tree_->GetId(child);
new_child_ids.insert(new_child_id);
@@ -395,6 +406,7 @@ void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
client_id_map_.erase(old_child_id);
DeleteClientSubtree(old_child);
+ delete old_child;
} else {
client_child_id_map[old_child_id] = old_child;
}
@@ -405,17 +417,22 @@ void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
out_update->nodes.push_back(AXNodeData());
AXNodeData* serialized_node = &out_update->nodes.back();
tree_->SerializeNode(node, serialized_node);
- if (serialized_node->id == client_root_->id)
+ // TODO(dmazzoni/dtseng): Make the serializer not depend on roles to identify
+ // the root.
+ if (serialized_node->id == client_root_->id &&
+ (serialized_node->role != AX_ROLE_ROOT_WEB_AREA &&
+ serialized_node->role != AX_ROLE_DESKTOP)) {
serialized_node->role = AX_ROLE_ROOT_WEB_AREA;
+ }
serialized_node->child_ids.clear();
// Iterate over the children, make note of the ones that are new
// and need to be serialized, and update the ClientTreeNode
// data structure to reflect the new tree.
- std::vector<AXSourceNode*> children_to_serialize;
- client_node->children.reserve(child_count);
- for (int i = 0; i < child_count; ++i) {
- AXSourceNode* child = tree_->GetChildAtIndex(node, i);
+ std::vector<AXSourceNode> children_to_serialize;
+ client_node->children.reserve(children.size());
+ for (size_t i = 0; i < children.size(); ++i) {
+ AXSourceNode& child = children[i];
int child_id = tree_->GetId(child);
// No need to do anything more with children that aren't new;
diff --git a/chromium/ui/accessibility/ax_tree_serializer_unittest.cc b/chromium/ui/accessibility/ax_tree_serializer_unittest.cc
index 3ec8de71f4f..a0c0fb76b1b 100644
--- a/chromium/ui/accessibility/ax_tree_serializer_unittest.cc
+++ b/chromium/ui/accessibility/ax_tree_serializer_unittest.cc
@@ -30,9 +30,9 @@ class AXTreeSerializerTest : public testing::Test {
AXTreeUpdate treedata1_;
scoped_ptr<AXSerializableTree> tree0_;
scoped_ptr<AXSerializableTree> tree1_;
- scoped_ptr<AXTreeSource<AXNode> > tree0_source_;
- scoped_ptr<AXTreeSource<AXNode> > tree1_source_;
- scoped_ptr<AXTreeSerializer<AXNode> > serializer_;
+ scoped_ptr<AXTreeSource<const AXNode*> > tree0_source_;
+ scoped_ptr<AXTreeSource<const AXNode*> > tree1_source_;
+ scoped_ptr<AXTreeSerializer<const AXNode*> > serializer_;
private:
DISALLOW_COPY_AND_ASSIGN(AXTreeSerializerTest);
@@ -48,7 +48,7 @@ void AXTreeSerializerTest::CreateTreeSerializer() {
// Serialize tree0 so that AXTreeSerializer thinks that its client
// is totally in sync.
tree0_source_.reset(tree0_->CreateTreeSource());
- serializer_.reset(new AXTreeSerializer<AXNode>(tree0_source_.get()));
+ serializer_.reset(new AXTreeSerializer<const AXNode*>(tree0_source_.get()));
AXTreeUpdate unused_update;
serializer_->SerializeChanges(tree0_->GetRoot(), &unused_update);
diff --git a/chromium/ui/accessibility/ax_tree_source.h b/chromium/ui/accessibility/ax_tree_source.h
index 35bdbb711c8..1f13d8f59f5 100644
--- a/chromium/ui/accessibility/ax_tree_source.h
+++ b/chromium/ui/accessibility/ax_tree_source.h
@@ -5,6 +5,8 @@
#ifndef UI_ACCESSIBILITY_AX_TREE_SOURCE_H_
#define UI_ACCESSIBILITY_AX_TREE_SOURCE_H_
+#include <vector>
+
#include "ui/accessibility/ax_node_data.h"
namespace ui {
@@ -17,21 +19,41 @@ namespace ui {
// as an AXNodeData. This is the primary interface to use when
// an accessibility tree will be sent over an IPC before being
// consumed.
-template<class AXNodeSource>
-class AX_EXPORT AXTreeSource {
+template<typename AXNodeSource>
+class AXTreeSource {
public:
virtual ~AXTreeSource() {}
- virtual AXNodeSource* GetRoot() const = 0;
- virtual AXNodeSource* GetFromId(int32 id) const = 0;
- virtual int32 GetId(const AXNodeSource* node) const = 0;
- virtual int GetChildCount(const AXNodeSource* node) const = 0;
- virtual AXNodeSource* GetChildAtIndex(const AXNodeSource* node, int index)
- const = 0;
- virtual AXNodeSource* GetParent(const AXNodeSource* node) const = 0;
+
+ // Get the root of the tree.
+ virtual AXNodeSource GetRoot() const = 0;
+
+ // Get a node by its id. If no node by that id exists in the tree, return a
+ // null node, i.e. one that will return false if you call IsValid on it.
+ virtual AXNodeSource GetFromId(int32 id) const = 0;
+
+ // Return the id of a node. All ids must be positive integers.
+ virtual int32 GetId(AXNodeSource node) const = 0;
+
+ // Append all children of |node| to |out_children|.
+ virtual void GetChildren(AXNodeSource node,
+ std::vector<AXNodeSource>* out_children) const = 0;
+
+ // Get the parent of |node|.
+ virtual AXNodeSource GetParent(AXNodeSource node) const = 0;
+
+ // Returns true if |node| is valid, and false if it's a null pointer or a
+ // node object representing the null pointer.
+ virtual bool IsValid(AXNodeSource node) const = 0;
+
+ // Returns true if two nodes are equal.
+ virtual bool IsEqual(AXNodeSource node1,
+ AXNodeSource node2) const = 0;
+
+ // Return a AXNodeSource representing null.
+ virtual AXNodeSource GetNull() const = 0;
// Serialize one node in the tree.
- virtual void SerializeNode(
- const AXNodeSource* node, AXNodeData* out_data) const = 0;
+ virtual void SerializeNode(AXNodeSource node, AXNodeData* out_data) const = 0;
protected:
AXTreeSource() {}
diff --git a/chromium/ui/accessibility/ax_tree_unittest.cc b/chromium/ui/accessibility/ax_tree_unittest.cc
index 9e21280a066..05369de316b 100644
--- a/chromium/ui/accessibility/ax_tree_unittest.cc
+++ b/chromium/ui/accessibility/ax_tree_unittest.cc
@@ -12,10 +12,58 @@
namespace ui {
+namespace {
+
+class FakeAXTreeDelegate : public AXTreeDelegate {
+ public:
+ virtual void OnNodeWillBeDeleted(AXNode* node) OVERRIDE {
+ deleted_ids_.push_back(node->id());
+ }
+
+ virtual void OnNodeCreated(AXNode* node) OVERRIDE {
+ created_ids_.push_back(node->id());
+ }
+
+ virtual void OnNodeChanged(AXNode* node) OVERRIDE {
+ changed_ids_.push_back(node->id());
+ }
+
+ virtual void OnNodeCreationFinished(AXNode* node) OVERRIDE {
+ creation_finished_ids_.push_back(node->id());
+ }
+
+ virtual void OnNodeChangeFinished(AXNode* node) OVERRIDE {
+ change_finished_ids_.push_back(node->id());
+ }
+
+ virtual void OnRootChanged(AXNode* new_root) OVERRIDE {
+ new_root_ids_.push_back(new_root->id());
+ }
+
+ const std::vector<int32>& deleted_ids() { return deleted_ids_; }
+ const std::vector<int32>& created_ids() { return created_ids_; }
+ const std::vector<int32>& creation_finished_ids() {
+ return creation_finished_ids_;
+ }
+ const std::vector<int32>& new_root_ids() { return new_root_ids_; }
+
+ private:
+ std::vector<int32> deleted_ids_;
+ std::vector<int32> created_ids_;
+ std::vector<int32> creation_finished_ids_;
+ std::vector<int32> changed_ids_;
+ std::vector<int32> change_finished_ids_;
+ std::vector<int32> new_root_ids_;
+};
+
+} // namespace
+
TEST(AXTreeTest, SerializeSimpleAXTree) {
AXNodeData root;
root.id = 1;
root.role = AX_ROLE_ROOT_WEB_AREA;
+ root.state = (1 << AX_STATE_FOCUSABLE) | (1 << AX_STATE_FOCUSED);
+ root.location = gfx::Rect(0, 0, 800, 600);
root.child_ids.push_back(2);
root.child_ids.push_back(3);
@@ -23,10 +71,13 @@ TEST(AXTreeTest, SerializeSimpleAXTree) {
button.id = 2;
button.role = AX_ROLE_BUTTON;
button.state = 0;
+ button.location = gfx::Rect(20, 20, 200, 30);
AXNodeData checkbox;
checkbox.id = 3;
checkbox.role = AX_ROLE_CHECK_BOX;
+ checkbox.state = 0;
+ checkbox.location = gfx::Rect(20, 50, 200, 30);
AXTreeUpdate initial_state;
initial_state.nodes.push_back(root);
@@ -34,29 +85,73 @@ TEST(AXTreeTest, SerializeSimpleAXTree) {
initial_state.nodes.push_back(checkbox);
AXSerializableTree src_tree(initial_state);
- scoped_ptr<AXTreeSource<AXNode> > tree_source(
+ scoped_ptr<AXTreeSource<const AXNode*> > tree_source(
src_tree.CreateTreeSource());
- AXTreeSerializer<AXNode> serializer(tree_source.get());
+ AXTreeSerializer<const AXNode*> serializer(tree_source.get());
AXTreeUpdate update;
serializer.SerializeChanges(src_tree.GetRoot(), &update);
AXTree dst_tree;
ASSERT_TRUE(dst_tree.Unserialize(update));
- AXNode* root_node = dst_tree.GetRoot();
+ const AXNode* root_node = dst_tree.GetRoot();
ASSERT_TRUE(root_node != NULL);
EXPECT_EQ(root.id, root_node->id());
EXPECT_EQ(root.role, root_node->data().role);
ASSERT_EQ(2, root_node->child_count());
- AXNode* button_node = root_node->ChildAtIndex(0);
+ const AXNode* button_node = root_node->ChildAtIndex(0);
EXPECT_EQ(button.id, button_node->id());
EXPECT_EQ(button.role, button_node->data().role);
- AXNode* checkbox_node = root_node->ChildAtIndex(1);
+ const AXNode* checkbox_node = root_node->ChildAtIndex(1);
EXPECT_EQ(checkbox.id, checkbox_node->id());
EXPECT_EQ(checkbox.role, checkbox_node->data().role);
+
+ EXPECT_EQ(
+ "id=1 rootWebArea FOCUSABLE FOCUSED (0, 0)-(800, 600) child_ids=2,3\n"
+ " id=2 button (20, 20)-(200, 30)\n"
+ " id=3 checkBox (20, 50)-(200, 30)\n",
+ dst_tree.ToString());
+}
+
+TEST(AXTreeTest, SerializeAXTreeUpdate) {
+ AXNodeData list;
+ list.id = 3;
+ list.role = AX_ROLE_LIST;
+ list.state = 0;
+ list.child_ids.push_back(4);
+ list.child_ids.push_back(5);
+ list.child_ids.push_back(6);
+
+ AXNodeData list_item_2;
+ list_item_2.id = 5;
+ list_item_2.role = AX_ROLE_LIST_ITEM;
+ list_item_2.state = 0;
+
+ AXNodeData list_item_3;
+ list_item_3.id = 6;
+ list_item_3.role = AX_ROLE_LIST_ITEM;
+ list_item_3.state = 0;
+
+ AXNodeData button;
+ button.id = 7;
+ button.role = AX_ROLE_BUTTON;
+ button.state = 0;
+
+ AXTreeUpdate update;
+ update.nodes.push_back(list);
+ update.nodes.push_back(list_item_2);
+ update.nodes.push_back(list_item_3);
+ update.nodes.push_back(button);
+
+ EXPECT_EQ(
+ "id=3 list (0, 0)-(0, 0) child_ids=4,5,6\n"
+ " id=5 listItem (0, 0)-(0, 0)\n"
+ " id=6 listItem (0, 0)-(0, 0)\n"
+ "id=7 button (0, 0)-(0, 0)\n",
+ update.ToString());
}
TEST(AXTreeTest, DeleteUnknownSubtreeFails) {
@@ -163,4 +258,41 @@ TEST(AXTreeTest, InvalidReparentingFails) {
ASSERT_EQ("Node 3 reparented from 2 to 1", tree.error());
}
+TEST(AXTreeTest, TreeDelegateIsCalled) {
+ AXTreeUpdate initial_state;
+ initial_state.nodes.resize(1);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
+
+ AXTree tree(initial_state);
+ AXTreeUpdate update;
+ update.node_id_to_clear = 1;
+ update.nodes.resize(2);
+ update.nodes[0].id = 2;
+ update.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
+ update.nodes[0].child_ids.push_back(3);
+ update.nodes[1].id = 3;
+
+ FakeAXTreeDelegate fake_delegate;
+ tree.SetDelegate(&fake_delegate);
+
+ EXPECT_TRUE(tree.Unserialize(update));
+
+ ASSERT_EQ(1U, fake_delegate.deleted_ids().size());
+ EXPECT_EQ(1, fake_delegate.deleted_ids()[0]);
+
+ ASSERT_EQ(2U, fake_delegate.created_ids().size());
+ EXPECT_EQ(2, fake_delegate.created_ids()[0]);
+ EXPECT_EQ(3, fake_delegate.created_ids()[1]);
+
+ ASSERT_EQ(2U, fake_delegate.creation_finished_ids().size());
+ EXPECT_EQ(2, fake_delegate.creation_finished_ids()[0]);
+ EXPECT_EQ(3, fake_delegate.creation_finished_ids()[1]);
+
+ ASSERT_EQ(1U, fake_delegate.new_root_ids().size());
+ EXPECT_EQ(2, fake_delegate.new_root_ids()[0]);
+
+ tree.SetDelegate(NULL);
+}
+
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_tree_update.cc b/chromium/ui/accessibility/ax_tree_update.cc
index dc785d9e06e..8e9d019349f 100644
--- a/chromium/ui/accessibility/ax_tree_update.cc
+++ b/chromium/ui/accessibility/ax_tree_update.cc
@@ -4,6 +4,9 @@
#include "ui/accessibility/ax_tree_update.h"
+#include "base/containers/hash_tables.h"
+#include "base/strings/string_number_conversions.h"
+
namespace ui {
AXTreeUpdate::AXTreeUpdate() : node_id_to_clear(0) {
@@ -12,4 +15,28 @@ AXTreeUpdate::AXTreeUpdate() : node_id_to_clear(0) {
AXTreeUpdate::~AXTreeUpdate() {
}
+std::string AXTreeUpdate::ToString() const {
+ std::string result;
+ if (node_id_to_clear != 0) {
+ result += "AXTreeUpdate: clear node " +
+ base::IntToString(node_id_to_clear) + "\n";
+ }
+
+ // The challenge here is that we want to indent the nodes being updated
+ // so that parent/child relationships are clear, but we don't have access
+ // to the rest of the tree for context, so we have to try to show the
+ // relative indentation of child nodes in this update relative to their
+ // parents.
+ base::hash_map<int32, int> id_to_indentation;
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ int indent = id_to_indentation[nodes[i].id];
+ result += std::string(2 * indent, ' ');
+ result += nodes[i].ToString() + "\n";
+ for (size_t j = 0; j < nodes[i].child_ids.size(); ++j)
+ id_to_indentation[nodes[i].child_ids[j]] = indent + 1;
+ }
+
+ return result;
+}
+
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_tree_update.h b/chromium/ui/accessibility/ax_tree_update.h
index 0280efe6449..314a78f823a 100644
--- a/chromium/ui/accessibility/ax_tree_update.h
+++ b/chromium/ui/accessibility/ax_tree_update.h
@@ -50,6 +50,9 @@ struct AX_EXPORT AXTreeUpdate {
// A vector of nodes to update, according to the rules above.
std::vector<AXNodeData> nodes;
+ // Return a multi-line indented string representation, for logging.
+ std::string ToString() const;
+
// TODO(dmazzoni): location changes
};
diff --git a/chromium/ui/accessibility/ax_view_state.cc b/chromium/ui/accessibility/ax_view_state.cc
new file mode 100644
index 00000000000..11044bc16dd
--- /dev/null
+++ b/chromium/ui/accessibility/ax_view_state.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/accessibility/ax_view_state.h"
+
+namespace ui {
+
+AXViewState::AXViewState()
+ : role(AX_ROLE_CLIENT),
+ selection_start(-1),
+ selection_end(-1),
+ index(-1),
+ count(-1),
+ state_(0) { }
+
+AXViewState::~AXViewState() { }
+
+void AXViewState::AddStateFlag(ui::AXState state_flag) {
+ state_ |= (1 << state_flag);
+}
+
+bool AXViewState::HasStateFlag(ui::AXState state_flag) const {
+ return 0 != (state_ & (1 << state_flag));
+}
+
+} // namespace ui
diff --git a/chromium/ui/accessibility/ax_view_state.h b/chromium/ui/accessibility/ax_view_state.h
new file mode 100644
index 00000000000..2f1aab09333
--- /dev/null
+++ b/chromium/ui/accessibility/ax_view_state.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_ACCESSIBILITY_AX_VIEW_STATE_H_
+#define UI_ACCESSIBILITY_AX_VIEW_STATE_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/strings/string16.h"
+#include "ui/accessibility/ax_enums.h"
+#include "ui/accessibility/ax_export.h"
+
+namespace ui {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AXViewState
+//
+// A cross-platform struct for storing the core accessibility information
+// that should be provided about any UI view to assistive technology (AT).
+//
+////////////////////////////////////////////////////////////////////////////////
+struct AX_EXPORT AXViewState {
+ public:
+ AXViewState();
+ ~AXViewState();
+
+ // Set or check bits in |state_|.
+ void AddStateFlag(ui::AXState state);
+ bool HasStateFlag(ui::AXState state) const;
+
+ // The view's state, a bitmask containing fields such as checked
+ // (for a checkbox) and protected (for a password text box). This "state"
+ // should not be confused with the class's name.
+ uint32 state() { return state_; }
+
+ // The view's role, like button or list box.
+ AXRole role;
+
+ // The view's name / label.
+ base::string16 name;
+
+ // The view's value, for example the text content.
+ base::string16 value;
+
+ // The name of the default action if the user clicks on this view.
+ base::string16 default_action;
+
+ // The keyboard shortcut to activate this view, if any.
+ base::string16 keyboard_shortcut;
+
+ // The selection start and end. Only applies to views with text content,
+ // such as a text box or combo box; start and end should be -1 otherwise.
+ int selection_start;
+ int selection_end;
+
+ // The selected item's index and the count of the number of items.
+ // Only applies to views with multiple choices like a listbox; both
+ // index and count should be -1 otherwise.
+ int index;
+ int count;
+
+ // An optional callback that can be used by accessibility clients to
+ // set the string value of this view. This only applies to roles where
+ // setting the value makes sense, like a text box. Not often used by
+ // screen readers, but often used by automation software to script
+ // things like logging into portals or filling forms.
+ //
+ // This callback is only valid for the lifetime of the view, and should
+ // be a safe no-op if the view is deleted. Typically, accessible views
+ // should use a WeakPtr when binding the callback.
+ base::Callback<void(const base::string16&)> set_value_callback;
+
+ private:
+ uint32 state_;
+};
+
+} // namespace ui
+
+#endif // UI_ACCESSIBILITY_AX_VIEW_STATE_H_