• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/utf_string_conversions.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/browser/ui/browser_window.h"
11 #include "chrome/common/render_messages.h"
12 #include "chrome/test/in_process_browser_test.h"
13 #include "chrome/test/ui_test_utils.h"
14 #include "content/browser/renderer_host/render_view_host.h"
15 #include "content/browser/renderer_host/render_widget_host.h"
16 #include "content/browser/renderer_host/render_widget_host_view.h"
17 #include "content/browser/tab_contents/tab_contents.h"
18 #include "content/common/notification_type.h"
19 
20 #if defined(OS_WIN)
21 #include <atlbase.h>
22 #include <atlcom.h>
23 #endif
24 
25 using webkit_glue::WebAccessibility;
26 
27 namespace {
28 
29 class RendererAccessibilityBrowserTest : public InProcessBrowserTest {
30  public:
RendererAccessibilityBrowserTest()31   RendererAccessibilityBrowserTest() {}
32 
33   // Tell the renderer to send an accessibility tree, then wait for the
34   // notification that it's been received.
GetWebAccessibilityTree()35   const WebAccessibility& GetWebAccessibilityTree() {
36     RenderWidgetHostView* host_view =
37         browser()->GetSelectedTabContents()->GetRenderWidgetHostView();
38     RenderWidgetHost* host = host_view->GetRenderWidgetHost();
39     RenderViewHost* view_host = static_cast<RenderViewHost*>(host);
40     view_host->set_save_accessibility_tree_for_testing(true);
41     view_host->EnableRendererAccessibility();
42     ui_test_utils::WaitForNotification(
43         NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
44     return view_host->accessibility_tree();
45   }
46 
47   // Make sure each node in the tree has an unique id.
RecursiveAssertUniqueIds(const WebAccessibility & node,base::hash_set<int> * ids)48   void RecursiveAssertUniqueIds(
49       const WebAccessibility& node, base::hash_set<int>* ids) {
50     ASSERT_TRUE(ids->find(node.id) == ids->end());
51     ids->insert(node.id);
52     for (size_t i = 0; i < node.children.size(); i++)
53       RecursiveAssertUniqueIds(node.children[i], ids);
54   }
55 
56   // InProcessBrowserTest
57   void SetUpInProcessBrowserTestFixture();
58   void TearDownInProcessBrowserTestFixture();
59 
60  protected:
61   std::string GetAttr(const WebAccessibility& node,
62                       const WebAccessibility::Attribute attr);
63 };
64 
SetUpInProcessBrowserTestFixture()65 void RendererAccessibilityBrowserTest::SetUpInProcessBrowserTestFixture() {
66 #if defined(OS_WIN)
67   // ATL needs a pointer to a COM module.
68   static CComModule module;
69   _pAtlModule = &module;
70 
71   // Make sure COM is initialized for this thread; it's safe to call twice.
72   ::CoInitialize(NULL);
73 #endif
74 }
75 
TearDownInProcessBrowserTestFixture()76 void RendererAccessibilityBrowserTest::TearDownInProcessBrowserTestFixture() {
77 #if defined(OS_WIN)
78   ::CoUninitialize();
79 #endif
80 }
81 
82 // Convenience method to get the value of a particular WebAccessibility
83 // node attribute as a UTF-8 const char*.
GetAttr(const WebAccessibility & node,const WebAccessibility::Attribute attr)84 std::string RendererAccessibilityBrowserTest::GetAttr(
85     const WebAccessibility& node, const WebAccessibility::Attribute attr) {
86   std::map<int32, string16>::const_iterator iter = node.attributes.find(attr);
87   if (iter != node.attributes.end())
88     return UTF16ToUTF8(iter->second);
89   else
90     return "";
91 }
92 
IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,CrossPlatformWebpageAccessibility)93 IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,
94                        CrossPlatformWebpageAccessibility) {
95   // Create a data url and load it.
96   const char url_str[] =
97       "data:text/html,"
98       "<!doctype html>"
99       "<html><head><title>Accessibility Test</title></head>"
100       "<body><input type='button' value='push' /><input type='checkbox' />"
101       "</body></html>";
102   GURL url(url_str);
103   browser()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
104   const WebAccessibility& tree = GetWebAccessibilityTree();
105 
106   // Check properties of the root element of the tree.
107   EXPECT_STREQ(url_str, GetAttr(tree, WebAccessibility::ATTR_DOC_URL).c_str());
108   EXPECT_STREQ(
109       "Accessibility Test",
110       GetAttr(tree, WebAccessibility::ATTR_DOC_TITLE).c_str());
111   EXPECT_STREQ(
112       "html", GetAttr(tree, WebAccessibility::ATTR_DOC_DOCTYPE).c_str());
113   EXPECT_STREQ(
114       "text/html", GetAttr(tree, WebAccessibility::ATTR_DOC_MIMETYPE).c_str());
115   EXPECT_STREQ("Accessibility Test", UTF16ToUTF8(tree.name).c_str());
116   EXPECT_EQ(WebAccessibility::ROLE_WEB_AREA, tree.role);
117 
118   // Check properites of the BODY element.
119   ASSERT_EQ(1U, tree.children.size());
120   const WebAccessibility& body = tree.children[0];
121   EXPECT_EQ(WebAccessibility::ROLE_GROUP, body.role);
122   EXPECT_STREQ("body", GetAttr(body, WebAccessibility::ATTR_HTML_TAG).c_str());
123   EXPECT_STREQ("block", GetAttr(body, WebAccessibility::ATTR_DISPLAY).c_str());
124 
125   // Check properties of the two children of the BODY element.
126   ASSERT_EQ(2U, body.children.size());
127 
128   const WebAccessibility& button = body.children[0];
129   EXPECT_EQ(WebAccessibility::ROLE_BUTTON, button.role);
130   EXPECT_STREQ(
131       "input", GetAttr(button, WebAccessibility::ATTR_HTML_TAG).c_str());
132   EXPECT_STREQ("push", UTF16ToUTF8(button.name).c_str());
133   EXPECT_STREQ(
134       "inline-block", GetAttr(button, WebAccessibility::ATTR_DISPLAY).c_str());
135   ASSERT_EQ(2U, button.html_attributes.size());
136   EXPECT_STREQ("type", UTF16ToUTF8(button.html_attributes[0].first).c_str());
137   EXPECT_STREQ("button", UTF16ToUTF8(button.html_attributes[0].second).c_str());
138   EXPECT_STREQ("value", UTF16ToUTF8(button.html_attributes[1].first).c_str());
139   EXPECT_STREQ("push", UTF16ToUTF8(button.html_attributes[1].second).c_str());
140 
141   const WebAccessibility& checkbox = body.children[1];
142   EXPECT_EQ(WebAccessibility::ROLE_CHECKBOX, checkbox.role);
143   EXPECT_STREQ(
144       "input", GetAttr(checkbox, WebAccessibility::ATTR_HTML_TAG).c_str());
145   EXPECT_STREQ(
146       "inline-block",
147       GetAttr(checkbox, WebAccessibility::ATTR_DISPLAY).c_str());
148   ASSERT_EQ(1U, checkbox.html_attributes.size());
149   EXPECT_STREQ(
150       "type", UTF16ToUTF8(checkbox.html_attributes[0].first).c_str());
151   EXPECT_STREQ(
152     "checkbox", UTF16ToUTF8(checkbox.html_attributes[0].second).c_str());
153 }
154 
IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,CrossPlatformUnselectedEditableTextAccessibility)155 IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,
156                        CrossPlatformUnselectedEditableTextAccessibility) {
157   // Create a data url and load it.
158   const char url_str[] =
159       "data:text/html,"
160       "<!doctype html>"
161       "<body>"
162       "<input value=\"Hello, world.\"/>"
163       "</body></html>";
164   GURL url(url_str);
165   browser()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
166 
167   const WebAccessibility& tree = GetWebAccessibilityTree();
168   ASSERT_EQ(1U, tree.children.size());
169   const WebAccessibility& body = tree.children[0];
170   ASSERT_EQ(1U, body.children.size());
171   const WebAccessibility& text = body.children[0];
172   EXPECT_EQ(WebAccessibility::ROLE_TEXT_FIELD, text.role);
173   EXPECT_STREQ(
174       "input", GetAttr(text, WebAccessibility::ATTR_HTML_TAG).c_str());
175   EXPECT_STREQ(
176       "0", GetAttr(text, WebAccessibility::ATTR_TEXT_SEL_START).c_str());
177   EXPECT_STREQ(
178       "0", GetAttr(text, WebAccessibility::ATTR_TEXT_SEL_END).c_str());
179   EXPECT_STREQ("Hello, world.", UTF16ToUTF8(text.value).c_str());
180 
181   // TODO(dmazzoni): as soon as more accessibility code is cross-platform,
182   // this code should test that the accessible info is dynamically updated
183   // if the selection or value changes.
184 }
185 
IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,CrossPlatformSelectedEditableTextAccessibility)186 IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,
187                        CrossPlatformSelectedEditableTextAccessibility) {
188   // Create a data url and load it.
189   const char url_str[] =
190       "data:text/html,"
191       "<!doctype html>"
192       "<body onload=\"document.body.children[0].select();\">"
193       "<input value=\"Hello, world.\"/>"
194       "</body></html>";
195   GURL url(url_str);
196   browser()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
197 
198   const WebAccessibility& tree = GetWebAccessibilityTree();
199   ASSERT_EQ(1U, tree.children.size());
200   const WebAccessibility& body = tree.children[0];
201   ASSERT_EQ(1U, body.children.size());
202   const WebAccessibility& text = body.children[0];
203   EXPECT_EQ(WebAccessibility::ROLE_TEXT_FIELD, text.role);
204   EXPECT_STREQ(
205       "input", GetAttr(text, WebAccessibility::ATTR_HTML_TAG).c_str());
206   EXPECT_STREQ(
207       "0", GetAttr(text, WebAccessibility::ATTR_TEXT_SEL_START).c_str());
208   EXPECT_STREQ(
209       "13", GetAttr(text, WebAccessibility::ATTR_TEXT_SEL_END).c_str());
210   EXPECT_STREQ("Hello, world.", UTF16ToUTF8(text.value).c_str());
211 }
212 
IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,CrossPlatformMultipleInheritanceAccessibility)213 IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,
214                        CrossPlatformMultipleInheritanceAccessibility) {
215   // In a WebKit accessibility render tree for a table, each cell is a
216   // child of both a row and a column, so it appears to use multiple
217   // inheritance. Make sure that the WebAccessibilityObject tree only
218   // keeps one copy of each cell, and uses an indirect child id for the
219   // additional reference to it.
220   const char url_str[] =
221       "data:text/html,"
222       "<!doctype html>"
223       "<table border=1><tr><td>1</td><td>2</td></tr></table>";
224   GURL url(url_str);
225   browser()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
226 
227   const WebAccessibility& tree = GetWebAccessibilityTree();
228   ASSERT_EQ(1U, tree.children.size());
229   const WebAccessibility& table = tree.children[0];
230   EXPECT_EQ(WebAccessibility::ROLE_TABLE, table.role);
231   const WebAccessibility& row = table.children[0];
232   EXPECT_EQ(WebAccessibility::ROLE_ROW, row.role);
233   const WebAccessibility& cell1 = row.children[0];
234   EXPECT_EQ(WebAccessibility::ROLE_CELL, cell1.role);
235   const WebAccessibility& cell2 = row.children[1];
236   EXPECT_EQ(WebAccessibility::ROLE_CELL, cell2.role);
237   const WebAccessibility& column1 = table.children[1];
238   EXPECT_EQ(WebAccessibility::ROLE_COLUMN, column1.role);
239   EXPECT_EQ(0U, column1.children.size());
240   EXPECT_EQ(1U, column1.indirect_child_ids.size());
241   EXPECT_EQ(cell1.id, column1.indirect_child_ids[0]);
242   const WebAccessibility& column2 = table.children[2];
243   EXPECT_EQ(WebAccessibility::ROLE_COLUMN, column2.role);
244   EXPECT_EQ(0U, column2.children.size());
245   EXPECT_EQ(1U, column2.indirect_child_ids.size());
246   EXPECT_EQ(cell2.id, column2.indirect_child_ids[0]);
247 }
248 
IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,CrossPlatformMultipleInheritanceAccessibility2)249 IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,
250                        CrossPlatformMultipleInheritanceAccessibility2) {
251   // Here's another html snippet where WebKit puts the same node as a child
252   // of two different parents. Instead of checking the exact output, just
253   // make sure that no id is reused in the resulting tree.
254   const char url_str[] =
255       "data:text/html,"
256       "<!doctype html>"
257       "<script>\n"
258       "  document.writeln('<q><section></section></q><q><li>');\n"
259       "  setTimeout(function() {\n"
260       "    document.close();\n"
261       "  }, 1);\n"
262       "</script>";
263   GURL url(url_str);
264   browser()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
265 
266   const WebAccessibility& tree = GetWebAccessibilityTree();
267   base::hash_set<int> ids;
268   RecursiveAssertUniqueIds(tree, &ids);
269 }
270 
IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,CrossPlatformIframeAccessibility)271 IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,
272                        CrossPlatformIframeAccessibility) {
273   // Create a data url and load it.
274   const char url_str[] =
275       "data:text/html,"
276       "<!doctype html><html><body>"
277       "<button>Button 1</button>"
278       "<iframe src='data:text/html,"
279       "<!doctype html><html><body><button>Button 2</button></body></html>"
280       "'></iframe>"
281       "<button>Button 3</button>"
282       "</body></html>";
283   GURL url(url_str);
284   browser()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
285 
286   const WebAccessibility& tree = GetWebAccessibilityTree();
287   ASSERT_EQ(1U, tree.children.size());
288   const WebAccessibility& body = tree.children[0];
289   ASSERT_EQ(3U, body.children.size());
290 
291   const WebAccessibility& button1 = body.children[0];
292   EXPECT_EQ(WebAccessibility::ROLE_BUTTON, button1.role);
293   EXPECT_STREQ("Button 1", UTF16ToUTF8(button1.name).c_str());
294 
295   const WebAccessibility& iframe = body.children[1];
296   EXPECT_STREQ("iframe",
297                GetAttr(iframe, WebAccessibility::ATTR_HTML_TAG).c_str());
298   ASSERT_EQ(1U, iframe.children.size());
299 
300   const WebAccessibility& scroll_area = iframe.children[0];
301   EXPECT_EQ(WebAccessibility::ROLE_SCROLLAREA, scroll_area.role);
302   ASSERT_EQ(1U, scroll_area.children.size());
303 
304   const WebAccessibility& sub_document = scroll_area.children[0];
305   EXPECT_EQ(WebAccessibility::ROLE_WEB_AREA, sub_document.role);
306   ASSERT_EQ(1U, sub_document.children.size());
307 
308   const WebAccessibility& sub_body = sub_document.children[0];
309   ASSERT_EQ(1U, sub_body.children.size());
310 
311   const WebAccessibility& button2 = sub_body.children[0];
312   EXPECT_EQ(WebAccessibility::ROLE_BUTTON, button2.role);
313   EXPECT_STREQ("Button 2", UTF16ToUTF8(button2.name).c_str());
314 
315   const WebAccessibility& button3 = body.children[2];
316   EXPECT_EQ(WebAccessibility::ROLE_BUTTON, button3.role);
317   EXPECT_STREQ("Button 3", UTF16ToUTF8(button3.name).c_str());
318 }
319 
IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,CrossPlatformDuplicateChildrenAccessibility)320 IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,
321                        CrossPlatformDuplicateChildrenAccessibility) {
322   // Here's another html snippet where WebKit has a parent node containing
323   // two duplicate child nodes. Instead of checking the exact output, just
324   // make sure that no id is reused in the resulting tree.
325   const char url_str[] =
326       "data:text/html,"
327       "<!doctype html>"
328       "<em><code ><h4 ></em>";
329   GURL url(url_str);
330   browser()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
331 
332   const WebAccessibility& tree = GetWebAccessibilityTree();
333   base::hash_set<int> ids;
334   RecursiveAssertUniqueIds(tree, &ids);
335 }
336 
337 }  // namespace
338