Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(193)

Unified Diff: ui/views/accessibility/native_view_accessibility_unittest.cc

Issue 2119413004: a11y: Exclude children of nested keyboard accessible controls from a11y tree. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/views/accessibility/native_view_accessibility_mac.mm ('k') | ui/views/view.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/views/accessibility/native_view_accessibility_unittest.cc
diff --git a/ui/views/accessibility/native_view_accessibility_unittest.cc b/ui/views/accessibility/native_view_accessibility_unittest.cc
index 0deb578dd92468ea2bfc457b50467a017cfc124a..436d81be3b504ba413e15c0b70c8aa4ee0599c19 100644
--- a/ui/views/accessibility/native_view_accessibility_unittest.cc
+++ b/ui/views/accessibility/native_view_accessibility_unittest.cc
@@ -13,12 +13,11 @@
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/label.h"
#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
namespace views {
namespace test {
-class NativeViewAccessibilityTest;
-
namespace {
class TestButton : public Button {
@@ -26,6 +25,18 @@ class TestButton : public Button {
TestButton() : Button(NULL) {}
};
+// Adds a View with given bounds and focusability to a parent.
+View* AddNewChildWithBoundsAndFocusability(View* parent,
+ const gfx::Rect& bounds,
+ bool focusable) {
+ View* child = new View;
+ child->SetBoundsRect(bounds);
+ parent->AddChildView(child);
+ child->SetFocusBehavior(focusable ? ClientView::FocusBehavior::ACCESSIBLE_ONLY
+ : ClientView::FocusBehavior::NEVER);
+ return child;
+}
+
} // namespace
class NativeViewAccessibilityTest : public ViewsTestBase {
@@ -36,9 +47,8 @@ class NativeViewAccessibilityTest : public ViewsTestBase {
void SetUp() override {
ViewsTestBase::SetUp();
- widget_ = new views::Widget;
- views::Widget::InitParams params =
- CreateParams(views::Widget::InitParams::TYPE_WINDOW);
+ widget_ = new Widget;
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
widget_->Init(params);
@@ -78,15 +88,25 @@ class NativeViewAccessibilityTest : public ViewsTestBase {
}
protected:
- views::Widget* widget_;
+ Widget* widget_;
TestButton* button_;
std::unique_ptr<NativeViewAccessibility> button_accessibility_;
Label* label_;
std::unique_ptr<NativeViewAccessibility> label_accessibility_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityTest);
};
TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) {
EXPECT_EQ(ui::AX_ROLE_BUTTON, button_accessibility()->GetData().role);
+ // Since the label is a subview of |button_|, and the button is keyboard
+ // focusable, the label is assumed to form part of the button and not have a
+ // role of its own.
+ EXPECT_EQ(ui::AX_ROLE_IGNORED, label_accessibility()->GetData().role);
+ // This will happen for all potentially keyboard-focusable Views with
+ // non-keyboard-focusable children, so if we make the button unfocusable, the
+ // label will be allowed to have its own role again.
+ button_->SetFocusBehavior(View::FocusBehavior::NEVER);
EXPECT_EQ(ui::AX_ROLE_STATIC_TEXT, label_accessibility()->GetData().role);
}
@@ -100,6 +120,13 @@ TEST_F(NativeViewAccessibilityTest, BoundsShouldMatch) {
}
TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) {
+ EXPECT_EQ(0, button_accessibility()->GetChildCount());
+ EXPECT_EQ(nullptr, button_accessibility()->ChildAtIndex(0));
+ EXPECT_EQ(button_->GetNativeViewAccessible(),
+ label_accessibility()->GetParent());
+
+ // If |button_| is no longer focusable, |label_| should show up again.
+ button_->SetFocusBehavior(View::FocusBehavior::NEVER);
EXPECT_EQ(1, button_accessibility()->GetChildCount());
EXPECT_EQ(label_->GetNativeViewAccessible(),
button_accessibility()->ChildAtIndex(0));
@@ -158,7 +185,7 @@ class TestNativeViewAccessibility : public NativeViewAccessibilityBase {
TEST_F(NativeViewAccessibilityTest, CrashOnWidgetDestroyed) {
std::unique_ptr<Widget> parent_widget(new Widget);
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(50, 50, 650, 650);
parent_widget->Init(params);
@@ -189,8 +216,7 @@ class AxTestViewsDelegate : public TestViewsDelegate {
AxTestViewsDelegate() {}
~AxTestViewsDelegate() override {}
- void NotifyAccessibilityEvent(views::View* view,
- ui::AXEvent event_type) override {
+ void NotifyAccessibilityEvent(View* view, ui::AXEvent event_type) override {
AXAuraObjCache* ax = AXAuraObjCache::GetInstance();
std::vector<AXAuraObjWrapper*> out_children;
AXAuraObjWrapper* ax_obj = ax->GetOrCreate(view->GetWidget());
@@ -209,7 +235,7 @@ class AXViewTest : public ViewsTestBase {
std::unique_ptr<TestViewsDelegate> views_delegate(
new AxTestViewsDelegate());
set_views_delegate(std::move(views_delegate));
- views::ViewsTestBase::SetUp();
+ ViewsTestBase::SetUp();
}
};
@@ -218,7 +244,7 @@ class AXViewTest : public ViewsTestBase {
TEST_F(AXViewTest, LayoutCalledInvalidateRootView) {
std::unique_ptr<Widget> widget(new Widget);
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget->Init(params);
widget->Show();
@@ -239,5 +265,211 @@ TEST_F(AXViewTest, LayoutCalledInvalidateRootView) {
}
#endif
+// Separate test class for a complicated test case for ignored accessibility
+// elements which are excluded from the accessibility tree.
+class NativeViewAccessibilityBaseIgnoredElementsTest : public ViewsTestBase {
+ public:
+ NativeViewAccessibilityBaseIgnoredElementsTest() {
+ a_root_ = new View;
+ NativeViewAccessibilityBase::GetForView(a_root_);
+ }
+
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+ // Set up an accessibility tree where X-nodes should be ignored. A Views
+ // hierarchy as shown on the left should expose the accessibility tree in
+ // the middle, and respond to hit tests as shown on the right. This test
+ // is here for completeness (in practice, it's unlikely a keyboard-focusable
+ // View will be needed as a descendant of another keyboard-focusable View).
+ //
+ // A A
+ // / \ / | \ +++---+---+---++---+---+---+++
+ // B X1 B F E ||| C | D | B || F | A | E |||
+ // / | \ / | \ / \ +++---+---+---++---+---+---+++
+ // C D X2 X3 X4 E C D
+ // | |
+ // X5 F
+ //
+ // Note: For hit tests, the parent's bounds will be divided equally amongst
+ // its children (including ignored elements). This means all Views belonging
+ // on the same level of the Views hierarchy will be the same size.
+ a_root_ = new View;
+ a_root_->SetSize(gfx::Size(400, 20));
+ a_root_->SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY);
+
+ // Create Views in the left subtree from |a_root_|.
+ gfx::Rect new_bounds(0, 0, a_root_->width() / 2, a_root_->height());
+ View* b = AddNewChildWithBoundsAndFocusability(a_root_, new_bounds, true);
+ // |a_root_| has 6 leaves, so divvy up space equally between them for hit
+ // tests.
+ new_bounds = gfx::Rect(0, 0, a_root_->width() / 6, a_root_->height());
+ View* c = AddNewChildWithBoundsAndFocusability(b, new_bounds, true);
+ View* x5 = AddNewChildWithBoundsAndFocusability(c, new_bounds, false);
+ // Update the |new_bounds| origin to position leaves next to each other.
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6);
+ View* d = AddNewChildWithBoundsAndFocusability(b, new_bounds, true);
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6);
+ View* x2 = AddNewChildWithBoundsAndFocusability(b, new_bounds, false);
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6);
+
+ // Create Views in the right subtree from root |a_root_|.
+ new_bounds.set_size(b->size());
+ View* x1 = AddNewChildWithBoundsAndFocusability(a_root_, new_bounds, false);
+ new_bounds.set_origin(gfx::Point());
+ View* x3 = AddNewChildWithBoundsAndFocusability(x1, new_bounds, false);
+ View* f = AddNewChildWithBoundsAndFocusability(x3, new_bounds, true);
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6);
+ View* x4 = AddNewChildWithBoundsAndFocusability(x1, new_bounds, false);
+ new_bounds.set_x(new_bounds.x() + a_root_->width() / 6);
+ View* e = AddNewChildWithBoundsAndFocusability(x1, new_bounds, true);
+
+ // Populate NPlatformativeViewAccessibility instances for convenience.
+ a_root_nva_ = NativeViewAccessibilityBase::GetForView(a_root_);
+ b_nva_ = NativeViewAccessibilityBase::GetForView(b);
+ c_b_nva_ = NativeViewAccessibilityBase::GetForView(c);
+ d_b_nva_ = NativeViewAccessibilityBase::GetForView(d);
+ e_x1_nva_ = NativeViewAccessibilityBase::GetForView(e);
+ f_x3_x1_nva_ = NativeViewAccessibilityBase::GetForView(f);
+ x1_nva_ = NativeViewAccessibilityBase::GetForView(x1);
+ x2_b_nva_ = NativeViewAccessibilityBase::GetForView(x2);
+ x3_x1_nva_ = NativeViewAccessibilityBase::GetForView(x3);
+ x4_x1_nva_ = NativeViewAccessibilityBase::GetForView(x4);
+ x5_c_b_nva_ = NativeViewAccessibilityBase::GetForView(x5);
+
+ // Add everything to a new widget, needed for hit testing and parent tests.
+ widget_ = new Widget;
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ widget_->Init(params);
+ widget_->GetContentsView()->AddChildView(a_root_);
+ widget_->Show();
+ }
+
+ void TearDown() override {
+ widget_->Close();
+ ViewsTestBase::TearDown();
+ }
+
+ protected:
+ // Owned by |widget_|.
+ View* a_root_;
+
+ // Owned by their respective View, which are owned by |widget_|.
+ // The following NativeViewAccessibilityBase objects are named after their
+ // corresponding node as shown in the diagrams in SetUp(), then a list of
+ // their ancestors (minus the root), separated by underscores.
+ NativeViewAccessibilityBase* a_root_nva_;
+ NativeViewAccessibilityBase* b_nva_;
+ NativeViewAccessibilityBase* c_b_nva_;
+ NativeViewAccessibilityBase* d_b_nva_;
+ NativeViewAccessibilityBase* e_x1_nva_;
+ NativeViewAccessibilityBase* f_x3_x1_nva_;
+ NativeViewAccessibilityBase* x1_nva_;
+ NativeViewAccessibilityBase* x2_b_nva_;
+ NativeViewAccessibilityBase* x3_x1_nva_;
+ NativeViewAccessibilityBase* x4_x1_nva_;
+ NativeViewAccessibilityBase* x5_c_b_nva_;
+
+ Widget* widget_ = nullptr;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityBaseIgnoredElementsTest);
+};
+
+// Check the number of a11y children returned accounts for ignored elements.
+TEST_F(NativeViewAccessibilityBaseIgnoredElementsTest, GetChildCount) {
+ EXPECT_EQ(3, a_root_nva_->GetChildCount());
+ EXPECT_EQ(2, b_nva_->GetChildCount());
+ // Leaf elements will return 0.
+ EXPECT_EQ(0, c_b_nva_->GetChildCount());
+ EXPECT_EQ(0, x5_c_b_nva_->GetChildCount());
+ EXPECT_EQ(0, d_b_nva_->GetChildCount());
+ EXPECT_EQ(0, x2_b_nva_->GetChildCount());
+ EXPECT_EQ(2, x1_nva_->GetChildCount());
+ EXPECT_EQ(1, x3_x1_nva_->GetChildCount());
+ EXPECT_EQ(0, f_x3_x1_nva_->GetChildCount());
+ EXPECT_EQ(0, x4_x1_nva_->GetChildCount());
+ EXPECT_EQ(0, e_x1_nva_->GetChildCount());
+}
+
+// Make sure the correct a11y child element is returned at its corresponding
+// index. Unignored children with ignored parents should move up in the tree.
+TEST_F(NativeViewAccessibilityBaseIgnoredElementsTest, ChildAtIndex) {
+ EXPECT_EQ(b_nva_->GetNativeObject(), a_root_nva_->ChildAtIndex(0));
+ EXPECT_EQ(f_x3_x1_nva_->GetNativeObject(), a_root_nva_->ChildAtIndex(1));
+ EXPECT_EQ(e_x1_nva_->GetNativeObject(), a_root_nva_->ChildAtIndex(2));
+ EXPECT_EQ(nullptr, a_root_nva_->ChildAtIndex(3));
+
+ EXPECT_EQ(c_b_nva_->GetNativeObject(), b_nva_->ChildAtIndex(0));
+ EXPECT_EQ(d_b_nva_->GetNativeObject(), b_nva_->ChildAtIndex(1));
+ EXPECT_EQ(nullptr, b_nva_->ChildAtIndex(2));
+
+ EXPECT_EQ(f_x3_x1_nva_->GetNativeObject(), x1_nva_->ChildAtIndex(0));
+ EXPECT_EQ(e_x1_nva_->GetNativeObject(), x1_nva_->ChildAtIndex(1));
+ EXPECT_EQ(nullptr, x1_nva_->ChildAtIndex(2));
+
+ EXPECT_EQ(f_x3_x1_nva_->GetNativeObject(), x3_x1_nva_->ChildAtIndex(0));
+
+ // Node with ignored children.
+ EXPECT_EQ(nullptr, c_b_nva_->ChildAtIndex(0));
+ // Node with 0 children.
+ EXPECT_EQ(nullptr, d_b_nva_->ChildAtIndex(0));
+ // Ignored node with 0 children.
+ EXPECT_EQ(nullptr, x2_b_nva_->ChildAtIndex(0));
+}
+
+// Check the a11y parents returned correspond to their unignored children.
+TEST_F(NativeViewAccessibilityBaseIgnoredElementsTest, GetParent) {
+ EXPECT_EQ(a_root_->parent()->GetNativeViewAccessible(),
+ a_root_nva_->GetParent());
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), b_nva_->GetParent());
+ EXPECT_EQ(b_nva_->GetNativeObject(), c_b_nva_->GetParent());
+ EXPECT_EQ(c_b_nva_->GetNativeObject(), x5_c_b_nva_->GetParent());
+ EXPECT_EQ(b_nva_->GetNativeObject(), d_b_nva_->GetParent());
+ EXPECT_EQ(b_nva_->GetNativeObject(), x2_b_nva_->GetParent());
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), x1_nva_->GetParent());
+
+ // Skips X1.
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), x3_x1_nva_->GetParent());
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), x4_x1_nva_->GetParent());
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), e_x1_nva_->GetParent());
+
+ // Skips X3 and X1.
+ EXPECT_EQ(a_root_->GetNativeViewAccessible(), f_x3_x1_nva_->GetParent());
+}
+
+// Test that a11y hit tests work correctly with ignored elements. A hit on a
+// ignored element should return its first unignored ancestor.
+TEST_F(NativeViewAccessibilityBaseIgnoredElementsTest, HitTestSync) {
+ // Move the hit point horizontally, incrementing by the leaf View width.
+ const int min_width = a_root_->GetBoundsInScreen().width() / 6;
+ int curr_x = a_root_->GetBoundsInScreen().x() + min_width / 2;
+ const int y = a_root_->GetBoundsInScreen().CenterPoint().y();
+
+ // HitTestSync isn't recursive, so manually do the recursion until arriving at
+ // the correct answer. I.e., the last NativeViewAccessible being tested for
+ // each |curr_x|, |y| pair below would be the correct return value if
+ // HitTestSync were recursive.
+ EXPECT_EQ(b_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y));
+ EXPECT_EQ(c_b_nva_->GetNativeObject(), b_nva_->HitTestSync(curr_x, y));
+ EXPECT_EQ(c_b_nva_->GetNativeObject(), c_b_nva_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(b_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y));
+ EXPECT_EQ(d_b_nva_->GetNativeObject(), b_nva_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(b_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y));
+ EXPECT_EQ(b_nva_->GetNativeObject(), b_nva_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(x1_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y));
+ EXPECT_EQ(x3_x1_nva_->GetNativeObject(), x1_nva_->HitTestSync(curr_x, y));
+ EXPECT_EQ(f_x3_x1_nva_->GetNativeObject(),
+ x3_x1_nva_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(x1_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y));
+ EXPECT_EQ(a_root_nva_->GetNativeObject(), x1_nva_->HitTestSync(curr_x, y));
+ curr_x += min_width;
+ EXPECT_EQ(x1_nva_->GetNativeObject(), a_root_nva_->HitTestSync(curr_x, y));
+ EXPECT_EQ(e_x1_nva_->GetNativeObject(), x1_nva_->HitTestSync(curr_x, y));
+}
+
} // namespace test
} // namespace views
« no previous file with comments | « ui/views/accessibility/native_view_accessibility_mac.mm ('k') | ui/views/view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698