Home | History | Annotate | Download | only in accessibility
      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:
     31   RendererAccessibilityBrowserTest() {}
     32 
     33   // Tell the renderer to send an accessibility tree, then wait for the
     34   // notification that it's been received.
     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.
     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 
     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 
     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*.
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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