Commit 73947c6f authored by Hiroki Sato's avatar Hiroki Sato Committed by Commit Bot

arc-a11y: Fix focusable and clickable computation

This change fixes focusable and clickable property computation for ARC
a11y nodes, and these properties are populated when a node has
corresponding action.

AX-Relnotes: Fixed an issue where Search+Space didn't work for some element in Android apps.
Bug: b:168461929
Bug: 1123056
Bug: 1126607
Test: manual, Search+Space on YT Music now works.
Test: AccessibilityNodeInfoDataWrapperTest
Change-Id: I6a9e534920fe1a2c51b70aee0ab0d8aeb00fdd6d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2409851
Commit-Queue: Hiroki Sato <hirokisato@chromium.org>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Reviewed-by: default avatarSara Kato <sarakato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#807381}
parent a4ccd66c
......@@ -102,15 +102,15 @@ bool AccessibilityNodeInfoDataWrapper::IsAccessibilityFocusableContainer()
const {
if (IsVirtualNode()) {
return GetProperty(AXBooleanProperty::SCREEN_READER_FOCUSABLE) ||
GetProperty(AXBooleanProperty::FOCUSABLE);
IsFocusable();
}
if (!IsImportantInAndroid() || (IsScrollableContainer() && !HasText()))
return false;
return GetProperty(AXBooleanProperty::SCREEN_READER_FOCUSABLE) ||
GetProperty(AXBooleanProperty::CLICKABLE) ||
GetProperty(AXBooleanProperty::FOCUSABLE) || IsToplevelScrollItem();
IsFocusable() || IsClickable() || IsToplevelScrollItem();
// TODO(hirokisato): probably check long clickable as well.
}
void AccessibilityNodeInfoDataWrapper::PopulateAXRole(
......@@ -296,7 +296,7 @@ void AccessibilityNodeInfoDataWrapper::PopulateAXState(
const bool focusable = tree_source_->UseFullFocusMode()
? IsAccessibilityFocusableContainer()
: GetProperty(AXBooleanProperty::FOCUSABLE);
: IsFocusable();
if (focusable)
out_data->AddState(ax::mojom::State::kFocusable);
......@@ -393,12 +393,12 @@ void AccessibilityNodeInfoDataWrapper::Serialize(
// Boolean properties.
PopulateAXState(out_data);
if (GetProperty(AXBooleanProperty::SCROLLABLE)) {
if (GetProperty(AXBooleanProperty::SCROLLABLE))
out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kScrollable, true);
}
if (GetProperty(AXBooleanProperty::CLICKABLE)) {
if (IsClickable())
out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kClickable, true);
}
if (GetProperty(AXBooleanProperty::SELECTED)) {
if (ui::SupportsSelected(out_data->role)) {
out_data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
......@@ -697,6 +697,17 @@ void AccessibilityNodeInfoDataWrapper::ComputeNameFromContentsInternal(
}
}
bool AccessibilityNodeInfoDataWrapper::IsClickable() const {
return GetProperty(AXBooleanProperty::CLICKABLE) ||
HasStandardAction(AXActionType::CLICK);
}
bool AccessibilityNodeInfoDataWrapper::IsFocusable() const {
return GetProperty(AXBooleanProperty::FOCUSABLE) ||
HasStandardAction(AXActionType::FOCUS) ||
HasStandardAction(AXActionType::CLEAR_FOCUS);
}
bool AccessibilityNodeInfoDataWrapper::IsScrollableContainer() const {
if (GetProperty(AXBooleanProperty::SCROLLABLE))
return true;
......@@ -739,21 +750,16 @@ bool AccessibilityNodeInfoDataWrapper::HasImportantPropertyInternal() const {
return true;
}
if (IsFocusable() || IsClickable())
return true;
// These properties are sorted in the same order of mojom file.
if (GetProperty(AXBooleanProperty::CHECKABLE) ||
GetProperty(AXBooleanProperty::FOCUSABLE) ||
GetProperty(AXBooleanProperty::SELECTED) ||
GetProperty(AXBooleanProperty::CLICKABLE) ||
GetProperty(AXBooleanProperty::EDITABLE)) {
return true;
}
if (HasStandardAction(AXActionType::FOCUS) ||
HasStandardAction(AXActionType::CLEAR_FOCUS) ||
HasStandardAction(AXActionType::CLICK)) {
return true;
}
ui::AXNodeData data;
PopulateAXRole(&data);
if (ui::IsControl(data.role))
......
......@@ -74,6 +74,9 @@ class AccessibilityNodeInfoDataWrapper : public AccessibilityInfoDataWrapper {
void ComputeNameFromContents(std::vector<std::string>* names) const;
void ComputeNameFromContentsInternal(std::vector<std::string>* names) const;
bool IsClickable() const;
bool IsFocusable() const;
bool IsScrollableContainer() const;
bool IsToplevelScrollItem() const;
......
......@@ -640,4 +640,87 @@ TEST_F(AccessibilityNodeInfoDataWrapperTest, ControlIsFocusable) {
ASSERT_TRUE(wrapper.CanBeAccessibilityFocused());
}
TEST_F(AccessibilityNodeInfoDataWrapperTest, FocusAndClickAction) {
AXNodeInfoData root;
root.id = 10;
AccessibilityNodeInfoDataWrapper root_wrapper(tree_source(), &root);
SetIdToWrapper(&root_wrapper);
SetProperty(&root, AXStringProperty::CLASS_NAME, "");
SetProperty(&root, AXBooleanProperty::IMPORTANCE, true);
SetProperty(&root, AXBooleanProperty::FOCUSABLE, true);
SetProperty(&root, AXBooleanProperty::CLICKABLE, true);
SetProperty(&root, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({1}));
AXNodeInfoData child1;
child1.id = 1;
AccessibilityNodeInfoDataWrapper child1_wrapper(tree_source(), &child1);
SetIdToWrapper(&child1_wrapper);
SetProperty(&child1, AXBooleanProperty::IMPORTANCE, true);
SetProperty(&child1, AXIntListProperty::CHILD_NODE_IDS,
std::vector<int>({2}));
SetParentId(child1.id, root.id);
AXNodeInfoData child2;
child2.id = 2;
AccessibilityNodeInfoDataWrapper child2_wrapper(tree_source(), &child2);
SetIdToWrapper(&child2_wrapper);
SetProperty(&child2, AXBooleanProperty::IMPORTANCE, true);
SetParentId(child2.id, child1.id);
SetProperty(&child2, AXStringProperty::CONTENT_DESCRIPTION, "test text");
set_full_focus_mode(true);
ui::AXNodeData data = CallSerialize(root_wrapper);
std::string name;
ASSERT_TRUE(
data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("test text", name);
ASSERT_TRUE(data.GetBoolAttribute(ax::mojom::BoolAttribute::kClickable));
EXPECT_TRUE(data.HasState(ax::mojom::State::kFocusable));
data = CallSerialize(child1_wrapper);
ASSERT_FALSE(
data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_FALSE(data.GetBoolAttribute(ax::mojom::BoolAttribute::kClickable));
EXPECT_FALSE(data.HasState(ax::mojom::State::kFocusable));
// Set click and focus action to child1. child1 will be clickable and
// focusable, and gets ax name from descendants.
SetProperty(&child1, AXIntListProperty::STANDARD_ACTION_IDS,
std::vector<int>({static_cast<int>(AXActionType::CLICK),
static_cast<int>(AXActionType::FOCUS)}));
data = CallSerialize(root_wrapper);
ASSERT_FALSE(
data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_TRUE(data.GetBoolAttribute(ax::mojom::BoolAttribute::kClickable));
EXPECT_TRUE(data.HasState(ax::mojom::State::kFocusable));
data = CallSerialize(child1_wrapper);
ASSERT_TRUE(
data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("test text", name);
ASSERT_TRUE(data.GetBoolAttribute(ax::mojom::BoolAttribute::kClickable));
EXPECT_TRUE(data.HasState(ax::mojom::State::kFocusable));
// Same for clear_focus action instead of focus action.
SetProperty(&child1, AXIntListProperty::STANDARD_ACTION_IDS,
std::vector<int>({static_cast<int>(AXActionType::CLICK),
static_cast<int>(AXActionType::CLEAR_FOCUS)}));
data = CallSerialize(root_wrapper);
ASSERT_FALSE(
data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_TRUE(data.GetBoolAttribute(ax::mojom::BoolAttribute::kClickable));
EXPECT_TRUE(data.HasState(ax::mojom::State::kFocusable));
data = CallSerialize(child1_wrapper);
ASSERT_TRUE(
data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
ASSERT_EQ("test text", name);
ASSERT_TRUE(data.GetBoolAttribute(ax::mojom::BoolAttribute::kClickable));
EXPECT_TRUE(data.HasState(ax::mojom::State::kFocusable));
}
} // namespace arc
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