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

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

Powered by Google App Engine
This is Rietveld 408576698