Commit 0679a597 authored by Nektarios Paisios's avatar Nektarios Paisios Committed by Commit Bot

TableView: Used AXVirtualView to make the table view control accessible

This patch depends on https://chromium-review.googlesource.com/c/chromium/src/+/1229234 which implemented a virtual accessibility view class.
I propose that we use the grid role for the table view and mark it readonly because in Views a table view is interactive like an ARIA grid.
On Windows it will be exposed as a list view control  and on Mac as a table view.
R=dmazzoni@chromium.org, ellyjones@chromium.org, sky@chromium.org, aleventhal@chromium.org

Bug: 811277
Change-Id: I9e02563f1220f300e7fd78dc4cbbf5ee5e585acb
Reviewed-on: https://chromium-review.googlesource.com/c/1363793
Commit-Queue: Nektarios Paisios <nektar@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#625201}
parent 7f0276f4
......@@ -9,6 +9,7 @@
#include <algorithm>
#include <utility>
#include "base/callback.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_tree_data.h"
#include "ui/accessibility/platform/ax_platform_node.h"
......@@ -104,6 +105,7 @@ std::unique_ptr<AXVirtualView> AXVirtualView::RemoveChildView(
std::unique_ptr<AXVirtualView> child = std::move(children_[cur_index]);
children_.erase(children_.begin() + cur_index);
child->virtual_parent_view_ = nullptr;
child->populate_data_callback_.Reset();
return child;
}
......@@ -159,6 +161,15 @@ ui::AXNodeData& AXVirtualView::GetCustomData() {
return custom_data_;
}
void AXVirtualView::SetPopulateDataCallback(
base::RepeatingCallback<void(const View&, ui::AXNodeData*)> callback) {
populate_data_callback_ = std::move(callback);
}
void AXVirtualView::UnsetPopulateDataCallback() {
populate_data_callback_.Reset();
}
// ui::AXPlatformNodeDelegate
const ui::AXNodeData& AXVirtualView::GetData() const {
......@@ -175,6 +186,8 @@ const ui::AXNodeData& AXVirtualView::GetData() const {
if (GetOwnerView() && GetOwnerView()->context_menu_controller())
node_data.AddAction(ax::mojom::Action::kShowContextMenu);
if (populate_data_callback_ && GetOwnerView())
populate_data_callback_.Run(*GetOwnerView(), &node_data);
return node_data;
}
......
......@@ -11,6 +11,7 @@
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "ui/accessibility/ax_enums.mojom.h"
......@@ -101,8 +102,16 @@ class VIEWS_EXPORT AXVirtualView : public ui::AXPlatformNodeDelegateBase {
const char* GetViewClassName() const;
gfx::NativeViewAccessible GetNativeObject() const;
void NotifyAccessibilityEvent(ax::mojom::Event event_type);
// Allows clients to modify the AXNodeData for this virtual view.
// Allows clients to modify the AXNodeData for this virtual view. This should
// be used for attributes that are relatively stable and do not change
// dynamically.
ui::AXNodeData& GetCustomData();
// Allows clients to modify the AXNodeData for this virtual view dynamically
// via a callback. This should be used for attributes that change often and
// would be queried every time a client accesses this view's AXNodeData.
void SetPopulateDataCallback(
base::RepeatingCallback<void(const View&, ui::AXNodeData*)> callback);
void UnsetPopulateDataCallback();
// ui::AXPlatformNodeDelegate
const ui::AXNodeData& GetData() const override;
......@@ -160,6 +169,8 @@ class VIEWS_EXPORT AXVirtualView : public ui::AXPlatformNodeDelegateBase {
ui::AXUniqueId unique_id_;
ui::AXNodeData custom_data_;
base::RepeatingCallback<void(const View&, ui::AXNodeData*)>
populate_data_callback_;
friend class ViewAccessibility;
DISALLOW_COPY_AND_ASSIGN(AXVirtualView);
......
......@@ -90,6 +90,7 @@ std::unique_ptr<AXVirtualView> ViewAccessibility::RemoveVirtualChildView(
std::move(virtual_children_[cur_index]);
virtual_children_.erase(virtual_children_.begin() + cur_index);
child->set_parent_view(nullptr);
child->UnsetPopulateDataCallback();
if (focused_virtual_child_ && child->Contains(focused_virtual_child_))
focused_virtual_child_ = nullptr;
return child;
......
......@@ -101,6 +101,11 @@ class VIEWS_EXPORT ViewAccessibility {
return static_cast<int>(virtual_children_.size());
}
AXVirtualView* virtual_child_at(int index) {
return const_cast<AXVirtualView*>(
const_cast<const ViewAccessibility*>(this)->virtual_child_at(index));
}
const AXVirtualView* virtual_child_at(int index) const {
DCHECK_GE(index, 0);
DCHECK_LT(index, virtual_child_count());
......
This diff is collapsed.
......@@ -17,6 +17,13 @@
#include "ui/views/view.h"
#include "ui/views/views_export.h"
namespace ui {
struct AXActionData;
struct AXNodeData;
} // namespace ui
// A TableView is a view that displays multiple rows with any number of columns.
// TableView is driven by a TableModel. The model returns the contents
// to display. TableModel also has an Observer which is used to notify
......@@ -34,6 +41,7 @@
// sort by way of overriding TableModel::CompareValues().
namespace views {
class AXVirtualView;
class FocusRing;
struct GroupRange;
class TableGrouper;
......@@ -327,6 +335,24 @@ class VIEWS_EXPORT TableView
base::string16* tooltip,
gfx::Point* tooltip_origin) const;
// Updates a set of accessibility views that expose the visible table contents
// to assistive software.
void UpdateVirtualAccessibilityChildren();
// Updates the internal accessibility state and fires the required
// accessibility events to indicate to assistive software which row is active
// and which cell is focused, if any.
void UpdateAccessibilityFocus();
// Returns the virtual accessibility view corresponding to the specified row.
// |row| should be a view index, not a model index.
AXVirtualView* GetVirtualAccessibilityRow(int row);
// Returns the virtual accessibility view corresponding to the specified cell.
// |row| should be a view index, not a model index.
// |visible_column_index| indexes into |visible_columns_|.
AXVirtualView* GetVirtualAccessibilityCell(int row, int visible_column_index);
ui::TableModel* model_;
std::vector<ui::TableColumn> columns_;
......
......@@ -11,9 +11,13 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/text_utils.h"
#include "ui/views/accessibility/ax_virtual_view.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/table/table_grouper.h"
#include "ui/views/controls/table/table_header.h"
#include "ui/views/controls/table/table_view_observer.h"
......@@ -47,6 +51,7 @@ class TableViewTestHelper {
int GetActiveVisibleColumnIndex() {
return table_->GetActiveVisibleColumnIndex();
}
TableHeader* header() { return table_->header_; }
void SetSelectionModel(const ui::ListSelectionModel& new_selection) {
......@@ -55,6 +60,15 @@ class TableViewTestHelper {
const gfx::FontList& font_list() { return table_->font_list_; }
AXVirtualView* GetVirtualAccessibilityRow(int row) {
return table_->GetVirtualAccessibilityRow(row);
}
AXVirtualView* GetVirtualAccessibilityCell(int row,
int visible_column_index) {
return table_->GetVirtualAccessibilityCell(row, visible_column_index);
}
private:
TableView* table_;
......@@ -344,6 +358,87 @@ TEST_F(TableViewTest, GetPaintRegion) {
helper_->GetPaintRegion(gfx::Rect(0, 0, 1, table_->height())));
}
TEST_F(TableViewTest, UpdateVirtualAccessibilityChildren) {
const ViewAccessibility& view_accessibility = table_->GetViewAccessibility();
ui::AXNodeData data;
view_accessibility.GetAccessibleNodeData(&data);
EXPECT_EQ(ax::mojom::Role::kListGrid, data.role);
EXPECT_TRUE(data.HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(ax::mojom::Restriction::kReadOnly, data.GetRestriction());
EXPECT_EQ(table_->RowCount(), static_cast<int>(data.GetIntAttribute(
ax::mojom::IntAttribute::kTableRowCount)));
EXPECT_EQ(helper_->visible_col_count(),
static_cast<size_t>(data.GetIntAttribute(
ax::mojom::IntAttribute::kTableColumnCount)));
// The header takes up another row.
ASSERT_EQ(table_->RowCount() + 1, view_accessibility.virtual_child_count());
const AXVirtualView* header = view_accessibility.virtual_child_at(0);
ASSERT_TRUE(header);
EXPECT_EQ(ax::mojom::Role::kRow, header->GetData().role);
ASSERT_EQ(
helper_->visible_col_count(),
static_cast<size_t>(const_cast<AXVirtualView*>(header)->GetChildCount()));
for (int j = 0; j < static_cast<int>(helper_->visible_col_count()); ++j) {
const AXVirtualView* header_cell = header->child_at(j);
ASSERT_TRUE(header_cell);
const ui::AXNodeData& header_cell_data = header_cell->GetData();
EXPECT_EQ(ax::mojom::Role::kColumnHeader, header_cell_data.role);
EXPECT_EQ(j, static_cast<int>(header_cell_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableCellColumnIndex)));
}
for (int i = 1; i < table_->RowCount() + 1; ++i) {
const AXVirtualView* row = view_accessibility.virtual_child_at(i);
ASSERT_TRUE(row);
const ui::AXNodeData& row_data = row->GetData();
EXPECT_EQ(ax::mojom::Role::kRow, row_data.role);
EXPECT_EQ(i - 1, static_cast<int>(row_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableRowIndex)));
ASSERT_EQ(
helper_->visible_col_count(),
static_cast<size_t>(const_cast<AXVirtualView*>(row)->GetChildCount()));
for (int j = 0; j < static_cast<int>(helper_->visible_col_count()); ++j) {
const AXVirtualView* cell = row->child_at(j);
ASSERT_TRUE(cell);
const ui::AXNodeData& cell_data = cell->GetData();
EXPECT_EQ(ax::mojom::Role::kCell, cell_data.role);
EXPECT_EQ(i - 1, static_cast<int>(cell_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableCellRowIndex)));
EXPECT_EQ(j, static_cast<int>(cell_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableCellColumnIndex)));
}
}
}
TEST_F(TableViewTest, GetVirtualAccessibilityRow) {
for (int i = 0; i < table_->RowCount(); ++i) {
const AXVirtualView* row = helper_->GetVirtualAccessibilityRow(i);
ASSERT_TRUE(row);
const ui::AXNodeData& row_data = row->GetData();
EXPECT_EQ(ax::mojom::Role::kRow, row_data.role);
EXPECT_EQ(i, static_cast<int>(row_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableRowIndex)));
}
}
TEST_F(TableViewTest, GetVirtualAccessibilityCell) {
for (int i = 0; i < table_->RowCount(); ++i) {
for (int j = 0; j < static_cast<int>(helper_->visible_col_count()); ++j) {
const AXVirtualView* cell = helper_->GetVirtualAccessibilityCell(i, j);
ASSERT_TRUE(cell);
const ui::AXNodeData& cell_data = cell->GetData();
EXPECT_EQ(ax::mojom::Role::kCell, cell_data.role);
EXPECT_EQ(i, static_cast<int>(cell_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableCellRowIndex)));
EXPECT_EQ(j, static_cast<int>(cell_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableCellColumnIndex)));
}
}
}
// Verifies SetColumnVisibility().
TEST_F(TableViewTest, ColumnVisibility) {
// Two columns should be visible.
......
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