Commit 9742a747 authored by Ethan Jimenez's avatar Ethan Jimenez Committed by Commit Bot

Fix BrowserAccessibility::IsLeaf to consider a node's child tree

1. https://chromium-review.googlesource.com/2171436 introduced a change
   to how `AXPlatformNodeBase::IsLeaf` works; previously, a node would
   start by querying `AXPlatformNodeBase::GetChildCount` (which relies
   on `BrowserAccessibility::PlatformChildCount`) to check whether the
   node has no children (which would make it a leaf by definition).

   After CL 2171436, `AXPlatformNodeBase::IsLeaf` is delegated to
   `BrowserAccessibility::PlatformIsLeaf`, but the case of an '<iframe>'
   is mishandled since now we don't check if the node has a child tree.

   Fixing `BrowserAccessibility::IsLeaf` to check for the child tree of
   a node; this will allow several `BrowserAccessibilityPosition`'s
   methods to correctly traverse into '<iframe>' elements.

2. Introducing a content browser test to validate that methods from
   `BrowserAccessibilityPosition` can navigate into iframes.

3. Rebaselining `DumpAccessibilityTreeTest.AccessibilityIframe` which
   incorrectly expected '<iframe>' nodes to be treated as leaves.

Bug: 1101480
Change-Id: Ib364ee78a39ef6b7854fc41985bd059dfced8a46
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2364049Reviewed-by: default avatarKurt Catti-Schmidt <kschmi@microsoft.com>
Reviewed-by: default avatarNektarios Paisios <nektar@chromium.org>
Commit-Queue: Ethan Jimenez <ethavar@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#800321}
parent 3e465d5d
......@@ -604,4 +604,74 @@ IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinBrowserTest,
}
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinBrowserTest, IFrameTraversal) {
LoadInitialAccessibilityTreeFromHtmlFilePath(
"/accessibility/html/iframe-traversal.html");
WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(),
"Text in iframe");
BrowserAccessibility* root_node = GetRootAndAssertNonNull();
BrowserAccessibility* before_iframe_node =
FindNodeAfter(root_node, "Before iframe");
ASSERT_NE(nullptr, before_iframe_node);
ASSERT_EQ(ax::mojom::Role::kStaticText, before_iframe_node->GetRole());
before_iframe_node = before_iframe_node->PlatformGetFirstChild();
ASSERT_NE(nullptr, before_iframe_node);
ASSERT_EQ(ax::mojom::Role::kInlineTextBox, before_iframe_node->GetRole());
BrowserAccessibility* inside_iframe_node =
FindNodeAfter(before_iframe_node, "Text in iframe");
ASSERT_NE(nullptr, inside_iframe_node);
ASSERT_EQ(ax::mojom::Role::kStaticText, inside_iframe_node->GetRole());
inside_iframe_node = inside_iframe_node->PlatformGetFirstChild();
ASSERT_NE(nullptr, inside_iframe_node);
ASSERT_EQ(ax::mojom::Role::kInlineTextBox, inside_iframe_node->GetRole());
BrowserAccessibility* after_iframe_node =
FindNodeAfter(inside_iframe_node, "After iframe");
ASSERT_NE(nullptr, after_iframe_node);
ASSERT_EQ(ax::mojom::Role::kStaticText, after_iframe_node->GetRole());
after_iframe_node = after_iframe_node->PlatformGetFirstChild();
ASSERT_NE(nullptr, after_iframe_node);
ASSERT_EQ(ax::mojom::Role::kInlineTextBox, after_iframe_node->GetRole());
EXPECT_LT(*before_iframe_node->CreatePositionAt(0),
*inside_iframe_node->CreatePositionAt(0));
EXPECT_EQ(*before_iframe_node->CreatePositionAt(13),
*inside_iframe_node->CreatePositionAt(0));
EXPECT_LT(*inside_iframe_node->CreatePositionAt(0),
*after_iframe_node->CreatePositionAt(0));
EXPECT_EQ(*inside_iframe_node->CreatePositionAt(14),
*after_iframe_node->CreatePositionAt(0));
// Traverse the leaves of the AXTree forwards.
BrowserAccessibilityPosition::AXPositionInstance tree_position =
root_node->CreatePositionAt(0)->CreateNextLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(before_iframe_node, tree_position->GetAnchor());
tree_position = tree_position->CreateNextLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(inside_iframe_node, tree_position->GetAnchor());
tree_position = tree_position->CreateNextLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(after_iframe_node, tree_position->GetAnchor());
tree_position = tree_position->CreateNextLeafTreePosition();
EXPECT_TRUE(tree_position->IsNullPosition());
// Traverse the leaves of the AXTree backwards.
tree_position = after_iframe_node->CreatePositionAt(0)
->CreatePositionAtEndOfAnchor()
->AsLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(after_iframe_node, tree_position->GetAnchor());
tree_position = tree_position->CreatePreviousLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(inside_iframe_node, tree_position->GetAnchor());
tree_position = tree_position->CreatePreviousLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(before_iframe_node, tree_position->GetAnchor());
tree_position = tree_position->CreatePreviousLeafTreePosition();
EXPECT_TRUE(tree_position->IsNullPosition());
}
} // namespace content
......@@ -124,13 +124,9 @@ ui::AXPlatformNode* BrowserAccessibility::GetAXPlatformNode() const {
}
uint32_t BrowserAccessibility::PlatformChildCount() const {
if (HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId)) {
if (PlatformGetRootOfChildTree())
return 1;
if (PlatformIsLeaf())
return 0;
}
return PlatformIsLeaf() ? 0 : InternalChildCount();
return PlatformGetRootOfChildTree() ? 1 : InternalChildCount();
}
BrowserAccessibility* BrowserAccessibility::PlatformGetParent() const {
......@@ -146,10 +142,8 @@ BrowserAccessibility* BrowserAccessibility::PlatformGetFirstChild() const {
}
BrowserAccessibility* BrowserAccessibility::PlatformGetLastChild() const {
BrowserAccessibility* last_unignored_child = InternalGetLastChild();
if (!last_unignored_child)
last_unignored_child = PlatformGetRootOfChildTree();
return last_unignored_child;
BrowserAccessibility* child_tree_root = PlatformGetRootOfChildTree();
return child_tree_root ? child_tree_root : InternalGetLastChild();
}
BrowserAccessibility* BrowserAccessibility::PlatformGetNextSibling() const {
......@@ -209,20 +203,12 @@ bool BrowserAccessibility::IsLineBreakObject() const {
BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
uint32_t child_index) const {
BrowserAccessibility* result = nullptr;
if (HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId)) {
DCHECK_EQ(node_->children().size(), 0u)
<< "A node should not have both children and a child tree.";
// child_trees do not have siblings.
if (child_index == 0)
result = PlatformGetRootOfChildTree();
} else {
result = InternalGetChild(child_index);
BrowserAccessibility* child_tree_root = PlatformGetRootOfChildTree();
if (child_tree_root) {
// A node with a child tree has only one child.
return child_index ? nullptr : child_tree_root;
}
return result;
return InternalGetChild(child_index);
}
BrowserAccessibility* BrowserAccessibility::PlatformGetClosestPlatformObject()
......@@ -1553,7 +1539,7 @@ bool BrowserAccessibility::IsLeaf() const {
return !child_count ||
(child_count == 1 && InternalGetFirstChild()->IsText());
}
return node()->IsLeaf();
return PlatformGetRootOfChildTree() ? false : node()->IsLeaf();
}
bool BrowserAccessibility::IsToplevelBrowserWindow() {
......@@ -2145,15 +2131,18 @@ bool BrowserAccessibility::SetHypertextSelection(int start_offset,
}
BrowserAccessibility* BrowserAccessibility::PlatformGetRootOfChildTree() const {
if (!HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId))
std::string child_tree_id;
if (!GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
&child_tree_id)) {
return nullptr;
AXTreeID child_tree_id = AXTreeID::FromString(
GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
}
DCHECK_EQ(node_->children().size(), 0u)
<< "A node should not have both children and a child tree.";
BrowserAccessibilityManager* child_manager =
BrowserAccessibilityManager::FromID(child_tree_id);
BrowserAccessibilityManager::FromID(AXTreeID::FromString(child_tree_id));
if (child_manager && child_manager->GetRoot()->PlatformGetParent() == this)
return child_manager->GetRoot();
return nullptr;
}
......
ROLE_SYSTEM_DOCUMENT parent='ROLE_SYSTEM_WINDOW' window_class='Chrome_RenderWidgetHostHWND' READONLY FOCUSABLE ia2_hypertext='<obj0>'
++IA2_ROLE_SECTION parent='ROLE_SYSTEM_DOCUMENT' window_class='Chrome_RenderWidgetHostHWND' ia2_hypertext='<obj0>'
++++IA2_ROLE_INTERNAL_FRAME name='Empty iframe' parent='ROLE_SYSTEM_GROUPING' window_class='Chrome_RenderWidgetHostHWND'
++++IA2_ROLE_INTERNAL_FRAME name='Empty iframe' parent='ROLE_SYSTEM_GROUPING' window_class='Chrome_RenderWidgetHostHWND' ia2_hypertext='<obj0>'
++++++ROLE_SYSTEM_DOCUMENT parent='ROLE_SYSTEM_DOCUMENT' window_class='Chrome_RenderWidgetHostHWND' READONLY FOCUSABLE
\ No newline at end of file
<!--
@WIN-ALLOW:ia2_hypertext=*
-->
<!DOCTYPE html>
<html>
<body>
<p>Before iframe</p>
<div>
<iframe src="/accessibility/html/frame/static_text.html">
</iframe>
</div>
<p>After iframe</p>
</body>
</html>
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