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