Commit cae9ffb0 authored by Katie D's avatar Katie D Committed by Commit Bot

Chrome can pick ARC++ tree depending on source_id.

This will allow us to pass more AccessibilityNodeInfoDatas even
if they belong to different windows, which is part of the upgrade
path planned for mapping ARC++ windows to Chrome OS.
See go/a11y-arc++-window-mapping > Upgrade path for more.

Bug: 891483
Change-Id: Ic7a1654691cec6d4229898d7d7aaeb3947953234
Reviewed-on: https://chromium-review.googlesource.com/1258207
Commit-Queue: Katie Dektar <katie@chromium.org>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Reviewed-by: default avatarYuki Awano <yawano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596276}
parent 8abd6839
...@@ -161,25 +161,53 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) { ...@@ -161,25 +161,53 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
// Next, we cache the nodes by id. During this process, we can detect the root // Next, we cache the nodes by id. During this process, we can detect the root
// node based upon the parent links we cached above. // node based upon the parent links we cached above.
// Finally, we cache each node's computed bounds, based on its descendants. // Finally, we cache each node's computed bounds, based on its descendants.
std::map<int32_t, int32_t> all_parent_map;
std::map<int32_t, std::vector<int32_t>> all_children_map;
for (size_t i = 0; i < event_data->node_data.size(); ++i) { for (size_t i = 0; i < event_data->node_data.size(); ++i) {
if (!event_data->node_data[i]->int_list_properties) if (!event_data->node_data[i]->int_list_properties)
continue; continue;
auto it = event_data->node_data[i]->int_list_properties->find( auto it = event_data->node_data[i]->int_list_properties->find(
AXIntListProperty::CHILD_NODE_IDS); AXIntListProperty::CHILD_NODE_IDS);
if (it != event_data->node_data[i]->int_list_properties->end()) { if (it != event_data->node_data[i]->int_list_properties->end()) {
all_children_map[event_data->node_data[i]->id] = it->second;
for (size_t j = 0; j < it->second.size(); ++j) for (size_t j = 0; j < it->second.size(); ++j)
parent_map_[it->second[j]] = event_data->node_data[i]->id; all_parent_map[it->second[j]] = event_data->node_data[i]->id;
}
}
// Now copy just the relevant subtree containing the source_id into the
// |parent_map_|.
// TODO(katie): This step can probably be removed and all items added
// directly to the parent map after window mapping is completed, because
// we can again assume that there is only one subtree with one root.
int32_t source_root = event_data->source_id;
// Walk up to the root from the source_id.
while (all_parent_map.find(source_root) != all_parent_map.end()) {
int32_t parent = all_parent_map[source_root];
source_root = parent;
}
root_id_ = source_root;
// Walk back down through children map to populate parent_map_.
std::stack<int32_t> stack;
stack.push(root_id_);
while (!stack.empty()) {
int32_t parent = stack.top();
stack.pop();
const std::vector<int32_t>& children = all_children_map[parent];
for (auto it = children.begin(); it != children.end(); ++it) {
parent_map_[*it] = parent;
stack.push(*it);
} }
} }
for (size_t i = 0; i < event_data->node_data.size(); ++i) { for (size_t i = 0; i < event_data->node_data.size(); ++i) {
int32_t id = event_data->node_data[i]->id; int32_t id = event_data->node_data[i]->id;
// Only map nodes in the parent_map and the root.
// This avoids adding other subtrees that are not interesting.
if (parent_map_.find(id) == parent_map_.end() && id != root_id_)
continue;
AXNodeInfoData* node = event_data->node_data[i].get(); AXNodeInfoData* node = event_data->node_data[i].get();
tree_map_[id] = node; tree_map_[id] = node;
if (parent_map_.find(id) == parent_map_.end()) {
CHECK_EQ(-1, root_id_) << "Duplicated root";
root_id_ = id;
}
if (GetProperty(node, AXBooleanProperty::FOCUSED)) { if (GetProperty(node, AXBooleanProperty::FOCUSED)) {
focused_node_id_ = id; focused_node_id_ = id;
...@@ -190,6 +218,9 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) { ...@@ -190,6 +218,9 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
// avoid an O(n^2) amount of work as the computed bounds uses descendant // avoid an O(n^2) amount of work as the computed bounds uses descendant
// bounds. // bounds.
for (int i = event_data->node_data.size() - 1; i >= 0; --i) { for (int i = event_data->node_data.size() - 1; i >= 0; --i) {
int32_t id = event_data->node_data[i]->id;
if (parent_map_.find(id) == parent_map_.end() && id != root_id_)
continue;
AXNodeInfoData* node = event_data->node_data[i].get(); AXNodeInfoData* node = event_data->node_data[i].get();
cached_computed_bounds_[node] = ComputeEnclosingBounds(node); cached_computed_bounds_[node] = ComputeEnclosingBounds(node);
} }
......
...@@ -77,6 +77,10 @@ class AXTreeSourceArcTest : public testing::Test, ...@@ -77,6 +77,10 @@ class AXTreeSourceArcTest : public testing::Test,
tree_->SerializeNode(node, out_data->get()); tree_->SerializeNode(node, out_data->get());
} }
mojom::AccessibilityNodeInfoData* CallGetFromId(int32_t id) const {
return tree_->GetFromId(id);
}
private: private:
void OnAction(const ui::AXActionData& data) const override {} void OnAction(const ui::AXActionData& data) const override {}
...@@ -271,4 +275,66 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) { ...@@ -271,4 +275,66 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
ASSERT_EQ("label content description", name); ASSERT_EQ("label content description", name);
} }
// TODO(katie): Maybe remove this test when adding AccessibilityWindowInfoData
// support per go/a11y-arc++-window-mapping if it is no longer needed.
TEST_F(AXTreeSourceArcTest, MultipleNodeSubtrees) {
// Run several times to try source_id from root, middle, or leaf of the tree.
int tree_size = 4;
for (int i = 0; i < 4; i++) {
auto event = AXEventData::New();
event->source_id = i + tree_size;
event->task_id = 1;
event->event_type = AXEventType::VIEW_FOCUSED;
// Make three non-overlapping trees. The middle tree in the list has the
// source_id of interest. Each tree has a root with one child, and that
// child has two leaf children.
int num_trees = 3;
for (int j = 0; j < num_trees; j++) {
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* root = event->node_data.back().get();
root->id = j * tree_size;
SetProperty(root, AXIntListProperty::CHILD_NODE_IDS,
std::vector<int>({j * tree_size + 1}));
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* child1 = event->node_data.back().get();
child1->id = j * tree_size + 1;
SetProperty(child1, AXIntListProperty::CHILD_NODE_IDS,
std::vector<int>({j * tree_size + 2, j * tree_size + 3}));
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* child2 = event->node_data.back().get();
child2->id = j * tree_size + 2;
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* child3 = event->node_data.back().get();
child3->id = j * tree_size + 3;
}
CallNotifyAccessibilityEvent(event.get());
// Check that only the middle tree was added, and that it is correct.
std::vector<AXNodeInfoData*> children;
CallGetChildren(event->node_data.at(tree_size).get(), &children);
ASSERT_EQ(1U, children.size());
EXPECT_EQ(5, children[0]->id);
children.clear();
CallGetChildren(event->node_data.at(tree_size + 1).get(), &children);
ASSERT_EQ(2U, children.size());
EXPECT_EQ(6, children[0]->id);
EXPECT_EQ(7, children[1]->id);
// The first and third roots are not part of the tree.
EXPECT_EQ(nullptr, CallGetFromId(0));
EXPECT_EQ(nullptr, CallGetFromId(1));
EXPECT_EQ(nullptr, CallGetFromId(2));
EXPECT_EQ(nullptr, CallGetFromId(3));
EXPECT_EQ(nullptr, CallGetFromId(8));
EXPECT_EQ(nullptr, CallGetFromId(9));
EXPECT_EQ(nullptr, CallGetFromId(10));
EXPECT_EQ(nullptr, CallGetFromId(11));
}
}
} // namespace arc } // namespace arc
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment