Commit 198c962d authored by Akihiro Ota's avatar Akihiro Ota Committed by Commit Bot

Make popup buttons inherit setsize from controlled element.

This change:
1. Adds logic for popup buttons to inherit setsize from a controlled
element. This is important because ChromeVox reports setsize when
focusing a popup button and we want to make sure it reports the correct
value.
2. Adds an AXTreeTest for new logic.
3. Adds ChromeVox test for new logic.
4. Splits flakey test, which was disabled, into two separate tests
which both pass.

expanded popup buttons.

Bug: 1108895
Change-Id: I45e70c8ec20123e0194159f9e884daf4413b6c23
AX-Relnotes: Fix issue where ChromeVox was reporting "0 items" for
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2315352
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#791464}
parent a0f7bda2
......@@ -2057,51 +2057,6 @@ TEST_F('ChromeVoxBackgroundTest', 'DISABLED_EventFromUser', function() {
});
});
// See https://crbug.com/997688
TEST_F('ChromeVoxBackgroundTest', 'DISABLED_PopUpButtonSetSize', function() {
const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree(
`
<div>
<select id="button">
<option value="Apple">Apple</option>
<option value="Banana">Banana</option>
</select>
</div>
<script>
let button = document.getElementById('button');
let expanded = false;
button.addEventListener('click', function(e) {
if (expanded) {
button.setAttribute('aria-expanded', false);
} else {
button.setAttribute('aria-expanded', true);
}
expanded = !expanded;
});
</script>
`,
function(root) {
const button = root.find({role: RoleType.POP_UP_BUTTON});
const click = button.doDefault.bind(button);
const focus = button.focus.bind(button);
mockFeedback.call(focus)
.expectSpeech('Apple')
.expectSpeech('Button')
.expectSpeech('has pop up')
.expectSpeech('Press Search+Space to activate')
.call(click)
.expectSpeech('Apple')
.expectSpeech('Button')
.expectSpeech('has pop up')
// SetSize is only reported if popup button is expanded.
.expectSpeech('with 2 items')
.expectSpeech('Expanded')
.expectSpeech('Press Search+Space to activate')
.replay();
});
});
TEST_F('ChromeVoxBackgroundTest', 'ReadPhoneticPronunciationTest', function() {
const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree(
......@@ -2860,3 +2815,48 @@ TEST_F('ChromeVoxBackgroundTest', 'SmartStickyModeJumpCommands', function() {
.replay();
});
});
TEST_F('ChromeVoxBackgroundTest', 'PopupButtonCollapsed', function() {
const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree(
`
<select id="button">
<option value="Apple">Apple</option>
<option value="Banana">Banana</option>
</select>
`,
function(root) {
mockFeedback.call(doCmd('jumpToTop'))
.expectSpeech(
'Apple', 'Button', 'has pop up', 'Collapsed',
'Press Search+Space to activate')
.replay();
});
});
TEST_F('ChromeVoxBackgroundTest', 'PopupButtonExpanded', function() {
const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree(
`
<button id="button" aria-haspopup="true" aria-expanded="true"
aria-controls="menu">
Click me
</button>
<ul id="menu"
role="menu"
aria-labelledby="button">
<li role="menuitem">Item 1</li>
<li role="menuitem">Item 2</li>
<li role="menuitem">Item 3</li>
</ul>
`,
function(root) {
mockFeedback
.call(doCmd('jumpToTop'))
// SetSize is only reported if popup button is expanded.
.expectSpeech(
'Click me', 'Button', 'has pop up', 'with 3 items', 'Expanded',
'Press Search+Space to activate')
.replay();
});
});
......@@ -2262,6 +2262,22 @@ base::Optional<int> AXTree::GetSetSize(const AXNode& node) {
if (!ordered_set)
return base::nullopt;
// For popup buttons that control a single element, inherit the controlled
// item's SetSize.
if (node.data().role == ax::mojom::Role::kPopUpButton) {
const auto& controls_ids = node.data().GetIntListAttribute(
ax::mojom::IntListAttribute::kControlsIds);
if (controls_ids.size() == 1 && GetFromId(controls_ids[0])) {
const AXNode& controlled_item = *GetFromId(controls_ids[0]);
base::Optional<int> controlled_item_set_size =
GetSetSize(controlled_item);
node_set_size_pos_in_set_info_map_[node.id()].set_size =
controlled_item_set_size;
return controlled_item_set_size;
}
}
// Compute, cache, then return.
ComputeSetSizePosInSetAndCache(node, ordered_set);
base::Optional<int> set_size =
......
......@@ -4400,6 +4400,58 @@ TEST(AXTreeTest, SetSizePosInSetHidden) {
EXPECT_OPTIONAL_EQ(4, option4->GetSetSize());
}
// Tests that we get the correct PosInSet and SetSize values when using an
// aria-controls relationship.
TEST(AXTreeTest, SetSizePosInSetControls) {
std::vector<int32_t> three;
three.push_back(3);
std::vector<int32_t> hundred;
hundred.push_back(100);
AXTreeUpdate tree_update;
tree_update.root_id = 1;
tree_update.nodes.resize(7);
tree_update.nodes[0].id = 1;
tree_update.nodes[0].role = ax::mojom::Role::kGenericContainer;
tree_update.nodes[0].child_ids = {2, 3, 7};
tree_update.nodes[1].id = 2;
tree_update.nodes[1].role = ax::mojom::Role::kPopUpButton; // SetSize = 3
tree_update.nodes[1].AddIntListAttribute(
ax::mojom::IntListAttribute::kControlsIds, three);
tree_update.nodes[1].SetHasPopup(ax::mojom::HasPopup::kMenu);
tree_update.nodes[2].id = 3;
tree_update.nodes[2].role = ax::mojom::Role::kMenu; // SetSize = 3
tree_update.nodes[2].child_ids = {4, 5, 6};
tree_update.nodes[3].id = 4;
tree_update.nodes[3].role = ax::mojom::Role::kMenuItem; // 1 of 3
tree_update.nodes[4].id = 5;
tree_update.nodes[4].role = ax::mojom::Role::kMenuItem; // 2 of 3
tree_update.nodes[5].id = 6;
tree_update.nodes[5].role = ax::mojom::Role::kMenuItem; // 3 of 3
tree_update.nodes[6].id = 7;
tree_update.nodes[6].role =
ax::mojom::Role::kPopUpButton; // Test an invalid controls id.
tree_update.nodes[6].AddIntListAttribute(
ax::mojom::IntListAttribute::kControlsIds, hundred);
AXTree tree(tree_update);
AXNode* button = tree.GetFromId(2);
EXPECT_OPTIONAL_EQ(3, button->GetSetSize());
EXPECT_FALSE(button->GetPosInSet());
AXNode* menu = tree.GetFromId(3);
EXPECT_OPTIONAL_EQ(3, menu->GetSetSize());
AXNode* item = tree.GetFromId(4);
EXPECT_OPTIONAL_EQ(1, item->GetPosInSet());
EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
item = tree.GetFromId(5);
EXPECT_OPTIONAL_EQ(2, item->GetPosInSet());
EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
item = tree.GetFromId(6);
EXPECT_OPTIONAL_EQ(3, item->GetPosInSet());
EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
button = tree.GetFromId(7);
EXPECT_OPTIONAL_EQ(0, button->GetSetSize());
}
TEST(AXTreeTest, OnNodeWillBeDeletedHasValidUnignoredParent) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
......
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