diff options
Diffstat (limited to 'chromium/content/browser/accessibility/browser_accessibility.cc')
-rw-r--r-- | chromium/content/browser/accessibility/browser_accessibility.cc | 242 |
1 files changed, 130 insertions, 112 deletions
diff --git a/chromium/content/browser/accessibility/browser_accessibility.cc b/chromium/content/browser/accessibility/browser_accessibility.cc index 64000eb7dff..a84e5a94575 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.cc +++ b/chromium/content/browser/accessibility/browser_accessibility.cc @@ -101,65 +101,24 @@ int GetBoundaryTextOffsetInsideBaseAnchor( void BrowserAccessibility::Init(BrowserAccessibilityManager* manager, ui::AXNode* node) { + DCHECK(manager); + DCHECK(node); manager_ = manager; node_ = node; } bool BrowserAccessibility::PlatformIsLeaf() const { - if (InternalChildCount() == 0) - return true; - - return PlatformIsLeafIncludingIgnored(); + // TODO(nektar): Remove in favor of IsLeaf. + return IsLeaf(); } bool BrowserAccessibility::PlatformIsLeafIncludingIgnored() const { - if (node()->children().size() == 0) - return true; - - // These types of objects may have children that we use as internal - // implementation details, but we want to expose them as leaves to platform - // accessibility APIs because screen readers might be confused if they find - // any children. - if (IsPlainTextField() || IsTextOnlyObject()) - return true; - - // Roles whose children are only presentational according to the ARIA and - // HTML5 Specs should be hidden from screen readers. - switch (GetRole()) { - // According to the ARIA and Core-AAM specs: - // https://w3c.github.io/aria/#button, - // https://www.w3.org/TR/core-aam-1.1/#exclude_elements - // button's children are presentational only and should be hidden from - // screen readers. However, we cannot enforce the leafiness of buttons - // because they may contain many rich, interactive descendants such as a day - // in a calendar, and screen readers will need to interact with these - // contents. See https://crbug.com/689204. - // So we decided to not enforce the leafiness of buttons and expose all - // children. The only exception to enforce leafiness is when the button has - // a single text child and to prevent screen readers from double speak. - case ax::mojom::Role::kButton: { - if (InternalChildCount() == 1 && - InternalGetFirstChild()->IsTextOnlyObject()) - return true; - return false; - } - case ax::mojom::Role::kDocCover: - case ax::mojom::Role::kGraphicsSymbol: - case ax::mojom::Role::kImage: - case ax::mojom::Role::kMeter: - case ax::mojom::Role::kScrollBar: - case ax::mojom::Role::kSlider: - case ax::mojom::Role::kSplitter: - case ax::mojom::Role::kProgressIndicator: - return true; - default: - return false; - } + return node()->IsLeafIncludingIgnored(); } bool BrowserAccessibility::CanFireEvents() const { // Allow events unless this object would be trimmed away. - return !PlatformIsChildOfLeafIncludingIgnored(); + return !IsChildOfLeaf(); } ui::AXPlatformNode* BrowserAccessibility::GetAXPlatformNode() const { @@ -179,11 +138,11 @@ uint32_t BrowserAccessibility::PlatformChildCount() const { } BrowserAccessibility* BrowserAccessibility::PlatformGetParent() const { - ui::AXNode* parent = node_->GetUnignoredParent(); + ui::AXNode* parent = node()->GetUnignoredParent(); if (parent) - return manager_->GetFromAXNode(parent); + return manager()->GetFromAXNode(parent); - return manager_->GetParentNodeFromParentTree(); + return manager()->GetParentNodeFromParentTree(); } BrowserAccessibility* BrowserAccessibility::PlatformGetFirstChild() const { @@ -249,11 +208,11 @@ bool BrowserAccessibility::IsIgnored() const { } bool BrowserAccessibility::IsTextOnlyObject() const { - return node_ && node_->IsText(); + return node()->IsText(); } bool BrowserAccessibility::IsLineBreakObject() const { - return node_ && node_->IsLineBreak(); + return node()->IsLineBreak(); } BrowserAccessibility* BrowserAccessibility::PlatformGetChild( @@ -274,18 +233,6 @@ BrowserAccessibility* BrowserAccessibility::PlatformGetChild( return result; } -bool BrowserAccessibility::PlatformIsChildOfLeafIncludingIgnored() const { - BrowserAccessibility* ancestor = InternalGetParent(); - - while (ancestor) { - if (ancestor->PlatformIsLeafIncludingIgnored()) - return true; - ancestor = ancestor->InternalGetParent(); - } - - return false; -} - BrowserAccessibility* BrowserAccessibility::PlatformGetClosestPlatformObject() const { BrowserAccessibility* platform_object = @@ -449,7 +396,7 @@ BrowserAccessibility::InternalChildrenEnd() const { } int32_t BrowserAccessibility::GetId() const { - return node_ ? node_->id() : -1; + return node()->id(); } gfx::RectF BrowserAccessibility::GetLocation() const { @@ -1077,11 +1024,11 @@ bool BrowserAccessibility::IsClickable() const { } bool BrowserAccessibility::IsTextField() const { - return IsPlainTextField() || IsRichTextField(); + return GetData().IsTextField(); } bool BrowserAccessibility::IsPasswordField() const { - return IsTextField() && HasState(ax::mojom::State::kProtected); + return GetData().IsPasswordField(); } bool BrowserAccessibility::IsPlainTextField() const { @@ -1089,8 +1036,7 @@ bool BrowserAccessibility::IsPlainTextField() const { } bool BrowserAccessibility::IsRichTextField() const { - return GetBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot) && - HasState(ax::mojom::State::kRichlyEditable); + return GetData().IsRichTextField(); } bool BrowserAccessibility::HasExplicitlyEmptyName() const { @@ -1199,18 +1145,7 @@ base::string16 BrowserAccessibility::GetHypertext() const { } base::string16 BrowserAccessibility::GetInnerText() const { - if (!InternalChildCount()) { - if (IsTextField()) - return GetString16Attribute(ax::mojom::StringAttribute::kValue); - return GetString16Attribute(ax::mojom::StringAttribute::kName); - } - - base::string16 text; - for (InternalChildIterator it = InternalChildrenBegin(); - it != InternalChildrenEnd(); ++it) { - text += (*it).GetInnerText(); - } - return text; + return base::UTF8ToUTF16(node()->GetInnerText()); } gfx::Rect BrowserAccessibility::RelativeToAbsoluteBounds( @@ -1488,10 +1423,43 @@ const ui::AXTreeData& BrowserAccessibility::GetTreeData() const { const ui::AXTree::Selection BrowserAccessibility::GetUnignoredSelection() const { - if (manager()) - return manager()->ax_tree()->GetUnignoredSelection(); - return ui::AXTree::Selection{-1, -1, -1, - ax::mojom::TextAffinity::kDownstream}; + DCHECK(manager()); + ui::AXTree::Selection selection = + manager()->ax_tree()->GetUnignoredSelection(); + + // "selection.anchor_offset" and "selection.focus_ofset" might need to be + // adjusted if the anchor or the focus nodes include ignored children. + const BrowserAccessibility* anchor_object = + manager()->GetFromID(selection.anchor_object_id); + if (anchor_object && !anchor_object->PlatformIsLeaf()) { + DCHECK_GE(selection.anchor_offset, 0); + if (size_t{selection.anchor_offset} < + anchor_object->node()->children().size()) { + const ui::AXNode* anchor_child = + anchor_object->node()->children()[selection.anchor_offset]; + DCHECK(anchor_child); + selection.anchor_offset = int{anchor_child->GetUnignoredIndexInParent()}; + } else { + selection.anchor_offset = anchor_object->GetChildCount(); + } + } + + const BrowserAccessibility* focus_object = + manager()->GetFromID(selection.focus_object_id); + if (focus_object && !focus_object->PlatformIsLeaf()) { + DCHECK_GE(selection.focus_offset, 0); + if (size_t{selection.focus_offset} < + focus_object->node()->children().size()) { + const ui::AXNode* focus_child = + focus_object->node()->children()[selection.focus_offset]; + DCHECK(focus_child); + selection.focus_offset = int{focus_child->GetUnignoredIndexInParent()}; + } else { + selection.focus_offset = focus_object->GetChildCount(); + } + } + + return selection; } ui::AXNodePosition::AXPositionInstance @@ -1521,7 +1489,7 @@ gfx::NativeViewAccessible BrowserAccessibility::GetParent() { } int BrowserAccessibility::GetChildCount() const { - return PlatformChildCount(); + return int{PlatformChildCount()}; } gfx::NativeViewAccessible BrowserAccessibility::ChildAtIndex(int index) { @@ -1565,15 +1533,31 @@ gfx::NativeViewAccessible BrowserAccessibility::GetPreviousSibling() { } bool BrowserAccessibility::IsChildOfLeaf() const { - BrowserAccessibility* ancestor = InternalGetParent(); - - while (ancestor) { - if (ancestor->PlatformIsLeaf()) - return true; - ancestor = ancestor->InternalGetParent(); + return node()->IsChildOfLeaf(); +} + +bool BrowserAccessibility::IsLeaf() const { + // According to the ARIA and Core-AAM specs: + // https://w3c.github.io/aria/#button, + // https://www.w3.org/TR/core-aam-1.1/#exclude_elements + // button's children are presentational only and should be hidden from + // screen readers. However, we cannot enforce the leafiness of buttons + // because they may contain many rich, interactive descendants such as a day + // in a calendar, and screen readers will need to interact with these + // contents. See https://crbug.com/689204. + // So we decided to not enforce the leafiness of buttons and expose all + // children. The only exception to enforce leafiness is when the button has + // a single text child and to prevent screen readers from double speak. + if (GetRole() == ax::mojom::Role::kButton) { + return InternalChildCount() == 1 && + InternalGetFirstChild()->IsTextOnlyObject(); } + return node()->IsLeaf(); +} - return false; +bool BrowserAccessibility::IsChildOfPlainTextField() const { + ui::AXNode* textfield_node = node()->GetTextFieldAncestor(); + return textfield_node && textfield_node->data().IsPlainTextField(); } gfx::NativeViewAccessible BrowserAccessibility::GetClosestPlatformObject() @@ -1705,7 +1689,7 @@ int BrowserAccessibility::GetIndexInParent() { // index at AXPlatformNodeBase. return -1; } - return node_ ? node_->GetUnignoredIndexInParent() : -1; + return node()->GetUnignoredIndexInParent(); } gfx::AcceleratedWidget @@ -1746,30 +1730,24 @@ base::Optional<bool> BrowserAccessibility::GetTableHasColumnOrRowHeaderNode() return node()->GetTableHasColumnOrRowHeaderNode(); } -std::vector<int32_t> BrowserAccessibility::GetColHeaderNodeIds() const { - std::vector<int32_t> result; - node()->GetTableCellColHeaderNodeIds(&result); - return result; +std::vector<ui::AXNode::AXID> BrowserAccessibility::GetColHeaderNodeIds() + const { + return node()->GetTableColHeaderNodeIds(); } -std::vector<int32_t> BrowserAccessibility::GetColHeaderNodeIds( +std::vector<ui::AXNode::AXID> BrowserAccessibility::GetColHeaderNodeIds( int col_index) const { - std::vector<int32_t> result; - node()->GetTableColHeaderNodeIds(col_index, &result); - return result; + return node()->GetTableColHeaderNodeIds(col_index); } -std::vector<int32_t> BrowserAccessibility::GetRowHeaderNodeIds() const { - std::vector<int32_t> result; - node()->GetTableCellRowHeaderNodeIds(&result); - return result; +std::vector<ui::AXNode::AXID> BrowserAccessibility::GetRowHeaderNodeIds() + const { + return node()->GetTableCellRowHeaderNodeIds(); } -std::vector<int32_t> BrowserAccessibility::GetRowHeaderNodeIds( +std::vector<ui::AXNode::AXID> BrowserAccessibility::GetRowHeaderNodeIds( int row_index) const { - std::vector<int32_t> result; - node()->GetTableRowHeaderNodeIds(row_index, &result); - return result; + return node()->GetTableRowHeaderNodeIds(row_index); } ui::AXPlatformNode* BrowserAccessibility::GetTableCaption() const { @@ -1871,9 +1849,49 @@ bool BrowserAccessibility::AccessibilityPerformAction( case ax::mojom::Action::kSetScrollOffset: manager_->SetScrollOffset(*this, data.target_point); return true; - case ax::mojom::Action::kSetSelection: - manager_->SetSelection(data); + case ax::mojom::Action::kSetSelection: { + // "data.anchor_offset" and "data.focus_ofset" might need to be adjusted + // if the anchor or the focus nodes include ignored children. + ui::AXActionData selection = data; + const BrowserAccessibility* anchor_object = + manager()->GetFromID(selection.anchor_node_id); + DCHECK(anchor_object); + if (!anchor_object->PlatformIsLeaf()) { + DCHECK_GE(selection.anchor_offset, 0); + const BrowserAccessibility* anchor_child = + anchor_object->InternalGetChild(uint32_t{selection.anchor_offset}); + if (anchor_child) { + selection.anchor_offset = + int{anchor_child->node()->index_in_parent()}; + selection.anchor_node_id = anchor_child->node()->parent()->id(); + } else { + // Since the child was not found, the only alternative is that this is + // an "after children" position. + selection.anchor_offset = + int{anchor_object->node()->children().size()}; + } + } + + const BrowserAccessibility* focus_object = + manager()->GetFromID(selection.focus_node_id); + DCHECK(focus_object); + if (!focus_object->PlatformIsLeaf()) { + DCHECK_GE(selection.focus_offset, 0); + const BrowserAccessibility* focus_child = + focus_object->InternalGetChild(uint32_t{selection.focus_offset}); + if (focus_child) { + selection.focus_offset = int{focus_child->node()->index_in_parent()}; + selection.focus_node_id = focus_child->node()->parent()->id(); + } else { + // Since the child was not found, the only alternative is that this is + // an "after children" position. + selection.focus_offset = int{focus_object->node()->children().size()}; + } + } + + manager_->SetSelection(selection); return true; + } case ax::mojom::Action::kSetValue: manager_->SetValue(*this, data.value); return true; |