Add requester frame param to mouse-lock calls.

Without a specific frame, we were using the local root of caller's
widget, which was showing wrong activation state in certain cases.

(cherry picked from commit 32a0b159fdace8433876da922b04bba4063f1533)

Bug: 981597
Change-Id: I45a5dd88ea4c487ff4bf3a722e72bb6923fb8319
TBR: [email protected]
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1721186
Commit-Queue: Navid Zolghadr <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Alex Moshchuk <[email protected]>
Reviewed-by: Navid Zolghadr <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#684359}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1741297
Cr-Commit-Position: refs/branch-heads/3865@{#283}
Cr-Branched-From: 0cdcc6158160790658d1f033d3db873603250124-refs/heads/master@{#681094}
diff --git a/content/browser/pointer_lock_browsertest.cc b/content/browser/pointer_lock_browsertest.cc
index 2f639b7..c87bf023 100644
--- a/content/browser/pointer_lock_browsertest.cc
+++ b/content/browser/pointer_lock_browsertest.cc
@@ -35,7 +35,7 @@
   void RequestToLockMouse(WebContents* web_contents,
                           bool user_gesture,
                           bool last_unlocked_by_target) override {
-    web_contents->GotResponseToLockMouseRequest(true);
+    web_contents->GotResponseToLockMouseRequest(user_gesture);
   }
 
   void LostMouseLock() override {}
@@ -115,7 +115,7 @@
   MockPointerLockWebContentsDelegate web_contents_delegate_;
 };
 
-IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLock) {
+IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockBasic) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
@@ -147,6 +147,46 @@
             EvalJs(child, "document.pointerLockElement == document.body"));
 }
 
+IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockAndUserActivation) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(b))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  FrameTreeNode* child = root->child_at(0);
+  FrameTreeNode* grand_child = child->child_at(0);
+
+  // Without user activation, pointer lock request from any (child or
+  // grand_child) frame fails.
+  EXPECT_TRUE(ExecJs(child, "document.body.requestPointerLock()",
+                     EXECUTE_SCRIPT_NO_USER_GESTURE));
+  EXPECT_EQ(false, EvalJs(child, "document.pointerLockElement == document.body",
+                          EXECUTE_SCRIPT_NO_USER_GESTURE));
+  EXPECT_TRUE(ExecJs(grand_child, "document.body.requestPointerLock()",
+                     EXECUTE_SCRIPT_NO_USER_GESTURE));
+  EXPECT_EQ(false,
+            EvalJs(grand_child, "document.pointerLockElement == document.body",
+                   EXECUTE_SCRIPT_NO_USER_GESTURE));
+
+  // Execute a empty (dummy) JS to activate the child frame.
+  EXPECT_TRUE(ExecJs(child, ""));
+
+  // With user activation in the child frame, pointer lock from the same frame
+  // succeeds.
+  EXPECT_TRUE(ExecJs(child, "document.body.requestPointerLock()",
+                     EXECUTE_SCRIPT_NO_USER_GESTURE));
+  EXPECT_EQ(true, EvalJs(child, "document.pointerLockElement == document.body",
+                         EXECUTE_SCRIPT_NO_USER_GESTURE));
+
+  // But with user activation in the child frame, pointer lock from the
+  // grand_child frame fails.
+  EXPECT_TRUE(ExecJs(grand_child, "document.body.requestPointerLock()",
+                     EXECUTE_SCRIPT_NO_USER_GESTURE));
+  EXPECT_EQ(false,
+            EvalJs(grand_child, "document.pointerLockElement == document.body",
+                   EXECUTE_SCRIPT_NO_USER_GESTURE));
+}
+
 IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockEventRouting) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 6f66951..8f9051c 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -341,7 +341,9 @@
   if (enable) {
     if (mouse_locked_ || !render_widget)
       return;
-    render_widget->mouse_lock_dispatcher()->LockMouse(this);
+    blink::WebLocalFrame* requester_frame =
+        Container()->GetDocument().GetFrame();
+    render_widget->mouse_lock_dispatcher()->LockMouse(this, requester_frame);
   } else {
     if (!mouse_locked_) {
       OnLockMouseACK(false);
diff --git a/content/renderer/mouse_lock_dispatcher.cc b/content/renderer/mouse_lock_dispatcher.cc
index 0749c9c..84c06db5 100644
--- a/content/renderer/mouse_lock_dispatcher.cc
+++ b/content/renderer/mouse_lock_dispatcher.cc
@@ -18,14 +18,15 @@
 MouseLockDispatcher::~MouseLockDispatcher() {
 }
 
-bool MouseLockDispatcher::LockMouse(LockTarget* target) {
+bool MouseLockDispatcher::LockMouse(LockTarget* target,
+                                    blink::WebLocalFrame* requester_frame) {
   if (MouseLockedOrPendingAction())
     return false;
 
   pending_lock_request_ = true;
   target_ = target;
 
-  SendLockMouseRequest();
+  SendLockMouseRequest(requester_frame);
   return true;
 }
 
diff --git a/content/renderer/mouse_lock_dispatcher.h b/content/renderer/mouse_lock_dispatcher.h
index d5c3be8..0e76aa6 100644
--- a/content/renderer/mouse_lock_dispatcher.h
+++ b/content/renderer/mouse_lock_dispatcher.h
@@ -10,6 +10,7 @@
 
 namespace blink {
 class WebMouseEvent;
+class WebLocalFrame;
 }  // namespace blink
 
 namespace content {
@@ -31,9 +32,10 @@
         const blink::WebMouseEvent& event) = 0;
   };
 
-  // Locks the mouse to the |target|. If true is returned, an asynchronous
-  // response to target->OnLockMouseACK() will follow.
-  bool LockMouse(LockTarget* target);
+  // Locks the mouse to |target| if |requester_frame| has transient user
+  // activation. If true is returned, an asynchronous response to
+  // target->OnLockMouseACK() will follow.
+  bool LockMouse(LockTarget* target, blink::WebLocalFrame* requester_frame);
   // Request to unlock the mouse. An asynchronous response to
   // target->OnMouseLockLost() will follow.
   void UnlockMouse(LockTarget* target);
@@ -55,7 +57,7 @@
  protected:
   // Subclasses must implement these methods to send mouse lock requests to the
   // browser.
-  virtual void SendLockMouseRequest() = 0;
+  virtual void SendLockMouseRequest(blink::WebLocalFrame* requester_frame) = 0;
   virtual void SendUnlockMouseRequest() = 0;
 
  private:
diff --git a/content/renderer/mouse_lock_dispatcher_browsertest.cc b/content/renderer/mouse_lock_dispatcher_browsertest.cc
index 48936a8..059d7f0 100644
--- a/content/renderer/mouse_lock_dispatcher_browsertest.cc
+++ b/content/renderer/mouse_lock_dispatcher_browsertest.cc
@@ -55,13 +55,14 @@
 
 }  // namespace
 
-// Test simple use of RenderViewImpl interface to WebKit for pointer lock.
+// Test simple use of RenderViewImpl interface for pointer lock.
 TEST_F(MouseLockDispatcherTest, BasicWebWidget) {
   // Start unlocked.
   EXPECT_FALSE(widget()->IsPointerLocked());
 
   // Lock.
-  EXPECT_TRUE(widget()->RequestPointerLock());
+  EXPECT_TRUE(widget()->RequestPointerLock(
+      view()->GetMainRenderFrame()->GetWebFrame()));
   widget()->OnMessageReceived(WidgetMsg_LockMouse_ACK(route_id_, true));
   EXPECT_TRUE(widget()->IsPointerLocked());
 
@@ -71,7 +72,8 @@
   EXPECT_FALSE(widget()->IsPointerLocked());
 
   // Attempt a lock, and have it fail.
-  EXPECT_TRUE(widget()->RequestPointerLock());
+  EXPECT_TRUE(widget()->RequestPointerLock(
+      view()->GetMainRenderFrame()->GetWebFrame()));
   widget()->OnMessageReceived(WidgetMsg_LockMouse_ACK(route_id_, false));
   EXPECT_FALSE(widget()->IsPointerLocked());
 }
@@ -89,7 +91,8 @@
   EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
 
   // Lock.
-  EXPECT_TRUE(dispatcher()->LockMouse(target_));
+  EXPECT_TRUE(dispatcher()->LockMouse(
+      target_, view()->GetMainRenderFrame()->GetWebFrame()));
   widget()->OnMessageReceived(WidgetMsg_LockMouse_ACK(route_id_, true));
   EXPECT_TRUE(dispatcher()->IsMouseLockedTo(target_));
 
@@ -102,7 +105,8 @@
   EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
 
   // Attempt a lock, and have it fail.
-  EXPECT_TRUE(dispatcher()->LockMouse(target_));
+  EXPECT_TRUE(dispatcher()->LockMouse(
+      target_, view()->GetMainRenderFrame()->GetWebFrame()));
   widget()->OnMessageReceived(WidgetMsg_LockMouse_ACK(route_id_, false));
   EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
 }
