Commit 3ae311d8 authored by Dominic Mazzoni's avatar Dominic Mazzoni Committed by Commit Bot

Add support for accessible tables with missing/bad data.

Previously we relied on Blink to correctly fill in the
row and column index of every cell in a table. This
tends to be expensive to do in Blink because it
requires invalidating and reserializing the entire table
if any cell changes.

Instead, try to defer as much of those calculations to
downstream to be computed when AT actually requests
table attributes.

This is more efficient, because Blink accessibility code
needs to run any time the page is updated, like when a
table is being built dynamically using JS - whereas AT
typically only queries table attributes when users
explore a table explicitly, so it makes sense to defer
computation until then.

This is also more accurate and less error-prone, as it
allows us to completely validate the table in a single
pass and fix any errors in table indexes.

Finally, a potential added benefit is that this makes it
easier to handle tables from Views or Arc++ that
don't have all of the table attributes set, we can
compute them automatically.

A follow-up change will remove the code that computes
some of these table attributes from Blink.

Bug: 832289

Change-Id: I252a592c854e47cf6d6e5bd6b7c4bc5d51775e72
Reviewed-on: https://chromium-review.googlesource.com/c/1307840Reviewed-by: default avatarAaron Leventhal <aleventhal@chromium.org>
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606940}
parent fabe78ea
...@@ -199,6 +199,22 @@ int32_t AXNode::GetTableRowCount() const { ...@@ -199,6 +199,22 @@ int32_t AXNode::GetTableRowCount() const {
return table_info->row_count; return table_info->row_count;
} }
int32_t AXNode::GetTableAriaColCount() const {
AXTableInfo* table_info = tree_->GetTableInfo(this);
if (!table_info)
return 0;
return table_info->aria_col_count;
}
int32_t AXNode::GetTableAriaRowCount() const {
AXTableInfo* table_info = tree_->GetTableInfo(this);
if (!table_info)
return 0;
return table_info->aria_row_count;
}
int32_t AXNode::GetTableCellCount() const { int32_t AXNode::GetTableCellCount() const {
AXTableInfo* table_info = tree_->GetTableInfo(this); AXTableInfo* table_info = tree_->GetTableInfo(this);
if (!table_info) if (!table_info)
...@@ -319,17 +335,27 @@ int32_t AXNode::GetTableCellIndex() const { ...@@ -319,17 +335,27 @@ int32_t AXNode::GetTableCellIndex() const {
} }
int32_t AXNode::GetTableCellColIndex() const { int32_t AXNode::GetTableCellColIndex() const {
// TODO(dmazzoni): Compute from AXTableInfo. http://crbug.com/832289 AXTableInfo* table_info = GetAncestorTableInfo();
int32_t col_index = 0; if (!table_info)
GetIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, &col_index); return 0;
return col_index;
int32_t index = GetTableCellIndex();
if (index == -1)
return 0;
return table_info->cell_data_vector[index].col_index;
} }
int32_t AXNode::GetTableCellRowIndex() const { int32_t AXNode::GetTableCellRowIndex() const {
// TODO(dmazzoni): Compute from AXTableInfo. http://crbug.com/832289 AXTableInfo* table_info = GetAncestorTableInfo();
int32_t row_index = 0; if (!table_info)
GetIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, &row_index); return 0;
return row_index;
int32_t index = GetTableCellIndex();
if (index == -1)
return 0;
return table_info->cell_data_vector[index].row_index;
} }
int32_t AXNode::GetTableCellColSpan() const { int32_t AXNode::GetTableCellColSpan() const {
...@@ -360,15 +386,27 @@ int32_t AXNode::GetTableCellRowSpan() const { ...@@ -360,15 +386,27 @@ int32_t AXNode::GetTableCellRowSpan() const {
} }
int32_t AXNode::GetTableCellAriaColIndex() const { int32_t AXNode::GetTableCellAriaColIndex() const {
int32_t col_index = 0; AXTableInfo* table_info = GetAncestorTableInfo();
GetIntAttribute(ax::mojom::IntAttribute::kAriaCellColumnIndex, &col_index); if (!table_info)
return col_index; return -0;
int32_t index = GetTableCellIndex();
if (index == -1)
return 0;
return table_info->cell_data_vector[index].aria_col_index;
} }
int32_t AXNode::GetTableCellAriaRowIndex() const { int32_t AXNode::GetTableCellAriaRowIndex() const {
int32_t row_index = 0; AXTableInfo* table_info = GetAncestorTableInfo();
GetIntAttribute(ax::mojom::IntAttribute::kAriaCellRowIndex, &row_index); if (!table_info)
return row_index; return -0;
int32_t index = GetTableCellIndex();
if (index == -1)
return 0;
return table_info->cell_data_vector[index].aria_row_index;
} }
void AXNode::GetTableCellColHeaderNodeIds( void AXNode::GetTableCellColHeaderNodeIds(
......
...@@ -191,14 +191,22 @@ class AX_EXPORT AXNode final { ...@@ -191,14 +191,22 @@ class AX_EXPORT AXNode final {
// Most of these functions construct and cache an AXTableInfo behind // Most of these functions construct and cache an AXTableInfo behind
// the scenes to infer many properties of tables. // the scenes to infer many properties of tables.
// //
// TODO(dmazzoni): Make these const - not trivial because AXTableInfo // These interfaces use attributes provided by the source of the
// does need to modify the AXTree. // AX tree where possible, but fills in missing details and ignores
// specified attributes when they're bad or inconsistent. That way
// you're guaranteed to get a valid, consistent table when using these
// interfaces.
// //
// Table-like nodes (including grids). // Table-like nodes (including grids). All indices are 0-based except
// ARIA indices are all 1-based. In other words, the top-left corner
// of the table is row 0, column 0, cell index 0 - but that same cell
// has a minimum ARIA row index of 1 and column index of 1.
bool IsTable() const; bool IsTable() const;
int32_t GetTableColCount() const; int32_t GetTableColCount() const;
int32_t GetTableRowCount() const; int32_t GetTableRowCount() const;
int32_t GetTableAriaColCount() const;
int32_t GetTableAriaRowCount() const;
int32_t GetTableCellCount() const; int32_t GetTableCellCount() const;
AXNode* GetTableCellFromIndex(int32_t index) const; AXNode* GetTableCellFromIndex(int32_t index) const;
AXNode* GetTableCellFromCoords(int32_t row_index, int32_t col_index) const; AXNode* GetTableCellFromCoords(int32_t row_index, int32_t col_index) const;
......
This diff is collapsed.
...@@ -19,6 +19,17 @@ class AXNode; ...@@ -19,6 +19,17 @@ class AXNode;
// This helper class computes info about tables and grids in AXTrees. // This helper class computes info about tables and grids in AXTrees.
class AX_EXPORT AXTableInfo { class AX_EXPORT AXTableInfo {
public: public:
struct CellData {
AXNode* cell;
int32_t cell_id;
int32_t col_index;
int32_t row_index;
int32_t col_span;
int32_t row_span;
int32_t aria_col_index;
int32_t aria_row_index;
};
// Returns nullptr if the node is not a valid table or grid node. // Returns nullptr if the node is not a valid table or grid node.
static AXTableInfo* Create(AXTree* tree, AXNode* table_node); static AXTableInfo* Create(AXTree* tree, AXNode* table_node);
...@@ -57,6 +68,9 @@ class AX_EXPORT AXTableInfo { ...@@ -57,6 +68,9 @@ class AX_EXPORT AXTableInfo {
// really is missing from the table. // really is missing from the table.
std::vector<std::vector<int32_t>> cell_ids; std::vector<std::vector<int32_t>> cell_ids;
// Array of cell data for every unique cell in the table.
std::vector<CellData> cell_data_vector;
// Set of all unique cell node IDs in the table. // Set of all unique cell node IDs in the table.
std::vector<int32_t> unique_cell_ids; std::vector<int32_t> unique_cell_ids;
...@@ -68,9 +82,19 @@ class AX_EXPORT AXTableInfo { ...@@ -68,9 +82,19 @@ class AX_EXPORT AXTableInfo {
// Map from each cell's node ID to its index in unique_cell_ids. // Map from each cell's node ID to its index in unique_cell_ids.
base::hash_map<int32_t, int32_t> cell_id_to_index; base::hash_map<int32_t, int32_t> cell_id_to_index;
// The ARIA row count and column count, if any ARIA table or grid
// attributes are used in the table at all.
int32_t aria_row_count = 0;
int32_t aria_col_count = 0;
private: private:
AXTableInfo(AXTree* tree, AXNode* table_node); AXTableInfo(AXTree* tree, AXNode* table_node);
void ClearVectors();
void BuildCellDataVectorFromRowAndCellNodes(
const std::vector<AXNode*>& row_nodes,
const std::vector<std::vector<AXNode*>>& cell_nodes_per_row);
void BuildCellAndHeaderVectorsFromCellData();
void UpdateExtraMacNodes(); void UpdateExtraMacNodes();
void ClearExtraMacNodes(); void ClearExtraMacNodes();
AXNode* CreateExtraMacColumnNode(int col_index); AXNode* CreateExtraMacColumnNode(int col_index);
......
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