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 37435e205a1ebc3635ac2f8bc831bd657be62f4f..357377a253c888bf827988ac3255f9dc5c51abf4 100644 |
--- a/ui/views/accessibility/native_view_accessibility_unittest.cc |
+++ b/ui/views/accessibility/native_view_accessibility_unittest.cc |
@@ -9,6 +9,7 @@ |
#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 { |
@@ -22,6 +23,20 @@ class TestButton : public Button { |
TestButton() : Button(NULL) {} |
}; |
+// Helper to add new View with specific bounds and focusability to a parent. |
+View* AddNewChildWithBoundsAndFocusability(View& parent, |
+ gfx::Rect bounds, |
+ bool focusable) { |
+ View* child = new View; |
+ child->SetBounds(bounds.x(), bounds.y(), bounds.width(), bounds.height()); |
+ parent.AddChildView(child); |
+ if (focusable) |
+ child->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY); |
+ else |
+ child->SetFocusBehavior(ClientView::FocusBehavior::NEVER); |
+ return child; |
+} |
+ |
} // namespace |
class NativeViewAccessibilityTest : public ViewsTestBase { |
@@ -31,40 +46,157 @@ class NativeViewAccessibilityTest : public ViewsTestBase { |
void SetUp() override { |
ViewsTestBase::SetUp(); |
- |
- widget_ = new views::Widget; |
- views::Widget::InitParams params = |
- CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
- params.bounds = gfx::Rect(0, 0, 200, 200); |
+ widget_ = new Widget; |
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); |
+ params.bounds = gfx::Rect(200, 50); |
widget_->Init(params); |
- button_ = new TestButton(); |
- button_->SetSize(gfx::Size(20, 20)); |
- button_accessibility_ = NativeViewAccessibility::Create(button_); |
+ button_ = new TestButton; |
+ button_accessibility_ = button_->GetNativeViewAccessibility(); |
- label_ = new Label(); |
+ label_ = new Label; |
button_->AddChildView(label_); |
- label_accessibility_ = NativeViewAccessibility::Create(label_); |
+ label_accessibility_ = label_->GetNativeViewAccessibility(); |
- widget_->SetContentsView(button_); |
+ // Make sure both views start off as non-leaf accessibility elements. |
+ button_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY); |
+ label_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY); |
+ button_->SetSize(gfx::Size(200, 50)); |
+ label_->SetSize(gfx::Size(200, 50)); |
+ |
+ widget_->GetContentsView()->AddChildView(button_); |
+ widget_->Show(); |
} |
void TearDown() override { |
- if (!widget_->IsClosed()) |
- widget_->Close(); |
- button_accessibility_->Destroy(); |
- button_accessibility_ = NULL; |
- label_accessibility_->Destroy(); |
- label_accessibility_ = NULL; |
+ // Closing the Widget deletes child Views and their NativeViewAccessibility. |
+ widget_->Close(); |
+ button_accessibility_ = nullptr; |
+ label_accessibility_ = nullptr; |
ViewsTestBase::TearDown(); |
} |
protected: |
- views::Widget* widget_; |
+ Widget* widget_ = nullptr; |
TestButton* button_; |
NativeViewAccessibility* button_accessibility_; |
Label* label_; |
NativeViewAccessibility* label_accessibility_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityTest); |
+}; |
+ |
+// Separate test class for a complicated test case for ignored accessibility |
+// elements. |
+class NativeViewAccessibilityIgnoredElementsTest : public ViewsTestBase { |
+ public: |
+ NativeViewAccessibilityIgnoredElementsTest() {} |
+ ~NativeViewAccessibilityIgnoredElementsTest() override {} |
+ |
+ void SetUp() override { |
+ ViewsTestBase::SetUp(); |
+ // Set up an accessibility tree like so (X should be ignored). So 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. |
+ // |
+ // A A |
+ // / \ / | \ +++---+---+---++---+---+---+++ |
+ // B X1 B F E ||| C | F | 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_ = new View; |
+ a_->SetSize(gfx::Size(400, 20)); |
+ a_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY); |
+ |
+ // Create Views in the left subtree from root |a_|. |
+ gfx::Rect new_bounds(0, 0, a_->width() / 2, a_->height()); |
+ b_ = AddNewChildWithBoundsAndFocusability(*a_, new_bounds, true); |
+ // |a_| has 6 leaves, so divvy up space equally between them for hit tests. |
+ new_bounds = gfx::Rect(0, 0, a_->width() / 6, a_->height()); |
+ c_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, true); |
+ 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_->width() / 6); |
+ d_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, true); |
+ new_bounds.set_x(new_bounds.x() + a_->width() / 6); |
+ x2_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, false); |
+ new_bounds.set_x(new_bounds.x() + a_->width() / 6); |
+ |
+ // Create Views in the right subtree from root |a_|. |
+ new_bounds.set_size(b_->size()); |
+ x1_ = AddNewChildWithBoundsAndFocusability(*a_, new_bounds, false); |
+ new_bounds.set_origin(gfx::Point()); |
+ x3_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, false); |
+ f_ = AddNewChildWithBoundsAndFocusability(*x3_, new_bounds, true); |
+ new_bounds.set_x(new_bounds.x() + a_->width() / 6); |
+ x4_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, false); |
+ new_bounds.set_x(new_bounds.x() + a_->width() / 6); |
+ e_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, true); |
+ |
+ // Populate NativeViewAccessibility instances for convenience. |
+ a_ax_ = a_->GetNativeViewAccessibility(); |
+ b_ax_ = b_->GetNativeViewAccessibility(); |
+ c_ax_ = c_->GetNativeViewAccessibility(); |
+ d_ax_ = d_->GetNativeViewAccessibility(); |
+ e_ax_ = e_->GetNativeViewAccessibility(); |
+ f_ax_ = f_->GetNativeViewAccessibility(); |
+ x1_ax_ = x1_->GetNativeViewAccessibility(); |
+ x2_ax_ = x2_->GetNativeViewAccessibility(); |
+ x3_ax_ = x3_->GetNativeViewAccessibility(); |
+ x4_ax_ = x4_->GetNativeViewAccessibility(); |
+ x5_ax_ = x5_->GetNativeViewAccessibility(); |
+ |
+ // 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_); |
+ widget_->Show(); |
+ } |
+ |
+ void TearDown() override { |
+ widget_->Close(); |
+ ViewsTestBase::TearDown(); |
+ } |
+ |
+ protected: |
+ // Don't use std::unique_ptr for the following Views - everything will be |
+ // inside a widget, which will handle their deletion. |
+ View* a_; |
+ View* b_; |
+ View* c_; |
+ View* d_; |
+ View* e_; |
+ View* f_; |
+ View* x1_; |
+ View* x2_; |
+ View* x3_; |
+ View* x4_; |
+ View* x5_; |
+ |
+ // Destroyed when the View they are attached to is destroyed. |
+ NativeViewAccessibility* a_ax_; |
+ NativeViewAccessibility* b_ax_; |
+ NativeViewAccessibility* c_ax_; |
+ NativeViewAccessibility* d_ax_; |
+ NativeViewAccessibility* e_ax_; |
+ NativeViewAccessibility* f_ax_; |
+ NativeViewAccessibility* x1_ax_; |
+ NativeViewAccessibility* x2_ax_; |
+ NativeViewAccessibility* x3_ax_; |
+ NativeViewAccessibility* x4_ax_; |
+ NativeViewAccessibility* x5_ax_; |
+ |
+ Widget* widget_ = nullptr; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityIgnoredElementsTest); |
}; |
TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) { |
@@ -85,6 +217,139 @@ TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) { |
button_accessibility_->ChildAtIndex(0)); |
EXPECT_EQ(button_->GetNativeViewAccessible(), |
label_accessibility_->GetParent()); |
+ |
+ // Indicate the button should be a leaf node by ignoring the label. |
+ label_->SetFocusBehavior(ClientView::FocusBehavior::NEVER); |
+ EXPECT_EQ(button_, label_->parent()); |
+ EXPECT_EQ(0, button_accessibility_->GetChildCount()); |
+ EXPECT_EQ(nullptr, button_accessibility_->ChildAtIndex(0)); |
+ |
+ // Check this is still true even with a child inside the label. |
+ View* label_child = AddNewChildWithBoundsAndFocusability( |
+ *label_, label_->GetBoundsInScreen(), true); |
+ EXPECT_EQ(1, button_accessibility_->GetChildCount()); |
+ EXPECT_EQ(label_child->GetNativeViewAccessible(), |
+ button_accessibility_->ChildAtIndex(0)); |
+ |
+ label_child->SetFocusBehavior(ClientView::FocusBehavior::NEVER); |
+ EXPECT_EQ(0, button_accessibility_->GetChildCount()); |
+ EXPECT_EQ(nullptr, button_accessibility_->ChildAtIndex(0)); |
+} |
+ |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, GetChildCount) { |
+ EXPECT_EQ(3, a_ax_->GetChildCount()); |
+ EXPECT_EQ(2, b_ax_->GetChildCount()); |
+ EXPECT_EQ(0, c_ax_->GetChildCount()); |
+ EXPECT_EQ(0, x5_ax_->GetChildCount()); |
+ EXPECT_EQ(0, d_ax_->GetChildCount()); |
+ EXPECT_EQ(0, x2_ax_->GetChildCount()); |
+ EXPECT_EQ(2, x1_ax_->GetChildCount()); |
+ EXPECT_EQ(1, x3_ax_->GetChildCount()); |
+ EXPECT_EQ(0, f_ax_->GetChildCount()); |
+ EXPECT_EQ(0, x4_ax_->GetChildCount()); |
+ EXPECT_EQ(0, e_ax_->GetChildCount()); |
+} |
+ |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, ChildAtIndex) { |
+ EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(0)); |
+ EXPECT_EQ(f_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(1)); |
+ EXPECT_EQ(e_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(2)); |
+ EXPECT_EQ(nullptr, a_ax_->ChildAtIndex(3)); |
+ |
+ EXPECT_EQ(c_->GetNativeViewAccessible(), b_ax_->ChildAtIndex(0)); |
+ EXPECT_EQ(d_->GetNativeViewAccessible(), b_ax_->ChildAtIndex(1)); |
+ EXPECT_EQ(nullptr, b_ax_->ChildAtIndex(2)); |
+ |
+ EXPECT_EQ(f_->GetNativeViewAccessible(), x1_ax_->ChildAtIndex(0)); |
+ EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->ChildAtIndex(1)); |
+ EXPECT_EQ(nullptr, x1_ax_->ChildAtIndex(2)); |
+ |
+ EXPECT_EQ(f_->GetNativeViewAccessible(), x3_ax_->ChildAtIndex(0)); |
+ |
+ // Node with ignored children. |
+ EXPECT_EQ(nullptr, c_ax_->ChildAtIndex(0)); |
+ // Node with 0 children. |
+ EXPECT_EQ(nullptr, d_ax_->ChildAtIndex(0)); |
+ // Ignored node with 0 children. |
+ EXPECT_EQ(nullptr, x2_ax_->ChildAtIndex(0)); |
+} |
+ |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, GetParent) { |
+#if defined(OS_MACOSX) |
+ // Only Mac is set up to return the native view going up to the Widget-level. |
+ EXPECT_EQ(widget_->GetNativeView(), a_ax_->GetParent()); |
+#else |
+ EXPECT_EQ(nullptr, a_ax_->GetParent()); |
+#endif |
+ EXPECT_EQ(a_->GetNativeViewAccessible(), b_ax_->GetParent()); |
+ EXPECT_EQ(b_->GetNativeViewAccessible(), c_ax_->GetParent()); |
+ EXPECT_EQ(c_->GetNativeViewAccessible(), x5_ax_->GetParent()); |
+ EXPECT_EQ(b_->GetNativeViewAccessible(), d_ax_->GetParent()); |
+ EXPECT_EQ(b_->GetNativeViewAccessible(), x2_ax_->GetParent()); |
+ |
+ EXPECT_EQ(a_->GetNativeViewAccessible(), x1_ax_->GetParent()); |
+ EXPECT_EQ(a_->GetNativeViewAccessible(), x3_ax_->GetParent()); |
+ EXPECT_EQ(a_->GetNativeViewAccessible(), x4_ax_->GetParent()); |
+ EXPECT_EQ(a_->GetNativeViewAccessible(), e_ax_->GetParent()); |
+ EXPECT_EQ(a_->GetNativeViewAccessible(), f_ax_->GetParent()); |
+} |
+ |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, IsLeafElement) { |
+ EXPECT_FALSE(a_ax_->IsLeafElement()); |
+ EXPECT_FALSE(b_ax_->IsLeafElement()); |
+ EXPECT_TRUE(c_ax_->IsLeafElement()); |
+ EXPECT_TRUE(d_ax_->IsLeafElement()); |
+ EXPECT_TRUE(e_ax_->IsLeafElement()); |
+ EXPECT_TRUE(f_ax_->IsLeafElement()); |
+ EXPECT_FALSE(x1_ax_->IsLeafElement()); |
+ EXPECT_TRUE(x2_ax_->IsLeafElement()); |
+ EXPECT_FALSE(x3_ax_->IsLeafElement()); |
+ EXPECT_TRUE(x4_ax_->IsLeafElement()); |
+ EXPECT_TRUE(x5_ax_->IsLeafElement()); |
+} |
+ |
+TEST_F(NativeViewAccessibilityIgnoredElementsTest, HitTestSync) { |
+ // Move the hit point horizontally, incrementing by the leaf View width. |
+ int min_width = a_->GetBoundsInScreen().width() / 6; |
+ int curr_x = a_->GetBoundsInScreen().x() + min_width / 2; |
+ const int y = a_->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_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(c_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(c_->GetNativeViewAccessible(), c_ax_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(d_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(x2_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(b_->GetNativeViewAccessible(), x2_ax_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(x3_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(f_->GetNativeViewAccessible(), x3_ax_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(x4_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(a_->GetNativeViewAccessible(), x4_ax_->HitTestSync(curr_x, y)); |
+ curr_x += min_width; |
+ EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y)); |
+ |
+ // The Widget should be revealed if |e_| and |a_| are ignored. |
+ a_->SetFocusBehavior(ClientView::FocusBehavior::NEVER); |
+ e_->SetFocusBehavior(ClientView::FocusBehavior::NEVER); |
+ EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y)); |
+ EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y)); |
+#if defined(OS_MACOSX) |
+ EXPECT_EQ(widget_->GetNativeView(), e_ax_->HitTestSync(curr_x, y)); |
+#else |
+ EXPECT_EQ(nullptr, e_ax_->HitTestSync(curr_x, y)); |
+#endif |
} |
// Subclass of NativeViewAccessibility that destroys itself when its |