1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 #include "base/strings/utf_string_conversions.h"
9 #include "content/browser/renderer_host/render_view_host_impl.h"
10 #include "content/public/browser/notification_service.h"
11 #include "content/public/browser/notification_types.h"
12 #include "content/public/browser/render_widget_host_view.h"
13 #include "content/shell/browser/shell.h"
14 #include "content/test/accessibility_browser_test_utils.h"
15 #include "content/test/content_browser_test.h"
16 #include "content/test/content_browser_test_utils.h"
17 #include "ui/accessibility/ax_node.h"
22 #include "base/win/scoped_com_initializer.h"
23 #include "ui/base/win/atl_module.h"
26 // TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
27 #if defined(OS_WIN) && defined(ARCH_CPU_X86_64)
28 #define MAYBE_TableSpan DISABLED_TableSpan
30 #define MAYBE_TableSpan TableSpan
35 class CrossPlatformAccessibilityBrowserTest
: public ContentBrowserTest
{
37 CrossPlatformAccessibilityBrowserTest() {}
39 // Tell the renderer to send an accessibility tree, then wait for the
40 // notification that it's been received.
41 const ui::AXTree
& GetAXTree(
42 AccessibilityMode accessibility_mode
= AccessibilityModeComplete
) {
43 AccessibilityNotificationWaiter
waiter(
44 shell(), accessibility_mode
, ui::AX_EVENT_LAYOUT_COMPLETE
);
45 waiter
.WaitForNotification();
46 return waiter
.GetAXTree();
49 // Make sure each node in the tree has an unique id.
50 void RecursiveAssertUniqueIds(
51 const ui::AXNode
* node
, base::hash_set
<int>* ids
) {
52 ASSERT_TRUE(ids
->find(node
->id()) == ids
->end());
53 ids
->insert(node
->id());
54 for (int i
= 0; i
< node
->child_count(); i
++)
55 RecursiveAssertUniqueIds(node
->ChildAtIndex(i
), ids
);
59 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE
;
60 virtual void TearDownInProcessBrowserTestFixture() OVERRIDE
;
63 std::string
GetAttr(const ui::AXNode
* node
,
64 const ui::AXStringAttribute attr
);
65 int GetIntAttr(const ui::AXNode
* node
,
66 const ui::AXIntAttribute attr
);
67 bool GetBoolAttr(const ui::AXNode
* node
,
68 const ui::AXBoolAttribute attr
);
72 scoped_ptr
<base::win::ScopedCOMInitializer
> com_initializer_
;
75 DISALLOW_COPY_AND_ASSIGN(CrossPlatformAccessibilityBrowserTest
);
78 void CrossPlatformAccessibilityBrowserTest::SetUpInProcessBrowserTestFixture() {
80 ui::win::CreateATLModuleIfNeeded();
81 com_initializer_
.reset(new base::win::ScopedCOMInitializer());
86 CrossPlatformAccessibilityBrowserTest::TearDownInProcessBrowserTestFixture() {
88 com_initializer_
.reset();
92 // Convenience method to get the value of a particular AXNode
93 // attribute as a UTF-8 string.
94 std::string
CrossPlatformAccessibilityBrowserTest::GetAttr(
95 const ui::AXNode
* node
,
96 const ui::AXStringAttribute attr
) {
97 const ui::AXNodeData
& data
= node
->data();
98 for (size_t i
= 0; i
< data
.string_attributes
.size(); ++i
) {
99 if (data
.string_attributes
[i
].first
== attr
)
100 return data
.string_attributes
[i
].second
;
102 return std::string();
105 // Convenience method to get the value of a particular AXNode
106 // integer attribute.
107 int CrossPlatformAccessibilityBrowserTest::GetIntAttr(
108 const ui::AXNode
* node
,
109 const ui::AXIntAttribute attr
) {
110 const ui::AXNodeData
& data
= node
->data();
111 for (size_t i
= 0; i
< data
.int_attributes
.size(); ++i
) {
112 if (data
.int_attributes
[i
].first
== attr
)
113 return data
.int_attributes
[i
].second
;
118 // Convenience method to get the value of a particular AXNode
119 // boolean attribute.
120 bool CrossPlatformAccessibilityBrowserTest::GetBoolAttr(
121 const ui::AXNode
* node
,
122 const ui::AXBoolAttribute attr
) {
123 const ui::AXNodeData
& data
= node
->data();
124 for (size_t i
= 0; i
< data
.bool_attributes
.size(); ++i
) {
125 if (data
.bool_attributes
[i
].first
== attr
)
126 return data
.bool_attributes
[i
].second
;
131 // Marked flaky per http://crbug.com/101984
132 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
133 DISABLED_WebpageAccessibility
) {
134 // Create a data url and load it.
135 const char url_str
[] =
138 "<html><head><title>Accessibility Test</title></head>"
139 "<body><input type='button' value='push' /><input type='checkbox' />"
142 NavigateToURL(shell(), url
);
143 const ui::AXTree
& tree
= GetAXTree();
144 const ui::AXNode
* root
= tree
.GetRoot();
146 // Check properties of the root element of the tree.
147 EXPECT_STREQ(url_str
,
148 GetAttr(root
, ui::AX_ATTR_DOC_URL
).c_str());
150 "Accessibility Test",
151 GetAttr(root
, ui::AX_ATTR_DOC_TITLE
).c_str());
153 "html", GetAttr(root
, ui::AX_ATTR_DOC_DOCTYPE
).c_str());
156 GetAttr(root
, ui::AX_ATTR_DOC_MIMETYPE
).c_str());
158 "Accessibility Test",
159 GetAttr(root
, ui::AX_ATTR_NAME
).c_str());
160 EXPECT_EQ(ui::AX_ROLE_ROOT_WEB_AREA
, root
->data().role
);
162 // Check properites of the BODY element.
163 ASSERT_EQ(1, root
->child_count());
164 const ui::AXNode
* body
= root
->ChildAtIndex(0);
165 EXPECT_EQ(ui::AX_ROLE_GROUP
, body
->data().role
);
167 GetAttr(body
, ui::AX_ATTR_HTML_TAG
).c_str());
168 EXPECT_STREQ("block",
169 GetAttr(body
, ui::AX_ATTR_DISPLAY
).c_str());
171 // Check properties of the two children of the BODY element.
172 ASSERT_EQ(2, body
->child_count());
174 const ui::AXNode
* button
= body
->ChildAtIndex(0);
175 EXPECT_EQ(ui::AX_ROLE_BUTTON
, button
->data().role
);
177 "input", GetAttr(button
, ui::AX_ATTR_HTML_TAG
).c_str());
180 GetAttr(button
, ui::AX_ATTR_NAME
).c_str());
183 GetAttr(button
, ui::AX_ATTR_DISPLAY
).c_str());
184 ASSERT_EQ(2U, button
->data().html_attributes
.size());
185 EXPECT_STREQ("type", button
->data().html_attributes
[0].first
.c_str());
186 EXPECT_STREQ("button", button
->data().html_attributes
[0].second
.c_str());
187 EXPECT_STREQ("value", button
->data().html_attributes
[1].first
.c_str());
188 EXPECT_STREQ("push", button
->data().html_attributes
[1].second
.c_str());
190 const ui::AXNode
* checkbox
= body
->ChildAtIndex(1);
191 EXPECT_EQ(ui::AX_ROLE_CHECK_BOX
, checkbox
->data().role
);
193 "input", GetAttr(checkbox
, ui::AX_ATTR_HTML_TAG
).c_str());
196 GetAttr(checkbox
, ui::AX_ATTR_DISPLAY
).c_str());
197 ASSERT_EQ(1U, checkbox
->data().html_attributes
.size());
198 EXPECT_STREQ("type", checkbox
->data().html_attributes
[0].first
.c_str());
199 EXPECT_STREQ("checkbox", checkbox
->data().html_attributes
[0].second
.c_str());
202 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
203 UnselectedEditableTextAccessibility
) {
204 // Create a data url and load it.
205 const char url_str
[] =
209 "<input value=\"Hello, world.\"/>"
212 NavigateToURL(shell(), url
);
214 const ui::AXTree
& tree
= GetAXTree();
215 const ui::AXNode
* root
= tree
.GetRoot();
216 ASSERT_EQ(1, root
->child_count());
217 const ui::AXNode
* body
= root
->ChildAtIndex(0);
218 ASSERT_EQ(1, body
->child_count());
219 const ui::AXNode
* text
= body
->ChildAtIndex(0);
220 EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD
, text
->data().role
);
222 "input", GetAttr(text
, ui::AX_ATTR_HTML_TAG
).c_str());
223 EXPECT_EQ(0, GetIntAttr(text
, ui::AX_ATTR_TEXT_SEL_START
));
224 EXPECT_EQ(0, GetIntAttr(text
, ui::AX_ATTR_TEXT_SEL_END
));
227 GetAttr(text
, ui::AX_ATTR_VALUE
).c_str());
229 // TODO(dmazzoni): as soon as more accessibility code is cross-platform,
230 // this code should test that the accessible info is dynamically updated
231 // if the selection or value changes.
234 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
235 SelectedEditableTextAccessibility
) {
236 // Create a data url and load it.
237 const char url_str
[] =
240 "<body onload=\"document.body.children[0].select();\">"
241 "<input value=\"Hello, world.\"/>"
244 NavigateToURL(shell(), url
);
246 const ui::AXTree
& tree
= GetAXTree();
247 const ui::AXNode
* root
= tree
.GetRoot();
248 ASSERT_EQ(1, root
->child_count());
249 const ui::AXNode
* body
= root
->ChildAtIndex(0);
250 ASSERT_EQ(1, body
->child_count());
251 const ui::AXNode
* text
= body
->ChildAtIndex(0);
252 EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD
, text
->data().role
);
254 "input", GetAttr(text
, ui::AX_ATTR_HTML_TAG
).c_str());
255 EXPECT_EQ(0, GetIntAttr(text
, ui::AX_ATTR_TEXT_SEL_START
));
256 EXPECT_EQ(13, GetIntAttr(text
, ui::AX_ATTR_TEXT_SEL_END
));
259 GetAttr(text
, ui::AX_ATTR_VALUE
).c_str());
262 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
263 MultipleInheritanceAccessibility
) {
264 // In a WebKit accessibility render tree for a table, each cell is a
265 // child of both a row and a column, so it appears to use multiple
266 // inheritance. Make sure that the ui::AXNodeDataObject tree only
267 // keeps one copy of each cell, and uses an indirect child id for the
268 // additional reference to it.
269 const char url_str
[] =
272 "<table border=1><tr><td>1</td><td>2</td></tr></table>";
274 NavigateToURL(shell(), url
);
276 const ui::AXTree
& tree
= GetAXTree();
277 const ui::AXNode
* root
= tree
.GetRoot();
278 ASSERT_EQ(1, root
->child_count());
279 const ui::AXNode
* table
= root
->ChildAtIndex(0);
280 EXPECT_EQ(ui::AX_ROLE_TABLE
, table
->data().role
);
281 const ui::AXNode
* row
= table
->ChildAtIndex(0);
282 EXPECT_EQ(ui::AX_ROLE_ROW
, row
->data().role
);
283 const ui::AXNode
* cell1
= row
->ChildAtIndex(0);
284 EXPECT_EQ(ui::AX_ROLE_CELL
, cell1
->data().role
);
285 const ui::AXNode
* cell2
= row
->ChildAtIndex(1);
286 EXPECT_EQ(ui::AX_ROLE_CELL
, cell2
->data().role
);
287 const ui::AXNode
* column1
= table
->ChildAtIndex(1);
288 EXPECT_EQ(ui::AX_ROLE_COLUMN
, column1
->data().role
);
289 EXPECT_EQ(0, column1
->child_count());
290 EXPECT_EQ(1U, column1
->data().intlist_attributes
.size());
291 EXPECT_EQ(ui::AX_ATTR_INDIRECT_CHILD_IDS
,
292 column1
->data().intlist_attributes
[0].first
);
293 const std::vector
<int32
> column1_indirect_child_ids
=
294 column1
->data().intlist_attributes
[0].second
;
295 EXPECT_EQ(1U, column1_indirect_child_ids
.size());
296 EXPECT_EQ(cell1
->id(), column1_indirect_child_ids
[0]);
297 const ui::AXNode
* column2
= table
->ChildAtIndex(2);
298 EXPECT_EQ(ui::AX_ROLE_COLUMN
, column2
->data().role
);
299 EXPECT_EQ(0, column2
->child_count());
300 EXPECT_EQ(ui::AX_ATTR_INDIRECT_CHILD_IDS
,
301 column2
->data().intlist_attributes
[0].first
);
302 const std::vector
<int32
> column2_indirect_child_ids
=
303 column2
->data().intlist_attributes
[0].second
;
304 EXPECT_EQ(1U, column2_indirect_child_ids
.size());
305 EXPECT_EQ(cell2
->id(), column2_indirect_child_ids
[0]);
308 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
309 MultipleInheritanceAccessibility2
) {
310 // Here's another html snippet where WebKit puts the same node as a child
311 // of two different parents. Instead of checking the exact output, just
312 // make sure that no id is reused in the resulting tree.
313 const char url_str
[] =
317 " document.writeln('<q><section></section></q><q><li>');\n"
318 " setTimeout(function() {\n"
319 " document.close();\n"
323 NavigateToURL(shell(), url
);
325 const ui::AXTree
& tree
= GetAXTree();
326 const ui::AXNode
* root
= tree
.GetRoot();
327 base::hash_set
<int> ids
;
328 RecursiveAssertUniqueIds(root
, &ids
);
331 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
332 IframeAccessibility
) {
333 // Create a data url and load it.
334 const char url_str
[] =
336 "<!doctype html><html><body>"
337 "<button>Button 1</button>"
338 "<iframe src='data:text/html,"
339 "<!doctype html><html><body><button>Button 2</button></body></html>"
341 "<button>Button 3</button>"
344 NavigateToURL(shell(), url
);
346 const ui::AXTree
& tree
= GetAXTree();
347 const ui::AXNode
* root
= tree
.GetRoot();
348 ASSERT_EQ(1, root
->child_count());
349 const ui::AXNode
* body
= root
->ChildAtIndex(0);
350 ASSERT_EQ(3, body
->child_count());
352 const ui::AXNode
* button1
= body
->ChildAtIndex(0);
353 EXPECT_EQ(ui::AX_ROLE_BUTTON
, button1
->data().role
);
356 GetAttr(button1
, ui::AX_ATTR_NAME
).c_str());
358 const ui::AXNode
* iframe
= body
->ChildAtIndex(1);
359 EXPECT_STREQ("iframe",
360 GetAttr(iframe
, ui::AX_ATTR_HTML_TAG
).c_str());
361 ASSERT_EQ(1, iframe
->child_count());
363 const ui::AXNode
* scroll_area
= iframe
->ChildAtIndex(0);
364 EXPECT_EQ(ui::AX_ROLE_SCROLL_AREA
, scroll_area
->data().role
);
365 ASSERT_EQ(1, scroll_area
->child_count());
367 const ui::AXNode
* sub_document
= scroll_area
->ChildAtIndex(0);
368 EXPECT_EQ(ui::AX_ROLE_WEB_AREA
, sub_document
->data().role
);
369 ASSERT_EQ(1, sub_document
->child_count());
371 const ui::AXNode
* sub_body
= sub_document
->ChildAtIndex(0);
372 ASSERT_EQ(1, sub_body
->child_count());
374 const ui::AXNode
* button2
= sub_body
->ChildAtIndex(0);
375 EXPECT_EQ(ui::AX_ROLE_BUTTON
, button2
->data().role
);
376 EXPECT_STREQ("Button 2",
377 GetAttr(button2
, ui::AX_ATTR_NAME
).c_str());
379 const ui::AXNode
* button3
= body
->ChildAtIndex(2);
380 EXPECT_EQ(ui::AX_ROLE_BUTTON
, button3
->data().role
);
381 EXPECT_STREQ("Button 3",
382 GetAttr(button3
, ui::AX_ATTR_NAME
).c_str());
385 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
386 DuplicateChildrenAccessibility
) {
387 // Here's another html snippet where WebKit has a parent node containing
388 // two duplicate child nodes. Instead of checking the exact output, just
389 // make sure that no id is reused in the resulting tree.
390 const char url_str
[] =
393 "<em><code ><h4 ></em>";
395 NavigateToURL(shell(), url
);
397 const ui::AXTree
& tree
= GetAXTree();
398 const ui::AXNode
* root
= tree
.GetRoot();
399 base::hash_set
<int> ids
;
400 RecursiveAssertUniqueIds(root
, &ids
);
403 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
411 const char url_str
[] =
416 " <td colspan=2>1</td><td>2</td>"
419 " <td>3</td><td colspan=2>4</td>"
423 NavigateToURL(shell(), url
);
425 const ui::AXTree
& tree
= GetAXTree();
426 const ui::AXNode
* root
= tree
.GetRoot();
427 const ui::AXNode
* table
= root
->ChildAtIndex(0);
428 EXPECT_EQ(ui::AX_ROLE_TABLE
, table
->data().role
);
429 ASSERT_GE(table
->child_count(), 5);
430 EXPECT_EQ(ui::AX_ROLE_ROW
, table
->ChildAtIndex(0)->data().role
);
431 EXPECT_EQ(ui::AX_ROLE_ROW
, table
->ChildAtIndex(1)->data().role
);
432 EXPECT_EQ(ui::AX_ROLE_COLUMN
, table
->ChildAtIndex(2)->data().role
);
433 EXPECT_EQ(ui::AX_ROLE_COLUMN
, table
->ChildAtIndex(3)->data().role
);
434 EXPECT_EQ(ui::AX_ROLE_COLUMN
, table
->ChildAtIndex(4)->data().role
);
436 GetIntAttr(table
, ui::AX_ATTR_TABLE_COLUMN_COUNT
));
437 EXPECT_EQ(2, GetIntAttr(table
, ui::AX_ATTR_TABLE_ROW_COUNT
));
439 const ui::AXNode
* cell1
= table
->ChildAtIndex(0)->ChildAtIndex(0);
440 const ui::AXNode
* cell2
= table
->ChildAtIndex(0)->ChildAtIndex(1);
441 const ui::AXNode
* cell3
= table
->ChildAtIndex(1)->ChildAtIndex(0);
442 const ui::AXNode
* cell4
= table
->ChildAtIndex(1)->ChildAtIndex(1);
444 ASSERT_EQ(ui::AX_ATTR_CELL_IDS
,
445 table
->data().intlist_attributes
[0].first
);
446 const std::vector
<int32
>& table_cell_ids
=
447 table
->data().intlist_attributes
[0].second
;
448 ASSERT_EQ(6U, table_cell_ids
.size());
449 EXPECT_EQ(cell1
->id(), table_cell_ids
[0]);
450 EXPECT_EQ(cell1
->id(), table_cell_ids
[1]);
451 EXPECT_EQ(cell2
->id(), table_cell_ids
[2]);
452 EXPECT_EQ(cell3
->id(), table_cell_ids
[3]);
453 EXPECT_EQ(cell4
->id(), table_cell_ids
[4]);
454 EXPECT_EQ(cell4
->id(), table_cell_ids
[5]);
456 EXPECT_EQ(0, GetIntAttr(cell1
,
457 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
));
458 EXPECT_EQ(0, GetIntAttr(cell1
,
459 ui::AX_ATTR_TABLE_CELL_ROW_INDEX
));
460 EXPECT_EQ(2, GetIntAttr(cell1
,
461 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
));
462 EXPECT_EQ(1, GetIntAttr(cell1
,
463 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
));
464 EXPECT_EQ(2, GetIntAttr(cell2
,
465 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
));
466 EXPECT_EQ(1, GetIntAttr(cell2
,
467 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
));
468 EXPECT_EQ(0, GetIntAttr(cell3
,
469 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
));
470 EXPECT_EQ(1, GetIntAttr(cell3
,
471 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
));
472 EXPECT_EQ(1, GetIntAttr(cell4
,
473 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
));
474 EXPECT_EQ(2, GetIntAttr(cell4
,
475 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
));
478 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest
,
480 const char url_str
[] =
483 "<div role='textbox' tabindex=0>"
487 NavigateToURL(shell(), url
);
488 const ui::AXTree
& tree
= GetAXTree();
489 const ui::AXNode
* root
= tree
.GetRoot();
490 ASSERT_EQ(1, root
->child_count());
491 const ui::AXNode
* textbox
= root
->ChildAtIndex(0);
492 EXPECT_EQ(true, GetBoolAttr(textbox
, ui::AX_ATTR_CAN_SET_VALUE
));
495 } // namespace content