Home | History | Annotate | Download | only in accessibility
      1 // Copyright (c) 2014 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 "content/browser/frame_host/render_frame_host_impl.h"
      6 #include "content/browser/web_contents/web_contents_impl.h"
      7 #include "content/common/accessibility_messages.h"
      8 #include "content/public/test/browser_test_utils.h"
      9 #include "content/public/test/content_browser_test.h"
     10 #include "content/public/test/content_browser_test_utils.h"
     11 #include "content/shell/browser/shell.h"
     12 #include "content/test/accessibility_browser_test_utils.h"
     13 #include "ui/accessibility/ax_node.h"
     14 #include "ui/accessibility/ax_tree.h"
     15 
     16 namespace content {
     17 
     18 class AccessibilityIpcErrorBrowserTest : public ContentBrowserTest {
     19  public:
     20   AccessibilityIpcErrorBrowserTest() {}
     21 
     22  protected:
     23   // Convenience method to get the value of a particular AXNode
     24   // attribute as a UTF-8 string.
     25   std::string GetAttr(const ui::AXNode* node,
     26                       const ui::AXStringAttribute attr) {
     27     const ui::AXNodeData& data = node->data();
     28     for (size_t i = 0; i < data.string_attributes.size(); ++i) {
     29       if (data.string_attributes[i].first == attr)
     30         return data.string_attributes[i].second;
     31     }
     32     return std::string();
     33   }
     34 
     35   DISALLOW_COPY_AND_ASSIGN(AccessibilityIpcErrorBrowserTest);
     36 };
     37 
     38 IN_PROC_BROWSER_TEST_F(AccessibilityIpcErrorBrowserTest,
     39                        ResetBrowserAccessibilityManager) {
     40   // Create a data url and load it.
     41   const char url_str[] =
     42       "data:text/html,"
     43       "<div aria-live='polite'>"
     44       "  <p id='p1'>Paragraph One</p>"
     45       "  <p id='p2'>Paragraph Two</p>"
     46       "</div>"
     47       "<button id='button'>Button</button>";
     48   GURL url(url_str);
     49   NavigateToURL(shell(), url);
     50 
     51   // Simulate a condition where the RFH can't create a
     52   // BrowserAccessibilityManager - like if there's no view.
     53   RenderFrameHostImpl* frame = static_cast<RenderFrameHostImpl*>(
     54       shell()->web_contents()->GetMainFrame());
     55   frame->set_disallow_browser_accessibility_manager_for_testing(true);
     56   ASSERT_EQ(nullptr, frame->GetOrCreateBrowserAccessibilityManager());
     57 
     58   {
     59     // Enable accessibility (passing AccessibilityModeComplete to
     60     // AccessibilityNotificationWaiter does this automatically) and wait for
     61     // the first event.
     62     AccessibilityNotificationWaiter waiter(
     63         shell(), AccessibilityModeComplete, ui::AX_EVENT_LAYOUT_COMPLETE);
     64     waiter.WaitForNotification();
     65   }
     66 
     67   // Make sure we still didn't create a BrowserAccessibilityManager.
     68   // This means that at least one accessibility IPC was lost.
     69   ASSERT_EQ(nullptr, frame->GetOrCreateBrowserAccessibilityManager());
     70 
     71   // Now allow a BrowserAccessibilityManager, simulating what would happen
     72   // if the RFH's view is created now.
     73   frame->set_disallow_browser_accessibility_manager_for_testing(false);
     74   ASSERT_TRUE(frame->GetOrCreateBrowserAccessibilityManager() != nullptr);
     75 
     76   {
     77     // Hide one of the elements on the page, and wait for an accessibility
     78     // notification triggered by the hide.
     79     AccessibilityNotificationWaiter waiter(
     80         shell(), AccessibilityModeComplete, ui::AX_EVENT_LIVE_REGION_CHANGED);
     81     ASSERT_TRUE(ExecuteScript(
     82         shell()->web_contents(),
     83         "document.getElementById('p1').style.display = 'none';"));
     84     waiter.WaitForNotification();
     85   }
     86 
     87   // Show that accessibility was reset because the frame doesn't have a
     88   // BrowserAccessibilityManager anymore.
     89   ASSERT_EQ(nullptr, frame->browser_accessibility_manager());
     90   const ui::AXTree* tree = nullptr;
     91   {
     92     // Ensure that we didn't kill the renderer; we can still send it messages.
     93     AccessibilityNotificationWaiter waiter(
     94         shell(), AccessibilityModeComplete, ui::AX_EVENT_FOCUS);
     95     ASSERT_TRUE(ExecuteScript(
     96         shell()->web_contents(),
     97         "document.getElementById('button').focus();"));
     98     waiter.WaitForNotification();
     99     tree = &waiter.GetAXTree();
    100   }
    101 
    102   // Get the accessibility tree, ensure it reflects the final state of the
    103   // document.
    104   const ui::AXNode* root = tree->GetRoot();
    105 
    106   // Use this for debugging if the test fails.
    107   VLOG(1) << tree->ToString();
    108 
    109   EXPECT_EQ(ui::AX_ROLE_ROOT_WEB_AREA, root->data().role);
    110   ASSERT_EQ(2, root->child_count());
    111 
    112   const ui::AXNode* live_region = root->ChildAtIndex(0);
    113   ASSERT_EQ(1, live_region->child_count());
    114   EXPECT_EQ(ui::AX_ROLE_DIV, live_region->data().role);
    115 
    116   const ui::AXNode* para = live_region->ChildAtIndex(0);
    117   EXPECT_EQ(ui::AX_ROLE_PARAGRAPH, para->data().role);
    118 
    119   const ui::AXNode* button_container = root->ChildAtIndex(1);
    120   EXPECT_EQ(ui::AX_ROLE_GROUP, button_container->data().role);
    121   ASSERT_EQ(1, button_container->child_count());
    122 
    123   const ui::AXNode* button = button_container->ChildAtIndex(0);
    124   EXPECT_EQ(ui::AX_ROLE_BUTTON, button->data().role);
    125   EXPECT_TRUE(button->data().state >> ui::AX_STATE_FOCUSED & 1);
    126 }
    127 
    128 IN_PROC_BROWSER_TEST_F(AccessibilityIpcErrorBrowserTest,
    129                        MultipleBadAccessibilityIPCsKillsRenderer) {
    130   // Create a data url and load it.
    131   const char url_str[] =
    132       "data:text/html,"
    133       "<button id='button'>Button</button>";
    134   GURL url(url_str);
    135   NavigateToURL(shell(), url);
    136   RenderFrameHostImpl* frame = static_cast<RenderFrameHostImpl*>(
    137       shell()->web_contents()->GetMainFrame());
    138 
    139   {
    140     // Enable accessibility (passing AccessibilityModeComplete to
    141     // AccessibilityNotificationWaiter does this automatically) and wait for
    142     // the first event.
    143     AccessibilityNotificationWaiter waiter(
    144         shell(), AccessibilityModeComplete, ui::AX_EVENT_LAYOUT_COMPLETE);
    145     waiter.WaitForNotification();
    146   }
    147 
    148   // Construct a bad accessibility message that BrowserAccessibilityManager
    149   // will reject.
    150   std::vector<AccessibilityHostMsg_EventParams> bad_accessibility_event_list;
    151   bad_accessibility_event_list.push_back(AccessibilityHostMsg_EventParams());
    152   bad_accessibility_event_list[0].update.node_id_to_clear = -2;
    153 
    154   // We should be able to reset accessibility |max_iterations-1| times
    155   // (see render_frame_host_impl.cc - kMaxAccessibilityResets),
    156   // but the subsequent time the renderer should be killed.
    157   int max_iterations = RenderFrameHostImpl::kMaxAccessibilityResets;
    158 
    159   for (int iteration = 0; iteration < max_iterations; iteration++) {
    160     // Send the browser accessibility the bad message.
    161     BrowserAccessibilityManager* manager =
    162         frame->GetOrCreateBrowserAccessibilityManager();
    163     manager->OnAccessibilityEvents(bad_accessibility_event_list);
    164 
    165     // Now the frame should have deleted the BrowserAccessibilityManager.
    166     ASSERT_EQ(nullptr, frame->browser_accessibility_manager());
    167 
    168     if (iteration == max_iterations - 1)
    169       break;
    170 
    171     AccessibilityNotificationWaiter waiter(
    172         shell(), AccessibilityModeComplete, ui::AX_EVENT_LAYOUT_COMPLETE);
    173     waiter.WaitForNotification();
    174   }
    175 
    176   // Wait for the renderer to be killed.
    177   RenderProcessHostWatcher render_process_watcher(
    178       frame->GetProcess(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
    179   render_process_watcher.Wait();
    180   ASSERT_FALSE(frame->IsRenderFrameLive());
    181 }
    182 
    183 }  // namespace content
    184