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( ...@@ -139,19 +139,18 @@ void AccessibilityNodeInfoDataWrapper::PopulateAXRole(
// need additional information contained only in the CollectionInfo. The // need additional information contained only in the CollectionInfo. The
// CollectionInfo should be an ancestor of this node. // CollectionInfo should be an ancestor of this node.
AXCollectionInfoData* collection_info = nullptr; AXCollectionInfoData* collection_info = nullptr;
for (AXNodeInfoData* container = node_ptr_; container;) { for (const ArcAccessibilityInfoData* container =
if (!container) static_cast<const ArcAccessibilityInfoData*>(this);
container;) {
if (!container || !container->IsNode())
break; break;
if (container->collection_info.get()) { if (container->IsNode() && container->GetNode()->collection_info.get()) {
collection_info = container->collection_info.get(); collection_info = container->GetNode()->collection_info.get();
break; break;
} }
ArcAccessibilityInfoData* container_data = container =
tree_source_->GetParent(tree_source_->GetFromId(container->id)); tree_source_->GetParent(tree_source_->GetFromId(container->GetId()));
if (!container_data->IsNode())
break;
container = container_data->GetNode();
} }
if (collection_info) { if (collection_info) {
...@@ -387,6 +386,10 @@ void AccessibilityNodeInfoDataWrapper::Serialize( ...@@ -387,6 +386,10 @@ void AccessibilityNodeInfoDataWrapper::Serialize(
local_bounds.width(), local_bounds.width(),
local_bounds.height()); 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. // Integer properties.
int32_t val; int32_t val;
......
...@@ -239,6 +239,7 @@ TEST_F(ArcAccessibilityHelperBridgeTest, TaskAndAXTreeLifecycle) { ...@@ -239,6 +239,7 @@ TEST_F(ArcAccessibilityHelperBridgeTest, TaskAndAXTreeLifecycle) {
// Same task id, different package name. // Same task id, different package name.
event2->node_data.clear(); event2->node_data.clear();
event2->node_data.push_back(arc::mojom::AccessibilityNodeInfoData::New()); event2->node_data.push_back(arc::mojom::AccessibilityNodeInfoData::New());
event2->source_id = 3;
event2->node_data[0]->id = 3; event2->node_data[0]->id = 3;
event2->node_data[0]->string_properties = event2->node_data[0]->string_properties =
base::flat_map<arc::mojom::AccessibilityStringProperty, std::string>(); base::flat_map<arc::mojom::AccessibilityStringProperty, std::string>();
......
...@@ -31,7 +31,7 @@ AXTreeSourceArc::AXTreeSourceArc(Delegate* delegate) ...@@ -31,7 +31,7 @@ AXTreeSourceArc::AXTreeSourceArc(Delegate* delegate)
: current_tree_serializer_(new AXTreeArcSerializer(this)), : current_tree_serializer_(new AXTreeArcSerializer(this)),
root_id_(-1), root_id_(-1),
window_id_(-1), window_id_(-1),
focused_node_id_(-1), focused_id_(-1),
is_notification_(false), is_notification_(false),
delegate_(delegate) {} delegate_(delegate) {}
...@@ -61,11 +61,31 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) { ...@@ -61,11 +61,31 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
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())
continue;
all_children_map[event_data->node_data[i]->id] = it->second; 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)
all_parent_map[it->second[j]] = event_data->node_data[i]->id; 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]] = window_id;
}
} }
// Now copy just the relevant subtree containing the source_id into the // Now copy just the relevant subtree containing the source_id into the
...@@ -103,8 +123,19 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) { ...@@ -103,8 +123,19 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
tree_map_[id] = tree_map_[id] =
std::make_unique<AccessibilityNodeInfoDataWrapper>(this, node); std::make_unique<AccessibilityNodeInfoDataWrapper>(this, node);
if (tree_map_[id]->IsFocused()) { if (tree_map_[id]->IsFocused())
focused_node_id_ = id; 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) { ...@@ -117,6 +148,14 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
continue; continue;
cached_computed_bounds_[id] = ComputeEnclosingBounds(tree_map_[id].get()); 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; ExtensionMsg_AccessibilityEventBundleParams event_bundle;
event_bundle.tree_id = tree_id(); event_bundle.tree_id = tree_id();
...@@ -147,10 +186,25 @@ void AXTreeSourceArc::NotifyActionResult(const ui::AXActionData& data, ...@@ -147,10 +186,25 @@ void AXTreeSourceArc::NotifyActionResult(const ui::AXActionData& data,
bool AXTreeSourceArc::GetTreeData(ui::AXTreeData* data) const { bool AXTreeSourceArc::GetTreeData(ui::AXTreeData* data) const {
data->tree_id = tree_id(); data->tree_id = tree_id();
if (focused_node_id_ >= 0) if (focused_id_ >= 0) {
data->focus_id = focused_node_id_; data->focus_id = focused_id_;
else if (root_id_ >= 0) } else if (root_id_ >= 0) {
ArcAccessibilityInfoData* root = GetRoot();
if (root->IsNode()) {
data->focus_id = root_id_; 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; return true;
} }
...@@ -261,9 +315,11 @@ void AXTreeSourceArc::SerializeNode(ArcAccessibilityInfoData* info_data, ...@@ -261,9 +315,11 @@ void AXTreeSourceArc::SerializeNode(ArcAccessibilityInfoData* info_data,
int32_t id = info_data->GetId(); int32_t id = info_data->GetId();
out_data->id = id; out_data->id = id;
// TODO(katie): this may not hold true with Windows. it's probably the root // If the node is the root, or if the node's parent is the root window,
// window's root node which is a kRootWebArea. // the role should be rootWebArea.
if (id == root_id_) 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; out_data->role = ax::mojom::Role::kRootWebArea;
else else
info_data->PopulateAXRole(out_data); info_data->PopulateAXRole(out_data);
...@@ -275,15 +331,20 @@ const gfx::Rect AXTreeSourceArc::GetBounds(ArcAccessibilityInfoData* info_data, ...@@ -275,15 +331,20 @@ const gfx::Rect AXTreeSourceArc::GetBounds(ArcAccessibilityInfoData* info_data,
aura::Window* active_window) const { aura::Window* active_window) const {
DCHECK_NE(root_id_, -1); DCHECK_NE(root_id_, -1);
gfx::Rect node_bounds = info_data->GetBounds(); gfx::Rect info_data_bounds = info_data->GetBounds();
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;
}
if (active_window && info_data->GetId() == root_id_) { // TODO(katie): offset_container_id should work and we shouldn't have to
// Top level window returns its bounds in dip. // go into this code path for each node.
aura::Window* toplevel_window = active_window->GetToplevelWindow(); aura::Window* toplevel_window = active_window->GetToplevelWindow();
float scale = toplevel_window->layer()->device_scale_factor(); float scale = toplevel_window->layer()->device_scale_factor();
views::Widget* widget = views::Widget* widget = views::Widget::GetWidgetForNativeView(active_window);
views::Widget::GetWidgetForNativeView(active_window);
DCHECK(widget); DCHECK(widget);
DCHECK(widget->widget_delegate()); DCHECK(widget->widget_delegate());
DCHECK(widget->widget_delegate()->GetContentsView()); DCHECK(widget->widget_delegate()->GetContentsView());
...@@ -292,7 +353,7 @@ const gfx::Rect AXTreeSourceArc::GetBounds(ArcAccessibilityInfoData* info_data, ...@@ -292,7 +353,7 @@ const gfx::Rect AXTreeSourceArc::GetBounds(ArcAccessibilityInfoData* info_data,
// Bounds of root node is relative to its container, i.e. contents view // Bounds of root node is relative to its container, i.e. contents view
// (ShellSurfaceBase). // (ShellSurfaceBase).
node_bounds.Offset( 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.x())),
static_cast<int>(-1.0f * scale * static_cast<float>(bounds.y()))); static_cast<int>(-1.0f * scale * static_cast<float>(bounds.y())));
...@@ -300,21 +361,14 @@ const gfx::Rect AXTreeSourceArc::GetBounds(ArcAccessibilityInfoData* info_data, ...@@ -300,21 +361,14 @@ const gfx::Rect AXTreeSourceArc::GetBounds(ArcAccessibilityInfoData* info_data,
// caption bar, e.g. content is rendered at y:0 instead of y:32 where 32 is // 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. // height of caption bar. Add back height of caption bar here.
if (widget->IsMaximized()) { if (widget->IsMaximized()) {
node_bounds.Offset( info_data_bounds.Offset(
0, static_cast<int>(scale * 0, static_cast<int>(scale *
static_cast<float>(widget->non_client_view() static_cast<float>(widget->non_client_view()
->frame_view() ->frame_view()
->GetBoundsForClientView() ->GetBoundsForClientView()
.y()))); .y())));
} }
return info_data_bounds;
return node_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;
} }
gfx::Rect AXTreeSourceArc::ComputeEnclosingBounds( gfx::Rect AXTreeSourceArc::ComputeEnclosingBounds(
...@@ -363,7 +417,7 @@ void AXTreeSourceArc::Reset() { ...@@ -363,7 +417,7 @@ void AXTreeSourceArc::Reset() {
cached_computed_bounds_.clear(); cached_computed_bounds_.clear();
current_tree_serializer_.reset(new AXTreeArcSerializer(this)); current_tree_serializer_.reset(new AXTreeArcSerializer(this));
root_id_ = -1; root_id_ = -1;
focused_node_id_ = -1; focused_id_ = -1;
extensions::AutomationEventRouter* router = extensions::AutomationEventRouter* router =
extensions::AutomationEventRouter::GetInstance(); extensions::AutomationEventRouter::GetInstance();
if (!router) if (!router)
......
...@@ -62,10 +62,10 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*, ...@@ -62,10 +62,10 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
// TODO(katie): should these be "friended" or "protected" instead? // TODO(katie): should these be "friended" or "protected" instead?
ArcAccessibilityInfoData* GetRoot() const override; ArcAccessibilityInfoData* GetRoot() const override;
ArcAccessibilityInfoData* GetFromId(int32_t id) const override; ArcAccessibilityInfoData* GetFromId(int32_t id) const override;
void SerializeNode(ArcAccessibilityInfoData* node, void SerializeNode(ArcAccessibilityInfoData* info_data,
ui::AXNodeData* out_data) const override; ui::AXNodeData* out_data) const override;
ArcAccessibilityInfoData* GetParent( ArcAccessibilityInfoData* GetParent(
ArcAccessibilityInfoData* node) const override; ArcAccessibilityInfoData* info_data) const override;
// Returns bounds of a node which can be passed to AXNodeData.location. Bounds // 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 // are returned in the following coordinates depending on whether it's root or
...@@ -74,7 +74,7 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*, ...@@ -74,7 +74,7 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
// - Non-root node is relative to the root node of this tree. // - Non-root node is relative to the root node of this tree.
// //
// focused_window is nullptr for notification. // focused_window is nullptr for notification.
const gfx::Rect GetBounds(ArcAccessibilityInfoData* node, const gfx::Rect GetBounds(ArcAccessibilityInfoData* info_data,
aura::Window* focused_window) const; aura::Window* focused_window) const;
bool is_notification() { return is_notification_; } bool is_notification() { return is_notification_; }
...@@ -84,21 +84,22 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*, ...@@ -84,21 +84,22 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
class FocusStealer; class FocusStealer;
// AXTreeSource overrides. // AXTreeSource overrides.
int32_t GetId(ArcAccessibilityInfoData* node) const override; int32_t GetId(ArcAccessibilityInfoData* info_data) const override;
void GetChildren( void GetChildren(
ArcAccessibilityInfoData* node, ArcAccessibilityInfoData* info_data,
std::vector<ArcAccessibilityInfoData*>* out_children) const override; std::vector<ArcAccessibilityInfoData*>* out_children) const override;
bool IsValid(ArcAccessibilityInfoData* node) const override; bool IsValid(ArcAccessibilityInfoData* info_data) const override;
bool IsEqual(ArcAccessibilityInfoData* node1, bool IsEqual(ArcAccessibilityInfoData* info_data1,
ArcAccessibilityInfoData* node2) const override; ArcAccessibilityInfoData* info_data2) const override;
ArcAccessibilityInfoData* GetNull() const override; ArcAccessibilityInfoData* GetNull() const override;
// Computes the smallest rect that encloses all of the descendants of |node|. // Computes the smallest rect that encloses all of the descendants of
gfx::Rect ComputeEnclosingBounds(ArcAccessibilityInfoData* node) const; // |info_data|.
gfx::Rect ComputeEnclosingBounds(ArcAccessibilityInfoData* info_data) const;
// Helper to recursively compute bounds for |node|. Returns true if non-empty // Helper to recursively compute bounds for |info_data|. Returns true if
// bounds were encountered. // non-empty bounds were encountered.
void ComputeEnclosingBoundsInternal(ArcAccessibilityInfoData* node, void ComputeEnclosingBoundsInternal(ArcAccessibilityInfoData* info_data,
gfx::Rect& computed_bounds) const; gfx::Rect& computed_bounds) const;
// AXHostDelegate overrides. // AXHostDelegate overrides.
...@@ -115,7 +116,7 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*, ...@@ -115,7 +116,7 @@ class AXTreeSourceArc : public ui::AXTreeSource<ArcAccessibilityInfoData*,
std::unique_ptr<AXTreeArcSerializer> current_tree_serializer_; std::unique_ptr<AXTreeArcSerializer> current_tree_serializer_;
int32_t root_id_; int32_t root_id_;
int32_t window_id_; int32_t window_id_;
int32_t focused_node_id_; int32_t focused_id_;
bool is_notification_; bool is_notification_;
// A delegate that handles accessibility actions on behalf of this tree. The // A delegate that handles accessibility actions on behalf of this tree. The
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h" #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_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 "components/arc/common/accessibility_helper.mojom.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/platform/ax_android_constants.h" #include "ui/accessibility/platform/ax_android_constants.h"
...@@ -22,6 +23,9 @@ using AXNodeInfoData = mojom::AccessibilityNodeInfoData; ...@@ -22,6 +23,9 @@ using AXNodeInfoData = mojom::AccessibilityNodeInfoData;
using AXRangeInfoData = mojom::AccessibilityRangeInfoData; using AXRangeInfoData = mojom::AccessibilityRangeInfoData;
using AXStringListProperty = mojom::AccessibilityStringListProperty; using AXStringListProperty = mojom::AccessibilityStringListProperty;
using AXStringProperty = mojom::AccessibilityStringProperty; using AXStringProperty = mojom::AccessibilityStringProperty;
using AXWindowInfoData = mojom::AccessibilityWindowInfoData;
using AXWindowIntListProperty = mojom::AccessibilityWindowIntListProperty;
using AXWindowStringProperty = mojom::AccessibilityWindowStringProperty;
void SetProperty(AXNodeInfoData* node, AXBooleanProperty prop, bool value) { void SetProperty(AXNodeInfoData* node, AXBooleanProperty prop, bool value) {
if (!node->boolean_properties) { if (!node->boolean_properties) {
...@@ -46,6 +50,16 @@ void SetProperty(AXNodeInfoData* node, AXIntProperty prop, int32_t value) { ...@@ -46,6 +50,16 @@ void SetProperty(AXNodeInfoData* node, AXIntProperty prop, int32_t value) {
node->int_properties.value().insert(std::make_pair(prop, 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, void SetProperty(AXNodeInfoData* node,
AXIntListProperty prop, AXIntListProperty prop,
const std::vector<int>& value) { const std::vector<int>& value) {
...@@ -56,6 +70,16 @@ void SetProperty(AXNodeInfoData* node, ...@@ -56,6 +70,16 @@ void SetProperty(AXNodeInfoData* node,
node->int_list_properties.value().insert(std::make_pair(prop, value)); 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, class AXTreeSourceArcTest : public testing::Test,
public AXTreeSourceArc::Delegate { public AXTreeSourceArc::Delegate {
public: public:
...@@ -67,13 +91,13 @@ class AXTreeSourceArcTest : public testing::Test, ...@@ -67,13 +91,13 @@ class AXTreeSourceArcTest : public testing::Test,
} }
void CallGetChildren( void CallGetChildren(
mojom::AccessibilityNodeInfoData* node, AXNodeInfoData* node,
std::vector<ArcAccessibilityInfoData*>* out_children) const { std::vector<ArcAccessibilityInfoData*>* out_children) const {
AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node); AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node);
tree_->GetChildren(&node_data, out_children); tree_->GetChildren(&node_data, out_children);
} }
void CallSerializeNode(mojom::AccessibilityNodeInfoData* node, void CallSerializeNode(AXNodeInfoData* node,
std::unique_ptr<ui::AXNodeData>* out_data) const { std::unique_ptr<ui::AXNodeData>* out_data) const {
ASSERT_TRUE(out_data); ASSERT_TRUE(out_data);
AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node); AccessibilityNodeInfoDataWrapper node_data(tree_.get(), node);
...@@ -81,10 +105,22 @@ class AXTreeSourceArcTest : public testing::Test, ...@@ -81,10 +105,22 @@ class AXTreeSourceArcTest : public testing::Test,
tree_->SerializeNode(&node_data, out_data->get()); 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 { ArcAccessibilityInfoData* CallGetFromId(int32_t id) const {
return tree_->GetFromId(id); return tree_->GetFromId(id);
} }
bool CallGetTreeData(ui::AXTreeData* data) {
return tree_->GetTreeData(data);
}
private: private:
void OnAction(const ui::AXActionData& data) const override {} void OnAction(const ui::AXActionData& data) const override {}
...@@ -99,6 +135,12 @@ TEST_F(AXTreeSourceArcTest, ReorderChildrenByLayout) { ...@@ -99,6 +135,12 @@ TEST_F(AXTreeSourceArcTest, ReorderChildrenByLayout) {
event->task_id = 1; event->task_id = 1;
event->event_type = AXEventType::VIEW_FOCUSED; 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()); event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* root = event->node_data.back().get(); AXNodeInfoData* root = event->node_data.back().get();
root->id = 0; root->id = 0;
...@@ -247,8 +289,6 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) { ...@@ -247,8 +289,6 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
// Populate the tree source with the data. // Populate the tree source with the data.
CallNotifyAccessibilityEvent(event.get()); CallNotifyAccessibilityEvent(event.get());
// Live edit name related attributes.
// No attributes. // No attributes.
std::unique_ptr<ui::AXNodeData> data; std::unique_ptr<ui::AXNodeData> data;
CallSerializeNode(root, &data); CallSerializeNode(root, &data);
...@@ -262,7 +302,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) { ...@@ -262,7 +302,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
CallSerializeNode(root, &data); CallSerializeNode(root, &data);
ASSERT_TRUE( ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name)); data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("", name); EXPECT_EQ("", name);
// Text (non-empty). // Text (non-empty).
root->string_properties->clear(); root->string_properties->clear();
...@@ -271,7 +311,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) { ...@@ -271,7 +311,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
CallSerializeNode(root, &data); CallSerializeNode(root, &data);
ASSERT_TRUE( ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name)); data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("label text", name); EXPECT_EQ("label text", name);
// Content description (empty), text (non-empty). // Content description (empty), text (non-empty).
SetProperty(root, AXStringProperty::CONTENT_DESCRIPTION, ""); SetProperty(root, AXStringProperty::CONTENT_DESCRIPTION, "");
...@@ -279,7 +319,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) { ...@@ -279,7 +319,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
CallSerializeNode(root, &data); CallSerializeNode(root, &data);
ASSERT_TRUE( ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name)); data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("label text", name); EXPECT_EQ("label text", name);
// Content description (non-empty), text (non-empty). // Content description (non-empty), text (non-empty).
root->string_properties.value()[AXStringProperty::CONTENT_DESCRIPTION] = root->string_properties.value()[AXStringProperty::CONTENT_DESCRIPTION] =
...@@ -288,7 +328,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) { ...@@ -288,7 +328,7 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
CallSerializeNode(root, &data); CallSerializeNode(root, &data);
ASSERT_TRUE( ASSERT_TRUE(
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name)); data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("label content description", name); EXPECT_EQ("label content description", name);
// Name from contents. // Name from contents.
...@@ -321,8 +361,111 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) { ...@@ -321,8 +361,111 @@ TEST_F(AXTreeSourceArcTest, AccessibleNameComputation) {
data->GetStringAttribute(ax::mojom::StringAttribute::kName, &name)); 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 // 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) { TEST_F(AXTreeSourceArcTest, MultipleNodeSubtrees) {
// Run several times to try source_id from root, middle, or leaf of the tree. // Run several times to try source_id from root, middle, or leaf of the tree.
int tree_size = 4; int tree_size = 4;
...@@ -383,4 +526,103 @@ TEST_F(AXTreeSourceArcTest, MultipleNodeSubtrees) { ...@@ -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 } // 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