Commit 5eea952e authored by Victor Fei's avatar Victor Fei Committed by Commit Bot

Refactor PosInSet & SetSize algorithm

Refactored the PosInSet & SetSize algorithm, more specifically:
  - AXTree::ComputeSetSizePosInSetAndCache
  - AXTree::PopulateOrderedSetItemsMap
To compute PosInSet & SetSize for ordered set items, we first need to
parse ordered set items from DOM structure and store/populate it into
a data structure, and from which we can perform PosInSet & SetSize
computations. Previously, we store these ordered set items into
std::vector<AXNode*>. Using std::vector works well when set items
from various hierarchical levels are nested in DOM structure. However,
for flattened structure where levels are distinguished by aria-level
attributes, the previous algorithm did not work well. Although
various changes (e.g. CL:1461539) have been made, the algorithm is
still not robust enough for handling flattened structures, Bug:952904.

The primary change in the current CL is to replace
|items_to_be_populated| from std::vector to std::map (and renaming it
to |items_map_to_be_populated|), where hierarchical levels are mapped
to lists of ordered sets:

  std::map<base::Optional<int32_t>, // Hierarchical level
        std::vector<OrderedSetContent>> // Ordered sets on that level

  struct AXTree::OrderedSetContent {
    std::vector<AXNode*> set_items_; // Ordered set items
    AXNode* set_container_; // Ordered set container
  };

This new data structure enables AXTree::PopulateOrderedSetItemsMap to
parse, sort, and store various ordered set items according to its
ordered set and hierarchical level, which greatly cleans up PosInSet
and SetSize algorithm, and more robust handlings for flattened ordered
set structures.