@@ -115,7 +119,8 @@
   EXPECT_CALL(*target_, OnMouseLockLost()).Times(0);
 
   // Lock.
-  EXPECT_TRUE(dispatcher()->LockMouse(target_));
+  EXPECT_TRUE(dispatcher()->LockMouse(
+      target_, view()->GetMainRenderFrame()->GetWebFrame()));
   widget()->OnMessageReceived(WidgetMsg_LockMouse_ACK(route_id_, true));
   EXPECT_TRUE(dispatcher()->IsMouseLockedTo(target_));
 
@@ -136,7 +141,8 @@
   EXPECT_CALL(*target_, OnMouseLockLost()).Times(0);
 
   // Lock request.
-  EXPECT_TRUE(dispatcher()->LockMouse(target_));
+  EXPECT_TRUE(dispatcher()->LockMouse(
+      target_, view()->GetMainRenderFrame()->GetWebFrame()));
 
   // Before receiving response delete the target.
   dispatcher()->OnLockTargetDestroyed(target_);
@@ -154,7 +160,8 @@
   EXPECT_CALL(*target_, OnMouseLockLost()).Times(0);
 
   // Lock request.
-  EXPECT_TRUE(dispatcher()->LockMouse(target_));
+  EXPECT_TRUE(dispatcher()->LockMouse(
+      target_, view()->GetMainRenderFrame()->GetWebFrame()));
 
   // Before receiving response delete the target.
   dispatcher()->OnLockTargetDestroyed(target_);
@@ -178,7 +185,8 @@
   dispatcher()->WillHandleMouseEvent(blink::WebMouseEvent());
 
   // Lock.
-  EXPECT_TRUE(dispatcher()->LockMouse(target_));
+  EXPECT_TRUE(dispatcher()->LockMouse(
+      target_, view()->GetMainRenderFrame()->GetWebFrame()));
   widget()->OnMessageReceived(WidgetMsg_LockMouse_ACK(route_id_, true));
   EXPECT_TRUE(dispatcher()->IsMouseLockedTo(target_));
 
@@ -205,11 +213,13 @@
   EXPECT_CALL(*target_, OnMouseLockLost());
 
   // Lock request for target.
-  EXPECT_TRUE(dispatcher()->LockMouse(target_));
+  EXPECT_TRUE(dispatcher()->LockMouse(
+      target_, view()->GetMainRenderFrame()->GetWebFrame()));
 
   // Fail attempt to lock alternate.
   EXPECT_FALSE(dispatcher()->IsMouseLockedTo(alternate_target_));
-  EXPECT_FALSE(dispatcher()->LockMouse(alternate_target_));
+  EXPECT_FALSE(dispatcher()->LockMouse(
+      alternate_target_, view()->GetMainRenderFrame()->GetWebFrame()));
 
   // Lock completion for target.
   widget()->OnMessageReceived(WidgetMsg_LockMouse_ACK(route_id_, true));
@@ -217,7 +227,8 @@
 
   // Fail attempt to lock alternate.
   EXPECT_FALSE(dispatcher()->IsMouseLockedTo(alternate_target_));
