blob: 1ff0d05ba5699ffa21eed2ee3c5c559d88f09eb5 [file] [log] [blame]
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/accessibility/aom_content_ax_tree.h"
#include <string>
#include "content/common/ax_content_node_data.h"
#include "content/renderer/accessibility/render_accessibility_impl.h"
#include "third_party/blink/public/web/web_ax_enums.h"
#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node.h"
namespace {
ax::mojom::BoolAttribute GetCorrespondingAXAttribute(
blink::WebAOMBoolAttribute attr) {
switch (attr) {
case blink::WebAOMBoolAttribute::AOM_ATTR_ATOMIC:
return ax::mojom::BoolAttribute::kLiveAtomic;
case blink::WebAOMBoolAttribute::AOM_ATTR_BUSY:
return ax::mojom::BoolAttribute::kBusy;
case blink::WebAOMBoolAttribute::AOM_ATTR_MODAL:
return ax::mojom::BoolAttribute::kModal;
case blink::WebAOMBoolAttribute::AOM_ATTR_SELECTED:
return ax::mojom::BoolAttribute::kSelected;
default:
return ax::mojom::BoolAttribute::kNone;
}
}
ax::mojom::IntAttribute GetCorrespondingAXAttribute(
blink::WebAOMIntAttribute attr) {
switch (attr) {
case blink::WebAOMIntAttribute::AOM_ATTR_COLUMN_COUNT:
return ax::mojom::IntAttribute::kTableColumnCount;
case blink::WebAOMIntAttribute::AOM_ATTR_COLUMN_INDEX:
return ax::mojom::IntAttribute::kTableColumnIndex;
case blink::WebAOMIntAttribute::AOM_ATTR_COLUMN_SPAN:
return ax::mojom::IntAttribute::kTableCellColumnSpan;
case blink::WebAOMIntAttribute::AOM_ATTR_HIERARCHICAL_LEVEL:
return ax::mojom::IntAttribute::kHierarchicalLevel;
case blink::WebAOMIntAttribute::AOM_ATTR_POS_IN_SET:
return ax::mojom::IntAttribute::kPosInSet;
case blink::WebAOMIntAttribute::AOM_ATTR_ROW_COUNT:
return ax::mojom::IntAttribute::kTableRowCount;
case blink::WebAOMIntAttribute::AOM_ATTR_ROW_INDEX:
return ax::mojom::IntAttribute::kTableRowIndex;
case blink::WebAOMIntAttribute::AOM_ATTR_ROW_SPAN:
return ax::mojom::IntAttribute::kTableCellRowSpan;
case blink::WebAOMIntAttribute::AOM_ATTR_SET_SIZE:
return ax::mojom::IntAttribute::kSetSize;
default:
return ax::mojom::IntAttribute::kNone;
}
}
ax::mojom::FloatAttribute GetCorrespondingAXAttribute(
blink::WebAOMFloatAttribute attr) {
switch (attr) {
case blink::WebAOMFloatAttribute::AOM_ATTR_VALUE_MIN:
return ax::mojom::FloatAttribute::kMinValueForRange;
case blink::WebAOMFloatAttribute::AOM_ATTR_VALUE_MAX:
return ax::mojom::FloatAttribute::kMaxValueForRange;
case blink::WebAOMFloatAttribute::AOM_ATTR_VALUE_NOW:
return ax::mojom::FloatAttribute::kValueForRange;
default:
return ax::mojom::FloatAttribute::kNone;
}
}
ax::mojom::StringAttribute GetCorrespondingAXAttribute(
blink::WebAOMStringAttribute attr) {
switch (attr) {
case blink::WebAOMStringAttribute::AOM_ATTR_AUTOCOMPLETE:
return ax::mojom::StringAttribute::kAutoComplete;
case blink::WebAOMStringAttribute::AOM_ATTR_KEY_SHORTCUTS:
return ax::mojom::StringAttribute::kKeyShortcuts;
case blink::WebAOMStringAttribute::AOM_ATTR_NAME:
return ax::mojom::StringAttribute::kName;
case blink::WebAOMStringAttribute::AOM_ATTR_PLACEHOLDER:
return ax::mojom::StringAttribute::kPlaceholder;
case blink::WebAOMStringAttribute::AOM_ATTR_ROLE_DESCRIPTION:
return ax::mojom::StringAttribute::kRoleDescription;
case blink::WebAOMStringAttribute::AOM_ATTR_VALUE_TEXT:
return ax::mojom::StringAttribute::kValue;
default:
return ax::mojom::StringAttribute::kNone;
}
}
ax::mojom::Restriction GetCorrespondingRestrictionFlag(
blink::WebAOMBoolAttribute attr) {
switch (attr) {
case blink::WebAOMBoolAttribute::AOM_ATTR_DISABLED:
return ax::mojom::Restriction::kDisabled;
case blink::WebAOMBoolAttribute::AOM_ATTR_READONLY:
return ax::mojom::Restriction::kReadOnly;
default:
return ax::mojom::Restriction::kNone;
}
}
ax::mojom::State GetCorrespondingStateFlag(blink::WebAOMBoolAttribute attr) {
switch (attr) {
case blink::WebAOMBoolAttribute::AOM_ATTR_EXPANDED:
return ax::mojom::State::kExpanded;
case blink::WebAOMBoolAttribute::AOM_ATTR_MULTILINE:
return ax::mojom::State::kMultiline;
case blink::WebAOMBoolAttribute::AOM_ATTR_MULTISELECTABLE:
return ax::mojom::State::kMultiselectable;
case blink::WebAOMBoolAttribute::AOM_ATTR_REQUIRED:
return ax::mojom::State::kRequired;
default:
return ax::mojom::State::kNone;
}
}
} // namespace
namespace content {
AomContentAxTree::AomContentAxTree(RenderFrameImpl* render_frame)
: render_frame_(render_frame) {}
bool AomContentAxTree::ComputeAccessibilityTree() {
AXContentTreeUpdate content_tree_update;
RenderAccessibilityImpl::SnapshotAccessibilityTree(
render_frame_, &content_tree_update, ui::kAXModeComplete);
// Hack to convert between AXContentNodeData and AXContentTreeData to just
// AXNodeData and AXTreeData to preserve content specific attributes while
// still being able to use AXTree's Unserialize method.
ui::AXTreeUpdate tree_update;
tree_update.has_tree_data = content_tree_update.has_tree_data;
ui::AXTreeData* tree_data = &(content_tree_update.tree_data);
tree_update.tree_data = *tree_data;
tree_update.node_id_to_clear = content_tree_update.node_id_to_clear;
tree_update.root_id = content_tree_update.root_id;
tree_update.nodes.assign(content_tree_update.nodes.begin(),
content_tree_update.nodes.end());
return tree_.Unserialize(tree_update);
}
bool AomContentAxTree::GetBoolAttributeForAXNode(
int32_t ax_id,
blink::WebAOMBoolAttribute attr,
bool* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
if (GetCorrespondingRestrictionFlag(attr) != ax::mojom::Restriction::kNone) {
return GetRestrictionAttributeForAXNode(ax_id, attr, out_param);
} else if (GetCorrespondingStateFlag(attr) != ax::mojom::State::kNone) {
return GetStateAttributeForAXNode(ax_id, attr, out_param);
}
ax::mojom::BoolAttribute ax_attr = GetCorrespondingAXAttribute(attr);
return node->data().GetBoolAttribute(ax_attr, out_param);
}
bool AomContentAxTree::GetCheckedStateForAXNode(int32_t ax_id,
blink::WebString* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
ax::mojom::CheckedState checked_state = static_cast<ax::mojom::CheckedState>(
node->data().GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
*out_param = blink::WebString::FromUTF8(ui::ToString(checked_state));
return true;
}
bool AomContentAxTree::GetIntAttributeForAXNode(int32_t ax_id,
blink::WebAOMIntAttribute attr,
int32_t* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
ax::mojom::IntAttribute ax_attr = GetCorrespondingAXAttribute(attr);
return node->data().GetIntAttribute(ax_attr, out_param);
}
bool AomContentAxTree::GetRestrictionAttributeForAXNode(
int32_t ax_id,
blink::WebAOMBoolAttribute attr,
bool* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
// Disabled and readOnly are stored on the node data as an int attribute,
// which indicates which type of kRestriction applies.
ax::mojom::Restriction restriction = static_cast<ax::mojom::Restriction>(
node->data().GetIntAttribute(ax::mojom::IntAttribute::kRestriction));
ax::mojom::Restriction ax_attr = GetCorrespondingRestrictionFlag(attr);
*out_param = (restriction == ax_attr);
return true;
}
bool AomContentAxTree::GetFloatAttributeForAXNode(
int32_t ax_id,
blink::WebAOMFloatAttribute attr,
float* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
ax::mojom::FloatAttribute ax_attr = GetCorrespondingAXAttribute(attr);
return node->data().GetFloatAttribute(ax_attr, out_param);
}
bool AomContentAxTree::GetStateAttributeForAXNode(
int32_t ax_id,
blink::WebAOMBoolAttribute attr,
bool* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
*out_param = node->data().HasState(GetCorrespondingStateFlag(attr));
return true;
}
bool AomContentAxTree::GetStringAttributeForAXNode(
int32_t ax_id,
blink::WebAOMStringAttribute attr,
blink::WebString* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
std::string out_string;
if (node && node->data().GetStringAttribute(GetCorrespondingAXAttribute(attr),
&out_string)) {
*out_param = blink::WebString::FromUTF8(out_string.c_str());
return true;
}
return false;
}
bool AomContentAxTree::GetRoleForAXNode(int32_t ax_id,
blink::WebString* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
*out_param = blink::WebString::FromUTF8(ui::ToString(node->data().role));
return true;
}
bool AomContentAxTree::GetParentIdForAXNode(int32_t ax_id, int32_t* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node || !node->parent())
return false;
*out_param = node->parent()->id();
return true;
}
bool AomContentAxTree::GetFirstChildIdForAXNode(int32_t ax_id,
int32_t* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node || !node->child_count())
return false;
ui::AXNode* child = node->ChildAtIndex(0);
DCHECK(child);
*out_param = child->id();
return true;
}
bool AomContentAxTree::GetLastChildIdForAXNode(int32_t ax_id,
int32_t* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node || !node->child_count())
return false;
ui::AXNode* child = node->ChildAtIndex(node->child_count() - 1);
DCHECK(child);
*out_param = child->id();
return true;
}
bool AomContentAxTree::GetPreviousSiblingIdForAXNode(int32_t ax_id,
int32_t* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
int index_in_parent = node->index_in_parent();
// Assumption: only when this node is the first child, does it not have a
// previous sibling.
if (index_in_parent == 0)
return false;
ui::AXNode* sibling = node->parent()->ChildAtIndex(index_in_parent - 1);
DCHECK(sibling);
*out_param = sibling->id();
return true;
}
bool AomContentAxTree::GetNextSiblingIdForAXNode(int32_t ax_id,
int32_t* out_param) {
ui::AXNode* node = tree_.GetFromId(ax_id);
if (!node)
return false;
int index_in_parent = node->index_in_parent();
// Assumption: When this node is the last child, it does not have a next
// sibling.
if (index_in_parent == (node->parent()->child_count() - 1))
return false;
ui::AXNode* sibling = node->parent()->ChildAtIndex(index_in_parent + 1);
DCHECK(sibling);
*out_param = sibling->id();
return true;
}
} // namespace content