Bug: 952904
Change-Id: Ia856290b143afcf911d9083c735dd7bf9bb3ddf9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2036908Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarIan Prest <iapres@microsoft.com>
Reviewed-by: default avatarAkihiro Ota <akihiroota@chromium.org>
Commit-Queue: Victor Fei <vicfei@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#745315}
parent 8fe57796
......@@ -1006,9 +1006,8 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
RunAriaTest(FILE_PATH_LITERAL("aria-sort-aria-grid.html"));
}
// Flaky. http://crbug.com/952904
IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
DISABLED_AccessibilityAriaSetCountsWithTreeLevels) {
AccessibilityAriaSetCountsWithTreeLevels) {
RunAriaTest(FILE_PATH_LITERAL("aria-set-counts-with-tree-levels.html"));
}
......
rootWebArea
++tree setSize=2
++++treeItem name='Item A' hierarchicalLevel=1 setSize=2 posInSet=1 selected=false
++++++staticText name='Item A'
++++++++inlineTextBox name='Item A'
++++treeItem name='Item A1' hierarchicalLevel=2 setSize=2 posInSet=1 selected=false
++++++staticText name='Item A1'
++++++++inlineTextBox name='Item A1'
++++treeItem name='Item A1x' hierarchicalLevel=3 setSize=3 posInSet=1 selected=false
++++++staticText name='Item A1x'
++++++++inlineTextBox name='Item A1x'
++++treeItem name='Item A1y' hierarchicalLevel=3 setSize=3 posInSet=2 selected=false
++++++staticText name='Item A1y'
++++++++inlineTextBox name='Item A1y'
++++treeItem name='Item A1z' hierarchicalLevel=3 setSize=3 posInSet=3 selected=false
++++++staticText name='Item A1z'
++++++++inlineTextBox name='Item A1z'
++++treeItem name='Item A2' hierarchicalLevel=2 setSize=2 posInSet=2 selected=false
++++++staticText name='Item A2'
++++++++inlineTextBox name='Item A2'
++++treeItem name='Item B' hierarchicalLevel=1 setSize=2 posInSet=2 selected=false
++++++staticText name='Item B'
++++++++inlineTextBox name='Item B'
++++treeItem name='Item B1' hierarchicalLevel=2 setSize=1 posInSet=1 selected=false
++++++staticText name='Item B1'
++++++++inlineTextBox name='Item B1'
++genericContainer ignored
++++tree setSize=2
++++++treeItem name='Item A' hierarchicalLevel=1 setSize=2 posInSet=1
++++++++staticText name='Item A'
++++++++++inlineTextBox name='Item A'
++++++treeItem name='Item A1' hierarchicalLevel=2 setSize=2 posInSet=1
++++++++staticText name='Item A1'
++++++++++inlineTextBox name='Item A1'
++++++treeItem name='Item A1x' hierarchicalLevel=3 setSize=3 posInSet=1
++++++++staticText name='Item A1x'
++++++++++inlineTextBox name='Item A1x'
++++++treeItem name='Item A1y' hierarchicalLevel=3 setSize=3 posInSet=2
++++++++staticText name='Item A1y'
++++++++++inlineTextBox name='Item A1y'
++++++treeItem name='Item A1z' hierarchicalLevel=3 setSize=3 posInSet=3
++++++++staticText name='Item A1z'
++++++++++inlineTextBox name='Item A1z'
++++++treeItem name='Item A2' hierarchicalLevel=2 setSize=2 posInSet=2
++++++++staticText name='Item A2'
++++++++++inlineTextBox name='Item A2'
++++++treeItem name='Item B' hierarchicalLevel=1 setSize=2 posInSet=2
++++++++staticText name='Item B'
++++++++++inlineTextBox name='Item B'
++++++treeItem name='Item B1' hierarchicalLevel=2 setSize=1 posInSet=1
++++++++staticText name='Item B1'
++++++++++inlineTextBox name='Item B1'
<!--
@BLINK-ALLOW:hierarchicalLevel*
@BLINK-ALLOW:setSize*
@BLINK-ALLOW:posInSet*
@BLINK-DENY:setSize=0
@BLINK-DENY:posInSet=0
-->
<!-- According to CORE-AAM:
For role="treeitem", walk the tree backward and forward until the explicit or
computed level becomes less than the current item's level. Count items only
if they are at the same level as the current item.
-->
<html>
<body>
<div role="tree">
......
......@@ -11,4 +11,3 @@ rootWebArea
++++++++++tabList horizontal hierarchicalLevel=3 setSize=1
++++++++++++tab name='Tab 1, level 3' setSize=1 posInSet=1 selected=false
++++++tab name='Tab 3, level 1' setSize=3 posInSet=3 selected=false
<-- End-of-file -->
......@@ -734,6 +734,19 @@ void AXNode::IdVectorToNodeVector(std::vector<int32_t>& ids,
}
}
base::Optional<int> AXNode::GetHierarchicalLevel() const {
int hierarchical_level =
GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
// According to the WAI_ARIA spec, a defined hierarchical level value is
// greater than 0.
// https://www.w3.org/TR/wai-aria-1.1/#aria-level
if (hierarchical_level > 0)
return base::Optional<int>(hierarchical_level);
return base::nullopt;
}
bool AXNode::IsOrderedSetItem() const {
return ui::IsItemLike(data().role);
}
......@@ -806,7 +819,8 @@ bool AXNode::SetRoleMatchesItemRole(const AXNode* ordered_set) const {
case ax::mojom::Role::kList:
return item_role == ax::mojom::Role::kListItem;
case ax::mojom::Role::kGroup:
return item_role == ax::mojom::Role::kListItem ||
return item_role == ax::mojom::Role::kComment ||
item_role == ax::mojom::Role::kListItem ||
item_role == ax::mojom::Role::kMenuItem ||
item_role == ax::mojom::Role::kMenuItemRadio ||
item_role == ax::mojom::Role::kTreeItem;
......
......@@ -264,6 +264,9 @@ class AX_EXPORT AXNode final {
return data().GetHtmlAttribute(attribute, value);
}
// Return the hierarchical level if supported.
base::Optional<int> GetHierarchicalLevel() const;
// PosInSet and SetSize public methods.
bool IsOrderedSetItem() const;
bool IsOrderedSet() const;
......
This diff is collapsed.
......@@ -142,15 +142,15 @@ class AX_EXPORT AXTree : public AXNode::OwnerTree {
// conflict with positive-numbered node IDs from tree sources.
int32_t GetNextNegativeInternalNodeId();
// Returns the pos_in_set of node. Looks in ordered_set_info_map_ for cached
// value. Calculates pos_in_set and set_size for node (and all other nodes in
// the same ordered set) if no value is present in the cache.
// Returns the pos_in_set of node. Looks in node_set_size_pos_in_set_info_map_
// for cached value. Calculates pos_in_set and set_size for node (and all
// other nodes in the same ordered set) if no value is present in the cache.
// This function is guaranteed to be only called on nodes that can hold
// pos_in_set values, minimizing the size of the cache.
int32_t GetPosInSet(const AXNode& node, const AXNode* ordered_set) override;
// Returns the set_size of node. Looks in ordered_set_info_map_ for cached
// value. Calculates pos_inset_set and set_size for node (and all other nodes
// in the same ordered set) if no value is present in the cache.
// Returns the set_size of node. Looks in node_set_size_pos_in_set_info_map_
// for cached value. Calculates pos_inset_set and set_size for node (and all
// other nodes in the same ordered set) if no value is present in the cache.
// This function is guaranteed to be only called on nodes that can hold
// set_size values, minimizing the size of the cache.
int32_t GetSetSize(const AXNode& node, const AXNode* ordered_set) override;
......@@ -323,43 +323,53 @@ class AX_EXPORT AXTree : public AXNode::OwnerTree {
bool enable_extra_mac_nodes_ = false;
// Contains pos_in_set and set_size data for an AXNode.
struct OrderedSetInfo {
int32_t pos_in_set;
int32_t set_size;
int32_t lowest_hierarchical_level;
OrderedSetInfo() : pos_in_set(0), set_size(0) {}
~OrderedSetInfo() {}
};
// Populates ordered set items vector with all items associated with
struct NodeSetSizePosInSetInfo;
// Represents the content of an ordered set which includes the ordered set
// items and the ordered set container if it exists.
struct OrderedSetContent;
// Maps a particular hierarchical level to a list of OrderedSetContents.
// Represents all ordered set items/container on a particular hierarchical
// level.
struct OrderedSetItemsMap;
// Populates |items_map_to_be_populated| with all items associated with
// |original_node| and within |ordered_set|. Only items whose roles match the
// role of the |ordered_set| will be added.
void PopulateOrderedSetItems(
void PopulateOrderedSetItemsMap(
const AXNode& original_node,
const AXNode* ordered_set,
std::vector<const AXNode*>& items_to_be_populated) const;
OrderedSetItemsMap& items_map_to_be_populated) const;
// Helper function for recursively populating ordered sets items vector with
// Helper function for recursively populating ordered sets items map with
// all items associated with |original_node| and |ordered_set|. |local_parent|
// tracks the recursively passed in child nodes of |ordered_set|.
void RecursivelyPopulateOrderedSetItems(
void RecursivelyPopulateOrderedSetItemsMap(
const AXNode& original_node,
const AXNode* ordered_set,
const AXNode* local_parent,
int32_t original_node_min_level,
std::vector<const AXNode*>& items_to_be_populated) const;
base::Optional<int> ordered_set_min_level,
base::Optional<int> prev_level,
OrderedSetItemsMap& items_map_to_be_populated) const;
// Helper for GetPosInSet and GetSetSize. Computes the pos_in_set and set_size
// values of all items in ordered_set and caches those values.
// Computes the pos_in_set and set_size values of all items in ordered_set and
// caches those values. Called by GetPosInSet and GetSetSize.
void ComputeSetSizePosInSetAndCache(const AXNode& node,
const AXNode* ordered_set);
// Helper for ComputeSetSizePosInSetAndCache. Computes and caches the
// pos_in_set and set_size values for a given OrderedSetContent.
void ComputeSetSizePosInSetAndCacheHelper(
const OrderedSetContent& ordered_set_content);
// Map from node ID to OrderedSetInfo.
// Item-like and ordered-set-like objects will map to populated OrderedSetInfo
// objects.
// All other objects will map to default-constructed OrderedSetInfo objects.
// Invalidated every time the tree is updated.
mutable std::unordered_map<int32_t, OrderedSetInfo> ordered_set_info_map_;
mutable std::unordered_map<int32_t, NodeSetSizePosInSetInfo>
node_set_size_pos_in_set_info_map_;
// AXTree owns pointers so copying is non-trivial.
DISALLOW_COPY_AND_ASSIGN(AXTree);
......
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