blob: b538e6aca72e4c56ab1b15574fc2dbe17a7f0657 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2017 The Chromium Authors
lfg84763c92017-02-16 18:55:152// 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
Kartar Singhb1bfa1a2024-06-24 13:14:577#include <string>
8
Keishi Hattori0e45c022021-11-27 09:25:529#include "base/memory/raw_ptr.h"
Ella Ge3fa5c3f2019-09-05 19:00:0610#include "base/test/scoped_feature_list.h"
Scott Littlef0fd9842019-08-09 19:56:5111#include "build/build_config.h"
Kartar Singhb1bfa1a2024-06-24 13:14:5712#include "components/input/render_widget_host_input_event_router.h"
danakj10f32372020-09-15 22:25:1613#include "content/browser/renderer_host/frame_tree.h"
lfg84763c92017-02-16 18:55:1514#include "content/browser/renderer_host/render_widget_host_impl.h"
lfg84763c92017-02-16 18:55:1515#include "content/browser/web_contents/web_contents_impl.h"
Arthur Sonzognibdeca8e2023-09-11 08:32:1216#include "content/common/features.h"
lfg84763c92017-02-16 18:55:1517#include "content/public/browser/web_contents_delegate.h"
Peter Kasting919ce652020-05-07 10:22:3618#include "content/public/test/browser_test.h"
lfg84763c92017-02-16 18:55:1519#include "content/public/test/browser_test_utils.h"
20#include "content/public/test/content_browser_test.h"
21#include "content/public/test/content_browser_test_utils.h"
jonross09d21de2018-06-13 12:31:3622#include "content/public/test/hit_test_region_observer.h"
lfg84763c92017-02-16 18:55:1523#include "content/public/test/test_utils.h"
24#include "content/shell/browser/shell.h"
25#include "content/test/content_browser_test_utils_internal.h"
26#include "net/dns/mock_host_resolver.h"
27#include "net/test/embedded_test_server/embedded_test_server.h"
Ella Ge3fa5c3f2019-09-05 19:00:0628#include "ui/base/ui_base_features.h"
lfg84763c92017-02-16 18:55:1529
30#ifdef USE_AURA
31#include "content/browser/renderer_host/render_widget_host_view_aura.h"
32#include "content/browser/web_contents/web_contents_view_aura.h"
33#endif // USE_AURA
Abdelrahman Eed180a7622025-06-13 11:39:5434#if BUILDFLAG(IS_ANDROID)
35#include "content/browser/renderer_host/render_widget_host_view_android.h"
36#include "content/browser/web_contents/web_contents_view_android.h"
37#endif // BUILDFLAG(IS_ANDROID)
lfg84763c92017-02-16 18:55:1538
39namespace content {
40
lfg84763c92017-02-16 18:55:1541class MockPointerLockWebContentsDelegate : public WebContentsDelegate {
42 public:
43 MockPointerLockWebContentsDelegate() {}
44 ~MockPointerLockWebContentsDelegate() override {}
45
Takumi Fujimoto4661871d2024-01-25 02:04:1846 void RequestPointerLock(WebContents* web_contents,
lfg84763c92017-02-16 18:55:1547 bool user_gesture,
48 bool last_unlocked_by_target) override {
James Hollyerd5c9de462020-03-10 19:02:4549 if (user_gesture)
Takumi Fujimoto4661871d2024-01-25 02:04:1850 web_contents->GotResponseToPointerLockRequest(
James Hollyerd5c9de462020-03-10 19:02:4551 blink::mojom::PointerLockResult::kSuccess);
52 else
Takumi Fujimoto4661871d2024-01-25 02:04:1853 web_contents->GotResponseToPointerLockRequest(
James Hollyerd5c9de462020-03-10 19:02:4554 blink::mojom::PointerLockResult::kRequiresUserGesture);
lfg84763c92017-02-16 18:55:1555 }
56
Takumi Fujimoto4661871d2024-01-25 02:04:1857 void LostPointerLock() override {}
lfg84763c92017-02-16 18:55:1558};
59
60#ifdef USE_AURA
Ella Gedd3c80b42019-09-25 01:17:5761class ScopedEnableUnadjustedMouseEventsForTesting
62 : public aura::ScopedEnableUnadjustedMouseEvents {
63 public:
64 explicit ScopedEnableUnadjustedMouseEventsForTesting() {}
65 ~ScopedEnableUnadjustedMouseEventsForTesting() override {}
66};
67
Ella Geb15a7572018-06-25 17:03:5068class MockPointerLockRenderWidgetHostView : public RenderWidgetHostViewAura {
69 public:
W. James MacLean13d834d2019-12-04 16:06:4570 MockPointerLockRenderWidgetHostView(RenderWidgetHost* host)
71 : RenderWidgetHostViewAura(host),
Ella Geb15a7572018-06-25 17:03:5072 host_(RenderWidgetHostImpl::From(host)) {}
73 ~MockPointerLockRenderWidgetHostView() override {
Takumi Fujimoto4661871d2024-01-25 02:04:1874 if (IsPointerLocked()) {
75 UnlockPointer();
76 }
Ella Geb15a7572018-06-25 17:03:5077 }
78
Takumi Fujimoto4661871d2024-01-25 02:04:1879 blink::mojom::PointerLockResult LockPointer(
James Hollyerd5c9de462020-03-10 19:02:4580 bool request_unadjusted_movement) override {
Ella Geb15a7572018-06-25 17:03:5081 event_handler()->mouse_locked_ = true;
Ella Ge3fa5c3f2019-09-05 19:00:0682 event_handler()->mouse_locked_unadjusted_movement_ =
Ella Gedd3c80b42019-09-25 01:17:5783 request_unadjusted_movement
84 ? std::make_unique<ScopedEnableUnadjustedMouseEventsForTesting>()
85 : nullptr;
James Hollyerd5c9de462020-03-10 19:02:4586 return blink::mojom::PointerLockResult::kSuccess;
Ella Geb15a7572018-06-25 17:03:5087 }
88
Takumi Fujimoto4661871d2024-01-25 02:04:1889 void UnlockPointer() override {
90 host_->LostPointerLock();
Ella Geb15a7572018-06-25 17:03:5091 event_handler()->mouse_locked_ = false;
Ella Gedd3c80b42019-09-25 01:17:5792 event_handler()->mouse_locked_unadjusted_movement_.reset();
Ella Geb15a7572018-06-25 17:03:5093 }
94
Takumi Fujimoto4661871d2024-01-25 02:04:1895 bool GetIsPointerLockedUnadjustedMovementForTesting() override {
96 return IsPointerLocked() &&
jameshollyer48a702f2020-04-11 04:21:4097 event_handler()->mouse_locked_unadjusted_movement_;
98 }
Ella Geb15a7572018-06-25 17:03:5099
100 void OnWindowFocused(aura::Window* gained_focus,
101 aura::Window* lost_focus) override {
102 // Ignore window focus events.
103 }
104
Takumi Fujimoto4661871d2024-01-25 02:04:18105 bool IsPointerLocked() override { return event_handler()->mouse_locked(); }
jameshollyer48a702f2020-04-11 04:21:40106
107 bool HasFocus() override { return has_focus_; }
Ella Ge3fa5c3f2019-09-05 19:00:06108
Keishi Hattori0e45c022021-11-27 09:25:52109 raw_ptr<RenderWidgetHostImpl> host_;
jameshollyer48a702f2020-04-11 04:21:40110 bool has_focus_ = true;
Ella Geb15a7572018-06-25 17:03:50111};
112
lfg84763c92017-02-16 18:55:15113void InstallCreateHooksForPointerLockBrowserTests() {
114 WebContentsViewAura::InstallCreateHookForTests(
W. James MacLean13d834d2019-12-04 16:06:45115 [](RenderWidgetHost* host) -> RenderWidgetHostViewAura* {
116 return new MockPointerLockRenderWidgetHostView(host);
lfg84763c92017-02-16 18:55:15117 });
118}
119#endif // USE_AURA
Abdelrahman Eed180a7622025-06-13 11:39:54120#if BUILDFLAG(IS_ANDROID)
121class MockPointerLockRenderWidgetHostView : public RenderWidgetHostViewAndroid {
122 public:
123 MockPointerLockRenderWidgetHostView(RenderWidgetHostImpl* rwhi,
124 gfx::NativeView parent_native_view,
125 cc::slim::Layer* parent_layer)
126 : RenderWidgetHostViewAndroid(rwhi, parent_native_view, parent_layer) {}
127 ~MockPointerLockRenderWidgetHostView() override {
128 if (pointer_locked_) {
129 UnlockPointer();
130 }
131 }
132
133 blink::mojom::PointerLockResult LockPointer(
134 bool request_unadjusted_movement) override {
135 pointer_locked_ = true;
136
137 return blink::mojom::PointerLockResult::kSuccess;
138 }
139
140 blink::mojom::PointerLockResult ChangePointerLock(
141 bool request_unadjusted_movement) override {
142 return blink::mojom::PointerLockResult::kSuccess;
143 }
144
145 void UnlockPointer() override {
146 if (RenderWidgetHostImpl* host =
147 RenderWidgetHostImpl::From(GetRenderWidgetHost())) {
148 host->LostPointerLock();
149 }
150 pointer_locked_ = false;
151 }
152
153 bool IsPointerLocked() override { return pointer_locked_; }
154
155 bool HasFocus() override { return true; }
156 bool CanBePointerLocked() override { return true; }
157
158 private:
159 bool pointer_locked_ = false;
160};
161
162void InstallCreateHooksForPointerLockBrowserTests() {
163 WebContentsViewAndroid::InstallCreateHookForTests(
164 [](RenderWidgetHostImpl* rwhi, gfx::NativeView parent_native_view,
165 cc::slim::Layer* parent_layer) -> RenderWidgetHostViewAndroid* {
166 return new MockPointerLockRenderWidgetHostView(rwhi, parent_native_view,
167 parent_layer);
168 });
169}
170#endif // BUILDFLAG(IS_ANDROID)
lfg84763c92017-02-16 18:55:15171
172class PointerLockBrowserTest : public ContentBrowserTest {
173 public:
174 PointerLockBrowserTest() {}
175
176 protected:
177 void SetUpCommandLine(base::CommandLine* command_line) override {
178 IsolateAllSitesForTesting(command_line);
179 }
180
181 void SetUp() override {
182 InstallCreateHooksForPointerLockBrowserTests();
183 ContentBrowserTest::SetUp();
184 }
185
186 void SetUpOnMainThread() override {
187 host_resolver()->AddRule("*", "127.0.0.1");
188 SetupCrossSiteRedirector(embedded_test_server());
189 ASSERT_TRUE(embedded_test_server()->Start());
190
191 web_contents()->SetDelegate(&web_contents_delegate_);
192 }
193
194 WebContentsImpl* web_contents() const {
195 return static_cast<WebContentsImpl*>(shell()->web_contents());
196 }
197
Nick Carterd73635b2018-03-13 18:31:41198 protected:
lfg84763c92017-02-16 18:55:15199 MockPointerLockWebContentsDelegate web_contents_delegate_;
200};
201
Liviu Tinta7f8bdfb2020-05-05 23:35:26202namespace {
203class PointerLockHelper {
204 public:
205 // requestPointerLock is an asynchronous operation. This method returns when
206 // document.body.requestPointerLock() either succeeds or fails.
207 // Returns true if Pointer Lock on body was successful.
208 static EvalJsResult RequestPointerLockOnBody(
209 const ToRenderFrameHost& execution_target,
210 const int options = EXECUTE_SCRIPT_DEFAULT_OPTIONS) {
211 return EvalJs(execution_target,
212 set_pointer_lock_promise_ +
213 "document.body.requestPointerLock();" +
214 wait_for_pointer_lock_promise_,
215 options);
216 }
217 static EvalJsResult RequestPointerLockWithUnadjustedMovementOnBody(
218 const ToRenderFrameHost& execution_target,
219 const int options = EXECUTE_SCRIPT_DEFAULT_OPTIONS) {
220 return EvalJs(
221 execution_target,
222 set_pointer_lock_promise_ +
223 "document.body.requestPointerLock({unadjustedMovement:true});" +
224 wait_for_pointer_lock_promise_,
225 options);
226 }
227 // exitPointerLock is an asynchronous operation. This method returns when
228 // document.exitPointerLock() either succeeds or fails.
229 // Returns true if Exit Pointer Lock was successful
230 static EvalJsResult ExitPointerLock(
231 const ToRenderFrameHost& execution_target,
232 const int options = EXECUTE_SCRIPT_DEFAULT_OPTIONS) {
233 return EvalJs(execution_target,
234 set_pointer_lock_promise_ + "document.exitPointerLock();" +
235 wait_for_pointer_lock_promise_,
236 options);
237 }
238 static EvalJsResult IsPointerLockOnBody(
239 const ToRenderFrameHost& execution_target,
240 const int options = EXECUTE_SCRIPT_DEFAULT_OPTIONS) {
241 return EvalJs(execution_target,
242 "document.pointerLockElement === document.body", options);
243 }
244
245 private:
246 static const std::string set_pointer_lock_promise_;
247 static const std::string wait_for_pointer_lock_promise_;
248};
249
250// static
251const std::string PointerLockHelper::set_pointer_lock_promise_ =
252 R"code(pointerLockPromise=new Promise(function (resolve, reject){
253 document.addEventListener('pointerlockchange', resolve);
254 document.addEventListener('pointerlockerror', reject);
255 });)code";
256// static
257const std::string PointerLockHelper::wait_for_pointer_lock_promise_ =
258 "(async()=> {return await pointerLockPromise.then(()=>true, "
259 "()=>false);})()";
260} // namespace
261
Liviu Tintacd230a52020-05-27 14:18:33262IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockBasic) {
lfg84763c92017-02-16 18:55:15263 GURL main_url(embedded_test_server()->GetURL(
264 "a.com", "/cross_site_iframe_factory.html?a(b)"));
265 EXPECT_TRUE(NavigateToURL(shell(), main_url));
266
Carlos Caballero15caeeb2021-10-27 09:57:55267 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lfg84763c92017-02-16 18:55:15268 FrameTreeNode* child = root->child_at(0);
269
270 // Request a pointer lock on the root frame's body.
Liviu Tinta7f8bdfb2020-05-05 23:35:26271 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(root));
272 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(root));
lfg84763c92017-02-16 18:55:15273
274 // Request a pointer lock on the child frame's body.
Liviu Tinta7f8bdfb2020-05-05 23:35:26275 EXPECT_EQ(false, PointerLockHelper::RequestPointerLockOnBody(child));
lfg84763c92017-02-16 18:55:15276 // Child frame should not be granted pointer lock since the root frame has it.
Liviu Tinta7f8bdfb2020-05-05 23:35:26277 EXPECT_EQ(false, PointerLockHelper::IsPointerLockOnBody(child));
lfg84763c92017-02-16 18:55:15278
279 // Release pointer lock on root frame.
Liviu Tinta7f8bdfb2020-05-05 23:35:26280 EXPECT_EQ(true, PointerLockHelper::ExitPointerLock(root));
James Hollyer5b0230f2020-03-10 03:52:26281
lfg84763c92017-02-16 18:55:15282 // Request a pointer lock on the child frame's body.
Liviu Tinta7f8bdfb2020-05-05 23:35:26283 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(child));
James Hollyer5b0230f2020-03-10 03:52:26284 // ensure request finishes before moving on.
James Hollyer5b0230f2020-03-10 03:52:26285
lfg84763c92017-02-16 18:55:15286 // Child frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26287 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(child));
lfg84763c92017-02-16 18:55:15288}
289
Liviu Tinta2775eb82020-05-28 13:33:36290IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockAndUserActivation) {
Mustaq Ahmed32a0b152019-08-06 16:01:26291 GURL main_url(embedded_test_server()->GetURL(
292 "a.com", "/cross_site_iframe_factory.html?a(b(b))"));
293 EXPECT_TRUE(NavigateToURL(shell(), main_url));
294
Carlos Caballero15caeeb2021-10-27 09:57:55295 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Mustaq Ahmed32a0b152019-08-06 16:01:26296 FrameTreeNode* child = root->child_at(0);
297 FrameTreeNode* grand_child = child->child_at(0);
298
299 // Without user activation, pointer lock request from any (child or
300 // grand_child) frame fails.
Liviu Tinta7f8bdfb2020-05-05 23:35:26301 EXPECT_EQ(false, PointerLockHelper::RequestPointerLockOnBody(
302 child, EXECUTE_SCRIPT_NO_USER_GESTURE));
303 EXPECT_EQ(false, PointerLockHelper::IsPointerLockOnBody(
304 child, EXECUTE_SCRIPT_NO_USER_GESTURE));
305
306 EXPECT_EQ(false, PointerLockHelper::RequestPointerLockOnBody(
307 grand_child, EXECUTE_SCRIPT_NO_USER_GESTURE));
308 EXPECT_EQ(false, PointerLockHelper::IsPointerLockOnBody(
309 grand_child, EXECUTE_SCRIPT_NO_USER_GESTURE));
Mustaq Ahmed32a0b152019-08-06 16:01:26310
311 // Execute a empty (dummy) JS to activate the child frame.
312 EXPECT_TRUE(ExecJs(child, ""));
313
314 // With user activation in the child frame, pointer lock from the same frame
315 // succeeds.
Liviu Tinta7f8bdfb2020-05-05 23:35:26316 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(
317 child, EXECUTE_SCRIPT_NO_USER_GESTURE));
318 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(
319 child, EXECUTE_SCRIPT_NO_USER_GESTURE));
Mustaq Ahmed32a0b152019-08-06 16:01:26320
321 // But with user activation in the child frame, pointer lock from the
322 // grand_child frame fails.
Liviu Tinta7f8bdfb2020-05-05 23:35:26323 EXPECT_EQ(false, PointerLockHelper::RequestPointerLockOnBody(
324 grand_child, EXECUTE_SCRIPT_NO_USER_GESTURE));
325 EXPECT_EQ(false, PointerLockHelper::IsPointerLockOnBody(
326 grand_child, EXECUTE_SCRIPT_NO_USER_GESTURE));
Mustaq Ahmed32a0b152019-08-06 16:01:26327}
328
Xida Chen365e7ca2021-05-25 17:39:01329// crbug.com/1210940: flaky on Linux
Xiaohan Wang1ecfd002022-01-19 22:33:10330#if BUILDFLAG(IS_LINUX)
Xida Chen365e7ca2021-05-25 17:39:01331#define MAYBE_PointerLockEventRouting DISABLED_PointerLockEventRouting
332#else
333#define MAYBE_PointerLockEventRouting PointerLockEventRouting
334#endif
335IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, MAYBE_PointerLockEventRouting) {
lfg84763c92017-02-16 18:55:15336 GURL main_url(embedded_test_server()->GetURL(
337 "a.com", "/cross_site_iframe_factory.html?a(b)"));
338 EXPECT_TRUE(NavigateToURL(shell(), main_url));
339
Carlos Caballero15caeeb2021-10-27 09:57:55340 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lfg84763c92017-02-16 18:55:15341 FrameTreeNode* child = root->child_at(0);
Kartar Singhb1bfa1a2024-06-24 13:14:57342 input::RenderWidgetHostInputEventRouter* router =
lfg84763c92017-02-16 18:55:15343 web_contents()->GetInputEventRouter();
344 RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
345 root->current_frame_host()->GetView());
346 RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>(
347 child->current_frame_host()->GetView());
348
kylechara7c549b2019-07-29 17:47:28349 WaitForHitTestData(child->current_frame_host());
Abdelrahman Eed180a7622025-06-13 11:39:54350 // Make sure that page_scale is 1, on Android page scale is not guaranteed to
351 // be 1
352 web_contents()->SetPageScale(1.0);
Ken Buchanan8a319fb2017-11-15 18:37:12353
arthursonzogni3d3ec9e2021-03-04 11:00:50354 std::string set_mouse_move_event_listener = R"(
355 mouseMoveExecuted = new Promise(function (resolve, reject) {
356 mousemoveHandler = function(e) {
357 x = e.x;
358 y = e.y;
359 mX = e.movementX;
360 mY = e.movementY;
361 resolve();
362 };
363 document.addEventListener('mousemove', mousemoveHandler, {once: true});
364 });
365 true; // A promise is defined above, but do not wait.
366 )";
367 std::string define_variables = R"(
368 var x;
369 var y;
370 var mX;
371 var mY;
372 var mouseMoveExecuted;
373 var mousemoveHandler;
374 )";
Liviu Tintacb1d46d2020-05-04 16:26:05375 // Add a mouse move event listener to the root frame.
376 EXPECT_TRUE(ExecJs(root, define_variables));
Liviu Tintad59e5072020-04-23 14:31:06377 EXPECT_TRUE(ExecJs(root, set_mouse_move_event_listener));
lfg84763c92017-02-16 18:55:15378
Ella Ge622de9b02019-03-20 21:42:43379 // Send a mouse move to root frame before lock to set last mouse position.
Daniel Cheng93c80a92018-02-14 19:02:43380 blink::WebMouseEvent mouse_event(
Dave Tapuska347d60a2020-04-21 23:55:47381 blink::WebInputEvent::Type::kMouseMove,
382 blink::WebInputEvent::kNoModifiers,
Daniel Cheng93c80a92018-02-14 19:02:43383 blink::WebInputEvent::GetStaticTimeStampForTests());
Ella Ge2ebcc1f62019-07-02 21:50:15384 mouse_event.pointer_type = blink::WebPointerProperties::PointerType::kMouse;
Ella Ge622de9b02019-03-20 21:42:43385 mouse_event.SetPositionInWidget(6, 7);
Ella Ge2ebcc1f62019-07-02 21:50:15386 mouse_event.SetPositionInScreen(6, 7);
Ella Ge622de9b02019-03-20 21:42:43387 mouse_event.movement_x = 8;
388 mouse_event.movement_y = 9;
lfg84763c92017-02-16 18:55:15389 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
390
Liviu Tintad59e5072020-04-23 14:31:06391 // wait for mouse move to fire mouse move event
392 EXPECT_EQ(true, EvalJs(root,
393 "(async ()=> {return await "
Liviu Tintacb1d46d2020-05-04 16:26:05394 "mouseMoveExecuted.then(()=>true);})();"));
Dave Tapuska9425f102024-04-19 18:18:31395 EXPECT_EQ("[6,7,0,0]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
Liviu Tinta7f8bdfb2020-05-05 23:35:26396 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(root));
Ella Ge622de9b02019-03-20 21:42:43397 // Root frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26398 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(root));
Liviu Tintad59e5072020-04-23 14:31:06399 EXPECT_TRUE(ExecJs(root, set_mouse_move_event_listener));
Ella Ge622de9b02019-03-20 21:42:43400
Ella Ge2ebcc1f62019-07-02 21:50:15401 mouse_event.SetPositionInWidget(10, 12);
402 mouse_event.SetPositionInScreen(10, 12);
Ella Ge622de9b02019-03-20 21:42:43403 mouse_event.movement_x = 12;
404 mouse_event.movement_y = 13;
405 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
406
Liviu Tintad59e5072020-04-23 14:31:06407 EXPECT_EQ(true, EvalJs(root,
408 "(async ()=> {return await "
Liviu Tintacb1d46d2020-05-04 16:26:05409 "mouseMoveExecuted.then(()=>true);})();"));
Ella Ge622de9b02019-03-20 21:42:43410 // Locked event has same coordinates as before locked.
Dave Tapuska9425f102024-04-19 18:18:31411 EXPECT_EQ("[6,7,4,5]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
lfg84763c92017-02-16 18:55:15412
Liviu Tinta7f8bdfb2020-05-05 23:35:26413 EXPECT_EQ(true, PointerLockHelper::ExitPointerLock(root));
414
415 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(child));
lfg84763c92017-02-16 18:55:15416
Liviu Tintacb1d46d2020-05-04 16:26:05417 // define all all global variables on the child
418 EXPECT_TRUE(ExecJs(child, define_variables));
lfg84763c92017-02-16 18:55:15419 // Child frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26420 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(child));
lfg84763c92017-02-16 18:55:15421
422 // Add a mouse move event listener to the child frame.
Liviu Tintad59e5072020-04-23 14:31:06423 EXPECT_TRUE(ExecJs(child, set_mouse_move_event_listener));
lfg84763c92017-02-16 18:55:15424
Ella Ge43cccf172017-10-19 15:44:16425 gfx::PointF transformed_point;
426 root_view->TransformPointToCoordSpaceForView(gfx::PointF(0, 0), child_view,
Ria Jiangbd22e55d2019-03-04 19:23:50427 &transformed_point);
Blink Reformat1c4d759e2017-04-09 16:34:54428 mouse_event.SetPositionInWidget(-transformed_point.x() + 14,
mustaqc51f3aab2017-04-05 15:43:11429 -transformed_point.y() + 15);
Ella Ge2ebcc1f62019-07-02 21:50:15430 mouse_event.SetPositionInScreen(-transformed_point.x() + 14,
431 -transformed_point.y() + 15);
Blink Reformat1c4d759e2017-04-09 16:34:54432 mouse_event.movement_x = 16;
433 mouse_event.movement_y = 17;
lfg84763c92017-02-16 18:55:15434 // We use root_view intentionally as the RenderWidgetHostInputEventRouter is
435 // responsible for correctly routing the event to the child frame.
436 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
437
Liviu Tintad59e5072020-04-23 14:31:06438 EXPECT_EQ(true, EvalJs(child,
439 "(async ()=> {return await "
440 "mouseMoveExecuted.then(()=>true);})()"));
Ella Ge622de9b02019-03-20 21:42:43441 // This is the first event to child render, so the coordinates is (0, 0)
Dave Tapuska9425f102024-04-19 18:18:31442 EXPECT_EQ("[0,0,0,0]", EvalJs(child, "JSON.stringify([x,y,mX,mY])"));
lfg84763c92017-02-16 18:55:15443}
444
lfg7d4caad2017-03-22 09:01:45445// Tests that the browser will not unlock the pointer if a RenderWidgetHostView
Liviu Tinta942fbf42020-05-28 17:04:16446// that doesn't hold the pointer lock is destroyed
447IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockChildFrameDetached) {
lfg7d4caad2017-03-22 09:01:45448 GURL main_url(embedded_test_server()->GetURL(
449 "a.com", "/cross_site_iframe_factory.html?a(b)"));
450 EXPECT_TRUE(NavigateToURL(shell(), main_url));
451
Carlos Caballero15caeeb2021-10-27 09:57:55452 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lfg7d4caad2017-03-22 09:01:45453
454 // Request a pointer lock on the root frame's body.
Liviu Tinta7f8bdfb2020-05-05 23:35:26455 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(root));
lfg7d4caad2017-03-22 09:01:45456 // Root frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26457 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(root));
lfg7d4caad2017-03-22 09:01:45458
459 // Root (platform) RenderWidgetHostView should have the pointer locked.
Takumi Fujimoto4661871d2024-01-25 02:04:18460 EXPECT_TRUE(root->current_frame_host()->GetView()->IsPointerLocked());
lfg7d4caad2017-03-22 09:01:45461 EXPECT_EQ(root->current_frame_host()->GetRenderWidgetHost(),
Takumi Fujimoto4661871d2024-01-25 02:04:18462 web_contents()->GetPointerLockWidget());
lfg7d4caad2017-03-22 09:01:45463
464 // Detach the child frame.
Nick Carterb7e71312018-08-03 23:36:13465 EXPECT_TRUE(ExecJs(root, "document.querySelector('iframe').remove()"));
lfg7d4caad2017-03-22 09:01:45466
467 // Root (platform) RenderWidgetHostView should still have the pointer locked.
Takumi Fujimoto4661871d2024-01-25 02:04:18468 EXPECT_TRUE(root->current_frame_host()->GetView()->IsPointerLocked());
lfg7d4caad2017-03-22 09:01:45469 EXPECT_EQ(root->current_frame_host()->GetRenderWidgetHost(),
Takumi Fujimoto4661871d2024-01-25 02:04:18470 web_contents()->GetPointerLockWidget());
lfg7d4caad2017-03-22 09:01:45471}
472
Nick Carterd73635b2018-03-13 18:31:41473// Tests that the browser will unlock the pointer if a RenderWidgetHostView that
474// holds the pointer lock crashes.
475IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest,
476 PointerLockInnerContentsCrashes) {
477 GURL main_url(embedded_test_server()->GetURL(
478 "a.com", "/cross_site_iframe_factory.html?a(b(b))"));
479 EXPECT_TRUE(NavigateToURL(shell(), main_url));
480
Carlos Caballero15caeeb2021-10-27 09:57:55481 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Nick Carterd73635b2018-03-13 18:31:41482
483 // Attach an inner WebContents; it's owned by the FrameTree, so we obtain an
484 // observer to it.
485 WebContents* inner_contents = CreateAndAttachInnerContents(
486 root->child_at(0)->child_at(0)->current_frame_host());
487 WebContentsDestroyedWatcher inner_death_observer(inner_contents);
488
489 // Override the delegate so that we can stub out pointer lock events.
490 inner_contents->SetDelegate(&web_contents_delegate_);
491
492 // Navigate the inner webcontents to a page.
493 EXPECT_TRUE(NavigateToURLFromRenderer(
494 inner_contents, embedded_test_server()->GetURL(
495 "c.com", "/cross_site_iframe_factory.html?c(d)")));
496
497 // Request a pointer lock to the inner WebContents's document.body.
Liviu Tinta7f8bdfb2020-05-05 23:35:26498 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(
Dave Tapuska327c06c92022-06-13 20:31:51499 inner_contents->GetPrimaryMainFrame()));
Liviu Tinta7f8bdfb2020-05-05 23:35:26500 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(
Dave Tapuska327c06c92022-06-13 20:31:51501 inner_contents->GetPrimaryMainFrame()));
Nick Carterd73635b2018-03-13 18:31:41502
503 // Root (platform) RenderWidgetHostView should have the pointer locked.
Takumi Fujimoto4661871d2024-01-25 02:04:18504 EXPECT_TRUE(root->current_frame_host()->GetView()->IsPointerLocked());
Nick Carterd73635b2018-03-13 18:31:41505
506 // The widget doing the lock is the one from the inner WebContents. A link
507 // to that RWH is saved into the outer webcontents.
508 RenderWidgetHost* expected_lock_widget =
Dave Tapuska327c06c92022-06-13 20:31:51509 inner_contents->GetPrimaryMainFrame()->GetView()->GetRenderWidgetHost();
Takumi Fujimoto4661871d2024-01-25 02:04:18510 EXPECT_EQ(expected_lock_widget, web_contents()->GetPointerLockWidget());
511 EXPECT_EQ(expected_lock_widget, web_contents()->pointer_lock_widget_);
512 EXPECT_EQ(
513 expected_lock_widget,
514 static_cast<WebContentsImpl*>(inner_contents)->pointer_lock_widget_);
Nick Carterd73635b2018-03-13 18:31:41515
516 // Crash the subframe process.
517 RenderProcessHost* crash_process =
518 root->child_at(0)->current_frame_host()->GetProcess();
519 RenderProcessHostWatcher crash_observer(
520 crash_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
521 crash_process->Shutdown(0);
522 crash_observer.Wait();
523
524 // Wait for destruction of |inner_contents|.
525 inner_death_observer.Wait();
526 inner_contents = nullptr;
527
528 // This should cancel the pointer lock.
Takumi Fujimoto4661871d2024-01-25 02:04:18529 EXPECT_EQ(nullptr, web_contents()->GetPointerLockWidget());
530 EXPECT_EQ(nullptr, web_contents()->pointer_lock_widget_.get());
531 EXPECT_FALSE(web_contents()->HasPointerLock(
Nick Carterd73635b2018-03-13 18:31:41532 root->current_frame_host()->GetRenderWidgetHost()));
533}
534
Lucas Gadania0ea0172018-09-20 18:31:37535IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockOopifCrashes) {
536 // This test runs three times, testing a crash at each level of the frametree.
537 for (int crash_depth = 0; crash_depth < 3; crash_depth++) {
538 GURL main_url(embedded_test_server()->GetURL(
539 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
540 EXPECT_TRUE(NavigateToURL(shell(), main_url));
541
Carlos Caballero15caeeb2021-10-27 09:57:55542 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Lucas Gadania0ea0172018-09-20 18:31:37543 FrameTreeNode* lock_node = root->child_at(0)->child_at(0);
544
545 // Pick which node to crash.
546 FrameTreeNode* crash_node = root;
547 for (int i = 0; i < crash_depth; i++)
548 crash_node = crash_node->child_at(0);
549
550 // Request a pointer lock to |lock_node|'s document.body.
Liviu Tinta7f8bdfb2020-05-05 23:35:26551 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(lock_node));
552 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(lock_node));
Lucas Gadania0ea0172018-09-20 18:31:37553
554 // Root (platform) RenderWidgetHostView should have the pointer locked.
Takumi Fujimoto4661871d2024-01-25 02:04:18555 EXPECT_TRUE(root->current_frame_host()->GetView()->IsPointerLocked());
Lucas Gadania0ea0172018-09-20 18:31:37556 EXPECT_EQ(lock_node->current_frame_host()->GetRenderWidgetHost(),
Takumi Fujimoto4661871d2024-01-25 02:04:18557 web_contents()->GetPointerLockWidget());
Lucas Gadania0ea0172018-09-20 18:31:37558
559 // Crash the process of |crash_node|.
560 RenderProcessHost* crash_process =
561 crash_node->current_frame_host()->GetProcess();
562 RenderProcessHostWatcher crash_observer(
563 crash_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
564 crash_process->Shutdown(0);
565 crash_observer.Wait();
566
567 // This should cancel the pointer lock.
Takumi Fujimoto4661871d2024-01-25 02:04:18568 EXPECT_EQ(nullptr, web_contents()->GetPointerLockWidget());
569 EXPECT_EQ(nullptr, web_contents()->pointer_lock_widget_.get());
570 EXPECT_FALSE(web_contents()->HasPointerLock(
Lucas Gadania0ea0172018-09-20 18:31:37571 root->current_frame_host()->GetRenderWidgetHost()));
572 if (crash_depth != 0)
Takumi Fujimoto4661871d2024-01-25 02:04:18573 EXPECT_FALSE(root->current_frame_host()->GetView()->IsPointerLocked());
Lucas Gadania0ea0172018-09-20 18:31:37574 else
575 EXPECT_EQ(nullptr, root->current_frame_host()->GetView());
576 }
577}
578
Xiaohan Wang1ecfd002022-01-19 22:33:10579#if BUILDFLAG(IS_LINUX)
Martin Šrámekbcbe62a2021-05-07 11:54:27580#define MAYBE_PointerLockWheelEventRouting DISABLED_PointerLockWheelEventRouting
581#else
582#define MAYBE_PointerLockWheelEventRouting PointerLockWheelEventRouting
583#endif
584IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest,
585 MAYBE_PointerLockWheelEventRouting) {
lfg9a467e12017-03-23 21:14:36586 GURL main_url(embedded_test_server()->GetURL(
587 "a.com", "/cross_site_iframe_factory.html?a(b)"));
588 EXPECT_TRUE(NavigateToURL(shell(), main_url));
589
Carlos Caballero15caeeb2021-10-27 09:57:55590 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lfg9a467e12017-03-23 21:14:36591 FrameTreeNode* child = root->child_at(0);
Kartar Singhb1bfa1a2024-06-24 13:14:57592 input::RenderWidgetHostInputEventRouter* router =
lfg9a467e12017-03-23 21:14:36593 web_contents()->GetInputEventRouter();
594 RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
595 root->current_frame_host()->GetView());
596 RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>(
597 child->current_frame_host()->GetView());
598
kylechara7c549b2019-07-29 17:47:28599 WaitForHitTestData(child->current_frame_host());
Abdelrahman Eed180a7622025-06-13 11:39:54600 // Make sure that page_scale is 1, on Android page scale is not guaranteed to
601 // be 1
602 web_contents()->SetPageScale(1.0);
Ken Buchanan8a319fb2017-11-15 18:37:12603
Ella Ge622de9b02019-03-20 21:42:43604 // Add a mouse move event listener to the root frame.
605 EXPECT_TRUE(ExecJs(
606 root,
607 "var x; var y; var dX; var dY; document.addEventListener('mousemove', "
608 "function(e) {x = e.x; y = e.y; mX = e.movementX; mY = e.movementY;});"));
609
610 // Send a mouse move to root frame before lock to set last mouse position.
611 blink::WebMouseEvent mouse_event(
Dave Tapuska347d60a2020-04-21 23:55:47612 blink::WebInputEvent::Type::kMouseMove,
613 blink::WebInputEvent::kNoModifiers,
Ella Ge622de9b02019-03-20 21:42:43614 blink::WebInputEvent::GetStaticTimeStampForTests());
Ella Ge2ebcc1f62019-07-02 21:50:15615 mouse_event.pointer_type = blink::WebPointerProperties::PointerType::kMouse;
Ella Ge622de9b02019-03-20 21:42:43616 mouse_event.SetPositionInWidget(6, 7);
Ella Ge2ebcc1f62019-07-02 21:50:15617 mouse_event.SetPositionInScreen(6, 7);
Ella Ge622de9b02019-03-20 21:42:43618 mouse_event.movement_x = 8;
619 mouse_event.movement_y = 9;
620 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
621
622 // Make sure that the renderer handled the input event.
623 MainThreadFrameObserver root_observer(root_view->GetRenderWidgetHost());
624 root_observer.Wait();
625
Dave Tapuska9425f102024-04-19 18:18:31626 EXPECT_EQ("[6,7,0,0]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
Ella Ge622de9b02019-03-20 21:42:43627
Liviu Tinta7f8bdfb2020-05-05 23:35:26628 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(root));
lfg9a467e12017-03-23 21:14:36629
630 // Root frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26631 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(root));
lfg9a467e12017-03-23 21:14:36632
633 // Add a mouse move wheel event listener to the root frame.
Nick Carterb7e71312018-08-03 23:36:13634 EXPECT_TRUE(ExecJs(
lfg9a467e12017-03-23 21:14:36635 root,
Nick Carterb7e71312018-08-03 23:36:13636 "var x; var y; var dX; var dY; document.addEventListener('mousewheel', "
lfg9a467e12017-03-23 21:14:36637 "function(e) {x = e.x; y = e.y; dX = e.deltaX; dY = e.deltaY;});"));
lfg9a467e12017-03-23 21:14:36638 root_observer.Wait();
639
640 blink::WebMouseWheelEvent wheel_event(
Dave Tapuska347d60a2020-04-21 23:55:47641 blink::WebInputEvent::Type::kMouseWheel,
642 blink::WebInputEvent::kNoModifiers,
Daniel Cheng93c80a92018-02-14 19:02:43643 blink::WebInputEvent::GetStaticTimeStampForTests());
Ella Ge2ebcc1f62019-07-02 21:50:15644 wheel_event.SetPositionInScreen(10, 11);
Blink Reformat1c4d759e2017-04-09 16:34:54645 wheel_event.delta_x = -12;
646 wheel_event.delta_y = -13;
sahel41942462017-07-06 14:14:37647 wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
lfg9a467e12017-03-23 21:14:36648 router->RouteMouseWheelEvent(root_view, &wheel_event, ui::LatencyInfo());
649
650 // Make sure that the renderer handled the input event.
651 root_observer.Wait();
652
Sahel Sharifye6d81f472018-07-11 20:40:26653 // All wheel events during a scroll sequence will be sent to a single target.
654 // Send a wheel end event to the current target before sending wheel events to
655 // a new target.
656 wheel_event.delta_x = 0;
657 wheel_event.delta_y = 0;
658 wheel_event.phase = blink::WebMouseWheelEvent::kPhaseEnded;
659 router->RouteMouseWheelEvent(root_view, &wheel_event, ui::LatencyInfo());
sahel955c3292017-08-17 14:56:44660
Sahel Sharifye6d81f472018-07-11 20:40:26661 // Make sure that the renderer handled the input event.
662 root_observer.Wait();
sahel955c3292017-08-17 14:56:44663
Ella Ge622de9b02019-03-20 21:42:43664 // Locked event has same coordinates as before locked.
665 EXPECT_EQ("[6,7,12,13]", EvalJs(root, "JSON.stringify([x, y, dX, dY])"));
lfg9a467e12017-03-23 21:14:36666
Liviu Tinta7f8bdfb2020-05-05 23:35:26667 EXPECT_EQ(true, PointerLockHelper::ExitPointerLock(root));
lfg9a467e12017-03-23 21:14:36668
Liviu Tinta7f8bdfb2020-05-05 23:35:26669 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(child));
lfg9a467e12017-03-23 21:14:36670
671 // Child frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26672 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(child));
lfg9a467e12017-03-23 21:14:36673
674 // Add a mouse move event listener to the child frame.
Nick Carterb7e71312018-08-03 23:36:13675 EXPECT_TRUE(ExecJs(
lfg9a467e12017-03-23 21:14:36676 child,
Nick Carterb7e71312018-08-03 23:36:13677 "var x; var y; var dX; var dY; document.addEventListener('mousewheel', "
lfg9a467e12017-03-23 21:14:36678 "function(e) {x = e.x; y = e.y; dX = e.deltaX; dY = e.deltaY;});"));
679 MainThreadFrameObserver child_observer(child_view->GetRenderWidgetHost());
680 child_observer.Wait();
681
Ella Ge43cccf172017-10-19 15:44:16682 gfx::PointF transformed_point;
683 root_view->TransformPointToCoordSpaceForView(gfx::PointF(0, 0), child_view,
Ria Jiangbd22e55d2019-03-04 19:23:50684 &transformed_point);
lfg9a467e12017-03-23 21:14:36685
Blink Reformat1c4d759e2017-04-09 16:34:54686 wheel_event.SetPositionInWidget(-transformed_point.x() + 14,
mustaqc51f3aab2017-04-05 15:43:11687 -transformed_point.y() + 15);
Ella Ge2ebcc1f62019-07-02 21:50:15688 wheel_event.SetPositionInScreen(-transformed_point.x() + 14,
689 -transformed_point.y() + 15);
Blink Reformat1c4d759e2017-04-09 16:34:54690 wheel_event.delta_x = -16;
691 wheel_event.delta_y = -17;
Sahel Sharifye6d81f472018-07-11 20:40:26692 wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
lfg9a467e12017-03-23 21:14:36693 // We use root_view intentionally as the RenderWidgetHostInputEventRouter is
694 // responsible for correctly routing the event to the child frame.
695 router->RouteMouseWheelEvent(root_view, &wheel_event, ui::LatencyInfo());
696
697 // Make sure that the renderer handled the input event.
698 child_observer.Wait();
699
Ella Ge622de9b02019-03-20 21:42:43700 // This is the first event to child render, so the coordinates is (0, 0)
701 EXPECT_EQ("[0,0,16,17]", EvalJs(child, "JSON.stringify([x, y, dX, dY])"));
lfg9a467e12017-03-23 21:14:36702}
703
Lucas Furukawa Gadanidd328daaa2018-03-19 22:38:59704IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockWidgetHidden) {
705 GURL main_url(embedded_test_server()->GetURL(
706 "a.com", "/cross_site_iframe_factory.html?a(b)"));
707 EXPECT_TRUE(NavigateToURL(shell(), main_url));
708
Carlos Caballero15caeeb2021-10-27 09:57:55709 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Lucas Furukawa Gadanidd328daaa2018-03-19 22:38:59710 FrameTreeNode* child = root->child_at(0);
711 RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>(
712 child->current_frame_host()->GetView());
713
kylechara7c549b2019-07-29 17:47:28714 WaitForHitTestData(child->current_frame_host());
Lucas Furukawa Gadanidd328daaa2018-03-19 22:38:59715
716 // Request a pointer lock on the child frame's body.
Liviu Tinta7f8bdfb2020-05-05 23:35:26717 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(child));
Lucas Furukawa Gadanidd328daaa2018-03-19 22:38:59718 // Child frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26719 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(child));
720
Takumi Fujimoto4661871d2024-01-25 02:04:18721 EXPECT_TRUE(child_view->IsPointerLocked());
722 EXPECT_EQ(child_view->host(), web_contents()->GetPointerLockWidget());
Lucas Furukawa Gadanidd328daaa2018-03-19 22:38:59723
724 child_view->Hide();
725
726 // Child frame should've released the mouse lock when hidden.
Takumi Fujimoto4661871d2024-01-25 02:04:18727 EXPECT_FALSE(child_view->IsPointerLocked());
728 EXPECT_EQ(nullptr, web_contents()->GetPointerLockWidget());
Lucas Furukawa Gadanidd328daaa2018-03-19 22:38:59729}
730
jameshollyer48a702f2020-04-11 04:21:40731#ifdef USE_AURA
732IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockOutOfFocus) {
733 GURL main_url(embedded_test_server()->GetURL(
734 "a.com", "/cross_site_iframe_factory.html?a(b)"));
735 EXPECT_TRUE(NavigateToURL(shell(), main_url));
736
Carlos Caballero15caeeb2021-10-27 09:57:55737 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
jameshollyer48a702f2020-04-11 04:21:40738 MockPointerLockRenderWidgetHostView* root_view =
739 static_cast<MockPointerLockRenderWidgetHostView*>(
740 root->current_frame_host()->GetView());
741
742 root_view->has_focus_ = false;
743 // Request a pointer lock on the root frame's body.
Liviu Tinta7f8bdfb2020-05-05 23:35:26744 EXPECT_EQ(false, PointerLockHelper::RequestPointerLockOnBody(root));
jameshollyer48a702f2020-04-11 04:21:40745 // Root frame should not have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26746 EXPECT_EQ(false, PointerLockHelper::IsPointerLockOnBody(root));
jameshollyer48a702f2020-04-11 04:21:40747}
748#endif
749
Mustaq Ahmed8f49a8b2f2021-06-25 22:42:28750IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockOnDroppedElem) {
751 GURL url = embedded_test_server()->GetURL(
752 "a.com", "/pointerlock_on_dropped_elem.html");
753 EXPECT_TRUE(NavigateToURL(shell(), url));
754
755 EXPECT_TRUE(ExecJs(shell(), "document.body.click();"));
756
757 // The second ExecJS() call here delays test termination so that the first
758 // call's async tasks get a chance to run.
759 EXPECT_TRUE(ExecJs(shell(), "", EXECUTE_SCRIPT_NO_USER_GESTURE));
760}
761
Abdelrahman Eed180a7622025-06-13 11:39:54762// unajustedMovement flag is not supported on Android
763#if BUILDFLAG(IS_ANDROID)
764#define MAYBE_PointerLockRequestUnadjustedMovement \
765 DISABLED_PointerLockRequestUnadjustedMovement
766#else
767#define MAYBE_PointerLockRequestUnadjustedMovement \
768 PointerLockRequestUnadjustedMovement
769#endif
Ella Ge04708ae2023-03-24 19:55:18770IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest,
Abdelrahman Eed180a7622025-06-13 11:39:54771 MAYBE_PointerLockRequestUnadjustedMovement) {
Ella Ge3fa5c3f2019-09-05 19:00:06772 GURL main_url(embedded_test_server()->GetURL(
773 "a.com", "/cross_site_iframe_factory.html?a(b)"));
774 EXPECT_TRUE(NavigateToURL(shell(), main_url));
775
Carlos Caballero15caeeb2021-10-27 09:57:55776 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ella Ge3fa5c3f2019-09-05 19:00:06777
Liviu Tintabf520b3c52020-04-23 14:33:15778 EXPECT_TRUE(ExecJs(root, "var pointerLockPromise;"));
779 std::string wait_for_pointer_lock_promise =
780 "(async ()=> {return await pointerLockPromise.then(()=>true, "
781 "()=>false);})()";
782 std::string set_pointer_lock_promise =
783 R"code(pointerLockPromise = new Promise( function(resolve, reject){
784 document.addEventListener('pointerlockchange', resolve);
785 document.addEventListener('pointerlockerror', reject)
786 });)code";
787
Ella Ge3fa5c3f2019-09-05 19:00:06788 // Request a pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26789 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(root));
Ella Ge3fa5c3f2019-09-05 19:00:06790 // Root frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26791 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(root));
Ella Ge3fa5c3f2019-09-05 19:00:06792 // Mouse is locked and unadjusted_movement is not set.
Takumi Fujimoto4661871d2024-01-25 02:04:18793 EXPECT_TRUE(root->current_frame_host()->GetView()->IsPointerLocked());
Ella Ge3fa5c3f2019-09-05 19:00:06794
Liviu Tintabf520b3c52020-04-23 14:33:15795 // Release pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26796 EXPECT_EQ(true, PointerLockHelper::ExitPointerLock(root));
Ella Ge3fa5c3f2019-09-05 19:00:06797
Xiaohan Wang1ecfd002022-01-19 22:33:10798#if defined(USE_AURA) || BUILDFLAG(IS_MAC)
Liviu Tinta7f8bdfb2020-05-05 23:35:26799 // Request a pointer lock with unadjustedMovement.
800 EXPECT_EQ(
801 true,
802 PointerLockHelper::RequestPointerLockWithUnadjustedMovementOnBody(root));
Ella Ge3fa5c3f2019-09-05 19:00:06803 // Root frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26804 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(root));
805
Ella Ge3fa5c3f2019-09-05 19:00:06806 // Mouse is locked and unadjusted_movement is set.
Takumi Fujimoto4661871d2024-01-25 02:04:18807 EXPECT_TRUE(root->current_frame_host()->GetView()->IsPointerLocked());
Ella Ge3fa5c3f2019-09-05 19:00:06808 EXPECT_TRUE(root->current_frame_host()
809 ->GetView()
Takumi Fujimoto4661871d2024-01-25 02:04:18810 ->GetIsPointerLockedUnadjustedMovementForTesting());
Ella Ge3fa5c3f2019-09-05 19:00:06811
812 // Release pointer lock, unadjusted_movement bit is reset.
Liviu Tinta7f8bdfb2020-05-05 23:35:26813 EXPECT_EQ(true, PointerLockHelper::ExitPointerLock(root));
814
Ella Ge3fa5c3f2019-09-05 19:00:06815 EXPECT_FALSE(root->current_frame_host()
816 ->GetView()
Takumi Fujimoto4661871d2024-01-25 02:04:18817 ->GetIsPointerLockedUnadjustedMovementForTesting());
Ella Ge3fa5c3f2019-09-05 19:00:06818#else
Liviu Tinta7f8bdfb2020-05-05 23:35:26819 // Request a pointer lock with unadjustedMovement.
Ella Ge3fa5c3f2019-09-05 19:00:06820 // On platform that does not support unadjusted movement yet, do not lock and
821 // a pointerlockerror event is dispatched.
Liviu Tinta7f8bdfb2020-05-05 23:35:26822 EXPECT_EQ(
823 false,
824 PointerLockHelper::RequestPointerLockWithUnadjustedMovementOnBody(root));
825 EXPECT_EQ(false, PointerLockHelper::IsPointerLockOnBody(root));
Takumi Fujimoto4661871d2024-01-25 02:04:18826 EXPECT_FALSE(root->current_frame_host()->GetView()->IsPointerLocked());
Ella Ge3fa5c3f2019-09-05 19:00:06827#endif
828}
Ella Ge2f7211292019-09-05 23:52:19829
830#if defined(USE_AURA)
Etienne Pierre-doray544a9a12021-04-14 22:48:13831// Flaky on all platforms http://crbug.com/1198612.
Ella Ge04708ae2023-03-24 19:55:18832IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, DISABLED_UnadjustedMovement) {
Ella Ge2f7211292019-09-05 23:52:19833 GURL main_url(embedded_test_server()->GetURL(
834 "a.com", "/cross_site_iframe_factory.html?a(b)"));
835 EXPECT_TRUE(NavigateToURL(shell(), main_url));
836
Carlos Caballero15caeeb2021-10-27 09:57:55837 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Kartar Singhb1bfa1a2024-06-24 13:14:57838 input::RenderWidgetHostInputEventRouter* router =
Ella Ge2f7211292019-09-05 23:52:19839 web_contents()->GetInputEventRouter();
840 RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
841 root->current_frame_host()->GetView());
842
843 // Add a mouse move event listener to the root frame.
844 EXPECT_TRUE(ExecJs(
845 root,
846 "var x; var y; var mX; var mY; document.addEventListener('mousemove', "
847 "function(e) {x = e.x; y = e.y; mX = e.movementX; mY = e.movementY;});"));
848
849 // Send a mouse move to root frame before lock.
850 blink::WebMouseEvent mouse_event(
Dave Tapuska347d60a2020-04-21 23:55:47851 blink::WebInputEvent::Type::kMouseMove,
852 blink::WebInputEvent::kNoModifiers,
Ella Ge2f7211292019-09-05 23:52:19853 blink::WebInputEvent::GetStaticTimeStampForTests());
854 mouse_event.pointer_type = blink::WebPointerProperties::PointerType::kMouse;
855 mouse_event.SetPositionInWidget(6, 7);
856 mouse_event.SetPositionInScreen(6, 7);
857 mouse_event.movement_x = 8;
858 mouse_event.movement_y = 9;
859 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
860
861 // Make sure that the renderer handled the input event.
862 MainThreadFrameObserver root_observer(root_view->GetRenderWidgetHost());
863 root_observer.Wait();
864
865 EXPECT_EQ("[6,7,0,0]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
866
867 // Request a pointer lock with unadjustedMovement.
Liviu Tinta7f8bdfb2020-05-05 23:35:26868 EXPECT_EQ(
869 true,
870 PointerLockHelper::RequestPointerLockWithUnadjustedMovementOnBody(root));
871
Ella Ge2f7211292019-09-05 23:52:19872 // Root frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26873 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(root));
874
Ella Ge2f7211292019-09-05 23:52:19875 // Mouse is locked and unadjusted_movement is not set.
Takumi Fujimoto4661871d2024-01-25 02:04:18876 EXPECT_TRUE(root->current_frame_host()->GetView()->IsPointerLocked());
Ella Ge2f7211292019-09-05 23:52:19877
878 mouse_event.SetPositionInWidget(10, 10);
879 mouse_event.SetPositionInScreen(10, 10);
880 mouse_event.movement_x = 12;
881 mouse_event.movement_y = 9;
882 mouse_event.is_raw_movement_event = true;
883 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
884 root_observer.Wait();
885
886 // Raw movement events movement value from WebMouseEvent.movement_x/y.
887 EXPECT_EQ("[6,7,12,9]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
888
889 mouse_event.SetPositionInWidget(20, 21);
890 mouse_event.SetPositionInScreen(20, 21);
891 mouse_event.movement_x = 1;
892 mouse_event.movement_y = 2;
893 mouse_event.is_raw_movement_event = false;
894 router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
895 root_observer.Wait();
896
897 // Non-raw movement events movement value from screen pos - last screen pos.
898 EXPECT_EQ("[6,7,10,11]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
899}
900#endif
James Hollyer0a8c7ee12020-04-04 07:45:16901
902#if defined(USE_AURA)
Alison Gale81f4f2c72024-04-22 19:33:31903// TODO(crbug.com/40635377): Remove failure test when fully implemented
Georg Neis35ff854b2024-12-17 02:02:08904#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
James Hollyer0a8c7ee12020-04-04 07:45:16905#define MAYBE_ChangeUnadjustedMovementFailure \
906 DISABLED_ChangeUnadjustedMovementFailure
907#else
908#define MAYBE_ChangeUnadjustedMovementFailure ChangeUnadjustedMovementFailure
909#endif
910// Tests that a subsequent request to RequestPointerLock with different
911// options inside a Child view gets piped to the proper places and gives
912// the proper unsupported error(this option is only supported on Windows
913// This was prompted by this bug: https://crbug.com/1062702
Ella Ge04708ae2023-03-24 19:55:18914IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest,
James Hollyer0a8c7ee12020-04-04 07:45:16915 MAYBE_ChangeUnadjustedMovementFailure) {
916 GURL main_url(embedded_test_server()->GetURL(
917 "a.com", "/cross_site_iframe_factory.html?a(b)"));
918 EXPECT_TRUE(NavigateToURL(shell(), main_url));
919
Carlos Caballero15caeeb2021-10-27 09:57:55920 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
James Hollyer0a8c7ee12020-04-04 07:45:16921 FrameTreeNode* child = root->child_at(0);
922 RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>(
923 child->current_frame_host()->GetView());
924
925 WaitForHitTestData(child->current_frame_host());
926
927 // Request a pointer lock on the child frame's body and wait for the promise
928 // to resolve.
Liviu Tinta7f8bdfb2020-05-05 23:35:26929 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(child));
James Hollyer0a8c7ee12020-04-04 07:45:16930 // Child frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26931 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(child));
932
Takumi Fujimoto4661871d2024-01-25 02:04:18933 EXPECT_TRUE(child_view->IsPointerLocked());
James Hollyer0a8c7ee12020-04-04 07:45:16934 EXPECT_FALSE(root->current_frame_host()
935 ->GetView()
Takumi Fujimoto4661871d2024-01-25 02:04:18936 ->GetIsPointerLockedUnadjustedMovementForTesting());
937 EXPECT_EQ(child_view->host(), web_contents()->GetPointerLockWidget());
James Hollyer0a8c7ee12020-04-04 07:45:16938
939 // Request to change pointer lock options and wait for return.
940 EXPECT_EQ(
941 "a JavaScript error: \"NotSupportedError: The options asked for in this "
942 "request are not supported on this platform.\"\n",
943 EvalJs(child,
944 "document.body.requestPointerLock({unadjustedMovement:true})")
Chris Fredricksonc28317922025-07-27 21:18:43945 .ExtractError());
James Hollyer0a8c7ee12020-04-04 07:45:16946
947 // The change errored out but the original lock should still be in place.
Takumi Fujimoto4661871d2024-01-25 02:04:18948 EXPECT_TRUE(child_view->IsPointerLocked());
James Hollyer0a8c7ee12020-04-04 07:45:16949 EXPECT_FALSE(root->current_frame_host()
950 ->GetView()
Takumi Fujimoto4661871d2024-01-25 02:04:18951 ->GetIsPointerLockedUnadjustedMovementForTesting());
952 EXPECT_EQ(child_view->host(), web_contents()->GetPointerLockWidget());
James Hollyer0a8c7ee12020-04-04 07:45:16953}
954#endif
955
956#if defined(USE_AURA)
Xiaohan Wang1ecfd002022-01-19 22:33:10957#if BUILDFLAG(IS_WIN)
James Hollyer0a8c7ee12020-04-04 07:45:16958// Tests that a subsequent request to RequestPointerLock with different
959// options inside a Child view gets piped to the proper places and updates
960// the option(this option is only supported on Windows).
961// This was prompted by this bug: https://crbug.com/1062702
Ella Ge04708ae2023-03-24 19:55:18962IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest,
James Hollyer0a8c7ee12020-04-04 07:45:16963 ChangeUnadjustedMovementSuccess) {
964 GURL main_url(embedded_test_server()->GetURL(
965 "a.com", "/cross_site_iframe_factory.html?a(b)"));
966 EXPECT_TRUE(NavigateToURL(shell(), main_url));
967
Carlos Caballero15caeeb2021-10-27 09:57:55968 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
James Hollyer0a8c7ee12020-04-04 07:45:16969 FrameTreeNode* child = root->child_at(0);
970 RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>(
971 child->current_frame_host()->GetView());
972
973 WaitForHitTestData(child->current_frame_host());
974
975 // Request a pointer lock on the child frame's body and wait for the promise
976 // to resolve.
Liviu Tinta7f8bdfb2020-05-05 23:35:26977 EXPECT_EQ(true, PointerLockHelper::RequestPointerLockOnBody(child));
James Hollyer0a8c7ee12020-04-04 07:45:16978 // Child frame should have been granted pointer lock.
Liviu Tinta7f8bdfb2020-05-05 23:35:26979 EXPECT_EQ(true, PointerLockHelper::IsPointerLockOnBody(child));
980
Takumi Fujimoto4661871d2024-01-25 02:04:18981 EXPECT_TRUE(child_view->IsPointerLocked());
James Hollyer0a8c7ee12020-04-04 07:45:16982 EXPECT_FALSE(root->current_frame_host()
983 ->GetView()
Takumi Fujimoto4661871d2024-01-25 02:04:18984 ->GetIsPointerLockedUnadjustedMovementForTesting());
985 EXPECT_EQ(child_view->host(), web_contents()->GetPointerLockWidget());
James Hollyer0a8c7ee12020-04-04 07:45:16986
987 // Request to change pointer lock options and wait for return.
988 EXPECT_EQ(
Chris Fredricksond72b1892025-07-10 22:04:42989 base::Value(),
James Hollyer0a8c7ee12020-04-04 07:45:16990 EvalJs(child,
991 "document.body.requestPointerLock({unadjustedMovement:true})"));
992
993 // The new changed lock should now be in place.
Takumi Fujimoto4661871d2024-01-25 02:04:18994 EXPECT_TRUE(child_view->IsPointerLocked());
James Hollyer0a8c7ee12020-04-04 07:45:16995 EXPECT_TRUE(root->current_frame_host()
996 ->GetView()
Takumi Fujimoto4661871d2024-01-25 02:04:18997 ->GetIsPointerLockedUnadjustedMovementForTesting());
998 EXPECT_EQ(child_view->host(), web_contents()->GetPointerLockWidget());
James Hollyer0a8c7ee12020-04-04 07:45:16999}
1000#endif // WIN_OS
1001#endif // USE_AURA
lfg84763c92017-02-16 18:55:151002} // namespace content