blob: 84781f03141e691aed70665265230f0fbd43441e [file] [log] [blame]
lfg84763c92017-02-16 18:55:151// Copyright 2017 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/pointer_lock_browsertest.h"
6
7#include "content/browser/frame_host/frame_tree.h"
8#include "content/browser/renderer_host/render_widget_host_impl.h"
9#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
10#include "content/browser/web_contents/web_contents_impl.h"
11#include "content/public/browser/web_contents_delegate.h"
12#include "content/public/test/browser_test_utils.h"
13#include "content/public/test/content_browser_test.h"
14#include "content/public/test/content_browser_test_utils.h"
15#include "content/public/test/test_utils.h"
16#include "content/shell/browser/shell.h"
17#include "content/test/content_browser_test_utils_internal.h"
18#include "net/dns/mock_host_resolver.h"
19#include "net/test/embedded_test_server/embedded_test_server.h"
20
21#ifdef USE_AURA
22#include "content/browser/renderer_host/render_widget_host_view_aura.h"
23#include "content/browser/web_contents/web_contents_view_aura.h"
24#endif // USE_AURA
25
26namespace content {
27
28namespace {
29
30#ifdef USE_AURA
31class MockRenderWidgetHostView : public RenderWidgetHostViewAura {
32 public:
33 MockRenderWidgetHostView(RenderWidgetHost* host, bool is_guest_view_hack)
Fady Samuel04b41242017-09-27 09:25:0034 : RenderWidgetHostViewAura(host,
35 is_guest_view_hack,
Scott Violetc884c7fd2017-11-03 23:51:0136 false /* is_mus_browser_plugin_guest */),
lfg84763c92017-02-16 18:55:1537 host_(RenderWidgetHostImpl::From(host)) {}
38 ~MockRenderWidgetHostView() override {
39 if (mouse_locked_)
40 UnlockMouse();
41 }
42
43 bool LockMouse() override {
44 mouse_locked_ = true;
45 return true;
46 }
47
48 void UnlockMouse() override {
49 host_->LostMouseLock();
50 mouse_locked_ = false;
51 }
52
53 bool IsMouseLocked() override { return mouse_locked_; }
54
55 bool HasFocus() const override { return true; }
56
57 void OnWindowFocused(aura::Window* gained_focus,
58 aura::Window* lost_focus) override {
59 // Ignore window focus events.
60 }
61
62 RenderWidgetHostImpl* host_;
63};
64#endif // USE_AURA
65
66} // namespace
67
68class MockPointerLockWebContentsDelegate : public WebContentsDelegate {
69 public:
70 MockPointerLockWebContentsDelegate() {}
71 ~MockPointerLockWebContentsDelegate() override {}
72
73 void RequestToLockMouse(WebContents* web_contents,
74 bool user_gesture,
75 bool last_unlocked_by_target) override {
76 web_contents->GotResponseToLockMouseRequest(true);
77 }
78
79 void LostMouseLock() override {}
80};
81
82#ifdef USE_AURA
83void InstallCreateHooksForPointerLockBrowserTests() {
84 WebContentsViewAura::InstallCreateHookForTests(
85 [](RenderWidgetHost* host,
86 bool is_guest_view_hack) -> RenderWidgetHostViewAura* {
87 return new MockRenderWidgetHostView(host, is_guest_view_hack);
88 });
89}
90#endif // USE_AURA
91
92class PointerLockBrowserTest : public ContentBrowserTest {
93 public:
94 PointerLockBrowserTest() {}
95
96 protected:
97 void SetUpCommandLine(base::CommandLine* command_line) override {
98 IsolateAllSitesForTesting(command_line);
99 }
100
101 void SetUp() override {
102 InstallCreateHooksForPointerLockBrowserTests();
103 ContentBrowserTest::SetUp();
104 }
105
106 void SetUpOnMainThread() override {
107 host_resolver()->AddRule("*", "127.0.0.1");
108 SetupCrossSiteRedirector(embedded_test_server());
109 ASSERT_TRUE(embedded_test_server()->Start());
110
111 web_contents()->SetDelegate(&web_contents_delegate_);
112 }
113
114 WebContentsImpl* web_contents() const {
115 return static_cast<WebContentsImpl*>(shell()->web_contents());
116 }
117
118 private:
119 MockPointerLockWebContentsDelegate web_contents_delegate_;
120};
121
122IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLock) {
123 GURL main_url(embedded_test_server()->GetURL(
124 "a.com", "/cross_site_iframe_factory.html?a(b)"));
125 EXPECT_TRUE(NavigateToURL(shell(), main_url));
126
127 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
128 FrameTreeNode* child = root->child_at(0);
129
130 // Request a pointer lock on the root frame's body.
131 EXPECT_TRUE(ExecuteScript(root, "document.body.requestPointerLock()"));
132
133 // Root frame should have been granted pointer lock.
134 bool locked = false;
135 EXPECT_TRUE(ExecuteScriptAndExtractBool(root,
136 "window.domAutomationController.send("
137 "document.pointerLockElement == "
138 "document.body);",
139 &locked));
140 EXPECT_TRUE(locked);
141
142 // Request a pointer lock on the child frame's body.
143 EXPECT_TRUE(ExecuteScript(child, "document.body.requestPointerLock()"));
144
145 // Child frame should not be granted pointer lock since the root frame has it.
146 EXPECT_TRUE(ExecuteScriptAndExtractBool(child,
147 "window.domAutomationController.send("
148 "document.pointerLockElement == "
149 "document.body);",
150 &locked));
151 EXPECT_FALSE(locked);
152
153 // Release pointer lock on root frame.
154 EXPECT_TRUE(ExecuteScript(root, "document.exitPointerLock()"));
155
156 // Request a pointer lock on the child frame's body.
157 EXPECT_TRUE(ExecuteScript(child, "document.body.requestPointerLock()"));
158
159 // Child frame should have been granted pointer lock.
160 EXPECT_TRUE(ExecuteScriptAndExtractBool(child,
161 "window.domAutomationController.send("
162 "document.pointerLockElement == "
163 "document.body);",
164 &locked));
165 EXPECT_TRUE(locked);
166}
167
168IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockEventRouting) {
169 GURL main_url(embedded_test_server()->GetURL(
170 "a.com", "/cross_site_iframe_factory.html?a(b)"));
171 EXPECT_TRUE(NavigateToURL(shell(), main_url));
172
173 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
174 FrameTreeNode* child = root->child_at(0);
175 RenderWidgetHostInputEventRouter* router =
176 web_contents()->GetInputEventRouter();
177 RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
178 root->current_frame_host()->GetView());
179 RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>(
180 child->current_frame_host()->GetView());
181
Ken Buchanan8a319fb2017-11-15 18:37:12182 WaitForChildFrameSurfaceReady(child->current_frame_host());
183
lfg84763c92017-02-16 18:55:15184 // Request a pointer lock on the root frame's body.
185 EXPECT_TRUE(ExecuteScript(root, "document.body.requestPointerLock()"));
186
187 // Root frame should have been granted pointer lock.
188 bool locked = false;
189 EXPECT_TRUE(ExecuteScriptAndExtractBool(root,
190 "window.domAutomationController.send("
191 "document.pointerLockElement == "
192 "document.body);",
193 &locked));
194 EXPECT_TRUE(locked);
195
196 // Add a mouse move event listener to the root frame.
197 EXPECT_TRUE(ExecuteScript(
198 root,
199 "var x; var y; var mX; var mY; document.addEventListener('mousemove', "
200 "function(e) {x = e.x; y = e.y; mX = e.movementX; mY = e.movementY;});"));
201
Daniel Cheng93c80a92018-02-14 19:02:43202 blink::WebMouseEvent mouse_event(
203 blink::WebInputEvent::kMouseMove, blink::WebInputEvent::kNoModifiers,
204 blink::WebInputEvent::GetStaticTimeStampForTests());
Blink Reformat1c4d759e2017-04-09 16:34:54205 mouse_event.SetPositionInWidget(10, 11);
206 mouse_event.movement_x = 12;
207 mouse_event.movement_y = 13;
lfg84763c92017-02-16 18:55:15208 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
209
210 // Make sure that the renderer handled the input event.
211 MainThreadFrameObserver root_observer(root_view->GetRenderWidgetHost());
212 root_observer.Wait();
213
214 int x, y, movementX, movementY;
215 EXPECT_TRUE(ExecuteScriptAndExtractInt(
216 root, "window.domAutomationController.send(x);", &x));
217 EXPECT_TRUE(ExecuteScriptAndExtractInt(
218 root, "window.domAutomationController.send(y);", &y));
219 EXPECT_TRUE(ExecuteScriptAndExtractInt(
220 root, "window.domAutomationController.send(mX);", &movementX));
221 EXPECT_TRUE(ExecuteScriptAndExtractInt(
222 root, "window.domAutomationController.send(mY);", &movementY));
223 EXPECT_EQ(10, x);
224 EXPECT_EQ(11, y);
225 EXPECT_EQ(12, movementX);
226 EXPECT_EQ(13, movementY);
227
228 // Release pointer lock on root frame.
229 EXPECT_TRUE(ExecuteScript(root, "document.exitPointerLock()"));
230
231 // Request a pointer lock on the child frame's body.
232 EXPECT_TRUE(ExecuteScript(child, "document.body.requestPointerLock()"));
233
234 // Child frame should have been granted pointer lock.
235 EXPECT_TRUE(ExecuteScriptAndExtractBool(child,
236 "window.domAutomationController.send("
237 "document.pointerLockElement == "
238 "document.body);",
239 &locked));
240 EXPECT_TRUE(locked);
241
242 // Add a mouse move event listener to the child frame.
243 EXPECT_TRUE(ExecuteScript(
244 child,
245 "var x; var y; var mX; var mY; document.addEventListener('mousemove', "
246 "function(e) {x = e.x; y = e.y; mX = e.movementX; mY = e.movementY;});"));
247
Ella Ge43cccf172017-10-19 15:44:16248 gfx::PointF transformed_point;
249 root_view->TransformPointToCoordSpaceForView(gfx::PointF(0, 0), child_view,
lfg84763c92017-02-16 18:55:15250 &transformed_point);
251
Blink Reformat1c4d759e2017-04-09 16:34:54252 mouse_event.SetPositionInWidget(-transformed_point.x() + 14,
mustaqc51f3aab2017-04-05 15:43:11253 -transformed_point.y() + 15);
Blink Reformat1c4d759e2017-04-09 16:34:54254 mouse_event.movement_x = 16;
255 mouse_event.movement_y = 17;
lfg84763c92017-02-16 18:55:15256 // We use root_view intentionally as the RenderWidgetHostInputEventRouter is
257 // responsible for correctly routing the event to the child frame.
258 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
259
260 // Make sure that the renderer handled the input event.
261 MainThreadFrameObserver child_observer(child_view->GetRenderWidgetHost());
262 child_observer.Wait();
263
264 EXPECT_TRUE(ExecuteScriptAndExtractInt(
265 child, "window.domAutomationController.send(x);", &x));
266 EXPECT_TRUE(ExecuteScriptAndExtractInt(
267 child, "window.domAutomationController.send(y);", &y));
268 EXPECT_TRUE(ExecuteScriptAndExtractInt(
269 child, "window.domAutomationController.send(mX);", &movementX));
270 EXPECT_TRUE(ExecuteScriptAndExtractInt(
271 child, "window.domAutomationController.send(mY);", &movementY));
272 EXPECT_EQ(14, x);
273 EXPECT_EQ(15, y);
274 EXPECT_EQ(16, movementX);
275 EXPECT_EQ(17, movementY);
276}
277
lfg7d4caad2017-03-22 09:01:45278// Tests that the browser will not unlock the pointer if a RenderWidgetHostView
279// that doesn't hold the pointer lock is destroyed.
lfgde6f0d1f2017-03-24 15:29:51280IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockChildFrameDetached) {
lfg7d4caad2017-03-22 09:01:45281 GURL main_url(embedded_test_server()->GetURL(
282 "a.com", "/cross_site_iframe_factory.html?a(b)"));
283 EXPECT_TRUE(NavigateToURL(shell(), main_url));
284
285 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
286
287 // Request a pointer lock on the root frame's body.
288 EXPECT_TRUE(ExecuteScript(root, "document.body.requestPointerLock()"));
289
290 // Root frame should have been granted pointer lock.
291 bool locked = false;
292 EXPECT_TRUE(ExecuteScriptAndExtractBool(root,
293 "window.domAutomationController.send("
294 "document.pointerLockElement == "
295 "document.body);",
296 &locked));
297 EXPECT_TRUE(locked);
298
299 // Root (platform) RenderWidgetHostView should have the pointer locked.
300 EXPECT_TRUE(root->current_frame_host()->GetView()->IsMouseLocked());
301 EXPECT_EQ(root->current_frame_host()->GetRenderWidgetHost(),
302 web_contents()->GetMouseLockWidget());
303
304 // Detach the child frame.
305 EXPECT_TRUE(ExecuteScript(root, "document.querySelector('iframe').remove()"));
306
307 // Root (platform) RenderWidgetHostView should still have the pointer locked.
308 EXPECT_TRUE(root->current_frame_host()->GetView()->IsMouseLocked());
309 EXPECT_EQ(root->current_frame_host()->GetRenderWidgetHost(),
310 web_contents()->GetMouseLockWidget());
311}
312
lfg9a467e12017-03-23 21:14:36313IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockWheelEventRouting) {
314 GURL main_url(embedded_test_server()->GetURL(
315 "a.com", "/cross_site_iframe_factory.html?a(b)"));
316 EXPECT_TRUE(NavigateToURL(shell(), main_url));
317
318 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
319 FrameTreeNode* child = root->child_at(0);
320 RenderWidgetHostInputEventRouter* router =
321 web_contents()->GetInputEventRouter();
322 RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
323 root->current_frame_host()->GetView());
324 RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>(
325 child->current_frame_host()->GetView());
326
Ken Buchanan8a319fb2017-11-15 18:37:12327 WaitForChildFrameSurfaceReady(child->current_frame_host());
328
lfg9a467e12017-03-23 21:14:36329 // Request a pointer lock on the root frame's body.
330 EXPECT_TRUE(ExecuteScript(root, "document.body.requestPointerLock()"));
331
332 // Root frame should have been granted pointer lock.
333 bool locked = false;
334 EXPECT_TRUE(ExecuteScriptAndExtractBool(root,
335 "window.domAutomationController.send("
336 "document.pointerLockElement == "
337 "document.body);",
338 &locked));
339 EXPECT_TRUE(locked);
340
341 // Add a mouse move wheel event listener to the root frame.
342 EXPECT_TRUE(ExecuteScript(
343 root,
344 "var x; var y; var mX; var mY; document.addEventListener('mousewheel', "
345 "function(e) {x = e.x; y = e.y; dX = e.deltaX; dY = e.deltaY;});"));
346 MainThreadFrameObserver root_observer(root_view->GetRenderWidgetHost());
347 root_observer.Wait();
348
349 blink::WebMouseWheelEvent wheel_event(
Blink Reformat1c4d759e2017-04-09 16:34:54350 blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
Daniel Cheng93c80a92018-02-14 19:02:43351 blink::WebInputEvent::GetStaticTimeStampForTests());
Blink Reformat1c4d759e2017-04-09 16:34:54352 wheel_event.SetPositionInWidget(10, 11);
353 wheel_event.delta_x = -12;
354 wheel_event.delta_y = -13;
sahel41942462017-07-06 14:14:37355 wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
lfg9a467e12017-03-23 21:14:36356 router->RouteMouseWheelEvent(root_view, &wheel_event, ui::LatencyInfo());
357
358 // Make sure that the renderer handled the input event.
359 root_observer.Wait();
360
sahel955c3292017-08-17 14:56:44361 if (root_view->wheel_scroll_latching_enabled()) {
362 // When wheel scroll latching is enabled all wheel events during a scroll
363 // sequence will be sent to a single target. Send a wheel end event to the
364 // current target before sending wheel events to a new target.
365 wheel_event.delta_x = 0;
366 wheel_event.delta_y = 0;
367 wheel_event.phase = blink::WebMouseWheelEvent::kPhaseEnded;
368 router->RouteMouseWheelEvent(root_view, &wheel_event, ui::LatencyInfo());
369
370 // Make sure that the renderer handled the input event.
371 root_observer.Wait();
372 }
373
lfg9a467e12017-03-23 21:14:36374 int x, y, deltaX, deltaY;
375 EXPECT_TRUE(ExecuteScriptAndExtractInt(
376 root, "window.domAutomationController.send(x);", &x));
377 EXPECT_TRUE(ExecuteScriptAndExtractInt(
378 root, "window.domAutomationController.send(y);", &y));
379 EXPECT_TRUE(ExecuteScriptAndExtractInt(
380 root, "window.domAutomationController.send(dX);", &deltaX));
381 EXPECT_TRUE(ExecuteScriptAndExtractInt(
382 root, "window.domAutomationController.send(dY);", &deltaY));
383 EXPECT_EQ(10, x);
384 EXPECT_EQ(11, y);
385 EXPECT_EQ(12, deltaX);
386 EXPECT_EQ(13, deltaY);
387
388 // Release pointer lock on root frame.
389 EXPECT_TRUE(ExecuteScript(root, "document.exitPointerLock()"));
390
391 // Request a pointer lock on the child frame's body.
392 EXPECT_TRUE(ExecuteScript(child, "document.body.requestPointerLock()"));
393
394 // Child frame should have been granted pointer lock.
395 EXPECT_TRUE(ExecuteScriptAndExtractBool(child,
396 "window.domAutomationController.send("
397 "document.pointerLockElement == "
398 "document.body);",
399 &locked));
400 EXPECT_TRUE(locked);
401
402 // Add a mouse move event listener to the child frame.
403 EXPECT_TRUE(ExecuteScript(
404 child,
405 "var x; var y; var mX; var mY; document.addEventListener('mousewheel', "
406 "function(e) {x = e.x; y = e.y; dX = e.deltaX; dY = e.deltaY;});"));
407 MainThreadFrameObserver child_observer(child_view->GetRenderWidgetHost());
408 child_observer.Wait();
409
Ella Ge43cccf172017-10-19 15:44:16410 gfx::PointF transformed_point;
411 root_view->TransformPointToCoordSpaceForView(gfx::PointF(0, 0), child_view,
lfg9a467e12017-03-23 21:14:36412 &transformed_point);
413
Blink Reformat1c4d759e2017-04-09 16:34:54414 wheel_event.SetPositionInWidget(-transformed_point.x() + 14,
mustaqc51f3aab2017-04-05 15:43:11415 -transformed_point.y() + 15);
Blink Reformat1c4d759e2017-04-09 16:34:54416 wheel_event.delta_x = -16;
417 wheel_event.delta_y = -17;
sahel955c3292017-08-17 14:56:44418 if (root_view->wheel_scroll_latching_enabled())
419 wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
lfg9a467e12017-03-23 21:14:36420 // We use root_view intentionally as the RenderWidgetHostInputEventRouter is
421 // responsible for correctly routing the event to the child frame.
422 router->RouteMouseWheelEvent(root_view, &wheel_event, ui::LatencyInfo());
423
424 // Make sure that the renderer handled the input event.
425 child_observer.Wait();
426
427 EXPECT_TRUE(ExecuteScriptAndExtractInt(
428 child, "window.domAutomationController.send(x);", &x));
429 EXPECT_TRUE(ExecuteScriptAndExtractInt(
430 child, "window.domAutomationController.send(y);", &y));
431 EXPECT_TRUE(ExecuteScriptAndExtractInt(
432 child, "window.domAutomationController.send(dX);", &deltaX));
433 EXPECT_TRUE(ExecuteScriptAndExtractInt(
434 child, "window.domAutomationController.send(dY);", &deltaY));
435 EXPECT_EQ(14, x);
436 EXPECT_EQ(15, y);
437 EXPECT_EQ(16, deltaX);
438 EXPECT_EQ(17, deltaY);
439}
440
lfg84763c92017-02-16 18:55:15441} // namespace content