diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-10-17 11:51:44 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-10-21 08:14:36 +0000 |
commit | 28150e5eb962a6e51bb7e8ba210c3076be017acf (patch) | |
tree | aaa500fd88587badbb18f20b6a3d317a8d472829 | |
parent | 729e9b30bbf2f70b2a37ba313d6e2655ee0cf4e0 (diff) | |
download | qtwebengine-chromium-28150e5eb962a6e51bb7e8ba210c3076be017acf.tar.gz |
[Backport] Security issue 981597
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: dcheng@chromium.org
Commit-Queue: Navid Zolghadr <nzolghadr@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Reviewed-by: Navid Zolghadr <nzolghadr@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#684359}
Cr-Commit-Position: refs/branch-heads/3865@{#283}
Cr-Branched-From: 0cdcc6158160790658d1f033d3db873603250124-refs/heads/master@{#681094}
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
13 files changed, 102 insertions, 41 deletions
diff --git a/chromium/content/browser/pointer_lock_browsertest.cc b/chromium/content/browser/pointer_lock_browsertest.cc index 05b56a7255e..8659230ac22 100644 --- a/chromium/content/browser/pointer_lock_browsertest.cc +++ b/chromium/content/browser/pointer_lock_browsertest.cc @@ -34,7 +34,7 @@ class MockPointerLockWebContentsDelegate : public WebContentsDelegate { 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 {} @@ -116,7 +116,7 @@ class PointerLockBrowserTest : public ContentBrowserTest { 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)); @@ -148,6 +148,46 @@ IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLock) { 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/chromium/content/renderer/browser_plugin/browser_plugin.cc b/chromium/content/renderer/browser_plugin/browser_plugin.cc index 9042b135753..dd955ecfd38 100644 --- a/chromium/content/renderer/browser_plugin/browser_plugin.cc +++ b/chromium/content/renderer/browser_plugin/browser_plugin.cc @@ -386,7 +386,9 @@ void BrowserPlugin::OnSetMouseLock(int browser_plugin_instance_id, 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/chromium/content/renderer/mouse_lock_dispatcher.cc b/chromium/content/renderer/mouse_lock_dispatcher.cc index 0749c9c9c21..84c06db5384 100644 --- a/chromium/content/renderer/mouse_lock_dispatcher.cc +++ b/chromium/content/renderer/mouse_lock_dispatcher.cc @@ -18,14 +18,15 @@ MouseLockDispatcher::MouseLockDispatcher() 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/chromium/content/renderer/mouse_lock_dispatcher.h b/chromium/content/renderer/mouse_lock_dispatcher.h index d5c3be85cca..0e76aa6c3ca 100644 --- a/chromium/content/renderer/mouse_lock_dispatcher.h +++ b/chromium/content/renderer/mouse_lock_dispatcher.h @@ -10,6 +10,7 @@ namespace blink { class WebMouseEvent; +class WebLocalFrame; } // namespace blink namespace content { @@ -31,9 +32,10 @@ class CONTENT_EXPORT MouseLockDispatcher { 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 @@ class CONTENT_EXPORT MouseLockDispatcher { 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/chromium/content/renderer/mouse_lock_dispatcher_browsertest.cc b/chromium/content/renderer/mouse_lock_dispatcher_browsertest.cc index 48936a86312..059d7f00fe7 100644 --- a/chromium/content/renderer/mouse_lock_dispatcher_browsertest.cc +++ b/chromium/content/renderer/mouse_lock_dispatcher_browsertest.cc @@ -55,13 +55,14 @@ class MouseLockDispatcherTest : public RenderViewTest { } // 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 @@ TEST_F(MouseLockDispatcherTest, BasicWebWidget) { 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 @@ TEST_F(MouseLockDispatcherTest, BasicMockLockTarget) { 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 @@ TEST_F(MouseLockDispatcherTest, BasicMockLockTarget) { 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 @@ TEST_F(MouseLockDispatcherTest, DeleteAndUnlock) { 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 @@ TEST_F(MouseLockDispatcherTest, DeleteWithPendingLockSuccess) { 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 @@ TEST_F(MouseLockDispatcherTest, DeleteWithPendingLockFail) { 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 @@ TEST_F(MouseLockDispatcherTest, MouseEventsNotReceived) { 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 @@ TEST_F(MouseLockDispatcherTest, MultipleTargets) { 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 @@ TEST_F(MouseLockDispatcherTest, MultipleTargets) { // 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/chromium/content/renderer/pepper/pepper_plugin_instance_impl.cc b/chromium/content/renderer/pepper/pepper_plugin_instance_impl.cc index f207d217517..67ff7d984f2 100644 --- a/chromium/content/renderer/pepper/pepper_plugin_instance_impl.cc +++ b/chromium/content/renderer/pepper/pepper_plugin_instance_impl.cc @@ -3320,7 +3320,9 @@ bool PepperPluginInstanceImpl::IsMouseLocked() { } bool PepperPluginInstanceImpl::LockMouse() { - return GetMouseLockDispatcher()->LockMouse(GetOrCreateLockTargetAdapter()); + WebLocalFrame* requester_frame = container_->GetDocument().GetFrame(); + return GetMouseLockDispatcher()->LockMouse(GetOrCreateLockTargetAdapter(), + requester_frame); } MouseLockDispatcher::LockTarget* diff --git a/chromium/content/renderer/render_widget.cc b/chromium/content/renderer/render_widget.cc index 87163fe92ce..1723c7b7522 100644 --- a/chromium/content/renderer/render_widget.cc +++ b/chromium/content/renderer/render_widget.cc @@ -3187,8 +3187,9 @@ gfx::Point RenderWidget::ConvertWindowPointToViewport(const gfx::Point& point) { 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/chromium/content/renderer/render_widget.h b/chromium/content/renderer/render_widget.h index 4e1534a98a9..cf4868e50f7 100644 --- a/chromium/content/renderer/render_widget.h +++ b/chromium/content/renderer/render_widget.h @@ -376,7 +376,7 @@ class CONTENT_EXPORT RenderWidget void ShowVirtualKeyboardOnElementFocus() override; void ConvertViewportToWindow(blink::WebRect* 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/chromium/content/renderer/render_widget_fullscreen_pepper.cc b/chromium/content/renderer/render_widget_fullscreen_pepper.cc index 9774ed67f75..af13410cdb4 100644 --- a/chromium/content/renderer/render_widget_fullscreen_pepper.cc +++ b/chromium/content/renderer/render_widget_fullscreen_pepper.cc @@ -57,7 +57,7 @@ class FullscreenMouseLockDispatcher : public MouseLockDispatcher { private: // MouseLockDispatcher implementation. - void SendLockMouseRequest() override; + void SendLockMouseRequest(blink::WebLocalFrame* requester_frame) override; void SendUnlockMouseRequest() override; RenderWidgetFullscreenPepper* widget_; @@ -116,7 +116,11 @@ FullscreenMouseLockDispatcher::FullscreenMouseLockDispatcher( 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/chromium/content/renderer/render_widget_mouse_lock_dispatcher.cc b/chromium/content/renderer/render_widget_mouse_lock_dispatcher.cc index bac8dc5749d..456f5e8bb94 100644 --- a/chromium/content/renderer/render_widget_mouse_lock_dispatcher.cc +++ b/chromium/content/renderer/render_widget_mouse_lock_dispatcher.cc @@ -20,15 +20,10 @@ RenderWidgetMouseLockDispatcher::RenderWidgetMouseLockDispatcher( 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/chromium/content/renderer/render_widget_mouse_lock_dispatcher.h b/chromium/content/renderer/render_widget_mouse_lock_dispatcher.h index b9f5cb2dce9..4e8179007d5 100644 --- a/chromium/content/renderer/render_widget_mouse_lock_dispatcher.h +++ b/chromium/content/renderer/render_widget_mouse_lock_dispatcher.h @@ -27,7 +27,7 @@ class RenderWidgetMouseLockDispatcher : public MouseLockDispatcher { private: // MouseLockDispatcher implementation. - void SendLockMouseRequest() override; + void SendLockMouseRequest(blink::WebLocalFrame* requester_frame) override; void SendUnlockMouseRequest() override; void OnLockMouseACK(bool succeeded); diff --git a/chromium/third_party/blink/public/web/web_widget_client.h b/chromium/third_party/blink/public/web/web_widget_client.h index b19ca4cc153..8c604141bbe 100644 --- a/chromium/third_party/blink/public/web/web_widget_client.h +++ b/chromium/third_party/blink/public/web/web_widget_client.h @@ -59,6 +59,7 @@ struct WebCursorInfo; struct WebFloatPoint; struct WebFloatRect; struct WebFloatSize; +class WebLocalFrame; class WebWidgetClient { public: @@ -120,12 +121,14 @@ class WebWidgetClient { // 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/chromium/third_party/blink/renderer/core/page/chrome_client_impl.cc b/chromium/third_party/blink/renderer/core/page/chrome_client_impl.cc index 939f59cac34..6837050955b 100644 --- a/chromium/third_party/blink/renderer/core/page/chrome_client_impl.cc +++ b/chromium/third_party/blink/renderer/core/page/chrome_client_impl.cc @@ -1026,7 +1026,7 @@ bool ChromeClientImpl::RequestPointerLock(LocalFrame* frame) { return WebLocalFrameImpl::FromFrame(frame) ->LocalRootFrameWidget() ->Client() - ->RequestPointerLock(); + ->RequestPointerLock(WebLocalFrameImpl::FromFrame(frame)); } void ChromeClientImpl::RequestPointerUnlock(LocalFrame* frame) { |