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