diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/ui/accessibility | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) | |
download | qtwebengine-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')
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_ |