- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / accessibility / cross_platform_accessibility_browsertest.cc
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.
4
5 #include <string>
6 #include <vector>
7
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
18 #if defined(OS_WIN)
19 #include <atlbase.h>
20 #include <atlcom.h>
21 #include "base/win/scoped_com_initializer.h"
22 #include "ui/base/win/atl_module.h"
23 #endif
24
25 // TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
26 #if defined(OS_WIN) && defined(ARCH_CPU_X86_64)
27 #define MAYBE_TableSpan DISABLED_TableSpan
28 #else
29 #define MAYBE_TableSpan TableSpan
30 #endif
31
32 namespace content {
33
34 class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest {
35  public:
36   CrossPlatformAccessibilityBrowserTest() {}
37
38   // Tell the renderer to send an accessibility tree, then wait for the
39   // notification that it's been received.
40   const AccessibilityNodeDataTreeNode& GetAccessibilityNodeDataTree(
41       AccessibilityMode accessibility_mode = AccessibilityModeComplete) {
42     AccessibilityNotificationWaiter waiter(
43         shell(), accessibility_mode, WebKit::WebAXEventLayoutComplete);
44     waiter.WaitForNotification();
45     return waiter.GetAccessibilityNodeDataTree();
46   }
47
48   // Make sure each node in the tree has an unique id.
49   void RecursiveAssertUniqueIds(
50       const AccessibilityNodeDataTreeNode& node, base::hash_set<int>* ids) {
51     ASSERT_TRUE(ids->find(node.id) == ids->end());
52     ids->insert(node.id);
53     for (size_t i = 0; i < node.children.size(); i++)
54       RecursiveAssertUniqueIds(node.children[i], ids);
55   }
56
57   // ContentBrowserTest
58   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
59   virtual void TearDownInProcessBrowserTestFixture() OVERRIDE;
60
61  protected:
62   std::string GetAttr(const AccessibilityNodeData& node,
63                       const AccessibilityNodeData::StringAttribute attr);
64   int GetIntAttr(const AccessibilityNodeData& node,
65                  const AccessibilityNodeData::IntAttribute attr);
66   bool GetBoolAttr(const AccessibilityNodeData& node,
67                    const AccessibilityNodeData::BoolAttribute attr);
68
69  private:
70 #if defined(OS_WIN)
71   scoped_ptr<base::win::ScopedCOMInitializer> com_initializer_;
72 #endif
73
74   DISALLOW_COPY_AND_ASSIGN(CrossPlatformAccessibilityBrowserTest);
75 };
76
77 void CrossPlatformAccessibilityBrowserTest::SetUpInProcessBrowserTestFixture() {
78 #if defined(OS_WIN)
79   ui::win::CreateATLModuleIfNeeded();
80   com_initializer_.reset(new base::win::ScopedCOMInitializer());
81 #endif
82 }
83
84 void
85 CrossPlatformAccessibilityBrowserTest::TearDownInProcessBrowserTestFixture() {
86 #if defined(OS_WIN)
87   com_initializer_.reset();
88 #endif
89 }
90
91 // Convenience method to get the value of a particular AccessibilityNodeData
92 // node attribute as a UTF-8 string.
93 std::string CrossPlatformAccessibilityBrowserTest::GetAttr(
94     const AccessibilityNodeData& node,
95     const AccessibilityNodeData::StringAttribute attr) {
96   for (size_t i = 0; i < node.string_attributes.size(); ++i) {
97     if (node.string_attributes[i].first == attr)
98       return node.string_attributes[i].second;
99   }
100   return std::string();
101 }
102
103 // Convenience method to get the value of a particular AccessibilityNodeData
104 // node integer attribute.
105 int CrossPlatformAccessibilityBrowserTest::GetIntAttr(
106     const AccessibilityNodeData& node,
107     const AccessibilityNodeData::IntAttribute attr) {
108   for (size_t i = 0; i < node.int_attributes.size(); ++i) {
109     if (node.int_attributes[i].first == attr)
110       return node.int_attributes[i].second;
111   }
112   return -1;
113 }
114
115 // Convenience method to get the value of a particular AccessibilityNodeData
116 // node boolean attribute.
117 bool CrossPlatformAccessibilityBrowserTest::GetBoolAttr(
118     const AccessibilityNodeData& node,
119     const AccessibilityNodeData::BoolAttribute attr) {
120   for (size_t i = 0; i < node.bool_attributes.size(); ++i) {
121     if (node.bool_attributes[i].first == attr)
122       return node.bool_attributes[i].second;
123   }
124   return false;
125 }
126
127 // Marked flaky per http://crbug.com/101984
128 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
129                        DISABLED_WebpageAccessibility) {
130   // Create a data url and load it.
131   const char url_str[] =
132       "data:text/html,"
133       "<!doctype html>"
134       "<html><head><title>Accessibility Test</title></head>"
135       "<body><input type='button' value='push' /><input type='checkbox' />"
136       "</body></html>";
137   GURL url(url_str);
138   NavigateToURL(shell(), url);
139   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
140
141   // Check properties of the root element of the tree.
142   EXPECT_STREQ(url_str,
143                GetAttr(tree, AccessibilityNodeData::ATTR_DOC_URL).c_str());
144   EXPECT_STREQ(
145       "Accessibility Test",
146       GetAttr(tree, AccessibilityNodeData::ATTR_DOC_TITLE).c_str());
147   EXPECT_STREQ(
148       "html", GetAttr(tree, AccessibilityNodeData::ATTR_DOC_DOCTYPE).c_str());
149   EXPECT_STREQ(
150       "text/html",
151       GetAttr(tree, AccessibilityNodeData::ATTR_DOC_MIMETYPE).c_str());
152   EXPECT_STREQ(
153       "Accessibility Test",
154       GetAttr(tree, AccessibilityNodeData::ATTR_NAME).c_str());
155   EXPECT_EQ(WebKit::WebAXRoleRootWebArea, tree.role);
156
157   // Check properites of the BODY element.
158   ASSERT_EQ(1U, tree.children.size());
159   const AccessibilityNodeDataTreeNode& body = tree.children[0];
160   EXPECT_EQ(WebKit::WebAXRoleGroup, body.role);
161   EXPECT_STREQ("body",
162                GetAttr(body, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
163   EXPECT_STREQ("block",
164                GetAttr(body, AccessibilityNodeData::ATTR_DISPLAY).c_str());
165
166   // Check properties of the two children of the BODY element.
167   ASSERT_EQ(2U, body.children.size());
168
169   const AccessibilityNodeDataTreeNode& button = body.children[0];
170   EXPECT_EQ(WebKit::WebAXRoleButton, button.role);
171   EXPECT_STREQ(
172       "input", GetAttr(button, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
173   EXPECT_STREQ(
174       "push",
175       GetAttr(button, AccessibilityNodeData::ATTR_NAME).c_str());
176   EXPECT_STREQ(
177       "inline-block",
178       GetAttr(button, AccessibilityNodeData::ATTR_DISPLAY).c_str());
179   ASSERT_EQ(2U, button.html_attributes.size());
180   EXPECT_STREQ("type", button.html_attributes[0].first.c_str());
181   EXPECT_STREQ("button", button.html_attributes[0].second.c_str());
182   EXPECT_STREQ("value", button.html_attributes[1].first.c_str());
183   EXPECT_STREQ("push", button.html_attributes[1].second.c_str());
184
185   const AccessibilityNodeDataTreeNode& checkbox = body.children[1];
186   EXPECT_EQ(WebKit::WebAXRoleCheckBox, checkbox.role);
187   EXPECT_STREQ(
188       "input", GetAttr(checkbox, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
189   EXPECT_STREQ(
190       "inline-block",
191       GetAttr(checkbox, AccessibilityNodeData::ATTR_DISPLAY).c_str());
192   ASSERT_EQ(1U, checkbox.html_attributes.size());
193   EXPECT_STREQ("type", checkbox.html_attributes[0].first.c_str());
194   EXPECT_STREQ("checkbox", checkbox.html_attributes[0].second.c_str());
195 }
196
197 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
198                        UnselectedEditableTextAccessibility) {
199   // Create a data url and load it.
200   const char url_str[] =
201       "data:text/html,"
202       "<!doctype html>"
203       "<body>"
204       "<input value=\"Hello, world.\"/>"
205       "</body></html>";
206   GURL url(url_str);
207   NavigateToURL(shell(), url);
208
209   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
210   ASSERT_EQ(1U, tree.children.size());
211   const AccessibilityNodeDataTreeNode& body = tree.children[0];
212   ASSERT_EQ(1U, body.children.size());
213   const AccessibilityNodeDataTreeNode& text = body.children[0];
214   EXPECT_EQ(WebKit::WebAXRoleTextField, text.role);
215   EXPECT_STREQ(
216       "input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
217   EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_START));
218   EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_END));
219   EXPECT_STREQ(
220       "Hello, world.",
221       GetAttr(text, AccessibilityNodeData::ATTR_VALUE).c_str());
222
223   // TODO(dmazzoni): as soon as more accessibility code is cross-platform,
224   // this code should test that the accessible info is dynamically updated
225   // if the selection or value changes.
226 }
227
228 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
229                        SelectedEditableTextAccessibility) {
230   // Create a data url and load it.
231   const char url_str[] =
232       "data:text/html,"
233       "<!doctype html>"
234       "<body onload=\"document.body.children[0].select();\">"
235       "<input value=\"Hello, world.\"/>"
236       "</body></html>";
237   GURL url(url_str);
238   NavigateToURL(shell(), url);
239
240   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
241   ASSERT_EQ(1U, tree.children.size());
242   const AccessibilityNodeDataTreeNode& body = tree.children[0];
243   ASSERT_EQ(1U, body.children.size());
244   const AccessibilityNodeDataTreeNode& text = body.children[0];
245   EXPECT_EQ(WebKit::WebAXRoleTextField, text.role);
246   EXPECT_STREQ(
247       "input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
248   EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_START));
249   EXPECT_EQ(13, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_END));
250   EXPECT_STREQ(
251       "Hello, world.",
252       GetAttr(text, AccessibilityNodeData::ATTR_VALUE).c_str());
253 }
254
255 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
256                        MultipleInheritanceAccessibility) {
257   // In a WebKit accessibility render tree for a table, each cell is a
258   // child of both a row and a column, so it appears to use multiple
259   // inheritance. Make sure that the AccessibilityNodeDataObject tree only
260   // keeps one copy of each cell, and uses an indirect child id for the
261   // additional reference to it.
262   const char url_str[] =
263       "data:text/html,"
264       "<!doctype html>"
265       "<table border=1><tr><td>1</td><td>2</td></tr></table>";
266   GURL url(url_str);
267   NavigateToURL(shell(), url);
268
269   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
270   ASSERT_EQ(1U, tree.children.size());
271   const AccessibilityNodeDataTreeNode& table = tree.children[0];
272   EXPECT_EQ(WebKit::WebAXRoleTable, table.role);
273   const AccessibilityNodeDataTreeNode& row = table.children[0];
274   EXPECT_EQ(WebKit::WebAXRoleRow, row.role);
275   const AccessibilityNodeDataTreeNode& cell1 = row.children[0];
276   EXPECT_EQ(WebKit::WebAXRoleCell, cell1.role);
277   const AccessibilityNodeDataTreeNode& cell2 = row.children[1];
278   EXPECT_EQ(WebKit::WebAXRoleCell, cell2.role);
279   const AccessibilityNodeDataTreeNode& column1 = table.children[1];
280   EXPECT_EQ(WebKit::WebAXRoleColumn, column1.role);
281   EXPECT_EQ(0U, column1.children.size());
282   EXPECT_EQ(1U, column1.intlist_attributes.size());
283   EXPECT_EQ(AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS,
284             column1.intlist_attributes[0].first);
285   const std::vector<int32> column1_indirect_child_ids =
286       column1.intlist_attributes[0].second;
287   EXPECT_EQ(1U, column1_indirect_child_ids.size());
288   EXPECT_EQ(cell1.id, column1_indirect_child_ids[0]);
289   const AccessibilityNodeDataTreeNode& column2 = table.children[2];
290   EXPECT_EQ(WebKit::WebAXRoleColumn, column2.role);
291   EXPECT_EQ(0U, column2.children.size());
292   EXPECT_EQ(AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS,
293             column2.intlist_attributes[0].first);
294   const std::vector<int32> column2_indirect_child_ids =
295       column2.intlist_attributes[0].second;
296   EXPECT_EQ(1U, column2_indirect_child_ids.size());
297   EXPECT_EQ(cell2.id, column2_indirect_child_ids[0]);
298 }
299
300 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
301                        MultipleInheritanceAccessibility2) {
302   // Here's another html snippet where WebKit puts the same node as a child
303   // of two different parents. Instead of checking the exact output, just
304   // make sure that no id is reused in the resulting tree.
305   const char url_str[] =
306       "data:text/html,"
307       "<!doctype html>"
308       "<script>\n"
309       "  document.writeln('<q><section></section></q><q><li>');\n"
310       "  setTimeout(function() {\n"
311       "    document.close();\n"
312       "  }, 1);\n"
313       "</script>";
314   GURL url(url_str);
315   NavigateToURL(shell(), url);
316
317   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
318   base::hash_set<int> ids;
319   RecursiveAssertUniqueIds(tree, &ids);
320 }
321
322 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
323                        IframeAccessibility) {
324   // Create a data url and load it.
325   const char url_str[] =
326       "data:text/html,"
327       "<!doctype html><html><body>"
328       "<button>Button 1</button>"
329       "<iframe src='data:text/html,"
330       "<!doctype html><html><body><button>Button 2</button></body></html>"
331       "'></iframe>"
332       "<button>Button 3</button>"
333       "</body></html>";
334   GURL url(url_str);
335   NavigateToURL(shell(), url);
336
337   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
338   ASSERT_EQ(1U, tree.children.size());
339   const AccessibilityNodeDataTreeNode& body = tree.children[0];
340   ASSERT_EQ(3U, body.children.size());
341
342   const AccessibilityNodeDataTreeNode& button1 = body.children[0];
343   EXPECT_EQ(WebKit::WebAXRoleButton, button1.role);
344   EXPECT_STREQ(
345       "Button 1",
346       GetAttr(button1, AccessibilityNodeData::ATTR_NAME).c_str());
347
348   const AccessibilityNodeDataTreeNode& iframe = body.children[1];
349   EXPECT_STREQ("iframe",
350                GetAttr(iframe, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
351   ASSERT_EQ(1U, iframe.children.size());
352
353   const AccessibilityNodeDataTreeNode& scroll_area = iframe.children[0];
354   EXPECT_EQ(WebKit::WebAXRoleScrollArea, scroll_area.role);
355   ASSERT_EQ(1U, scroll_area.children.size());
356
357   const AccessibilityNodeDataTreeNode& sub_document = scroll_area.children[0];
358   EXPECT_EQ(WebKit::WebAXRoleWebArea, sub_document.role);
359   ASSERT_EQ(1U, sub_document.children.size());
360
361   const AccessibilityNodeDataTreeNode& sub_body = sub_document.children[0];
362   ASSERT_EQ(1U, sub_body.children.size());
363
364   const AccessibilityNodeDataTreeNode& button2 = sub_body.children[0];
365   EXPECT_EQ(WebKit::WebAXRoleButton, button2.role);
366   EXPECT_STREQ("Button 2",
367                GetAttr(button2, AccessibilityNodeData::ATTR_NAME).c_str());
368
369   const AccessibilityNodeDataTreeNode& button3 = body.children[2];
370   EXPECT_EQ(WebKit::WebAXRoleButton, button3.role);
371   EXPECT_STREQ("Button 3",
372                GetAttr(button3, AccessibilityNodeData::ATTR_NAME).c_str());
373 }
374
375 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
376                        DuplicateChildrenAccessibility) {
377   // Here's another html snippet where WebKit has a parent node containing
378   // two duplicate child nodes. Instead of checking the exact output, just
379   // make sure that no id is reused in the resulting tree.
380   const char url_str[] =
381       "data:text/html,"
382       "<!doctype html>"
383       "<em><code ><h4 ></em>";
384   GURL url(url_str);
385   NavigateToURL(shell(), url);
386
387   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
388   base::hash_set<int> ids;
389   RecursiveAssertUniqueIds(tree, &ids);
390 }
391
392 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
393                        MAYBE_TableSpan) {
394   // +---+---+---+
395   // |   1   | 2 |
396   // +---+---+---+
397   // | 3 |   4   |
398   // +---+---+---+
399
400   const char url_str[] =
401       "data:text/html,"
402       "<!doctype html>"
403       "<table border=1>"
404       " <tr>"
405       "  <td colspan=2>1</td><td>2</td>"
406       " </tr>"
407       " <tr>"
408       "  <td>3</td><td colspan=2>4</td>"
409       " </tr>"
410       "</table>";
411   GURL url(url_str);
412   NavigateToURL(shell(), url);
413
414   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
415   const AccessibilityNodeDataTreeNode& table = tree.children[0];
416   EXPECT_EQ(WebKit::WebAXRoleTable, table.role);
417   ASSERT_GE(table.children.size(), 5U);
418   EXPECT_EQ(WebKit::WebAXRoleRow, table.children[0].role);
419   EXPECT_EQ(WebKit::WebAXRoleRow, table.children[1].role);
420   EXPECT_EQ(WebKit::WebAXRoleColumn, table.children[2].role);
421   EXPECT_EQ(WebKit::WebAXRoleColumn, table.children[3].role);
422   EXPECT_EQ(WebKit::WebAXRoleColumn, table.children[4].role);
423   EXPECT_EQ(3,
424             GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT));
425   EXPECT_EQ(2, GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_ROW_COUNT));
426
427   const AccessibilityNodeDataTreeNode& cell1 = table.children[0].children[0];
428   const AccessibilityNodeDataTreeNode& cell2 = table.children[0].children[1];
429   const AccessibilityNodeDataTreeNode& cell3 = table.children[1].children[0];
430   const AccessibilityNodeDataTreeNode& cell4 = table.children[1].children[1];
431
432   ASSERT_EQ(AccessibilityNodeData::ATTR_CELL_IDS,
433             table.intlist_attributes[0].first);
434   const std::vector<int32>& table_cell_ids =
435       table.intlist_attributes[0].second;
436   ASSERT_EQ(6U, table_cell_ids.size());
437   EXPECT_EQ(cell1.id, table_cell_ids[0]);
438   EXPECT_EQ(cell1.id, table_cell_ids[1]);
439   EXPECT_EQ(cell2.id, table_cell_ids[2]);
440   EXPECT_EQ(cell3.id, table_cell_ids[3]);
441   EXPECT_EQ(cell4.id, table_cell_ids[4]);
442   EXPECT_EQ(cell4.id, table_cell_ids[5]);
443
444   EXPECT_EQ(0, GetIntAttr(cell1,
445                           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
446   EXPECT_EQ(0, GetIntAttr(cell1,
447                           AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX));
448   EXPECT_EQ(2, GetIntAttr(cell1,
449                           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
450   EXPECT_EQ(1, GetIntAttr(cell1,
451                           AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN));
452   EXPECT_EQ(2, GetIntAttr(cell2,
453                           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
454   EXPECT_EQ(1, GetIntAttr(cell2,
455                           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
456   EXPECT_EQ(0, GetIntAttr(cell3,
457                           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
458   EXPECT_EQ(1, GetIntAttr(cell3,
459                           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
460   EXPECT_EQ(1, GetIntAttr(cell4,
461                           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
462   EXPECT_EQ(2, GetIntAttr(cell4,
463                           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
464 }
465
466 IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
467                        WritableElement) {
468   const char url_str[] =
469       "data:text/html,"
470       "<!doctype html>"
471       "<div role='textbox' tabindex=0>"
472       " Some text"
473       "</div>";
474   GURL url(url_str);
475   NavigateToURL(shell(), url);
476   const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
477
478   ASSERT_EQ(1U, tree.children.size());
479   const AccessibilityNodeDataTreeNode& textbox = tree.children[0];
480
481   EXPECT_EQ(
482       true, GetBoolAttr(textbox, AccessibilityNodeData::ATTR_CAN_SET_VALUE));
483 }
484
485 }  // namespace content