Commit cf9e9cbd authored by Martin Robinson's avatar Martin Robinson Committed by Commit Bot

Properly set focus navigation starting point on AtkText children

When grabbing focus or setting the sequential focus navigation starting
point for AtkText nodes, we should use the character offset to set it
on the proper child node. This ensures that tab navigation works as
expected after calling AtkText APIs on a parent node.

Bug: 955349
Change-Id: Idf8677f7d4fb205fee46905c80fb1bf5bc9baf40
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1599616
Commit-Queue: Martin Robinson <mrobinson@igalia.com>
Reviewed-by: default avatarJoanmarie Diggs <jdiggs@igalia.com>
Cr-Commit-Position: refs/heads/master@{#658516}
parent 7daa5f57
...@@ -763,11 +763,17 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest, ...@@ -763,11 +763,17 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
R"HTML(<!DOCTYPE html> R"HTML(<!DOCTYPE html>
<html> <html>
<body> <body>
<a href="http://google.com">0</a> <div>
<div>1</div> 0
<a href="http://google.com">2</a> <a href="http://google.com">1</a>
<div>3</div> 2
<a href="http://google.com">4</a> <a href="http://google.com">3</a>
4
<a href="http://google.com">5</a>
6
<a href="http://google.com">7</a>
8
</div>
</body> </body>
</html>)HTML"); </html>)HTML");
...@@ -775,12 +781,19 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest, ...@@ -775,12 +781,19 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
AtkObject* document = GetRendererAccessible(); AtkObject* document = GetRendererAccessible();
ASSERT_TRUE(ATK_IS_COMPONENT(document)); ASSERT_TRUE(ATK_IS_COMPONENT(document));
AtkObject* child_3 = atk_object_ref_accessible_child(document, 3); AtkObject* parent_div = atk_object_ref_accessible_child(document, 0);
AtkObject* child_4 = atk_object_ref_accessible_child(document, 4); EXPECT_NE(parent_div, nullptr);
AtkObject* child_2 = atk_object_ref_accessible_child(parent_div, 2);
AtkObject* child_3 = atk_object_ref_accessible_child(parent_div, 3);
AtkObject* child_7 = atk_object_ref_accessible_child(parent_div, 7);
EXPECT_NE(child_2, nullptr);
EXPECT_NE(child_3, nullptr);
EXPECT_NE(child_7, nullptr);
// Move the caret to the "3" div. This should also set the sequential // Move the caret to the "3" div. This should also set the sequential
// focus navigation starting point. // focus navigation starting point.
atk_text_set_caret_offset(ATK_TEXT(child_3), 0); atk_text_set_caret_offset(ATK_TEXT(child_2), 0);
auto waiter = std::make_unique<AccessibilityNotificationWaiter>( auto waiter = std::make_unique<AccessibilityNotificationWaiter>(
shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kFocus); shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kFocus);
...@@ -797,12 +810,39 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest, ...@@ -797,12 +810,39 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
ui::VKEY_TAB, false, false, false, false); ui::VKEY_TAB, false, false, false, false);
waiter->WaitForNotification(); waiter->WaitForNotification();
AtkStateSet* state_set = atk_object_ref_state_set(child_4); AtkStateSet* state_set = atk_object_ref_state_set(child_3);
ASSERT_TRUE(atk_state_set_contains_state(state_set, ATK_STATE_FOCUSED));
g_object_unref(state_set);
// Now we repeat a similar test, but this time setting the caret offset
// on the parent node. In this case, the sequential navigation starting
// point should move to the appropriate child.
atk_text_set_caret_offset(ATK_TEXT(parent_div), 13);
SimulateKeyPress(shell()->web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
ui::VKEY_TAB, false, false, false, false);
waiter->WaitForNotification();
SimulateKeyPress(shell()->web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
ui::VKEY_TAB, false, false, false, false);
waiter->WaitForNotification();
state_set = atk_object_ref_state_set(child_7);
ASSERT_TRUE(atk_state_set_contains_state(state_set, ATK_STATE_FOCUSED));
g_object_unref(state_set);
// Now test setting the caret in a node that can accept focus. That
// node should actually receive focus.
atk_text_set_caret_offset(ATK_TEXT(child_3), 0);
SimulateKeyPress(shell()->web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
ui::VKEY_TAB, false, false, false, false);
waiter->WaitForNotification();
state_set = atk_object_ref_state_set(child_3);
ASSERT_TRUE(atk_state_set_contains_state(state_set, ATK_STATE_FOCUSED)); ASSERT_TRUE(atk_state_set_contains_state(state_set, ATK_STATE_FOCUSED));
g_object_unref(state_set); g_object_unref(state_set);
g_object_unref(child_2);
g_object_unref(child_3); g_object_unref(child_3);
g_object_unref(child_4); g_object_unref(child_7);
} }
} // namespace content } // namespace content
...@@ -1144,7 +1144,9 @@ gboolean SetCaretOffset(AtkText* atk_text, gint offset) { ...@@ -1144,7 +1144,9 @@ gboolean SetCaretOffset(AtkText* atk_text, gint offset) {
// Orca expects atk_text_set_caret_offset to either focus the target element // Orca expects atk_text_set_caret_offset to either focus the target element
// or set the sequential focus navigation starting point there. // or set the sequential focus navigation starting point there.
obj->GrabFocusOrSetSequentialFocusNavigationStartingPoint(); int utf16_offset = obj->UnicodeToUTF16OffsetInText(offset);
obj->GrabFocusOrSetSequentialFocusNavigationStartingPointAtOffset(
utf16_offset);
return TRUE; return TRUE;
} }
...@@ -3542,6 +3544,49 @@ bool AXPlatformNodeAuraLinux::GrabFocus() { ...@@ -3542,6 +3544,49 @@ bool AXPlatformNodeAuraLinux::GrabFocus() {
return delegate_->AccessibilityPerformAction(action_data); return delegate_->AccessibilityPerformAction(action_data);
} }
bool AXPlatformNodeAuraLinux::
GrabFocusOrSetSequentialFocusNavigationStartingPointAtOffset(int offset) {
int child_count = delegate_->GetChildCount();
if (IsPlainTextField() || child_count == 0)
return GrabFocusOrSetSequentialFocusNavigationStartingPoint();
// When this node has children, we walk through them to figure out what child
// node should get focus. We are essentially repeating the process used when
// building the hypertext here.
int current_offset = 0;
for (int i = 0; i < child_count; ++i) {
auto* child =
AtkObjectToAXPlatformNodeAuraLinux(delegate_->ChildAtIndex(i));
if (child->IsTextOnlyObject()) {
current_offset +=
child->GetString16Attribute(ax::mojom::StringAttribute::kName).size();
} else {
// Add an offset for the embedded character.
current_offset += 1;
}
// If the offset is larger than our size, try to work with the last child,
// which is also the behavior of SetCaretOffset.
if (offset <= current_offset || i == child_count - 1) {
// When deciding to do this on the parent or the child we want to err
// toward doing it on a focusable node. If neither node is focusable, we
// should call GrabFocusOrSetSequentialFocusNavigationStartingPoint on
// the child.
bool can_focus_node = GetData().HasState(ax::mojom::State::kFocusable);
bool can_focus_child =
child->GetData().HasState(ax::mojom::State::kFocusable);
if (can_focus_node && !can_focus_child)
return GrabFocusOrSetSequentialFocusNavigationStartingPoint();
else
return child->GrabFocusOrSetSequentialFocusNavigationStartingPoint();
}
}
NOTREACHED();
return false;
}
bool AXPlatformNodeAuraLinux:: bool AXPlatformNodeAuraLinux::
GrabFocusOrSetSequentialFocusNavigationStartingPoint() { GrabFocusOrSetSequentialFocusNavigationStartingPoint() {
if (GetData().HasState(ax::mojom::State::kFocusable) || if (GetData().HasState(ax::mojom::State::kFocusable) ||
......
...@@ -59,6 +59,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { ...@@ -59,6 +59,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
gint y, gint y,
AtkCoordType coord_type); AtkCoordType coord_type);
bool GrabFocus(); bool GrabFocus();
bool GrabFocusOrSetSequentialFocusNavigationStartingPointAtOffset(int offset);
bool GrabFocusOrSetSequentialFocusNavigationStartingPoint(); bool GrabFocusOrSetSequentialFocusNavigationStartingPoint();
bool DoDefaultAction(); bool DoDefaultAction();
const gchar* GetDefaultActionName(); const gchar* GetDefaultActionName();
......
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