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