-  EXPECT_FALSE(dispatcher()->LockMouse(alternate_target_));
+  EXPECT_FALSE(dispatcher()->LockMouse(
+      alternate_target_, view()->GetMainRenderFrame()->GetWebFrame()));
 
   // Receive mouse event to only one target.
   dispatcher()->WillHandleMouseEvent(blink::WebMouseEvent());
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 7a5cd52..5b5a840 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -3268,7 +3268,9 @@
 }
 
 bool PepperPluginInstanceImpl::LockMouse() {
-  return GetMouseLockDispatcher()->LockMouse(GetOrCreateLockTargetAdapter());
+  WebLocalFrame* requester_frame = container_->GetDocument().GetFrame();
+  return GetMouseLockDispatcher()->LockMouse(GetOrCreateLockTargetAdapter(),
+                                             requester_frame);
 }
 
 MouseLockDispatcher::LockTarget*
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 734c561..9640295 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -3650,8 +3650,9 @@
   return gfx::ToRoundedPoint(ConvertWindowPointToViewport(gfx::PointF(point)));
 }
 
-bool RenderWidget::RequestPointerLock() {
-  return mouse_lock_dispatcher_->LockMouse(webwidget_mouse_lock_target_.get());
+bool RenderWidget::RequestPointerLock(WebLocalFrame* requester_frame) {
+  return mouse_lock_dispatcher_->LockMouse(webwidget_mouse_lock_target_.get(),
+                                           requester_frame);
 }
 
 void RenderWidget::RequestPointerUnlock() {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 20f02c0b..62588c09 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -420,7 +420,7 @@
   void ConvertViewportToWindow(blink::WebRect* rect) override;
   void ConvertViewportToWindow(blink::WebFloatRect* rect) override;
   void ConvertWindowToViewport(blink::WebFloatRect* rect) override;
-  bool RequestPointerLock() override;
+  bool RequestPointerLock(blink::WebLocalFrame* requester_frame) override;
   void RequestPointerUnlock() override;
   bool IsPointerLocked() override;
   void StartDragging(network::mojom::ReferrerPolicy policy,
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index 06e9fd5..cc9ed31 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -57,7 +57,7 @@
 
  private:
   // MouseLockDispatcher implementation.
-  void SendLockMouseRequest() override;
+  void SendLockMouseRequest(blink::WebLocalFrame* requester_frame) override;
   void SendUnlockMouseRequest() override;
 
   RenderWidgetFullscreenPepper* widget_;
@@ -116,7 +116,11 @@
 FullscreenMouseLockDispatcher::~FullscreenMouseLockDispatcher() {
 }
 
-void FullscreenMouseLockDispatcher::SendLockMouseRequest() {
+void FullscreenMouseLockDispatcher::SendLockMouseRequest(
+    blink::WebLocalFrame*) {
+  // TODO(mustaq): Why is it not checking user activation state at all?  In
+  // particular, the last Boolean param ("privileged") in the IPC below looks
+  // scary without this check.
   widget_->Send(
       new WidgetHostMsg_LockMouse(widget_->routing_id(), false, true));
 }
diff --git a/content/renderer/render_widget_mouse_lock_dispatcher.cc b/content/renderer/render_widget_mouse_lock_dispatcher.cc
index bac8dc5..456f5e8 100644
--- a/content/renderer/render_widget_mouse_lock_dispatcher.cc
+++ b/content/renderer/render_widget_mouse_lock_dispatcher.cc
@@ -20,15 +20,10 @@
 
 RenderWidgetMouseLockDispatcher::~RenderWidgetMouseLockDispatcher() {}
 
-void RenderWidgetMouseLockDispatcher::SendLockMouseRequest() {
-  blink::WebWidget* web_widget = render_widget_->GetWebWidget();
-  blink::WebLocalFrame* web_local_frame =
-      (web_widget && web_widget->IsWebFrameWidget())
-          ? static_cast<blink::WebFrameWidget*>(web_widget)->LocalRoot()
-          : nullptr;
-
+void RenderWidgetMouseLockDispatcher::SendLockMouseRequest(
+    blink::WebLocalFrame* requester_frame) {
   bool user_gesture =
-      blink::WebUserGestureIndicator::IsProcessingUserGesture(web_local_frame);
+      blink::WebUserGestureIndicator::IsProcessingUserGesture(requester_frame);
   render_widget_->Send(new WidgetHostMsg_LockMouse(render_widget_->routing_id(),
                                                    user_gesture, false));
 }
diff --git a/content/renderer/render_widget_mouse_lock_dispatcher.h b/content/renderer/render_widget_mouse_lock_dispatcher.h
index b9f5cb2d..4e81790 100644
--- a/content/renderer/render_widget_mouse_lock_dispatcher.h
+++ b/content/renderer/render_widget_mouse_lock_dispatcher.h
@@ -27,7 +27,7 @@
 
  private:
   // MouseLockDispatcher implementation.
-  void SendLockMouseRequest() override;
+  void SendLockMouseRequest(blink::WebLocalFrame* requester_frame) override;
   void SendUnlockMouseRequest() override;
 
   void OnLockMouseACK(bool succeeded);
diff --git a/content/shell/test_runner/web_widget_test_proxy.cc b/content/shell/test_runner/web_widget_test_proxy.cc
index 4536afb..33c7bce 100644
--- a/content/shell/test_runner/web_widget_test_proxy.cc
+++ b/content/shell/test_runner/web_widget_test_proxy.cc
@@ -89,7 +89,7 @@
   }
 }
 
-bool WebWidgetTestProxy::RequestPointerLock() {
+bool WebWidgetTestProxy::RequestPointerLock(blink::WebLocalFrame*) {
   return GetViewTestRunner()->RequestPointerLock();
 }
 
diff --git a/content/shell/test_runner/web_widget_test_proxy.h b/content/shell/test_runner/web_widget_test_proxy.h
index 04b356f..6abcf04 100644
--- a/content/shell/test_runner/web_widget_test_proxy.h
+++ b/content/shell/test_runner/web_widget_test_proxy.h
@@ -65,7 +65,7 @@
 
   // WebWidgetClient implementation.
   void ScheduleAnimation() override;
-  bool RequestPointerLock() override;
+  bool RequestPointerLock(blink::WebLocalFrame* requester_frame) override;
   void RequestPointerUnlock() override;
   bool IsPointerLocked() override;
   void SetToolTipText(const blink::WebString& text,
diff --git a/third_party/blink/public/web/web_widget_client.h b/third_party/blink/public/web/web_widget_client.h
index 94d0749..2c4e56a 100644
--- a/third_party/blink/public/web/web_widget_client.h
+++ b/third_party/blink/public/web/web_widget_client.h
@@ -73,6 +73,7 @@
 struct WebFloatPoint;
 struct WebFloatRect;
 struct WebFloatSize;
+class WebLocalFrame;
 
 class WebWidgetClient {
  public:
@@ -145,12 +146,14 @@
   // Called when a tooltip should be shown at the current cursor position.
   virtual void SetToolTipText(const WebString&, WebTextDirection hint) {}
 
-  // Requests to lock the mouse cursor. If true is returned, the success
-  // result will be asynchronously returned via a single call to
-  // WebWidget::didAcquirePointerLock() or
+  // Requests to lock the mouse cursor for the |requester_frame| in the
+  // widget. If true is returned, the success result will be asynchronously
+  // returned via a single call to WebWidget::didAcquirePointerLock() or
   // WebWidget::didNotAcquirePointerLock().
   // If false, the request has been denied synchronously.
-  virtual bool RequestPointerLock() { return false; }
+  virtual bool RequestPointerLock(WebLocalFrame* requester_frame) {
+    return false;
+  }
 
   // Cause the pointer lock to be released. This may be called at any time,
   // including when a lock is pending but not yet acquired.
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 2df0aef..f033c08 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -1122,7 +1122,7 @@
   return WebLocalFrameImpl::FromFrame(frame)
       ->LocalRootFrameWidget()
       ->Client()
-      ->RequestPointerLock();
+      ->RequestPointerLock(WebLocalFrameImpl::FromFrame(frame));
 }
 
 void ChromeClientImpl::RequestPointerUnlock(LocalFrame* frame) {