| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/accessibility/tree_generator.h" |
| |
| #include "ui/accessibility/ax_serializable_tree.h" |
| #include "ui/accessibility/ax_tree.h" |
| #include "ui/accessibility/ax_tree_data.h" |
| #include "ui/accessibility/ax_tree_id.h" |
| |
| namespace ui { |
| |
| TreeGenerator::TreeGenerator(int max_node_count, bool permutations) |
| : max_node_count_(max_node_count), |
| permutations_(permutations), |
| total_unique_tree_count_(0) { |
| unique_tree_count_by_size_.push_back(0); |
| for (int i = 1; i <= max_node_count; ++i) { |
| int unique_tree_count = UniqueTreeCountForNodeCount(i, permutations); |
| unique_tree_count_by_size_.push_back(unique_tree_count); |
| total_unique_tree_count_ += unique_tree_count; |
| } |
| } |
| |
| TreeGenerator::~TreeGenerator() = default; |
| |
| int TreeGenerator::UniqueTreeCount() const { |
| return total_unique_tree_count_; |
| } |
| |
| void TreeGenerator::BuildUniqueTree(int tree_index, AXTree* out_tree) const { |
| AXTreeUpdate update; |
| BuildUniqueTreeUpdate(tree_index, &update); |
| CHECK(out_tree->Unserialize(update)) << out_tree->error(); |
| } |
| |
| int TreeGenerator::IgnoredPermutationCountPerUniqueTree(int tree_index) const { |
| int unique_tree_count_so_far = 0; |
| for (int node_count = 1; node_count <= max_node_count_; ++node_count) { |
| int unique_tree_count = unique_tree_count_by_size_[node_count]; |
| if (tree_index - unique_tree_count_so_far < unique_tree_count) { |
| // Each node other than the root can be either ignored or not, |
| // so return 2 ^ (node_count - 1) |
| return 1 << (node_count - 1); |
| } |
| unique_tree_count_so_far += unique_tree_count; |
| } |
| |
| NOTREACHED(); |
| } |
| |
| void TreeGenerator::BuildUniqueTreeWithIgnoredNodes( |
| int tree_index, |
| int ignored_index, |
| std::optional<int> focused_node, |
| AXTree* out_tree) const { |
| // Enable the behavior whereby all focused nodes will be exposed to the |
| // platform accessibility layer. This behavior is currently disabled in |
| // production code, but is enabled in tests so that it could be tested |
| // thoroughly before it is turned on for all code. |
| // |
| // TODO(nektar): Turn this on in a followup patch. |
| // AXTree::SetFocusedNodeShouldNeverBeIgnored(); |
| |
| AXTreeUpdate update; |
| BuildUniqueTreeUpdate(tree_index, &update); |
| |
| int node_count = static_cast<int>(update.nodes.size()); |
| CHECK_GE(ignored_index, 0); |
| CHECK_LT(ignored_index, 1 << (node_count - 1)); |
| CHECK(!focused_node || *focused_node >= 0); |
| CHECK(!focused_node || *focused_node < node_count); |
| |
| for (int i = 0; i < node_count - 1; i++) { |
| if (ignored_index & (1 << i)) |
| update.nodes[i + 1].AddState(ax::mojom::State::kIgnored); |
| } |
| |
| if (focused_node) { |
| AXTreeData tree_data; |
| tree_data.tree_id = AXTreeID::CreateNewAXTreeID(); |
| tree_data.focused_tree_id = tree_data.tree_id; |
| tree_data.focus_id = update.nodes[*focused_node].id; |
| update.has_tree_data = true; |
| update.tree_data = tree_data; |
| } |
| |
| CHECK(out_tree->Unserialize(update)) << out_tree->error(); |
| } |
| |
| void TreeGenerator::BuildUniqueTreeUpdate(int tree_index, |
| AXTreeUpdate* out_update) const { |
| CHECK_LT(tree_index, total_unique_tree_count_); |
| |
| int unique_tree_count_so_far = 0; |
| for (int node_count = 1; node_count <= max_node_count_; ++node_count) { |
| int unique_tree_count = unique_tree_count_by_size_[node_count]; |
| if (tree_index - unique_tree_count_so_far < unique_tree_count) { |
| BuildUniqueTreeUpdateWithSize( |
| node_count, tree_index - unique_tree_count_so_far, out_update); |
| return; |
| } |
| unique_tree_count_so_far += unique_tree_count; |
| } |
| } |
| |
| void TreeGenerator::BuildUniqueTreeUpdateWithSize( |
| int node_count, |
| int tree_index, |
| AXTreeUpdate* out_update) const { |
| std::vector<int> indices; |
| std::vector<int> permuted; |
| int unique_tree_count = unique_tree_count_by_size_[node_count]; |
| CHECK_LT(tree_index, unique_tree_count); |
| |
| if (permutations_) { |
| // 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); |
| } |
| } else { |
| for (int i = 0; i < node_count; ++i) |
| permuted.push_back(i + 1); |
| } |
| |
| // Build an AXTreeUpdate. The first two nodes of the tree always |
| // go in the same place. |
| out_update->root_id = permuted[0]; |
| out_update->nodes.resize(node_count); |
| out_update->nodes[0].id = permuted[0]; |
| if (node_count > 1) { |
| out_update->nodes[0].child_ids.push_back(permuted[1]); |
| out_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) { |
| out_update->nodes[i].id = permuted[i]; |
| int parent_index = (tree_index % i); |
| tree_index /= i; |
| out_update->nodes[parent_index].child_ids.push_back(permuted[i]); |
| } |
| } |
| |
| } // namespace ui |