Commit 413ca727 authored by Akihiro Ota's avatar Akihiro Ota Committed by Commit Bot

Implement Cache for pos_in_set and set_size values

Improve upon previous implementation of GetPosInSet and GetSetSize
for AXNode, which computed pos_in_set and set_size values upon every
call of either function. Introduced cache for these values in AXTree,
which maps a node's id to a struct holding its pos_in_set and set_size
values. Cache is invalidated upon updates to the AXTree.

Change-Id: I70c0878b05b53b98648af1f2b76d1e1bf6e6d294
Reviewed-on: https://chromium-review.googlesource.com/c/1351782Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/master@{#613316}
parent ce92577b
...@@ -512,87 +512,78 @@ void AXNode::IdVectorToNodeVector(std::vector<int32_t>& ids, ...@@ -512,87 +512,78 @@ void AXNode::IdVectorToNodeVector(std::vector<int32_t>& ids,
} }
} }
// Returns true if the node's role uses PosInSet and SetSize // pos_in_set and set_size related functions.
// Returns false otherwise. // Uses AXTree's cache to calculate node's pos_in_set.
bool AXNode::IsSetSizePosInSetUsedInRole() const { int32_t AXNode::GetPosInSet() {
switch (data().role) { // Only allow this to be called on nodes that can hold pos_in_set values,
case ax::mojom::Role::kArticle: // which are defined in the ARIA spec.
case ax::mojom::Role::kListItem: if (!IsItemLike(data().role))
case ax::mojom::Role::kMenuItem: return 0;
case ax::mojom::Role::kMenuItemRadio:
case ax::mojom::Role::kTab:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kTreeItem:
case ax::mojom::Role::kListBoxOption:
case ax::mojom::Role::kRadioButton:
return true;
default: // See AXTree::GetPosInSet
return false; return tree_->GetPosInSet(*this);
}
} }
// Returns true if a node's role matches with the role of its container. // Uses AXTree's cache to calculate node's pos_in_set.
int32_t AXNode::GetSetSize() {
// Only allow this to be called on nodes that can hold set_size values, which
// are defined in the ARIA spec.
if (!(IsItemLike(data().role) || IsSetLike(data().role)))
return 0;
// See AXTree::GetSetSize
return tree_->GetSetSize(*this);
}
// Returns true if the role of ordered set matches the role of item.
// Returns false otherwise. // Returns false otherwise.
bool AXNode::ContainerRoleMatches(AXNode* container) const { bool AXNode::SetRoleMatchesItemRole(const AXNode* ordered_set) const {
ax::mojom::Role container_role = container->data().role; ax::mojom::Role item_role = data().role;
switch (data().role) {
case ax::mojom::Role::kArticle: // Switch on role of ordered set
return container_role == ax::mojom::Role::kFeed; switch (ordered_set->data().role) {
case ax::mojom::Role::kFeed:
return item_role == ax::mojom::Role::kArticle;
case ax::mojom::Role::kListItem: case ax::mojom::Role::kList:
return container_role == ax::mojom::Role::kList || return item_role == ax::mojom::Role::kListItem;
container_role == ax::mojom::Role::kGroup;
case ax::mojom::Role::kMenuItem: case ax::mojom::Role::kGroup:
return container_role == ax::mojom::Role::kMenu || return item_role == ax::mojom::Role::kListItem ||
container_role == ax::mojom::Role::kGroup || item_role == ax::mojom::Role::kMenuItem ||
container_role == ax::mojom::Role::kMenuBar; item_role == ax::mojom::Role::kMenuItemRadio ||
item_role == ax::mojom::Role::kTreeItem;
case ax::mojom::Role::kMenuItemRadio: case ax::mojom::Role::kMenu:
return container_role == ax::mojom::Role::kGroup || return item_role == ax::mojom::Role::kMenuItem ||
container_role == ax::mojom::Role::kMenu || item_role == ax::mojom::Role::kMenuItemRadio ||
container_role == ax::mojom::Role::kMenuBar; item_role == ax::mojom::Role::kMenuItemCheckBox;
case ax::mojom::Role::kTab: case ax::mojom::Role::kMenuBar:
return container_role == ax::mojom::Role::kTabList; return item_role == ax::mojom::Role::kMenuItem ||
item_role == ax::mojom::Role::kMenuItemRadio ||
item_role == ax::mojom::Role::kMenuItemCheckBox;
case ax::mojom::Role::kMenuItemCheckBox: case ax::mojom::Role::kTabList:
return container_role == ax::mojom::Role::kMenu || return item_role == ax::mojom::Role::kTab;
container_role == ax::mojom::Role::kMenuBar;
case ax::mojom::Role::kTreeItem: case ax::mojom::Role::kTree:
return container_role == ax::mojom::Role::kTree || return item_role == ax::mojom::Role::kTreeItem;
container_role == ax::mojom::Role::kGroup;
case ax::mojom::Role::kListBoxOption: case ax::mojom::Role::kListBox:
return container_role == ax::mojom::Role::kListBox; return item_role == ax::mojom::Role::kListBoxOption;
case ax::mojom::Role::kRadioButton: case ax::mojom::Role::kRadioGroup:
return container_role == ax::mojom::Role::kRadioGroup; return item_role == ax::mojom::Role::kRadioButton;
default: default:
return false; return false;
} }
} }
int32_t AXNode::GetPosInSet() { // Finds ordered set that immediately contains node.
int32_t pos = -1; // Is not required for set's role to match node's role.
int32_t size = -1; AXNode* AXNode::GetOrderedSet() const {
ComputeSetSizePosInSet(&pos, &size);
return pos;
}
int32_t AXNode::GetSetSize() {
int32_t pos = -1;
int32_t size = -1;
ComputeSetSizePosInSet(&pos, &size);
return size;
}
// Finds and returns a pointer to node's container.
// Is not required to have a role that matches node's role.
// Returns nullptr if node is not contained within container.
AXNode* AXNode::GetContainer() const {
AXNode* result = parent(); AXNode* result = parent();
// Continue walking up while parent is invalid, ignored, or is a generic // Continue walking up while parent is invalid, ignored, or is a generic
// container. // container.
...@@ -604,126 +595,4 @@ AXNode* AXNode::GetContainer() const { ...@@ -604,126 +595,4 @@ AXNode* AXNode::GetContainer() const {
return result; return result;
} }
// Populates items vector with all nodes within container whose roles match.
void AXNode::PopulateContainerItems(AXNode* container,
AXNode* local_parent,
std::vector<AXNode*>& items) const {
// Stop searching current path if roles of local_parent and container match.
// Don't compare the container to itself.
if (!(container == local_parent))
if (local_parent->data().role == container->data().role)
return;
for (int i = 0; i < local_parent->child_count(); ++i) {
AXNode* child = local_parent->children_[i];
// Add child to items if role matches with root container's role.
if (child->ContainerRoleMatches(container))
items.push_back(child);
// Recurse if there is a generic container or is ignored.
if (child->data().role == ax::mojom::Role::kGenericContainer ||
child->data().role == ax::mojom::Role::kIgnored) {
PopulateContainerItems(container, child, items);
}
}
}
// Computes pos_in_set and set_size values for this node.
void AXNode::ComputeSetSizePosInSet(int32_t* out_pos_in_set,
int32_t* out_set_size) {
// Error checks
AXNode* container = GetContainer();
if (!(container && IsSetSizePosInSetUsedInRole() &&
ContainerRoleMatches(container))) {
*out_pos_in_set = 0;
*out_set_size = 0;
return;
}
// Find all items within parent container and add to vector.
std::vector<AXNode*> items;
PopulateContainerItems(container, container, items);
// Necessary for calculating set_size. Keeps track of largest assigned
// kSetSize for each role.
std::unordered_map<ax::mojom::Role, int> largest_assigned_set_size;
// Iterate over vector of items and calculate pos_in_set and set_size for
// each. Items is not guaranteed to be populated with items of the same role.
// Use dictionary that maps role to frequency to calculate pos_in_set.
std::unordered_map<ax::mojom::Role, int> role_counts;
AXNode* node;
ax::mojom::Role node_role;
// Compute pos_in_set values.
for (unsigned int i = 0; i < items.size(); ++i) {
node = items[i];
node_role = node->data().role;
int32_t pos_in_set_value = 0;
if (role_counts.find(node_role) == role_counts.end())
// This is the first node with its role.
pos_in_set_value = 1;
else {
// This is the next node with its role.
pos_in_set_value = role_counts[node_role] + 1;
}
// Check if node has kPosInSet assigned. This assignment takes precedence
// over previous assignment.
if (node->HasIntAttribute(ax::mojom::IntAttribute::kPosInSet)) {
pos_in_set_value =
node->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet);
// If invalid assignment (decrease or duplicate), adjust value.
if (pos_in_set_value <= role_counts[node_role]) {
pos_in_set_value = role_counts[node_role] + 1;
}
}
// Assign pos_in_set and update role counts.
if (node == this) {
*out_pos_in_set = pos_in_set_value;
}
role_counts[node_role] = pos_in_set_value;
// Check if kSetSize is assigned and update if it's the largest assigned
// kSetSize.
if (node->HasIntAttribute(ax::mojom::IntAttribute::kSetSize))
largest_assigned_set_size[node_role] =
std::max(largest_assigned_set_size[node_role],
node->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
}
// Compute set_size values.
for (unsigned int j = 0; j < items.size(); ++j) {
node = items[j];
node_role = node->data().role;
// TODO (akihiroota): List objects should report SetSize
// The SetSize of a node is the maximum of the following candidate values:
// 1. The PosInSet of the last value in the container (with same role as
// node's)
// 2. The Largest assigned SetSize in the container
// 3. The SetSize assigned within the node's container
int32_t pos_candidate = role_counts[node_role];
int32_t largest_set_size_candidate = 0;
if (largest_assigned_set_size.find(node_role) !=
largest_assigned_set_size.end()) {
largest_set_size_candidate = largest_assigned_set_size[node_role];
}
int32_t container_candidate = 0;
if (container->HasIntAttribute(ax::mojom::IntAttribute::kSetSize)) {
container_candidate =
container->GetIntAttribute(ax::mojom::IntAttribute::kSetSize);
}
// Assign set_size
if (node == this) {
*out_set_size =
std::max(std::max(pos_candidate, largest_set_size_candidate),
container_candidate);
}
}
}
} // namespace ui } // namespace ui
...@@ -32,6 +32,9 @@ class AX_EXPORT AXNode final { ...@@ -32,6 +32,9 @@ class AX_EXPORT AXNode final {
virtual AXTableInfo* GetTableInfo(const AXNode* table_node) const = 0; virtual AXTableInfo* GetTableInfo(const AXNode* table_node) const = 0;
// See AXTree. // See AXTree.
virtual AXNode* GetFromId(int32_t id) const = 0; virtual AXNode* GetFromId(int32_t id) const = 0;
virtual int32_t GetPosInSet(const AXNode& item) = 0;
virtual int32_t GetSetSize(const AXNode& node) = 0;
}; };
// The constructor requires a parent, id, and index in parent, but // The constructor requires a parent, id, and index in parent, but
...@@ -186,6 +189,12 @@ class AX_EXPORT AXNode final { ...@@ -186,6 +189,12 @@ class AX_EXPORT AXNode final {
// PosInSet and SetSize public methods // PosInSet and SetSize public methods
int32_t GetPosInSet(); int32_t GetPosInSet();
int32_t GetSetSize(); int32_t GetSetSize();
// Finds and returns a pointer to ordered set containing node.
AXNode* GetOrderedSet() const;
// Helpers for GetPosInSet and GetSetSize.
// Returns true if the role of ordered set matches the role of item.
// Returns false otherwise.
bool SetRoleMatchesItemRole(const AXNode* ordered_set) const;
const std::string& GetInheritedStringAttribute( const std::string& GetInheritedStringAttribute(
ax::mojom::StringAttribute attribute) const; ax::mojom::StringAttribute attribute) const;
...@@ -262,21 +271,6 @@ class AX_EXPORT AXNode final { ...@@ -262,21 +271,6 @@ class AX_EXPORT AXNode final {
void IdVectorToNodeVector(std::vector<int32_t>& ids, void IdVectorToNodeVector(std::vector<int32_t>& ids,
std::vector<AXNode*>* nodes) const; std::vector<AXNode*>* nodes) const;
// Helpers for GetPosInSet and GetSetSize.
// Returns true if the role of parent container matches the role of node.
// Returns false otherwise.
bool ContainerRoleMatches(AXNode* parent) const;
// Returns true if the node's role uses PosInSet and SetSize
// Returns false otherwise.
bool IsSetSizePosInSetUsedInRole() const;
// Finds and returns a pointer to node's container.
AXNode* GetContainer() const;
// Populates items vector with all nodes within container whose roles match.
void PopulateContainerItems(AXNode* container,
AXNode* local_parent,
std::vector<AXNode*>& items) const;
// Computes pos_in_set and set_size values for this node.
void ComputeSetSizePosInSet(int32_t* out_pos_in_set, int32_t* out_set_size);
OwnerTree* tree_; // Owns this. OwnerTree* tree_; // Owns this.
int index_in_parent_; int index_in_parent_;
......
...@@ -160,6 +160,23 @@ bool IsImage(const ax::mojom::Role role) { ...@@ -160,6 +160,23 @@ bool IsImage(const ax::mojom::Role role) {
} }
} }
bool IsItemLike(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kArticle:
case ax::mojom::Role::kListItem:
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemRadio:
case ax::mojom::Role::kTab:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kTreeItem:
case ax::mojom::Role::kListBoxOption:
case ax::mojom::Role::kRadioButton:
return true;
default:
return false;
}
}
bool IsLink(const ax::mojom::Role role) { bool IsLink(const ax::mojom::Role role) {
switch (role) { switch (role) {
case ax::mojom::Role::kDocBackLink: case ax::mojom::Role::kDocBackLink:
...@@ -241,6 +258,23 @@ bool IsRowContainer(const ax::mojom::Role role) { ...@@ -241,6 +258,23 @@ bool IsRowContainer(const ax::mojom::Role role) {
} }
} }
bool IsSetLike(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kFeed:
case ax::mojom::Role::kList:
case ax::mojom::Role::kGroup:
case ax::mojom::Role::kMenu:
case ax::mojom::Role::kMenuBar:
case ax::mojom::Role::kTabList:
case ax::mojom::Role::kTree:
case ax::mojom::Role::kListBox:
case ax::mojom::Role::kRadioGroup:
return true;
default:
return false;
}
}
bool IsTableHeader(ax::mojom::Role role) { bool IsTableHeader(ax::mojom::Role role) {
switch (role) { switch (role) {
case ax::mojom::Role::kColumnHeader: case ax::mojom::Role::kColumnHeader:
......
...@@ -41,6 +41,10 @@ AX_EXPORT bool IsHeadingOrTableHeader(const ax::mojom::Role role); ...@@ -41,6 +41,10 @@ AX_EXPORT bool IsHeadingOrTableHeader(const ax::mojom::Role role);
// Returns true if the provided role belongs to an image, graphic, canvas, etc. // Returns true if the provided role belongs to an image, graphic, canvas, etc.
AX_EXPORT bool IsImage(const ax::mojom::Role role); AX_EXPORT bool IsImage(const ax::mojom::Role role);
// Returns true if the provided role is item-like, specifically if it can hold
// pos_in_set and set_size values.
AX_EXPORT bool IsItemLike(const ax::mojom::Role role);
// Returns true if the provided role belongs to a link. // Returns true if the provided role belongs to a link.
AX_EXPORT bool IsLink(const ax::mojom::Role role); AX_EXPORT bool IsLink(const ax::mojom::Role role);
...@@ -61,6 +65,10 @@ AX_EXPORT bool IsMenuRelated(const ax::mojom::Role role); ...@@ -61,6 +65,10 @@ AX_EXPORT bool IsMenuRelated(const ax::mojom::Role role);
// table or grid row. // table or grid row.
AX_EXPORT bool IsRowContainer(const ax::mojom::Role role); AX_EXPORT bool IsRowContainer(const ax::mojom::Role role);
// Returns true if the provided role is ordered-set like, specifically if it
// can hold set_size values.
AX_EXPORT bool IsSetLike(const ax::mojom::Role role);
// Returns true if the provided role belongs to a table header. // Returns true if the provided role belongs to a table header.
AX_EXPORT bool IsTableHeader(ax::mojom::Role role); AX_EXPORT bool IsTableHeader(ax::mojom::Role role);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "ui/accessibility/accessibility_switches.h" #include "ui/accessibility/accessibility_switches.h"
#include "ui/accessibility/ax_node.h" #include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/accessibility/ax_table_info.h" #include "ui/accessibility/ax_table_info.h"
#include "ui/gfx/transform.h" #include "ui/gfx/transform.h"
...@@ -468,6 +469,9 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { ...@@ -468,6 +469,9 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) {
this, root_->id() != old_root_id, changes); this, root_->id() != old_root_id, changes);
} }
// Clear list_info_map_
ordered_set_info_map_.clear();
return true; return true;
} }
...@@ -878,4 +882,133 @@ int32_t AXTree::GetNextNegativeInternalNodeId() { ...@@ -878,4 +882,133 @@ int32_t AXTree::GetNextNegativeInternalNodeId() {
return return_value; return return_value;
} }
// Populates items vector with all items within ordered set whose roles match.
void AXTree::PopulateOrderedSetItems(const AXNode* ordered_set,
const AXNode* local_parent,
std::vector<const AXNode*>& items) const {
// Stop searching current path if roles of local_parent and ordered set match.
// Don't compare the container to itself.
if (!(ordered_set == local_parent)) {
if (local_parent->data().role == ordered_set->data().role)
return;
}
for (int i = 0; i < local_parent->child_count(); ++i) {
const AXNode* child = local_parent->GetUnignoredChildAtIndex(i);
// Add child to items if role matches with ordered set's role.
if (child->SetRoleMatchesItemRole(ordered_set))
items.push_back(child);
// Recurse if there is a generic container or is ignored.
if (child->data().role == ax::mojom::Role::kGenericContainer ||
child->data().role == ax::mojom::Role::kIgnored) {
PopulateOrderedSetItems(ordered_set, child, items);
}
}
}
// Given an ordered_set, compute pos_in_set and set_size for all of its items
// and store values in cache.
void AXTree::ComputeSetSizePosInSetAndCache(const AXNode* ordered_set) {
// Default ordered_set's pos_in_set and set_size to 0.
ordered_set_info_map_[ordered_set->id()] = OrderedSetInfo();
// Find all items within ordered_set and add to vector.
std::vector<const AXNode*> items;
PopulateOrderedSetItems(ordered_set, ordered_set, items);
// Keep track of the number of elements ordered_set has
int32_t num_elements = 0;
// Necessary for calculating set_size.
int32_t largest_assigned_set_size = 0;
// Compute pos_in_set_values.
for (size_t i = 0; i < items.size(); ++i) {
const AXNode* item = items[i];
ordered_set_info_map_[item->id()] = OrderedSetInfo();
int32_t pos_in_set_value = 0;
pos_in_set_value = num_elements + 1;
// Check if item has a valid kPosInSet assignment, which takes precedence
// over previous assignment. Invalid assignments are decreasing or
// duplicates, and should be ignored.
pos_in_set_value =
std::max(pos_in_set_value,
item->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet));
// Assign pos_in_set and update role counts.
ordered_set_info_map_[item->id()].pos_in_set = pos_in_set_value;
num_elements = pos_in_set_value;
// Check if kSetSize is assigned and update if it's the largest assigned
// kSetSize.
if (item->HasIntAttribute(ax::mojom::IntAttribute::kSetSize))
largest_assigned_set_size =
std::max(largest_assigned_set_size,
item->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
}
// Compute set_size value.
// The SetSize of an ordered set (and all of its items) is the maximum of the
// following candidate values:
// 1. The PosInSet of the last item in the ordered set
// 2. The Largest assigned SetSize in the ordered set.
// 3. The SetSize assigned within the ordered set.
int32_t pos_candidate = num_elements;
int32_t largest_set_size_candidate = largest_assigned_set_size;
int32_t ordered_set_candidate = 0;
if (ordered_set->HasIntAttribute(ax::mojom::IntAttribute::kSetSize)) {
ordered_set_candidate =
ordered_set->GetIntAttribute(ax::mojom::IntAttribute::kSetSize);
}
int32_t set_size_value =
std::max(std::max(pos_candidate, largest_set_size_candidate),
ordered_set_candidate);
// Assign set_size to ordered set
ordered_set_info_map_[ordered_set->id()].set_size = set_size_value;
// Assign set_size to items
for (size_t j = 0; j < items.size(); ++j) {
const AXNode* item = items[j];
ordered_set_info_map_[item->id()].set_size = set_size_value;
}
}
// Returns the pos_in_set of item. Looks in ordered_set_info_map_ for cached
// value. Calculates pos_in_set and set_size for item (and all other items 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 AXTree::GetPosInSet(const AXNode& item) {
// If item's id is not in the cache, compute it.
if (ordered_set_info_map_.find(item.id()) == ordered_set_info_map_.end())
ComputeSetSizePosInSetAndCache(item.GetOrderedSet());
return ordered_set_info_map_[item.id()].pos_in_set;
}
// Returns the set_size of node. node could be an ordered set or an item.
// Looks in ordered_set_info_map_ for cached value. Calculates pos_inset_set
// and set_size for all nodes in 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 AXTree::GetSetSize(const AXNode& node) {
const AXNode* ordered_set;
// If node's id is not in the cache, compute it.
if (ordered_set_info_map_.find(node.id()) == ordered_set_info_map_.end()) {
// If node is item-like, find its outerlying ordered set
if (IsItemLike(node.data().role))
ordered_set = node.GetOrderedSet();
// If its set-like, then it is the ordered set
else
ordered_set = &node;
ComputeSetSizePosInSetAndCache(ordered_set);
}
return ordered_set_info_map_[node.id()].set_size;
}
} // namespace ui } // namespace ui
...@@ -257,6 +257,19 @@ class AX_EXPORT AXTree : public AXNode::OwnerTree { ...@@ -257,6 +257,19 @@ class AX_EXPORT AXTree : public AXNode::OwnerTree {
// conflict with positive-numbered node IDs from tree sources. // conflict with positive-numbered node IDs from tree sources.
int32_t GetNextNegativeInternalNodeId(); int32_t GetNextNegativeInternalNodeId();
// Returns the pos_in_set of item. Looks in ordered_set_info_map_ for cached
// value. Calculates pos_in_set and set_size for item (and all other items 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& item) 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.
// 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) override;
private: private:
friend class AXTableInfoTest; friend class AXTableInfoTest;
...@@ -340,6 +353,27 @@ class AX_EXPORT AXTree : public AXNode::OwnerTree { ...@@ -340,6 +353,27 @@ class AX_EXPORT AXTree : public AXNode::OwnerTree {
// this code to be unit-tested on other platforms (for example, more // this code to be unit-tested on other platforms (for example, more
// code sanitizers run on Linux). // code sanitizers run on Linux).
bool enable_extra_mac_nodes_ = false; 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;
OrderedSetInfo() : pos_in_set(0), set_size(0) {}
~OrderedSetInfo() {}
};
// Populates items vector with all items within ordered_set whose roles match.
void PopulateOrderedSetItems(const AXNode* ordered_set,
const AXNode* local_parent,
std::vector<const AXNode*>& items) 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.
void ComputeSetSizePosInSetAndCache(const AXNode* ordered_set);
// Map from node ID to OrderedSetInfo, if the given node
// is an element within an ordered set.
// Invalidated every time the tree is updated.
mutable std::unordered_map<int32_t, OrderedSetInfo> ordered_set_info_map_;
}; };
} // namespace ui } // namespace ui
......
This diff is collapsed.
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