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 192 193 194 195 196 197 198 199 200 201 202 203 204
|
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <optional>
#include <tuple>
#include "base/strings/string_number_conversions.h"
#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/test/accessibility_notification_waiter.h"
#include "content/public/test/browser_test.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/public/test/hit_test_region_observer.h"
#include "content/public/test/scoped_accessibility_mode_override.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/platform/browser_accessibility.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/event.h"
#include "ui/events/event_sink.h"
#include "ui/events/event_utils.h"
namespace content {
class TouchAccessibilityBrowserTest : public ContentBrowserTest {
public:
TouchAccessibilityBrowserTest() {}
TouchAccessibilityBrowserTest(const TouchAccessibilityBrowserTest&) = delete;
TouchAccessibilityBrowserTest& operator=(
const TouchAccessibilityBrowserTest&) = delete;
protected:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
accessibility_mode_.emplace(ui::kAXModeComplete);
}
void TearDownOnMainThread() override { accessibility_mode_.reset(); }
void NavigateToUrlAndWaitForAccessibilityTree(const GURL& url) {
AccessibilityNotificationWaiter waiter(shell()->web_contents(),
ax::mojom::Event::kLoadComplete);
EXPECT_TRUE(NavigateToURL(shell(), url));
// TODO(crbug.com/40844856): Investigate why this does not return
// true.
ASSERT_TRUE(waiter.WaitForNotification());
}
void SendTouchExplorationEvent(int x, int y) {
aura::Window* window = shell()->web_contents()->GetContentNativeView();
ui::EventSink* sink = window->GetHost()->GetEventSink();
gfx::Rect bounds = window->GetBoundsInRootWindow();
gfx::Point location(bounds.x() + x, bounds.y() + y);
int flags = ui::EF_TOUCH_ACCESSIBILITY;
std::unique_ptr<ui::Event> mouse_move_event(
new ui::MouseEvent(ui::EventType::kMouseMoved, location, location,
ui::EventTimeForNow(), flags, 0));
std::ignore = sink->OnEventFromSource(mouse_move_event.get());
}
private:
std::optional<ScopedAccessibilityModeOverride> accessibility_mode_;
};
IN_PROC_BROWSER_TEST_F(TouchAccessibilityBrowserTest,
TouchExplorationSendsHoverEvents) {
// Create HTML with a 7 x 5 table, each exactly 50 x 50 pixels.
std::string html_url =
"data:text/html,"
"<style>"
" body { margin: 0; }"
" table { border-spacing: 0; border-collapse: collapse; }"
" td { width: 50px; height: 50px; padding: 0; }"
"</style>"
"<body>"
"<table>";
int cell = 0;
for (int row = 0; row < 5; ++row) {
html_url += "<tr>";
for (int col = 0; col < 7; ++col) {
html_url += "<td>" + base::NumberToString(cell) + "</td>";
++cell;
}
html_url += "</tr>";
}
html_url += "</table></body>";
NavigateToUrlAndWaitForAccessibilityTree(GURL(html_url));
// Get the BrowserAccessibilityManager.
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
ui::BrowserAccessibilityManager* manager =
web_contents->GetRootBrowserAccessibilityManager();
ASSERT_NE(nullptr, manager);
// Loop over all of the cells in the table. For each one, send a simulated
// touch exploration event in the center of that cell, and assert that we
// get an accessibility hover event fired in the correct cell.
AccessibilityNotificationWaiter waiter(shell()->web_contents(),
ax::mojom::Event::kHover);
for (int row = 0; row < 5; ++row) {
for (int col = 0; col < 7; ++col) {
std::string expected_cell_text = base::NumberToString(row * 7 + col);
VLOG(1) << "Sending event in row " << row << " col " << col
<< " with text " << expected_cell_text;
SendTouchExplorationEvent(50 * col + 25, 50 * row + 25);
// Wait until we get a hover event in the expected grid cell.
// Tolerate additional events, keep looping until we get the expected one.
std::string cell_text;
do {
ASSERT_TRUE(waiter.WaitForNotification());
int target_id = waiter.event_target_id();
ui::BrowserAccessibility* hit = manager->GetFromID(target_id);
ui::BrowserAccessibility* child = hit->PlatformGetChild(0);
ASSERT_NE(nullptr, child);
cell_text =
child->GetStringAttribute(ax::mojom::StringAttribute::kName);
VLOG(1) << "Got hover event in cell with text: " << cell_text;
} while (cell_text != expected_cell_text);
}
}
}
IN_PROC_BROWSER_TEST_F(TouchAccessibilityBrowserTest,
TouchExplorationInIframe) {
NavigateToUrlAndWaitForAccessibilityTree(embedded_test_server()->GetURL(
"/accessibility/html/iframe-coordinates.html"));
WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(),
"Ordinary Button");
// Get the BrowserAccessibilityManager for the first child frame.
RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame());
RenderFrameHostImpl* child_frame =
main_frame->frame_tree_node()->child_at(0)->current_frame_host();
ui::BrowserAccessibilityManager* child_manager =
child_frame->GetOrCreateBrowserAccessibilityManager();
ASSERT_NE(nullptr, child_manager);
// Send a touch exploration event to the button in the first iframe.
// A touch exploration event is just a mouse move event with
// the ui::EF_TOUCH_ACCESSIBILITY flag set.
AccessibilityNotificationWaiter waiter(shell()->web_contents(),
ax::mojom::Event::kHover);
SendTouchExplorationEvent(50, 350);
ASSERT_TRUE(waiter.WaitForNotification());
int target_id = waiter.event_target_id();
ui::BrowserAccessibility* hit = child_manager->GetFromID(target_id);
EXPECT_EQ(ax::mojom::Role::kButton, hit->GetRole());
std::string text = hit->GetStringAttribute(ax::mojom::StringAttribute::kName);
EXPECT_EQ("Ordinary Button", text);
}
IN_PROC_BROWSER_TEST_F(TouchAccessibilityBrowserTest,
TouchExplorationInCrossSiteIframe) {
NavigateToUrlAndWaitForAccessibilityTree(embedded_test_server()->GetURL(
"/accessibility/html/iframe-coordinates-cross-process.html"));
WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(),
"Ordinary Button");
// Get the BrowserAccessibilityManager for the first child frame.
RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame());
RenderFrameHostImpl* child_frame =
main_frame->frame_tree_node()->child_at(0)->current_frame_host();
ui::BrowserAccessibilityManager* child_manager =
child_frame->GetOrCreateBrowserAccessibilityManager();
ASSERT_NE(nullptr, child_manager);
// If OOPIFs are enabled, wait until hit testing data is ready, otherwise the
// touch event will not get sent to the correct renderer process. However the
// |child_frame| being used here is not actually a
// RenderWidgetHostViewChildFrame.
WaitForHitTestData(child_frame);
// Send a touch exploration event to the button in the first iframe.
// A touch exploration event is just a mouse move event with
// the ui::EF_TOUCH_ACCESSIBILITY flag set.
AccessibilityNotificationWaiter waiter(shell()->web_contents(),
ax::mojom::Event::kHover);
SendTouchExplorationEvent(50, 350);
ASSERT_TRUE(waiter.WaitForNotification());
int target_id = waiter.event_target_id();
ui::BrowserAccessibility* hit = child_manager->GetFromID(target_id);
EXPECT_EQ(ax::mojom::Role::kButton, hit->GetRole());
std::string text = hit->GetStringAttribute(ax::mojom::StringAttribute::kName);
EXPECT_EQ("Ordinary Button", text);
}
} // namespace content
|