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
......
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