Commit 02cb40dc authored by Katie D's avatar Katie D Committed by Commit Bot

Use ARC++ windows in Chrome accessibility tree.

This completes work from go/a11y-arc++-window-mapping,
but does not yet remove the restriction that we only look at the
interesting subtree.

That restriction is still valid because we only want one
root node ID. If we assume that there is only one, we can remove
this restriction.

Bug: 891483
Change-Id: Ib9581be294de88c6ed4c4e9934536859edc9b4e7
Reviewed-on: https://chromium-review.googlesource.com/c/1319858
Commit-Queue: Katie Dektar <katie@chromium.org>
Reviewed-by: default avatarYuki Awano <yawano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609400}
parent 67b185c6
......@@ -139,19 +139,18 @@ void AccessibilityNodeInfoDataWrapper::PopulateAXRole(
// need additional information contained only in the CollectionInfo. The
// CollectionInfo should be an ancestor of this node.
AXCollectionInfoData* collection_info = nullptr;
for (AXNodeInfoData* container = node_ptr_; container;) {
if (!container)
for (const ArcAccessibilityInfoData* container =
static_cast<const ArcAccessibilityInfoData*>(this);
container;) {
if (!container || !container->IsNode())
break;
if (container->collection_info.get()) {
collection_info = container->collection_info.get();
if (container->IsNode() && container->GetNode()->collection_info.get()) {
collection_info = container->GetNode()->collection_info.get();
break;
}
ArcAccessibilityInfoData* container_data =
tree_source_->GetParent(tree_source_->GetFromId(container->id));
if (!container_data->IsNode())
break;
container = container_data->GetNode();
container =
tree_source_->GetParent(tree_source_->GetFromId(container->GetId()));
}
if (collection_info) {
......@@ -387,6 +386,10 @@ void AccessibilityNodeInfoDataWrapper::Serialize(
local_bounds.width(),
local_bounds.height());
}
// TODO(katie): Try using offset_container_id to make bounds calculations
// more efficient. If this is the child of the root, set the
// offset_container_id to be the root. Otherwise, set it to the first node
// child of the root.
// Integer properties.
int32_t val;
......
......@@ -239,6 +239,7 @@ TEST_F(ArcAccessibilityHelperBridgeTest, TaskAndAXTreeLifecycle) {
// Same task id, different package name.
event2->node_data.clear();
event2->node_data.push_back(arc::mojom::AccessibilityNodeInfoData::New());
event2->source_id = 3;
event2->node_data[0]->id = 3;
event2->node_data[0]->string_properties =
base::flat_map<arc::mojom::AccessibilityStringProperty, std::string>();
......
......@@ -31,7 +31,7 @@ AXTreeSourceArc::AXTreeSourceArc(Delegate* delegate)
: current_tree_serializer_(new AXTreeArcSerializer(this)),
root_id_(-1),
window_id_(-1),
focused_node_id_(-1),
focused_id_(-1),
is_notification_(false),
delegate_(delegate) {}
......@@ -61,10 +61,30 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
continue;
auto it = event_data->node_data[i]->int_list_properties->find(
AXIntListProperty::CHILD_NODE_IDS);
if (it != event_data->node_data[i]->int_list_properties->end()) {
all_children_map[event_data->node_data[i]->id] = it->second;
if (it == event_data->node_data[i]->int_list_properties->end())
continue;
all_children_map[event_data->node_data[i]->id] = it->second;
for (size_t j = 0; j < it->second.size(); ++j)
all_parent_map[it->second[j]] = event_data->node_data[i]->id;
}
if (event_data->window_data) {
for (size_t i = 0; i < event_data->window_data->size(); ++i) {
int32_t window_id = event_data->window_data->at(i)->window_id;
int32_t root_node_id = event_data->window_data->at(i)->root_node_id;
if (root_node_id) {
all_parent_map[root_node_id] = window_id;
all_children_map[window_id] = {root_node_id};
}
if (!event_data->window_data->at(i)->int_list_properties)
continue;
auto it = event_data->window_data->at(i)->int_list_properties->find(
mojom::AccessibilityWindowIntListProperty::CHILD_WINDOW_IDS);
if (it == event_data->window_data->at(i)->int_list_properties->end())
continue;
all_children_map[window_id].insert(all_children_map[window_id].begin(),
it->second.begin(), it->second.end());
for (size_t j = 0; j < it->second.size(); ++j)
all_parent_map[it->second[j]] = event_data->node_data[i]->id;
all_parent_map[it->second[j]] = window_id;
}
}
......@@ -103,8 +123,19 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
tree_map_[id] =
std::make_unique<AccessibilityNodeInfoDataWrapper>(this, node);
if (tree_map_[id]->IsFocused()) {
focused_node_id_ = id;
if (tree_map_[id]->IsFocused())
focused_id_ = id;
}
if (event_data->window_data) {
for (size_t i = 0; i < event_data->window_data->size(); ++i) {
int32_t id = event_data->window_data->at(i)->window_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;
AXWindowInfoData* window = event_data->window_data->at(i).get();
tree_map_[id] =
std::make_unique<AccessibilityWindowInfoDataWrapper>(this, window);
}
}
......@@ -117,6 +148,14 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
continue;
cached_computed_bounds_[id] = ComputeEnclosingBounds(tree_map_[id].get());
}
if (event_data->window_data) {
for (int i = event_data->window_data->size() - 1; i >= 0; --i) {
int32_t id = event_data->window_data->at(i)->window_id;
if (parent_map_.find(id) == parent_map_.end() && id != root_id_)
continue;
cached_computed_bounds_[id] = ComputeEnclosingBounds(tree_map_[id].get());
}
}
ExtensionMsg_AccessibilityEventBundleParams event_bundle;
event_bundle.tree_id = tree_id();
......@@ -147,10 +186,25 @@ void AXTreeSourceArc::NotifyActionResult(const ui::AXActionData& data,
bool AXTreeSourceArc::GetTreeData(ui::AXTreeData* data) const {
data->tree_id = tree_id();
if (focused_node_id_ >= 0)
data->focus_id = focused_node_id_;
else if (root_id_ >= 0)
data->focus_id = root_id_;
if (focused_id_ >= 0) {
data->focus_id = focused_id_;
} else if (root_id_ >= 0) {
ArcAccessibilityInfoData* root = GetRoot();
if (root->IsNode()) {
data->focus_id = root_id_;
} else {
std::vector<ArcAccessibilityInfoData*> children;
root->GetChildren(&children);
if (!children.empty()) {
for (size_t i = 0; i < children.size(); ++i) {
if (children[i]->IsNode()) {
data->focus_id = children[i]->GetId();
break;
}
}
}
}
}
return true;
}
......@@ -261,9 +315,11 @@ void AXTreeSourceArc::SerializeNode(ArcAccessibilityInfoData* info_data,
int32_t id = info_data->GetId();
out_data->id = id;
// TODO(katie): this may not hold true with Windows. it's probably the root
// window's root node which is a kRootWebArea.
if (id == root_id_)
// If the node is the root, or if the node's parent is the root window,
// the role should be rootWebArea.
if (info_data->IsNode() && id == root_id_)
out_data->role = ax::mojom::Role::kRootWebArea;
else if (info_data->IsNode() && parent_map_.at(id) == root_id_)
out_data->role = ax::mojom::Role::kRootWebArea;
else
info_data->PopulateAXRole(out_data);
......@@ -275,46 +331,44 @@ const gfx::Rect AXTreeSourceArc::GetBounds(ArcAccessibilityInfoData* info_data,
aura::Window* active_window) const {
DCHECK_NE(root_id_, -1);
gfx::Rect node_bounds = info_data->GetBounds();
if (active_window && info_data->GetId() == root_id_) {
// Top level window returns its bounds in dip.
aura::Window* toplevel_window = active_window->GetToplevelWindow();
float scale = toplevel_window->layer()->device_scale_factor();
views::Widget* widget =
views::Widget::GetWidgetForNativeView(active_window);
DCHECK(widget);
DCHECK(widget->widget_delegate());
DCHECK(widget->widget_delegate()->GetContentsView());
const gfx::Rect bounds =
widget->widget_delegate()->GetContentsView()->GetBoundsInScreen();
// Bounds of root node is relative to its container, i.e. contents view
// (ShellSurfaceBase).
node_bounds.Offset(
static_cast<int>(-1.0f * scale * static_cast<float>(bounds.x())),
static_cast<int>(-1.0f * scale * static_cast<float>(bounds.y())));
// On Android side, content is rendered without considering height of
// caption bar, e.g. content is rendered at y:0 instead of y:32 where 32 is
// height of caption bar. Add back height of caption bar here.
if (widget->IsMaximized()) {
node_bounds.Offset(
0, static_cast<int>(scale *
static_cast<float>(widget->non_client_view()
->frame_view()
->GetBoundsForClientView()
.y())));
}
gfx::Rect info_data_bounds = info_data->GetBounds();
return node_bounds;
if (!active_window) {
gfx::Rect root_bounds = GetRoot()->GetBounds();
info_data_bounds.Offset(-1 * root_bounds.x(), -1 * root_bounds.y());
return info_data_bounds;
}
// Bounds of non-root node is relative to its tree's root.
gfx::Rect root_bounds = GetFromId(root_id_)->GetBounds();
node_bounds.Offset(-1 * root_bounds.x(), -1 * root_bounds.y());
return node_bounds;
// TODO(katie): offset_container_id should work and we shouldn't have to
// go into this code path for each node.
aura::Window* toplevel_window = active_window->GetToplevelWindow();
float scale = toplevel_window->layer()->device_scale_factor();
views::Widget* widget = views::Widget::GetWidgetForNativeView(active_window);
DCHECK(widget);
DCHECK(widget->widget_delegate());
DCHECK(widget->widget_delegate()->GetContentsView());
const gfx::Rect bounds =
widget->widget_delegate()->GetContentsView()->GetBoundsInScreen();
// Bounds of root node is relative to its container, i.e. contents view
// (ShellSurfaceBase).
info_data_bounds.Offset(
static_cast<int>(-1.0f * scale * static_cast<float>(bounds.x())),
static_cast<int>(-1.0f * scale * static_cast<float>(bounds.y())));
// On Android side, content is rendered without considering height of
// caption bar, e.g. content is rendered at y:0 instead of y:32 where 32 is
// height of caption bar. Add back height of caption bar here.
if (widget->IsMaximized()) {
info_data_bounds.Offset(
0, static_cast<int>(scale *
static_cast<float>(widget->non_client_view()
->frame_view()
->GetBoundsForClientView()
.y())));
}
return info_data_bounds;
}
gfx::Rect AXTreeSourceArc::ComputeEnclosingBounds(
......@@ -363,7 +417,7 @@ void AXTreeSourceArc::Reset() {
cached_computed_bounds_.clear();
current_tree_serializer_.reset(new AXTreeArcSerializer(this));
root_id_ = -1;
focused_node_id_ = -1;
focused_id_ = -1;
extensions::AutomationEventRouter* router =
extensions::AutomationEventRouter::GetInstance();
if (!router)
......
......@@ -62,10 +62,10 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
// TODO(katie): should these be "friended" or "protected" instead?
ArcAccessibilityInfoData* GetRoot() const override;
ArcAccessibilityInfoData* GetFromId(int32_t id) const override;
void SerializeNode(ArcAccessibilityInfoData* node,
void SerializeNode(ArcAccessibilityInfoData* info_data,
ui::AXNodeData* out_data) const override;
ArcAccessibilityInfoData* GetParent(
ArcAccessibilityInfoData* node) const override;
ArcAccessibilityInfoData* info_data) const override;
// Returns bounds of a node which can be passed to AXNodeData.location. Bounds
// are returned in the following coordinates depending on whether it's root or
......@@ -74,7 +74,7 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
// - Non-root node is relative to the root node of this tree.
//
// focused_window is nullptr for notification.
const gfx::Rect GetBounds(ArcAccessibilityInfoData* node,
const gfx::Rect GetBounds(ArcAccessibilityInfoData* info_data,
aura::Window* focused_window) const;
bool is_notification() { return is_notification_; }
......@@ -84,21 +84,22 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
class FocusStealer;
// AXTreeSource overrides.
int32_t GetId(ArcAccessibilityInfoData* node) const override;
int32_t GetId(ArcAccessibilityInfoData* info_data) const override;
void GetChildren(
ArcAccessibilityInfoData* node,
ArcAccessibilityInfoData* info_data,
std::vector<ArcAccessibilityInfoData*>* out_children) const override;
bool IsValid(ArcAccessibilityInfoData* node) const override;
bool IsEqual(ArcAccessibilityInfoData* node1,
ArcAccessibilityInfoData* node2) const override;
bool IsValid(ArcAccessibilityInfoData* info_data) const override;
bool IsEqual(ArcAccessibilityInfoData* info_data1,
ArcAccessibilityInfoData* info_data2) const override;
ArcAccessibilityInfoData* GetNull() const override;
// Computes the smallest rect that encloses all of the descendants of |node|.
gfx::Rect ComputeEnclosingBounds(ArcAccessibilityInfoData* node) const;
// Computes the smallest rect that encloses all of the descendants of
// |info_data|.
gfx::Rect ComputeEnclosingBounds(ArcAccessibilityInfoData* info_data) const;
// Helper to recursively compute bounds for |node|. Returns true if non-empty
// bounds were encountered.
void ComputeEnclosingBoundsInternal(ArcAccessibilityInfoData* node,
// Helper to recursively compute bounds for |info_data|. Returns true if
// non-empty bounds were encountered.
void ComputeEnclosingBoundsInternal(ArcAccessibilityInfoData* info_data,
gfx::Rect& computed_bounds) const;
// AXHostDelegate overrides.
......@@ -115,7 +116,7 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
std::unique_ptr<AXTreeArcSerializer> current_tree_serializer_;
int32_t root_id_;
int32_t window_id_;
int32_t focused_node_id_;
int32_t focused_id_;
bool is_notification_;
// A delegate that handles accessibility actions on behalf of this tree. The
......
......@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
#include "chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h"
#include "chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h"
#include "components/arc/common/accessibility_helper.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/platform/ax_android_constants.h"
......@@ -22,6 +23,9 @@ using AXNodeInfoData = mojom::AccessibilityNodeInfoData;
using AXRangeInfoData = mojom::AccessibilityRangeInfoData;
using AXStringListProperty = mojom::AccessibilityStringListProperty;
using AXStringProperty = mojom::AccessibilityStringProperty;
using AXWindowInfoData = mojom::AccessibilityWindowInfoData;
using AXWindowIntListProperty = mojom::AccessibilityWindowIntListProperty;
using AXWindowStringProperty = mojom::AccessibilityWindowStringProperty;
void SetProperty(AXNodeInfoData* node, AXBooleanProperty prop, bool value) {
if (!node->boolean_properties) {
......@@ -46,6 +50,16 @@ void SetProperty(AXNodeInfoData* node, AXIntProperty prop, int32_t value) {
node->int_properties.value().insert(std::make_pair(prop, value));
}
void SetProperty(AXWindowInfoData* window,
AXWindowStringProperty prop,
const std::string& value) {
if (!window->string_properties) {
window->string_properties =
base::flat_map<AXWindowStringProperty, std::string>();
}
window->string_properties.value().insert(std::make_pair(prop, value));
}
void SetProperty(AXNodeInfoData* node,
AXIntListProperty prop,
const std::vector<int>& value) {
......@@ -56,6 +70,16 @@ void SetProperty(AXNodeInfoData* node,
node->int_list_properties.value().insert(std::make_pair(prop, value));
}
void SetProperty(AXWindowInfoData* window,
AXWindowIntListProperty prop,
const std::vector<int>& value) {
if (!window->int_list_properties) {
window->int_list_properties =
base::flat_map<AXWindowIntListProperty, std::vector<int>>();
}
window->int_list_properties.value().insert(std::make_pair(prop, value));
}
class AXTreeSourceArcTest : public testing::Test,
public AXTreeSourceArc::Delegate {
public:
......@@ -67,13 +91,13 @@ class AXTreeSourceArcTest : public testing::Test,
}
void CallGetChildren(
mojom::AccessibilityNodeInfoData* node,
AXNodeInfoData* node,
std::vector<ArcAccessibilityInfoData*>* out_children) const {
AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node);
tree_->GetChildren(&node_data, out_children);
}
void CallSerializeNode(mojom::AccessibilityNodeInfoData* node,
void CallSerializeNode(AXNodeInfoData* node,
std::unique_ptr<ui::AXNodeData>* out_data) const {
ASSERT_TRUE(out_data);
AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node);
......@@ -81,10 +105,22 @@ class AXTreeSourceArcTest : public testing::Test,
tree_->SerializeNode(&node_data, out_data->get());
}
void CallSerializeWindow(AXWindowInfoData* window,
std::unique_ptr<ui::AXNodeData>* out_data) const {
ASSERT_TRUE(out_data);
AccessibilityWindowInfoDataWrapper window_data(tree_.get(), window);
*out_data = std::make_unique<ui::AXNodeData>();
tree_->SerializeNode(&window_data, out_data->get());
}
ArcAccessibilityInfoData* CallGetFromId(int32_t id) const {
return tree_->GetFromId(id);
}
bool CallGetTreeData(ui::AXTreeData* data) {
return tree_->GetTreeData(data);
}
private:
void OnAction(const ui::AXActionData& data) const override {}
......@@ -99,6 +135,12 @@ TEST_F(AXTreeSourceArcTest, ReorderChildrenByLayout) {
event->task_id = 1;
event->event_type = AXEventType::VIEW_FOCUSED;
event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* root_window = event->window_data->back().get();
root_window->window_id = 100;
root_window->root_node_id = 0;
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* root = event->node_data.back().get();
root->id = 0;
......@@ -247,8 +289,6 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
// Populate the tree source with the data.
CallNotifyAccessibilityEvent(event.get());
// Live edit name related attributes.
// No attributes.
std::unique_ptr<ui::AXNodeData> data;
CallSerializeNode(root, &data);
......@@ -262,7 +302,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
CallSerializeNode(root, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("", name);
EXPECT_EQ("", name);
// Text (non-empty).
root->string_properties->clear();
......@@ -271,7 +311,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
CallSerializeNode(root, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("label text", name);
EXPECT_EQ("label text", name);
// Content description (empty), text (non-empty).
SetProperty(root, AXStringProperty::CONTENT_DESCRIPTION, "");
......@@ -279,7 +319,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
CallSerializeNode(root, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("label text", name);
EXPECT_EQ("label text", name);
// Content description (non-empty), text (non-empty).
root->string_properties.value()[AXStringProperty::CONTENT_DESCRIPTION] =
......@@ -288,7 +328,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
CallSerializeNode(root, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("label content description", name);
EXPECT_EQ("label content description", name);
// Name from contents.
......@@ -321,8 +361,111 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
}
TEST_F(AXTreeSourceArcTest, AccessibleNameComputationWindow) {
auto event = AXEventData::New();
event->source_id = 0;
event->task_id = 1;
event->event_type = AXEventType::VIEW_FOCUSED;
event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* root = event->window_data->back().get();
root->window_id = 0;
CallNotifyAccessibilityEvent(event.get());
// Live edit name related attributes.
// No attributes.
std::unique_ptr<ui::AXNodeData> data;
CallSerializeWindow(root, &data);
std::string name;
ASSERT_FALSE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
// Title attribute
SetProperty(root, AXWindowStringProperty::TITLE, "window title");
CallSerializeWindow(root, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
EXPECT_EQ("window title", name);
}
TEST_F(AXTreeSourceArcTest, AccessibleNameComputationWindowWithChildren) {
auto event = AXEventData::New();
event->source_id = 3;
event->task_id = 1;
event->event_type = AXEventType::VIEW_FOCUSED;
event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* root = event->window_data->back().get();
root->window_id = 0;
root->root_node_id = 3;
SetProperty(root, AXWindowIntListProperty::CHILD_WINDOW_IDS, {2, 5});
SetProperty(root, AXWindowStringProperty::TITLE, "window title");
// Add a child window.
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* child = event->window_data->back().get();
child->window_id = 2;
child->root_node_id = 4;
SetProperty(child, AXWindowStringProperty::TITLE, "child window title");
// Add a child node.
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* node = event->node_data.back().get();
node->id = 3;
SetProperty(node, AXStringProperty::TEXT, "node text");
// Add a child node to the child window as well.
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* child_node = event->node_data.back().get();
child_node->id = 4;
SetProperty(child_node, AXStringProperty::TEXT, "child node text");
// Add a child window with no children as well.
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* child2 = event->window_data->back().get();
child2->window_id = 5;
SetProperty(child2, AXWindowStringProperty::TITLE, "child2 window title");
CallNotifyAccessibilityEvent(event.get());
std::unique_ptr<ui::AXNodeData> data;
std::string name;
CallSerializeWindow(root, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
EXPECT_EQ("window title", name);
EXPECT_NE(ax::mojom::Role::kRootWebArea, data->role);
CallSerializeWindow(child, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
EXPECT_EQ("child window title", name);
EXPECT_NE(ax::mojom::Role::kRootWebArea, data->role);
CallSerializeNode(node, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
EXPECT_EQ("node text", name);
EXPECT_EQ(ax::mojom::Role::kRootWebArea, data->role);
CallSerializeNode(child_node, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
EXPECT_EQ("child node text", name);
EXPECT_NE(ax::mojom::Role::kRootWebArea, data->role);
CallSerializeWindow(child2, &data);
ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
EXPECT_EQ("child2 window title", name);
EXPECT_NE(ax::mojom::Role::kRootWebArea, data->role);
}
// TODO(katie): Maybe remove this test when adding AccessibilityWindowInfoData
// support per go/a11y-arc++-window-mapping if it is no longer needed.
// support per go/a11y-arc++-window-mapping if it is no longer needed. This
// depends on if we can assume that each tree has exactly one root.
TEST_F(AXTreeSourceArcTest, MultipleNodeSubtrees) {
// Run several times to try source_id from root, middle, or leaf of the tree.
int tree_size = 4;
......@@ -383,4 +526,103 @@ TEST_F(AXTreeSourceArcTest, MultipleNodeSubtrees) {
}
}
TEST_F(AXTreeSourceArcTest, ComplexTreeStructure) {
int tree_size = 4;
int num_trees = 3;
auto event = AXEventData::New();
event->source_id = 4;
event->task_id = 1;
event->event_type = AXEventType::VIEW_FOCUSED;
event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* root_window = event->window_data->back().get();
// Pick large numbers for the IDs so as not to overlap.
root_window->window_id = 1000;
SetProperty(root_window, AXWindowIntListProperty::CHILD_WINDOW_IDS,
{100, 200, 300});
// Make three non-overlapping trees rooted at the same window. One tree has
// the source_id of interest. Each subtree has a root window, which has a
// root node with one child, and that child has two leaf children.
for (int i = 0; i < num_trees; i++) {
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* child_window = event->window_data->back().get();
child_window->window_id = (i + 1) * 100;
child_window->root_node_id = i * tree_size + 1;
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* root = event->node_data.back().get();
root->id = i * tree_size + 1;
root->window_id = (i + 1) * 100;
SetProperty(root, AXIntListProperty::CHILD_NODE_IDS,
std::vector<int>({i * tree_size + 2}));
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* child1 = event->node_data.back().get();
child1->id = i * tree_size + 2;
SetProperty(child1, AXIntListProperty::CHILD_NODE_IDS,
std::vector<int>({i * tree_size + 3, i * tree_size + 4}));
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* child2 = event->node_data.back().get();
child2->id = i * tree_size + 3;
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* child3 = event->node_data.back().get();
child3->id = i * tree_size + 4;
}
CallNotifyAccessibilityEvent(event.get());
// Check that each node subtree tree was added, and that it is correct.
std::vector<ArcAccessibilityInfoData*> children;
for (int i = 0; i < num_trees; i++) {
CallGetChildren(event->node_data.at(i * tree_size).get(), &children);
ASSERT_EQ(1U, children.size());
EXPECT_EQ(i * tree_size + 2, children[0]->GetId());
children.clear();
CallGetChildren(event->node_data.at(i * tree_size + 1).get(), &children);
ASSERT_EQ(2U, children.size());
EXPECT_EQ(i * tree_size + 3, children[0]->GetId());
EXPECT_EQ(i * tree_size + 4, children[1]->GetId());
children.clear();
}
}
TEST_F(AXTreeSourceArcTest, GetTreeDataAppliesFocus) {
auto event = AXEventData::New();
event->source_id = 5;
event->task_id = 1;
event->event_type = AXEventType::VIEW_FOCUSED;
event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* root = event->window_data->back().get();
root->window_id = 5;
SetProperty(root, AXWindowIntListProperty::CHILD_WINDOW_IDS, {1});
// Add a child window.
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* child = event->window_data->back().get();
child->window_id = 1;
CallNotifyAccessibilityEvent(event.get());
ui::AXTreeData data;
// Nothing should be focused when there are no nodes.
EXPECT_TRUE(CallGetTreeData(&data));
EXPECT_EQ(-1, data.focus_id);
// Add a child node.
root->root_node_id = 2;
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* node = event->node_data.back().get();
node->id = 2;
CallNotifyAccessibilityEvent(event.get());
EXPECT_TRUE(CallGetTreeData(&data));
EXPECT_EQ(2, data.focus_id);
}
} // 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