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

Side by Side 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: Fix compile error and final test in NativeWidgetMacTest. Created 4 years, 2 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/strings/utf_string_conversions.h" 5 #include "base/strings/utf_string_conversions.h"
6 #include "ui/accessibility/ax_view_state.h" 6 #include "ui/accessibility/ax_view_state.h"
7 #include "ui/gfx/geometry/rect_conversions.h" 7 #include "ui/gfx/geometry/rect_conversions.h"
8 #include "ui/views/accessibility/native_view_accessibility.h" 8 #include "ui/views/accessibility/native_view_accessibility.h"
9 #include "ui/views/controls/button/button.h" 9 #include "ui/views/controls/button/button.h"
10 #include "ui/views/controls/label.h" 10 #include "ui/views/controls/label.h"
11 #include "ui/views/test/views_test_base.h" 11 #include "ui/views/test/views_test_base.h"
12 #include "ui/views/widget/widget.h"
12 13
13 namespace views { 14 namespace views {
14 namespace test { 15 namespace test {
15 16
16 class NativeViewAccessibilityTest; 17 class NativeViewAccessibilityTest;
17 18
18 namespace { 19 namespace {
19 20
20 class TestButton : public Button { 21 class TestButton : public Button {
21 public: 22 public:
22 TestButton() : Button(NULL) {} 23 TestButton() : Button(NULL) {}
23 }; 24 };
24 25
26 // Helper to add new View with specific bounds and focusability to a parent.
27 View* AddNewChildWithBoundsAndFocusability(View& parent,
28 gfx::Rect bounds,
29 bool focusable) {
30 View* child = new View;
31 child->SetBounds(bounds.x(), bounds.y(), bounds.width(), bounds.height());
32 parent.AddChildView(child);
33 if (focusable)
34 child->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY);
35 else
36 child->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
37 return child;
38 }
39
25 } // namespace 40 } // namespace
26 41
27 class NativeViewAccessibilityTest : public ViewsTestBase { 42 class NativeViewAccessibilityTest : public ViewsTestBase {
28 public: 43 public:
29 NativeViewAccessibilityTest() {} 44 NativeViewAccessibilityTest() {}
30 ~NativeViewAccessibilityTest() override {} 45 ~NativeViewAccessibilityTest() override {}
31 46
32 void SetUp() override { 47 void SetUp() override {
33 ViewsTestBase::SetUp(); 48 ViewsTestBase::SetUp();
34 button_.reset(new TestButton()); 49 button_.reset(new TestButton());
35 button_->SetSize(gfx::Size(20, 20)); 50 button_accessibility_ = button_->GetNativeViewAccessibility();
36 button_accessibility_ = NativeViewAccessibility::Create(button_.get());
37 51
38 label_.reset(new Label); 52 label_.reset(new Label);
39 button_->AddChildView(label_.get()); 53 button_->AddChildView(label_.get());
40 label_accessibility_ = NativeViewAccessibility::Create(label_.get()); 54 label_accessibility_ = label_->GetNativeViewAccessibility();
55
56 // Make sure both views start off as non-leaf accessibility elements.
57 button_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY);
58 label_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY);
59 // Give them a size.
60 button_->SetSize(gfx::Size(200, 50));
61 label_->SetSize(gfx::Size(200, 50));
41 } 62 }
42 63
43 void TearDown() override { 64 void TearDown() override {
44 button_accessibility_->Destroy(); 65 // If views are in a widget, remove them first to avoid deleting twice.
45 button_accessibility_ = NULL; 66 if (widget_) {
46 label_accessibility_->Destroy(); 67 widget_->GetContentsView()->RemoveChildView(button_.get());
47 label_accessibility_ = NULL; 68 widget_->Close();
69 }
70 button_accessibility_ = nullptr;
71 label_accessibility_ = nullptr;
48 ViewsTestBase::TearDown(); 72 ViewsTestBase::TearDown();
49 } 73 }
50 74
75 void AddViewsToWidget() {
76 widget_ = new Widget;
77 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
78 widget_->Init(params);
79 widget_->GetContentsView()->AddChildView(button_.get());
80 widget_->Show();
81 }
82
83 Widget* widget() { return widget_; }
84
51 protected: 85 protected:
52 std::unique_ptr<TestButton> button_; 86 std::unique_ptr<TestButton> button_;
53 NativeViewAccessibility* button_accessibility_; 87 NativeViewAccessibility* button_accessibility_;
54 std::unique_ptr<Label> label_; 88 std::unique_ptr<Label> label_;
55 NativeViewAccessibility* label_accessibility_; 89 NativeViewAccessibility* label_accessibility_;
90
91 private:
92 Widget* widget_ = nullptr;
93
94 DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityTest);
95 };
96
97 // Separate test class for a complicated test case for ignored accessibility
98 // elements.
99 class NativeViewAccessibilityIgnoredElementsTest : public ViewsTestBase {
100 public:
101 NativeViewAccessibilityIgnoredElementsTest() {}
102 ~NativeViewAccessibilityIgnoredElementsTest() override {}
103
104 void SetUp() override {
105 ViewsTestBase::SetUp();
106 // Set up an accessibility tree like so (X should be ignored). So a Views
107 // hierarchy as shown on the left should expose the accessibility tree in
108 // the middle, and respond to hit tests as shown on the right.
109 //
110 // A A
111 // / \ / | \ +++---+---+---++---+---+---+++
112 // B X1 B F E ||| C | F | B || F | A | E |||
113 // / | \ / | \ / \ +++---+---+---++---+---+---+++
114 // C D X2 X3 X4 E C D
115 // | |
116 // X5 F
117 //
118 // Note: For hit tests, the parent's bounds will be divided equally amongst
119 // its children (including ignored elements). This means all Views belonging
120 // on the same level of the Views hierarchy will be the same size.
121 a_ = new View;
122 a_->SetSize(gfx::Size(400, 20));
123 a_->SetFocusBehavior(ClientView::FocusBehavior::ACCESSIBLE_ONLY);
124
125 // Create Views in the left subtree from root |a_|.
126 gfx::Rect new_bounds(0, 0, a_->width() / 2, a_->height());
127 b_ = AddNewChildWithBoundsAndFocusability(*a_, new_bounds, true);
128 // |a_| has 6 leaves, so divvy up space equally between them for hit tests.
129 new_bounds = gfx::Rect(0, 0, a_->width() / 6, a_->height());
130 c_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, true);
131 x5_ = AddNewChildWithBoundsAndFocusability(*c_, new_bounds, false);
132 // Update the |new_bounds| origin to position leaves next to each other.
133 new_bounds.set_x(new_bounds.x() + a_->width() / 6);
134 d_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, true);
135 new_bounds.set_x(new_bounds.x() + a_->width() / 6);
136 x2_ = AddNewChildWithBoundsAndFocusability(*b_, new_bounds, false);
137 new_bounds.set_x(new_bounds.x() + a_->width() / 6);
138
139 // Create Views in the right subtree from root |a_|.
140 new_bounds.set_size(b_->size());
141 x1_ = AddNewChildWithBoundsAndFocusability(*a_, new_bounds, false);
142 new_bounds.set_origin(gfx::Point());
143 x3_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, false);
144 f_ = AddNewChildWithBoundsAndFocusability(*x3_, new_bounds, true);
145 new_bounds.set_x(new_bounds.x() + a_->width() / 6);
146 x4_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, false);
147 new_bounds.set_x(new_bounds.x() + a_->width() / 6);
148 e_ = AddNewChildWithBoundsAndFocusability(*x1_, new_bounds, true);
149
150 // Populate NativeViewAccessibility instances for convenience.
151 a_ax_ = a_->GetNativeViewAccessibility();
152 b_ax_ = b_->GetNativeViewAccessibility();
153 c_ax_ = c_->GetNativeViewAccessibility();
154 d_ax_ = d_->GetNativeViewAccessibility();
155 e_ax_ = e_->GetNativeViewAccessibility();
156 f_ax_ = f_->GetNativeViewAccessibility();
157 x1_ax_ = x1_->GetNativeViewAccessibility();
158 x2_ax_ = x2_->GetNativeViewAccessibility();
159 x3_ax_ = x3_->GetNativeViewAccessibility();
160 x4_ax_ = x4_->GetNativeViewAccessibility();
161 x5_ax_ = x5_->GetNativeViewAccessibility();
162
163 // Add everything to a new widget, needed for hit testing and parent tests.
164 widget_ = new Widget;
165 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
166 widget_->Init(params);
167 widget_->GetContentsView()->AddChildView(a_);
168 widget_->Show();
169 }
170
171 void TearDown() override {
172 widget_->Close();
173 ViewsTestBase::TearDown();
174 }
175
176 Widget* widget() { return widget_; }
177
178 protected:
179 // Don't use std::unique_ptr for the following Views - everything will be
180 // inside a widget, which will handle their deletion.
181 View* a_;
182 View* b_;
183 View* c_;
184 View* d_;
185 View* e_;
186 View* f_;
187 View* x1_;
188 View* x2_;
189 View* x3_;
190 View* x4_;
191 View* x5_;
192
193 // Destroyed when the View they are attached to is destroyed.
194 NativeViewAccessibility* a_ax_;
195 NativeViewAccessibility* b_ax_;
196 NativeViewAccessibility* c_ax_;
197 NativeViewAccessibility* d_ax_;
198 NativeViewAccessibility* e_ax_;
199 NativeViewAccessibility* f_ax_;
200 NativeViewAccessibility* x1_ax_;
201 NativeViewAccessibility* x2_ax_;
202 NativeViewAccessibility* x3_ax_;
203 NativeViewAccessibility* x4_ax_;
204 NativeViewAccessibility* x5_ax_;
205
206 private:
207 Widget* widget_ = nullptr;
208
209 DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityIgnoredElementsTest);
56 }; 210 };
57 211
58 TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) { 212 TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) {
59 EXPECT_EQ(ui::AX_ROLE_BUTTON, button_accessibility_->GetData().role); 213 EXPECT_EQ(ui::AX_ROLE_BUTTON, button_accessibility_->GetData().role);
60 EXPECT_EQ(ui::AX_ROLE_STATIC_TEXT, label_accessibility_->GetData().role); 214 EXPECT_EQ(ui::AX_ROLE_STATIC_TEXT, label_accessibility_->GetData().role);
61 } 215 }
62 216
63 TEST_F(NativeViewAccessibilityTest, BoundsShouldMatch) { 217 TEST_F(NativeViewAccessibilityTest, BoundsShouldMatch) {
64 gfx::Rect bounds = gfx::ToEnclosingRect( 218 gfx::Rect bounds = gfx::ToEnclosingRect(
65 button_accessibility_->GetData().location); 219 button_accessibility_->GetData().location);
66 bounds.Offset(button_accessibility_->GetGlobalCoordinateOffset()); 220 bounds.Offset(button_accessibility_->GetGlobalCoordinateOffset());
67 EXPECT_EQ(button_->GetBoundsInScreen(), bounds); 221 EXPECT_EQ(button_->GetBoundsInScreen(), bounds);
68 } 222 }
69 223
70 TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) { 224 TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) {
225 // View::IsAccessibilityFocusable() requires a parent widget.
226 AddViewsToWidget();
227
71 EXPECT_EQ(1, button_accessibility_->GetChildCount()); 228 EXPECT_EQ(1, button_accessibility_->GetChildCount());
72 EXPECT_EQ(label_->GetNativeViewAccessible(), 229 EXPECT_EQ(label_->GetNativeViewAccessible(),
73 button_accessibility_->ChildAtIndex(0)); 230 button_accessibility_->ChildAtIndex(0));
74 EXPECT_EQ(button_->GetNativeViewAccessible(), 231 EXPECT_EQ(button_->GetNativeViewAccessible(),
75 label_accessibility_->GetParent()); 232 label_accessibility_->GetParent());
233
234 // Indicate the button should be a leaf node by ignoring the label.
235 label_->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
236 EXPECT_EQ(button_.get(), label_->parent());
237 EXPECT_EQ(0, button_accessibility_->GetChildCount());
238 EXPECT_EQ(nullptr, button_accessibility_->ChildAtIndex(0));
239
240 // Check this is still true even with a child inside the label.
241 View* label_child = AddNewChildWithBoundsAndFocusability(
242 *label_, label_->GetBoundsInScreen(), true);
243 EXPECT_EQ(1, button_accessibility_->GetChildCount());
244 EXPECT_EQ(label_child->GetNativeViewAccessible(),
245 button_accessibility_->ChildAtIndex(0));
246
247 label_child->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
248 EXPECT_EQ(0, button_accessibility_->GetChildCount());
249 EXPECT_EQ(nullptr, button_accessibility_->ChildAtIndex(0));
250 }
251
252 TEST_F(NativeViewAccessibilityIgnoredElementsTest, GetChildCount) {
253 EXPECT_EQ(3, a_ax_->GetChildCount());
254 EXPECT_EQ(2, b_ax_->GetChildCount());
255 EXPECT_EQ(0, c_ax_->GetChildCount());
256 EXPECT_EQ(0, x5_ax_->GetChildCount());
257 EXPECT_EQ(0, d_ax_->GetChildCount());
258 EXPECT_EQ(0, x2_ax_->GetChildCount());
259 EXPECT_EQ(2, x1_ax_->GetChildCount());
260 EXPECT_EQ(1, x3_ax_->GetChildCount());
261 EXPECT_EQ(0, f_ax_->GetChildCount());
262 EXPECT_EQ(0, x4_ax_->GetChildCount());
263 EXPECT_EQ(0, e_ax_->GetChildCount());
264 }
265
266 TEST_F(NativeViewAccessibilityIgnoredElementsTest, ChildAtIndex) {
267 EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(0));
268 EXPECT_EQ(f_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(1));
269 EXPECT_EQ(e_->GetNativeViewAccessible(), a_ax_->ChildAtIndex(2));
270 EXPECT_EQ(nullptr, a_ax_->ChildAtIndex(3));
271
272 EXPECT_EQ(c_->GetNativeViewAccessible(), b_ax_->ChildAtIndex(0));
273 EXPECT_EQ(d_->GetNativeViewAccessible(), b_ax_->ChildAtIndex(1));
274 EXPECT_EQ(nullptr, b_ax_->ChildAtIndex(2));
275
276 EXPECT_EQ(f_->GetNativeViewAccessible(), x1_ax_->ChildAtIndex(0));
277 EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->ChildAtIndex(1));
278 EXPECT_EQ(nullptr, x1_ax_->ChildAtIndex(2));
279
280 EXPECT_EQ(f_->GetNativeViewAccessible(), x3_ax_->ChildAtIndex(0));
281
282 // Node with ignored children.
283 EXPECT_EQ(nullptr, c_ax_->ChildAtIndex(0));
284 // Node with 0 children.
285 EXPECT_EQ(nullptr, d_ax_->ChildAtIndex(0));
286 // Ignored node with 0 children.
287 EXPECT_EQ(nullptr, x2_ax_->ChildAtIndex(0));
288 }
289
290 TEST_F(NativeViewAccessibilityIgnoredElementsTest, GetParent) {
291 #if defined(OS_MACOSX)
292 // Only Mac is set up to return the native view going up to the Widget-level.
293 EXPECT_EQ(widget()->GetNativeView(), a_ax_->GetParent());
294 #else
295 EXPECT_EQ(nullptr, a_ax_->GetParent());
296 #endif
297 EXPECT_EQ(a_->GetNativeViewAccessible(), b_ax_->GetParent());
298 EXPECT_EQ(b_->GetNativeViewAccessible(), c_ax_->GetParent());
299 EXPECT_EQ(c_->GetNativeViewAccessible(), x5_ax_->GetParent());
300 EXPECT_EQ(b_->GetNativeViewAccessible(), d_ax_->GetParent());
301 EXPECT_EQ(b_->GetNativeViewAccessible(), x2_ax_->GetParent());
302
303 EXPECT_EQ(a_->GetNativeViewAccessible(), x1_ax_->GetParent());
304 EXPECT_EQ(a_->GetNativeViewAccessible(), x3_ax_->GetParent());
305 EXPECT_EQ(a_->GetNativeViewAccessible(), x4_ax_->GetParent());
306 EXPECT_EQ(a_->GetNativeViewAccessible(), e_ax_->GetParent());
307 EXPECT_EQ(a_->GetNativeViewAccessible(), f_ax_->GetParent());
308 }
309
310 TEST_F(NativeViewAccessibilityIgnoredElementsTest, IsLeafElement) {
311 EXPECT_FALSE(a_ax_->IsLeafElement());
312 EXPECT_FALSE(b_ax_->IsLeafElement());
313 EXPECT_TRUE(c_ax_->IsLeafElement());
314 EXPECT_TRUE(d_ax_->IsLeafElement());
315 EXPECT_TRUE(e_ax_->IsLeafElement());
316 EXPECT_TRUE(f_ax_->IsLeafElement());
317 EXPECT_FALSE(x1_ax_->IsLeafElement());
318 EXPECT_TRUE(x2_ax_->IsLeafElement());
319 EXPECT_FALSE(x3_ax_->IsLeafElement());
320 EXPECT_TRUE(x4_ax_->IsLeafElement());
321 EXPECT_TRUE(x5_ax_->IsLeafElement());
322 }
323
324 TEST_F(NativeViewAccessibilityIgnoredElementsTest, HitTestSync) {
325 // Move the hit point horizontally, incrementing by the leaf View width.
326 int min_width = a_->GetBoundsInScreen().width() / 6;
327 int curr_x = a_->GetBoundsInScreen().x() + min_width / 2;
328 const int y = a_->GetBoundsInScreen().CenterPoint().y();
329
330 // HitTestSync isn't recursive, so manually do the recursion until arriving at
331 // the correct answer. I.e., the last NativeViewAccessible being tested for
332 // each |curr_x|, |y| pair below would be the correct return value if
333 // HitTestSync were recursive.
334 EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
335 EXPECT_EQ(c_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y));
336 EXPECT_EQ(c_->GetNativeViewAccessible(), c_ax_->HitTestSync(curr_x, y));
337 curr_x += min_width;
338 EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
339 EXPECT_EQ(d_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y));
340 curr_x += min_width;
341 EXPECT_EQ(b_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
342 EXPECT_EQ(x2_->GetNativeViewAccessible(), b_ax_->HitTestSync(curr_x, y));
343 EXPECT_EQ(b_->GetNativeViewAccessible(), x2_ax_->HitTestSync(curr_x, y));
344 curr_x += min_width;
345 EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
346 EXPECT_EQ(x3_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y));
347 EXPECT_EQ(f_->GetNativeViewAccessible(), x3_ax_->HitTestSync(curr_x, y));
348 curr_x += min_width;
349 EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
350 EXPECT_EQ(x4_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y));
351 EXPECT_EQ(a_->GetNativeViewAccessible(), x4_ax_->HitTestSync(curr_x, y));
352 curr_x += min_width;
353 EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
354 EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y));
355
356 // The Widget should be revealed if |e_| and |a_| are ignored.
357 a_->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
358 e_->SetFocusBehavior(ClientView::FocusBehavior::NEVER);
359 EXPECT_EQ(x1_->GetNativeViewAccessible(), a_ax_->HitTestSync(curr_x, y));
360 EXPECT_EQ(e_->GetNativeViewAccessible(), x1_ax_->HitTestSync(curr_x, y));
361 #if defined(OS_MACOSX)
362 EXPECT_EQ(widget()->GetNativeView(), e_ax_->HitTestSync(curr_x, y));
363 #else
364 EXPECT_EQ(nullptr, e_ax_->HitTestSync(curr_x, y));
365 #endif
76 } 366 }
77 367
78 // Subclass of NativeViewAccessibility that destroys itself when its 368 // Subclass of NativeViewAccessibility that destroys itself when its
79 // parent widget is destroyed, for the purposes of making sure this 369 // parent widget is destroyed, for the purposes of making sure this
80 // doesn't lead to a crash. 370 // doesn't lead to a crash.
81 class TestNativeViewAccessibility : public NativeViewAccessibility { 371 class TestNativeViewAccessibility : public NativeViewAccessibility {
82 public: 372 public:
83 explicit TestNativeViewAccessibility(View* view) 373 explicit TestNativeViewAccessibility(View* view)
84 : NativeViewAccessibility(view) {} 374 : NativeViewAccessibility(view) {}
85 375
(...skipping 20 matching lines...) Expand all
106 // WidgetObserver. Note that TestNativeViewAccessibility is a subclass 396 // WidgetObserver. Note that TestNativeViewAccessibility is a subclass
107 // defined above that destroys itself when its parent widget is destroyed. 397 // defined above that destroys itself when its parent widget is destroyed.
108 TestNativeViewAccessibility* child_accessible = 398 TestNativeViewAccessibility* child_accessible =
109 new TestNativeViewAccessibility(child_widget->GetRootView()); 399 new TestNativeViewAccessibility(child_widget->GetRootView());
110 child_accessible->SetParentWidget(parent_widget.get()); 400 child_accessible->SetParentWidget(parent_widget.get());
111 parent_widget.reset(); 401 parent_widget.reset();
112 } 402 }
113 403
114 } // namespace test 404 } // namespace test
115 } // namespace views 405 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698