|
@@ -0,0 +1,580 @@
|
|
|
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
+From: Julie Jeongeun Kim <[email protected]>
|
|
|
+Date: Fri, 10 Apr 2020 05:50:17 +0000
|
|
|
+Subject: a11y: Iterate all descendants for GetSelectionCount
|
|
|
+
|
|
|
+This CL iterates all descendants for GetSelectionCount and
|
|
|
+GetSelectedChild. When listbox has group children, it should
|
|
|
+iterates their children as well to check the select state.
|
|
|
+
|
|
|
+Bug: 1058961
|
|
|
+Change-Id: Ib6459bf6f47023d4258ef4c2f2dc545739d7a61b
|
|
|
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2115211
|
|
|
+Commit-Queue: Julie Kim <[email protected]>
|
|
|
+Reviewed-by: Nektarios Paisios <[email protected]>
|
|
|
+Reviewed-by: Aaron Leventhal <[email protected]>
|
|
|
+Reviewed-by: Joanmarie Diggs <[email protected]>
|
|
|
+Cr-Commit-Position: refs/heads/master@{#758140}
|
|
|
+
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
|
|
|
+index 773ca872f48b548af2cf9bf5b27291e85a7a563d..2f150dbbae32f9faae0f8b55a65133fb333aa5e5 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
|
|
|
+@@ -1512,19 +1512,10 @@ AtkObject* RefSelection(AtkSelection* selection, gint requested_child_index) {
|
|
|
+ if (!obj)
|
|
|
+ return nullptr;
|
|
|
+
|
|
|
+- int child_count = obj->GetChildCount();
|
|
|
+- gint selected_count = 0;
|
|
|
+- for (int i = 0; i < child_count; ++i) {
|
|
|
+- AtkObject* child = obj->ChildAtIndex(i);
|
|
|
+- AXPlatformNodeAuraLinux* child_ax_node =
|
|
|
+- AtkObjectToAXPlatformNodeAuraLinux(child);
|
|
|
+- if (!child_ax_node)
|
|
|
+- continue;
|
|
|
+-
|
|
|
+- if (child_ax_node->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) {
|
|
|
+- if (selected_count == requested_child_index)
|
|
|
+- return static_cast<AtkObject*>(g_object_ref(child));
|
|
|
+- ++selected_count;
|
|
|
++ if (auto* selected_child = obj->GetSelectedItem(requested_child_index)) {
|
|
|
++ if (AtkObject* atk_object = selected_child->GetNativeViewAccessible()) {
|
|
|
++ g_object_ref(atk_object);
|
|
|
++ return atk_object;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1539,19 +1530,7 @@ gint GetSelectionCount(AtkSelection* selection) {
|
|
|
+ if (!obj)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+- int child_count = obj->GetChildCount();
|
|
|
+- gint selected_count = 0;
|
|
|
+- for (int i = 0; i < child_count; ++i) {
|
|
|
+- AXPlatformNodeAuraLinux* child =
|
|
|
+- AtkObjectToAXPlatformNodeAuraLinux(obj->ChildAtIndex(i));
|
|
|
+- if (!child)
|
|
|
+- continue;
|
|
|
+-
|
|
|
+- if (child->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
|
|
|
+- ++selected_count;
|
|
|
+- }
|
|
|
+-
|
|
|
+- return selected_count;
|
|
|
++ return obj->GetSelectionCount();
|
|
|
+ }
|
|
|
+
|
|
|
+ gboolean IsChildSelected(AtkSelection* selection, gint index) {
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
|
|
|
+index fdae0972a0197133d80bfb8a935a4d30ec73a8e3..9ce1fa7e36aa6fd4255772226718388c1c148f40 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
|
|
|
+@@ -1859,6 +1859,8 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkSelectionInterface) {
|
|
|
+ AXNodeData root;
|
|
|
+ root.id = 1;
|
|
|
+ root.role = ax::mojom::Role::kListBox;
|
|
|
++ root.AddState(ax::mojom::State::kFocusable);
|
|
|
++ root.AddState(ax::mojom::State::kMultiselectable);
|
|
|
+ root.child_ids.push_back(2);
|
|
|
+ root.child_ids.push_back(3);
|
|
|
+ root.child_ids.push_back(4);
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
|
|
|
+index ebff0e2b2640fa44ff189a735e98ec2c558fe44d..12ec1674a79bdc1f28d5d2b1bb4792b81cd8608f 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_base.cc
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_base.cc
|
|
|
+@@ -130,7 +130,7 @@ int AXPlatformNodeBase::GetChildCount() const {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+-gfx::NativeViewAccessible AXPlatformNodeBase::ChildAtIndex(int index) {
|
|
|
++gfx::NativeViewAccessible AXPlatformNodeBase::ChildAtIndex(int index) const {
|
|
|
+ if (delegate_)
|
|
|
+ return delegate_->ChildAtIndex(index);
|
|
|
+ return nullptr;
|
|
|
+@@ -2058,6 +2058,54 @@ ui::TextAttributeList AXPlatformNodeBase::ComputeTextAttributes() const {
|
|
|
+ return attributes;
|
|
|
+ }
|
|
|
+
|
|
|
++int AXPlatformNodeBase::GetSelectionCount() const {
|
|
|
++ int max_items = GetMaxSelectableItems();
|
|
|
++ if (!max_items)
|
|
|
++ return 0;
|
|
|
++ return GetSelectedItems(max_items);
|
|
|
++}
|
|
|
++
|
|
|
++AXPlatformNodeBase* AXPlatformNodeBase::GetSelectedItem(
|
|
|
++ int selected_index) const {
|
|
|
++ DCHECK_GE(selected_index, 0);
|
|
|
++ int max_items = GetMaxSelectableItems();
|
|
|
++ if (max_items == 0)
|
|
|
++ return nullptr;
|
|
|
++ if (selected_index >= max_items)
|
|
|
++ return nullptr;
|
|
|
++
|
|
|
++ std::vector<AXPlatformNodeBase*> selected_children;
|
|
|
++ int requested_count = selected_index + 1;
|
|
|
++ int returned_count = GetSelectedItems(requested_count, &selected_children);
|
|
|
++
|
|
|
++ if (returned_count <= selected_index)
|
|
|
++ return nullptr;
|
|
|
++
|
|
|
++ DCHECK(!selected_children.empty());
|
|
|
++ DCHECK_LT(selected_index, static_cast<int>(selected_children.size()));
|
|
|
++ return selected_children[selected_index];
|
|
|
++}
|
|
|
++
|
|
|
++int AXPlatformNodeBase::GetSelectedItems(
|
|
|
++ int max_items,
|
|
|
++ std::vector<AXPlatformNodeBase*>* out_selected_items) const {
|
|
|
++ int selected_count = 0;
|
|
|
++ // TODO(Nektar): Remove const_cast by making all tree traversal methods const.
|
|
|
++ for (AXPlatformNodeBase* child =
|
|
|
++ const_cast<AXPlatformNodeBase*>(this)->GetFirstChild();
|
|
|
++ child && selected_count < max_items; child = child->GetNextSibling()) {
|
|
|
++ if (!IsItemLike(child->GetData().role)) {
|
|
|
++ selected_count += child->GetSelectedItems(max_items - selected_count,
|
|
|
++ out_selected_items);
|
|
|
++ } else if (child->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) {
|
|
|
++ selected_count++;
|
|
|
++ if (out_selected_items)
|
|
|
++ out_selected_items->emplace_back(child);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ return selected_count;
|
|
|
++}
|
|
|
++
|
|
|
+ void AXPlatformNodeBase::SanitizeTextAttributeValue(const std::string& input,
|
|
|
+ std::string* output) const {
|
|
|
+ DCHECK(output);
|
|
|
+@@ -2116,4 +2164,20 @@ std::string AXPlatformNodeBase::ComputeDetailsRoles() const {
|
|
|
+ return base::JoinString(details_roles_vector, " ");
|
|
|
+ }
|
|
|
+
|
|
|
++int AXPlatformNodeBase::GetMaxSelectableItems() const {
|
|
|
++ if (!GetData().HasState(ax::mojom::State::kFocusable))
|
|
|
++ return 0;
|
|
|
++
|
|
|
++ if (IsLeaf())
|
|
|
++ return 0;
|
|
|
++
|
|
|
++ if (!IsContainerWithSelectableChildren(GetData().role))
|
|
|
++ return 0;
|
|
|
++
|
|
|
++ int max_items = 1;
|
|
|
++ if (GetData().HasState(ax::mojom::State::kMultiselectable))
|
|
|
++ max_items = std::numeric_limits<int>::max();
|
|
|
++ return max_items;
|
|
|
++}
|
|
|
++
|
|
|
+ } // namespace ui
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
|
|
|
+index b46cf48d223b49d196aff10b5822e35628a45469..0355f281a600979f59a25086fd7400201693bf89 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_base.h
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_base.h
|
|
|
+@@ -64,7 +64,7 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
|
|
|
+ gfx::NativeViewAccessible GetFocus();
|
|
|
+ gfx::NativeViewAccessible GetParent() const;
|
|
|
+ int GetChildCount() const;
|
|
|
+- gfx::NativeViewAccessible ChildAtIndex(int index);
|
|
|
++ gfx::NativeViewAccessible ChildAtIndex(int index) const;
|
|
|
+
|
|
|
+ std::string GetName() const;
|
|
|
+ base::string16 GetNameAsString16() const;
|
|
|
+@@ -312,6 +312,24 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
|
|
|
+
|
|
|
+ ui::TextAttributeList ComputeTextAttributes() const;
|
|
|
+
|
|
|
++ // Get the number of items selected. It checks kMultiselectable and
|
|
|
++ // kFocusable. and uses GetSelectedItems to get the selected number.
|
|
|
++ int GetSelectionCount() const;
|
|
|
++
|
|
|
++ // If this object is a container that supports selectable children, returns
|
|
|
++ // the selected item at the provided index.
|
|
|
++ AXPlatformNodeBase* GetSelectedItem(int selected_index) const;
|
|
|
++
|
|
|
++ // If this object is a container that supports selectable children,
|
|
|
++ // returns the number of selected items in this container.
|
|
|
++ // |out_selected_items| could be set to nullptr if the caller just
|
|
|
++ // needs to know the number of items selected.
|
|
|
++ // |max_items| represents the number that the caller expects as a
|
|
|
++ // maximum. For a single selection list box, it will be 1.
|
|
|
++ int GetSelectedItems(
|
|
|
++ int max_items,
|
|
|
++ std::vector<AXPlatformNodeBase*>* out_selected_items = nullptr) const;
|
|
|
++
|
|
|
+ //
|
|
|
+ // Delegate. This is a weak reference which owns |this|.
|
|
|
+ //
|
|
|
+@@ -454,6 +472,11 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
|
|
|
+
|
|
|
+ std::string GetInvalidValue() const;
|
|
|
+
|
|
|
++ // Based on the characteristics of this object, such as its role and the
|
|
|
++ // presence of a multiselectable attribute, returns the maximum number of
|
|
|
++ // selectable children that this object could potentially contain.
|
|
|
++ int GetMaxSelectableItems() const;
|
|
|
++
|
|
|
+ mutable AXHypertext hypertext_;
|
|
|
+
|
|
|
+ private:
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_base_unittest.cc b/ui/accessibility/platform/ax_platform_node_base_unittest.cc
|
|
|
+index d0bc34a70e5c96062a2d1a24b2a6e8c087098b47..401d5be24b9f1957f4425f32b150006d094c6838 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_base_unittest.cc
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_base_unittest.cc
|
|
|
+@@ -96,4 +96,200 @@ TEST(AXPlatformNodeBaseTest, InnerTextIgnoresInvisibleAndIgnored) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
++TEST(AXPlatformNodeBaseTest, TestSelectedChildren) {
|
|
|
++ AXPlatformNode::NotifyAddAXModeFlags(kAXModeComplete);
|
|
|
++
|
|
|
++ AXNodeData root_data;
|
|
|
++ root_data.id = 1;
|
|
|
++ root_data.role = ax::mojom::Role::kListBox;
|
|
|
++ root_data.AddState(ax::mojom::State::kFocusable);
|
|
|
++ root_data.child_ids = {2, 3};
|
|
|
++
|
|
|
++ AXNodeData item_1_data;
|
|
|
++ item_1_data.id = 2;
|
|
|
++ item_1_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++ item_1_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
|
|
|
++
|
|
|
++ AXNodeData item_2_data;
|
|
|
++ item_2_data.id = 3;
|
|
|
++ item_2_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++
|
|
|
++ AXTreeUpdate update;
|
|
|
++ update.root_id = 1;
|
|
|
++ update.nodes = {root_data, item_1_data, item_2_data};
|
|
|
++ AXTree tree(update);
|
|
|
++
|
|
|
++ auto* root = static_cast<AXPlatformNodeBase*>(
|
|
|
++ TestAXNodeWrapper::GetOrCreate(&tree, tree.root())->ax_platform_node());
|
|
|
++
|
|
|
++ int num = root->GetSelectionCount();
|
|
|
++ EXPECT_EQ(num, 1);
|
|
|
++
|
|
|
++ gfx::NativeViewAccessible first_child = root->ChildAtIndex(0);
|
|
|
++ AXPlatformNodeBase* first_selected_node = root->GetSelectedItem(0);
|
|
|
++ EXPECT_EQ(first_child, first_selected_node->GetNativeViewAccessible());
|
|
|
++ EXPECT_EQ(nullptr, root->GetSelectedItem(1));
|
|
|
++}
|
|
|
++
|
|
|
++TEST(AXPlatformNodeBaseTest, TestSelectedChildrenWithGroup) {
|
|
|
++ AXPlatformNode::NotifyAddAXModeFlags(kAXModeComplete);
|
|
|
++
|
|
|
++ AXNodeData root_data;
|
|
|
++ root_data.id = 1;
|
|
|
++ root_data.role = ax::mojom::Role::kListBox;
|
|
|
++ root_data.AddState(ax::mojom::State::kFocusable);
|
|
|
++ root_data.AddState(ax::mojom::State::kMultiselectable);
|
|
|
++ root_data.child_ids = {2, 3};
|
|
|
++
|
|
|
++ AXNodeData group_1_data;
|
|
|
++ group_1_data.id = 2;
|
|
|
++ group_1_data.role = ax::mojom::Role::kGroup;
|
|
|
++ group_1_data.child_ids = {4, 5};
|
|
|
++
|
|
|
++ AXNodeData group_2_data;
|
|
|
++ group_2_data.id = 3;
|
|
|
++ group_2_data.role = ax::mojom::Role::kGroup;
|
|
|
++ group_2_data.child_ids = {6, 7};
|
|
|
++
|
|
|
++ AXNodeData item_1_data;
|
|
|
++ item_1_data.id = 4;
|
|
|
++ item_1_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++ item_1_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
|
|
|
++
|
|
|
++ AXNodeData item_2_data;
|
|
|
++ item_2_data.id = 5;
|
|
|
++ item_2_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++
|
|
|
++ AXNodeData item_3_data;
|
|
|
++ item_3_data.id = 6;
|
|
|
++ item_3_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++
|
|
|
++ AXNodeData item_4_data;
|
|
|
++ item_4_data.id = 7;
|
|
|
++ item_4_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++ item_4_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
|
|
|
++
|
|
|
++ AXTreeUpdate update;
|
|
|
++ update.root_id = 1;
|
|
|
++ update.nodes = {root_data, group_1_data, group_2_data, item_1_data,
|
|
|
++ item_2_data, item_3_data, item_4_data};
|
|
|
++ AXTree tree(update);
|
|
|
++
|
|
|
++ auto* root = static_cast<AXPlatformNodeBase*>(
|
|
|
++ TestAXNodeWrapper::GetOrCreate(&tree, tree.root())->ax_platform_node());
|
|
|
++
|
|
|
++ int num = root->GetSelectionCount();
|
|
|
++ EXPECT_EQ(num, 2);
|
|
|
++
|
|
|
++ gfx::NativeViewAccessible first_group_child =
|
|
|
++ static_cast<AXPlatformNodeBase*>(
|
|
|
++ AXPlatformNode::FromNativeViewAccessible(root->ChildAtIndex(0)))
|
|
|
++ ->ChildAtIndex(0);
|
|
|
++ AXPlatformNodeBase* first_selected_node = root->GetSelectedItem(0);
|
|
|
++ EXPECT_EQ(first_group_child, first_selected_node->GetNativeViewAccessible());
|
|
|
++
|
|
|
++ gfx::NativeViewAccessible second_group_child =
|
|
|
++ static_cast<AXPlatformNodeBase*>(
|
|
|
++ AXPlatformNode::FromNativeViewAccessible(root->ChildAtIndex(1)))
|
|
|
++ ->ChildAtIndex(1);
|
|
|
++ AXPlatformNodeBase* second_selected_node = root->GetSelectedItem(1);
|
|
|
++ EXPECT_EQ(second_group_child,
|
|
|
++ second_selected_node->GetNativeViewAccessible());
|
|
|
++}
|
|
|
++
|
|
|
++TEST(AXPlatformNodeBaseTest, TestSelectedChildrenMixed) {
|
|
|
++ AXPlatformNode::NotifyAddAXModeFlags(kAXModeComplete);
|
|
|
++
|
|
|
++ // Build the below tree which is mixed with listBoxOption and group.
|
|
|
++ // id=1 listBox FOCUSABLE MULTISELECTABLE (0, 0)-(0, 0) child_ids=2,3,4,9
|
|
|
++ // ++id=2 listBoxOption (0, 0)-(0, 0) selected=true
|
|
|
++ // ++id=3 group (0, 0)-(0, 0) child_ids=5,6
|
|
|
++ // ++++id=5 listBoxOption (0, 0)-(0, 0) selected=true
|
|
|
++ // ++++id=6 listBoxOption (0, 0)-(0, 0)
|
|
|
++ // ++id=4 group (0, 0)-(0, 0) child_ids=7,8
|
|
|
++ // ++++id=7 listBoxOption (0, 0)-(0, 0)
|
|
|
++ // ++++id=8 listBoxOption (0, 0)-(0, 0) selected=true
|
|
|
++ // ++id=9 listBoxOption (0, 0)-(0, 0) selected=true
|
|
|
++
|
|
|
++ AXNodeData root_data;
|
|
|
++ root_data.id = 1;
|
|
|
++ root_data.role = ax::mojom::Role::kListBox;
|
|
|
++ root_data.AddState(ax::mojom::State::kFocusable);
|
|
|
++ root_data.AddState(ax::mojom::State::kMultiselectable);
|
|
|
++ root_data.child_ids = {2, 3, 4, 9};
|
|
|
++
|
|
|
++ AXNodeData item_1_data;
|
|
|
++ item_1_data.id = 2;
|
|
|
++ item_1_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++ item_1_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
|
|
|
++
|
|
|
++ AXNodeData group_1_data;
|
|
|
++ group_1_data.id = 3;
|
|
|
++ group_1_data.role = ax::mojom::Role::kGroup;
|
|
|
++ group_1_data.child_ids = {5, 6};
|
|
|
++
|
|
|
++ AXNodeData item_2_data;
|
|
|
++ item_2_data.id = 5;
|
|
|
++ item_2_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++ item_2_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
|
|
|
++
|
|
|
++ AXNodeData item_3_data;
|
|
|
++ item_3_data.id = 6;
|
|
|
++ item_3_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++
|
|
|
++ AXNodeData group_2_data;
|
|
|
++ group_2_data.id = 4;
|
|
|
++ group_2_data.role = ax::mojom::Role::kGroup;
|
|
|
++ group_2_data.child_ids = {7, 8};
|
|
|
++
|
|
|
++ AXNodeData item_4_data;
|
|
|
++ item_4_data.id = 7;
|
|
|
++ item_4_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++
|
|
|
++ AXNodeData item_5_data;
|
|
|
++ item_5_data.id = 8;
|
|
|
++ item_5_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++ item_5_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
|
|
|
++
|
|
|
++ AXNodeData item_6_data;
|
|
|
++ item_6_data.id = 9;
|
|
|
++ item_6_data.role = ax::mojom::Role::kListBoxOption;
|
|
|
++ item_6_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
|
|
|
++
|
|
|
++ AXTreeUpdate update;
|
|
|
++ update.root_id = 1;
|
|
|
++ update.nodes = {root_data, item_1_data, group_1_data,
|
|
|
++ item_2_data, item_3_data, group_2_data,
|
|
|
++ item_4_data, item_5_data, item_6_data};
|
|
|
++ AXTree tree(update);
|
|
|
++
|
|
|
++ auto* root = static_cast<AXPlatformNodeBase*>(
|
|
|
++ TestAXNodeWrapper::GetOrCreate(&tree, tree.root())->ax_platform_node());
|
|
|
++
|
|
|
++ int num = root->GetSelectionCount();
|
|
|
++ EXPECT_EQ(num, 4);
|
|
|
++
|
|
|
++ gfx::NativeViewAccessible first_child = root->ChildAtIndex(0);
|
|
|
++ AXPlatformNodeBase* first_selected_node = root->GetSelectedItem(0);
|
|
|
++ EXPECT_EQ(first_child, first_selected_node->GetNativeViewAccessible());
|
|
|
++
|
|
|
++ gfx::NativeViewAccessible first_group_child =
|
|
|
++ static_cast<AXPlatformNodeBase*>(
|
|
|
++ AXPlatformNode::FromNativeViewAccessible(root->ChildAtIndex(1)))
|
|
|
++ ->ChildAtIndex(0);
|
|
|
++ AXPlatformNodeBase* second_selected_node = root->GetSelectedItem(1);
|
|
|
++ EXPECT_EQ(first_group_child, second_selected_node->GetNativeViewAccessible());
|
|
|
++
|
|
|
++ gfx::NativeViewAccessible second_group_child =
|
|
|
++ static_cast<AXPlatformNodeBase*>(
|
|
|
++ AXPlatformNode::FromNativeViewAccessible(root->ChildAtIndex(2)))
|
|
|
++ ->ChildAtIndex(1);
|
|
|
++ AXPlatformNodeBase* third_selected_node = root->GetSelectedItem(2);
|
|
|
++ EXPECT_EQ(second_group_child, third_selected_node->GetNativeViewAccessible());
|
|
|
++
|
|
|
++ gfx::NativeViewAccessible fourth_child = root->ChildAtIndex(3);
|
|
|
++ AXPlatformNodeBase* fourth_selected_node = root->GetSelectedItem(3);
|
|
|
++ EXPECT_EQ(fourth_child, fourth_selected_node->GetNativeViewAccessible());
|
|
|
++}
|
|
|
++
|
|
|
+ } // namespace ui
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_unittest.cc b/ui/accessibility/platform/ax_platform_node_unittest.cc
|
|
|
+index c652b37ec7f3a345da4986463962fee9423f213b..480a723325393b55774bbe80b14934dbe1ce95df 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_unittest.cc
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_unittest.cc
|
|
|
+@@ -356,13 +356,13 @@ AXTreeUpdate AXPlatformNodeTest::BuildListBox(
|
|
|
+ bool option_1_is_selected,
|
|
|
+ bool option_2_is_selected,
|
|
|
+ bool option_3_is_selected,
|
|
|
+- ax::mojom::State additional_state /* ax::mojom::State::kNone */) {
|
|
|
++ const std::vector<ax::mojom::State>& additional_state) {
|
|
|
+ AXNodeData listbox;
|
|
|
+ listbox.id = 1;
|
|
|
+ listbox.SetName("ListBox");
|
|
|
+ listbox.role = ax::mojom::Role::kListBox;
|
|
|
+- if (additional_state != ax::mojom::State::kNone)
|
|
|
+- listbox.AddState(additional_state);
|
|
|
++ for (auto state : additional_state)
|
|
|
++ listbox.AddState(state);
|
|
|
+
|
|
|
+ AXNodeData option_1;
|
|
|
+ option_1.id = 2;
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_unittest.h b/ui/accessibility/platform/ax_platform_node_unittest.h
|
|
|
+index 3207332eb8370edf850bac6d8eee72ff8f9fad85..d004a0585ebbdaa6944e05ba48be8936a775e3cd 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_unittest.h
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_unittest.h
|
|
|
+@@ -48,10 +48,11 @@ class AXPlatformNodeTest : public testing::Test, public TestAXTreeManager {
|
|
|
+ AXTreeUpdate Build3X3Table();
|
|
|
+ AXTreeUpdate BuildAriaColumnAndRowCountGrids();
|
|
|
+
|
|
|
+- AXTreeUpdate BuildListBox(bool option_1_is_selected,
|
|
|
+- bool option_2_is_selected,
|
|
|
+- bool option_3_is_selected,
|
|
|
+- ax::mojom::State additional_state);
|
|
|
++ AXTreeUpdate BuildListBox(
|
|
|
++ bool option_1_is_selected,
|
|
|
++ bool option_2_is_selected,
|
|
|
++ bool option_3_is_selected,
|
|
|
++ const std::vector<ax::mojom::State>& additional_state);
|
|
|
+ };
|
|
|
+
|
|
|
+ } // namespace ui
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
|
|
|
+index eadcb741d948798bab9269044170ede3474f237d..a61d4121d9eeb336e72dbb6a3e2bf884a9b4e799 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_win.cc
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_win.cc
|
|
|
+@@ -2131,15 +2131,10 @@ IFACEMETHODIMP AXPlatformNodeWin::GetSelection(SAFEARRAY** result) {
|
|
|
+ WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SELECTION_GETSELECTION);
|
|
|
+ UIA_VALIDATE_CALL_1_ARG(result);
|
|
|
+
|
|
|
+- std::vector<AXPlatformNodeWin*> selected_children;
|
|
|
+- LONG child_count = GetDelegate()->GetChildCount();
|
|
|
+- for (LONG i = 0; i < child_count; ++i) {
|
|
|
+- auto* child = static_cast<AXPlatformNodeWin*>(
|
|
|
+- FromNativeViewAccessible(GetDelegate()->ChildAtIndex(i)));
|
|
|
+- DCHECK(child);
|
|
|
+- if (child->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
|
|
|
+- selected_children.push_back(child);
|
|
|
+- }
|
|
|
++ std::vector<AXPlatformNodeBase*> selected_children;
|
|
|
++ int max_items = GetMaxSelectableItems();
|
|
|
++ if (max_items)
|
|
|
++ GetSelectedItems(max_items, &selected_children);
|
|
|
+
|
|
|
+ LONG selected_children_count = selected_children.size();
|
|
|
+ *result = SafeArrayCreateVector(VT_UNKNOWN, 0, selected_children_count);
|
|
|
+@@ -2147,9 +2142,10 @@ IFACEMETHODIMP AXPlatformNodeWin::GetSelection(SAFEARRAY** result) {
|
|
|
+ return E_OUTOFMEMORY;
|
|
|
+
|
|
|
+ for (LONG i = 0; i < selected_children_count; ++i) {
|
|
|
++ AXPlatformNodeWin* children =
|
|
|
++ static_cast<AXPlatformNodeWin*>(selected_children[i]);
|
|
|
+ HRESULT hr = SafeArrayPutElement(
|
|
|
+- *result, &i,
|
|
|
+- static_cast<IRawElementProviderSimple*>(selected_children[i]));
|
|
|
++ *result, &i, static_cast<IRawElementProviderSimple*>(children));
|
|
|
+ if (FAILED(hr)) {
|
|
|
+ SafeArrayDestroy(*result);
|
|
|
+ *result = nullptr;
|
|
|
+diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
|
|
|
+index 4a1df770606ac88bb44121b58f67198258c8adf2..2092d5c263dafa452c4977d53678ad81aa6575e1 100644
|
|
|
+--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
|
|
|
++++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
|
|
|
+@@ -4655,8 +4655,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIANavigate) {
|
|
|
+ TEST_F(AXPlatformNodeWinTest, TestISelectionProviderCanSelectMultipleDefault) {
|
|
|
+ Init(BuildListBox(/*option_1_is_selected*/ false,
|
|
|
+ /*option_2_is_selected*/ false,
|
|
|
+- /*option_3_is_selected*/ false,
|
|
|
+- /*additional_state*/ ax::mojom::State::kNone));
|
|
|
++ /*option_3_is_selected*/ false, {}));
|
|
|
+
|
|
|
+ ComPtr<ISelectionProvider> selection_provider(
|
|
|
+ QueryInterfaceFromNode<ISelectionProvider>(GetRootAsAXNode()));
|
|
|
+@@ -4668,10 +4667,12 @@ TEST_F(AXPlatformNodeWinTest, TestISelectionProviderCanSelectMultipleDefault) {
|
|
|
+ }
|
|
|
+
|
|
|
+ TEST_F(AXPlatformNodeWinTest, TestISelectionProviderCanSelectMultipleTrue) {
|
|
|
++ const std::vector<ax::mojom::State> state = {
|
|
|
++ ax::mojom::State::kMultiselectable, ax::mojom::State::kFocusable};
|
|
|
+ Init(BuildListBox(/*option_1_is_selected*/ false,
|
|
|
+ /*option_2_is_selected*/ false,
|
|
|
+ /*option_3_is_selected*/ false,
|
|
|
+- /*additional_state*/ ax::mojom::State::kMultiselectable));
|
|
|
++ /*additional_state*/ state));
|
|
|
+
|
|
|
+ ComPtr<ISelectionProvider> selection_provider(
|
|
|
+ QueryInterfaceFromNode<ISelectionProvider>(GetRootAsAXNode()));
|
|
|
+@@ -4687,7 +4688,7 @@ TEST_F(AXPlatformNodeWinTest,
|
|
|
+ Init(BuildListBox(/*option_1_is_selected*/ false,
|
|
|
+ /*option_2_is_selected*/ false,
|
|
|
+ /*option_3_is_selected*/ false,
|
|
|
+- /*additional_state*/ ax::mojom::State::kNone));
|
|
|
++ /*additional_state*/ {}));
|
|
|
+
|
|
|
+ ComPtr<ISelectionProvider> selection_provider(
|
|
|
+ QueryInterfaceFromNode<ISelectionProvider>(GetRootAsAXNode()));
|
|
|
+@@ -4702,7 +4703,7 @@ TEST_F(AXPlatformNodeWinTest, TestISelectionProviderIsSelectionRequiredTrue) {
|
|
|
+ Init(BuildListBox(/*option_1_is_selected*/ false,
|
|
|
+ /*option_2_is_selected*/ false,
|
|
|
+ /*option_3_is_selected*/ false,
|
|
|
+- /*additional_state*/ ax::mojom::State::kRequired));
|
|
|
++ /*additional_state*/ {ax::mojom::State::kRequired}));
|
|
|
+
|
|
|
+ ComPtr<ISelectionProvider> selection_provider(
|
|
|
+ QueryInterfaceFromNode<ISelectionProvider>(GetRootAsAXNode()));
|
|
|
+@@ -4717,7 +4718,7 @@ TEST_F(AXPlatformNodeWinTest, TestISelectionProviderGetSelectionNoneSelected) {
|
|
|
+ Init(BuildListBox(/*option_1_is_selected*/ false,
|
|
|
+ /*option_2_is_selected*/ false,
|
|
|
+ /*option_3_is_selected*/ false,
|
|
|
+- /*additional_state*/ ax::mojom::State::kNone));
|
|
|
++ /*additional_state*/ {ax::mojom::State::kFocusable}));
|
|
|
+
|
|
|
+ ComPtr<ISelectionProvider> selection_provider(
|
|
|
+ QueryInterfaceFromNode<ISelectionProvider>(GetRootAsAXNode()));
|
|
|
+@@ -4743,7 +4744,7 @@ TEST_F(AXPlatformNodeWinTest,
|
|
|
+ Init(BuildListBox(/*option_1_is_selected*/ false,
|
|
|
+ /*option_2_is_selected*/ true,
|
|
|
+ /*option_3_is_selected*/ false,
|
|
|
+- /*additional_state*/ ax::mojom::State::kNone));
|
|
|
++ /*additional_state*/ {ax::mojom::State::kFocusable}));
|
|
|
+
|
|
|
+ ComPtr<ISelectionProvider> selection_provider(
|
|
|
+ QueryInterfaceFromNode<ISelectionProvider>(GetRootAsAXNode()));
|
|
|
+@@ -4775,10 +4776,12 @@ TEST_F(AXPlatformNodeWinTest,
|
|
|
+
|
|
|
+ TEST_F(AXPlatformNodeWinTest,
|
|
|
+ TestISelectionProviderGetSelectionMultipleItemsSelected) {
|
|
|
++ const std::vector<ax::mojom::State> state = {
|
|
|
++ ax::mojom::State::kMultiselectable, ax::mojom::State::kFocusable};
|
|
|
+ Init(BuildListBox(/*option_1_is_selected*/ true,
|
|
|
+ /*option_2_is_selected*/ true,
|
|
|
+ /*option_3_is_selected*/ true,
|
|
|
+- /*additional_state*/ ax::mojom::State::kNone));
|
|
|
++ /*additional_state*/ state));
|
|
|
+
|
|
|
+ ComPtr<ISelectionProvider> selection_provider(
|
|
|
+ QueryInterfaceFromNode<ISelectionProvider>(GetRootAsAXNode()));
|