Chromium Code Reviews| Index: content/browser/pointer_lock_browsertest.cc |
| diff --git a/content/browser/pointer_lock_browsertest.cc b/content/browser/pointer_lock_browsertest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..06bb080903ab9c8377b75aa0f5e549af04dc7459 |
| --- /dev/null |
| +++ b/content/browser/pointer_lock_browsertest.cc |
| @@ -0,0 +1,282 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/browser/pointer_lock_browsertest.h" |
| + |
| +#include "content/browser/frame_host/frame_tree.h" |
| +#include "content/browser/renderer_host/render_widget_host_impl.h" |
| +#include "content/browser/renderer_host/render_widget_host_input_event_router.h" |
| +#include "content/browser/web_contents/web_contents_impl.h" |
| +#include "content/public/browser/web_contents_delegate.h" |
| +#include "content/public/test/browser_test_utils.h" |
| +#include "content/public/test/content_browser_test.h" |
| +#include "content/public/test/content_browser_test_utils.h" |
| +#include "content/public/test/test_utils.h" |
| +#include "content/shell/browser/shell.h" |
| +#include "content/test/content_browser_test_utils_internal.h" |
| +#include "net/dns/mock_host_resolver.h" |
| +#include "net/test/embedded_test_server/embedded_test_server.h" |
| + |
| +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN) |
|
Charlie Reis
2017/02/15 21:38:49
Any reason not to use USE_AURA? Might simplify th
lfg
2017/02/15 22:02:48
Done.
|
| +#include "content/browser/renderer_host/render_widget_host_view_aura.h" |
| +#include "content/browser/web_contents/web_contents_view_aura.h" |
| +#endif |
| + |
| +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN) || \ |
| + defined(OS_MACOSX) |
|
Charlie Reis
2017/02/15 21:38:49
Is there an easier way to do this in the build fil
lfg
2017/02/15 22:02:47
Done.
|
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN) |
| +class MockRenderWidgetHostView : public RenderWidgetHostViewAura { |
| + public: |
| + MockRenderWidgetHostView(RenderWidgetHost* host, bool is_guest_view_hack) |
| + : RenderWidgetHostViewAura(host, is_guest_view_hack), |
| + host_(RenderWidgetHostImpl::From(host)) {} |
| + ~MockRenderWidgetHostView() override { |
| + if (mouse_locked_) |
| + UnlockMouse(); |
| + } |
| + |
| + bool LockMouse() override { |
| + mouse_locked_ = true; |
| + |
|
Charlie Reis
2017/02/15 21:38:49
nit: No need for blank line here.
lfg
2017/02/15 22:02:47
Done.
|
| + return true; |
| + } |
| + |
| + void UnlockMouse() override { |
| + host_->LostMouseLock(); |
| + mouse_locked_ = false; |
| + } |
| + |
| + bool IsMouseLocked() override { return mouse_locked_; } |
| + |
| + bool HasFocus() const override { return true; } |
| + |
| + void OnWindowFocused(aura::Window* gained_focus, |
| + aura::Window* lost_focus) override { |
| + // Ignore window focus events. |
| + } |
| + |
| + RenderWidgetHostImpl* host_; |
| +}; |
| +#endif |
| + |
| +} // namespace |
| + |
| +class MockPointerLockWebContentsDelegate : public WebContentsDelegate { |
| + public: |
| + MockPointerLockWebContentsDelegate() {} |
| + ~MockPointerLockWebContentsDelegate() override {} |
| + |
| + void RequestToLockMouse(WebContents* web_contents, |
| + bool user_gesture, |
| + bool last_unlocked_by_target) override { |
| + web_contents->GotResponseToLockMouseRequest(true); |
| + } |
| + |
| + void LostMouseLock() override {} |
| +}; |
| + |
| +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN) |
| +void InstallCreateHooksForPointerLockBrowserTests() { |
| + WebContentsViewAura::InstallCreateHookForTests( |
| + [](RenderWidgetHost* host, |
| + bool is_guest_view_hack) -> RenderWidgetHostViewAura* { |
| + return new MockRenderWidgetHostView(host, is_guest_view_hack); |
| + }); |
| +} |
| +#endif |
| + |
| +class PointerLockBrowserTest : public ContentBrowserTest { |
| + public: |
| + PointerLockBrowserTest() {} |
| + |
| + protected: |
| + void SetUpCommandLine(base::CommandLine* command_line) override { |
| + IsolateAllSitesForTesting(command_line); |
| + } |
| + |
| + void SetUp() override { |
| + InstallCreateHooksForPointerLockBrowserTests(); |
| + ContentBrowserTest::SetUp(); |
| + } |
| + |
| + void SetUpOnMainThread() override { |
| + host_resolver()->AddRule("*", "127.0.0.1"); |
| + SetupCrossSiteRedirector(embedded_test_server()); |
| + ASSERT_TRUE(embedded_test_server()->Start()); |
| + |
| + web_contents()->SetDelegate(&web_contents_delegate_); |
| + } |
| + |
| + WebContentsImpl* web_contents() const { |
| + return static_cast<WebContentsImpl*>(shell()->web_contents()); |
| + } |
| + |
| + private: |
| + MockPointerLockWebContentsDelegate web_contents_delegate_; |
| +}; |
| + |
| +IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLock) { |
| + GURL main_url(embedded_test_server()->GetURL( |
| + "a.com", "/cross_site_iframe_factory.html?a(b)")); |
| + EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| + |
| + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); |
| + FrameTreeNode* child = root->child_at(0); |
| + |
| + // Request a pointer lock on the root frame's body. |
| + EXPECT_TRUE(ExecuteScript(root, "document.body.requestPointerLock()")); |
| + |
| + // Root frame should have been granted pointer lock. |
| + bool locked = false; |
| + EXPECT_TRUE(ExecuteScriptAndExtractBool(root, |
| + "window.domAutomationController.send(" |
| + "document.pointerLockElement == " |
| + "document.body);", |
| + &locked)); |
| + EXPECT_TRUE(locked); |
| + |
| + // Request a pointer lock on the child frame's body. |
| + EXPECT_TRUE(ExecuteScript(child, "document.body.requestPointerLock()")); |
| + |
| + // Child frame should not be granted pointer lock since the root frame has it. |
| + EXPECT_TRUE(ExecuteScriptAndExtractBool(child, |
| + "window.domAutomationController.send(" |
| + "document.pointerLockElement == " |
| + "document.body);", |
| + &locked)); |
| + EXPECT_FALSE(locked); |
| + |
| + // Release pointer lock on root frame. |
| + EXPECT_TRUE(ExecuteScript(root, "document.exitPointerLock()")); |
| + |
| + // Request a pointer lock on the child frame's body. |
| + EXPECT_TRUE(ExecuteScript(child, "document.body.requestPointerLock()")); |
| + |
| + // Child frame should have been granted pointer lock. |
| + EXPECT_TRUE(ExecuteScriptAndExtractBool(child, |
| + "window.domAutomationController.send(" |
| + "document.pointerLockElement == " |
| + "document.body);", |
| + &locked)); |
| + EXPECT_TRUE(locked); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockEventRouting) { |
| + GURL main_url(embedded_test_server()->GetURL( |
| + "a.com", "/cross_site_iframe_factory.html?a(b)")); |
| + EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| + |
| + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); |
| + FrameTreeNode* child = root->child_at(0); |
| + RenderWidgetHostInputEventRouter* router = |
| + web_contents()->GetInputEventRouter(); |
| + RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>( |
| + root->current_frame_host()->GetView()); |
| + RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>( |
| + child->current_frame_host()->GetView()); |
| + |
| + // Request a pointer lock on the root frame's body. |
| + EXPECT_TRUE(ExecuteScript(root, "document.body.requestPointerLock()")); |
| + |
| + // Root frame should have been granted pointer lock. |
| + bool locked = false; |
| + EXPECT_TRUE(ExecuteScriptAndExtractBool(root, |
| + "window.domAutomationController.send(" |
| + "document.pointerLockElement == " |
| + "document.body);", |
| + &locked)); |
| + EXPECT_TRUE(locked); |
| + |
| + // Add a mouse move event listener to the root frame. |
| + EXPECT_TRUE(ExecuteScript( |
| + root, |
| + "var x; var y; var mX; var mY; document.addEventListener('mousemove', " |
| + "function(e) {x = e.x; y = e.y; mX = e.movementX; mY = e.movementY;});")); |
| + |
| + blink::WebMouseEvent mouse_event(blink::WebInputEvent::MouseMove, |
| + blink::WebInputEvent::NoModifiers, |
| + blink::WebInputEvent::TimeStampForTesting); |
| + mouse_event.x = 10; |
| + mouse_event.y = 10; |
| + mouse_event.movementX = 10; |
| + mouse_event.movementY = 10; |
|
Charlie Reis
2017/02/15 21:38:49
nit: Maybe set these to different values (e.g., 10
lfg
2017/02/15 22:02:47
Done.
|
| + router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo()); |
| + |
| + // Make sure that the renderer handled the input event. |
| + MainThreadFrameObserver root_observer(root_view->GetRenderWidgetHost()); |
| + root_observer.Wait(); |
| + |
| + int x, y, movementX, movementY; |
| + EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| + root, "window.domAutomationController.send(x);", &x)); |
| + EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| + root, "window.domAutomationController.send(y);", &y)); |
| + EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| + root, "window.domAutomationController.send(mX);", &movementX)); |
| + EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| + root, "window.domAutomationController.send(mY);", &movementY)); |
| + EXPECT_EQ(10, x); |
| + EXPECT_EQ(10, y); |
| + EXPECT_EQ(10, movementX); |
| + EXPECT_EQ(10, movementY); |
| + |
| + // Release pointer lock on root frame. |
| + EXPECT_TRUE(ExecuteScript(root, "document.exitPointerLock()")); |
| + |
| + // Request a pointer lock on the child frame's body. |
| + EXPECT_TRUE(ExecuteScript(child, "document.body.requestPointerLock()")); |
| + |
| + // Child frame should have been granted pointer lock. |
| + EXPECT_TRUE(ExecuteScriptAndExtractBool(child, |
| + "window.domAutomationController.send(" |
| + "document.pointerLockElement == " |
| + "document.body);", |
| + &locked)); |
| + EXPECT_TRUE(locked); |
| + |
| + // Add a mouse move event listener to the child frame. |
| + EXPECT_TRUE(ExecuteScript( |
| + child, |
| + "var x; var y; var mX; var mY; document.addEventListener('mousemove', " |
| + "function(e) {x = e.x; y = e.y; mX = e.movementX; mY = e.movementY;});")); |
| + |
| + gfx::Point transformed_point; |
| + root_view->TransformPointToCoordSpaceForView(gfx::Point(0, 0), child_view, |
| + &transformed_point); |
| + |
| + mouse_event.x = -transformed_point.x() + 11; |
| + mouse_event.y = -transformed_point.y() + 11; |
| + mouse_event.movementX = 11; |
| + mouse_event.movementY = 11; |
| + // We use root_view intentionally as the RenderWidgetHostInputEventRouter is |
| + // responsible for correctly routing the event to the child frame. |
| + router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo()); |
| + |
| + // Make sure that the renderer handled the input event. |
| + MainThreadFrameObserver child_observer(child_view->GetRenderWidgetHost()); |
| + child_observer.Wait(); |
| + |
| + EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| + child, "window.domAutomationController.send(x);", &x)); |
| + EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| + child, "window.domAutomationController.send(y);", &y)); |
| + EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| + child, "window.domAutomationController.send(mX);", &movementX)); |
| + EXPECT_TRUE(ExecuteScriptAndExtractInt( |
| + child, "window.domAutomationController.send(mY);", &movementY)); |
| + EXPECT_EQ(11, x); |
| + EXPECT_EQ(11, y); |
| + EXPECT_EQ(11, movementX); |
| + EXPECT_EQ(11, movementY); |
| +} |
| + |
| +} // namespace content |
| + |
| +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN) || |
| + // defined(OS_MACOSX) |