Commit d73635b0 authored by Nick Carter's avatar Nick Carter Committed by Commit Bot

Add mechanism to test inner WebContents from a content_browsertest.

Write a test of mouse_lock_widget_ cleanup. Fix WebContentsImpl so that it passes.

Bug: 820593, 821187

Change-Id: I99cd6b8c728f7e4501205574db8d17b206f2856e
Reviewed-on: https://chromium-review.googlesource.com/957479
Commit-Queue: Nick Carter <nick@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarLucas Gadani <lfg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542865}
parent 7b4e16aa
...@@ -115,7 +115,7 @@ class PointerLockBrowserTest : public ContentBrowserTest { ...@@ -115,7 +115,7 @@ class PointerLockBrowserTest : public ContentBrowserTest {
return static_cast<WebContentsImpl*>(shell()->web_contents()); return static_cast<WebContentsImpl*>(shell()->web_contents());
} }
private: protected:
MockPointerLockWebContentsDelegate web_contents_delegate_; MockPointerLockWebContentsDelegate web_contents_delegate_;
}; };
...@@ -310,6 +310,78 @@ IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockChildFrameDetached) { ...@@ -310,6 +310,78 @@ IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockChildFrameDetached) {
web_contents()->GetMouseLockWidget()); web_contents()->GetMouseLockWidget());
} }
// Tests that the browser will unlock the pointer if a RenderWidgetHostView that
// holds the pointer lock crashes.
IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest,
PointerLockInnerContentsCrashes) {
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();
// Attach an inner WebContents; it's owned by the FrameTree, so we obtain an
// observer to it.
WebContents* inner_contents = CreateAndAttachInnerContents(
root->child_at(0)->child_at(0)->current_frame_host());
WebContentsDestroyedWatcher inner_death_observer(inner_contents);
// Override the delegate so that we can stub out pointer lock events.
inner_contents->SetDelegate(&web_contents_delegate_);
// Navigate the inner webcontents to a page.
EXPECT_TRUE(NavigateToURLFromRenderer(
inner_contents, embedded_test_server()->GetURL(
"c.com", "/cross_site_iframe_factory.html?c(d)")));
// Request a pointer lock to the inner WebContents's document.body.
std::string result;
EXPECT_TRUE(ExecuteScriptAndExtractString(inner_contents->GetMainFrame(), R"(
(new Promise((resolve, reject) => {
document.addEventListener('pointerlockchange', resolve);
document.addEventListener('pointerlockerror', reject);
}).then(() => {
window.domAutomationController.send(
(document.pointerLockElement == document.body) ?
"success" : "error");
}).catch(error => {
window.domAutomationController.send("" + error);
}));
document.body.requestPointerLock();)",
&result));
EXPECT_EQ("success", result);
// Root (platform) RenderWidgetHostView should have the pointer locked.
EXPECT_TRUE(root->current_frame_host()->GetView()->IsMouseLocked());
// The widget doing the lock is the one from the inner WebContents. A link
// to that RWH is saved into the outer webcontents.
RenderWidgetHost* expected_lock_widget =
inner_contents->GetMainFrame()->GetView()->GetRenderWidgetHost();
EXPECT_EQ(expected_lock_widget, web_contents()->GetMouseLockWidget());
EXPECT_EQ(expected_lock_widget, web_contents()->mouse_lock_widget_);
EXPECT_EQ(expected_lock_widget,
static_cast<WebContentsImpl*>(inner_contents)->mouse_lock_widget_);
// Crash the subframe process.
RenderProcessHost* crash_process =
root->child_at(0)->current_frame_host()->GetProcess();
RenderProcessHostWatcher crash_observer(
crash_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
crash_process->Shutdown(0);
crash_observer.Wait();
// Wait for destruction of |inner_contents|.
inner_death_observer.Wait();
inner_contents = nullptr;
// This should cancel the pointer lock.
EXPECT_EQ(nullptr, web_contents()->GetMouseLockWidget());
EXPECT_EQ(nullptr, web_contents()->mouse_lock_widget_);
EXPECT_FALSE(web_contents()->HasMouseLock(
root->current_frame_host()->GetRenderWidgetHost()));
}
IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockWheelEventRouting) { IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockWheelEventRouting) {
GURL main_url(embedded_test_server()->GetURL( GURL main_url(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)")); "a.com", "/cross_site_iframe_factory.html?a(b)"));
......
...@@ -605,6 +605,9 @@ WebContentsImpl::~WebContentsImpl() { ...@@ -605,6 +605,9 @@ WebContentsImpl::~WebContentsImpl() {
outermost->SetAsFocusedWebContentsIfNecessary(); outermost->SetAsFocusedWebContentsIfNecessary();
} }
if (mouse_lock_widget_)
mouse_lock_widget_->RejectMouseLockOrUnlockIfNecessary();
for (FrameTreeNode* node : frame_tree_.Nodes()) { for (FrameTreeNode* node : frame_tree_.Nodes()) {
// Delete all RFHs pending shutdown, which will lead the corresponding RVHs // Delete all RFHs pending shutdown, which will lead the corresponding RVHs
// to be shutdown and be deleted as well. // to be shutdown and be deleted as well.
......
...@@ -980,6 +980,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, ...@@ -980,6 +980,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
FRIEND_TEST_ALL_PREFIXES(DevToolsProtocolTest, PageDisableWithOpenedDialog); FRIEND_TEST_ALL_PREFIXES(DevToolsProtocolTest, PageDisableWithOpenedDialog);
FRIEND_TEST_ALL_PREFIXES(DevToolsProtocolTest, FRIEND_TEST_ALL_PREFIXES(DevToolsProtocolTest,
PageDisableWithNoDialogManager); PageDisableWithNoDialogManager);
FRIEND_TEST_ALL_PREFIXES(PointerLockBrowserTest,
PointerLockInnerContentsCrashes);
// So |find_request_manager_| can be accessed for testing. // So |find_request_manager_| can be accessed for testing.
friend class FindRequestManagerTest; friend class FindRequestManagerTest;
......
...@@ -20,8 +20,11 @@ ...@@ -20,8 +20,11 @@
#include "base/values.h" #include "base/values.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "components/variations/variations_params_manager.h" #include "components/variations/variations_params_manager.h"
#include "content/browser/frame_host/render_frame_host_delegate.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/common/url_schemes.h" #include "content/common/url_schemes.h"
#include "content/public/browser/browser_child_process_host_iterator.h" #include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/browser/browser_plugin_guest_delegate.h"
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
...@@ -222,6 +225,60 @@ void DeprecatedEnableFeatureWithParam(const base::Feature& feature, ...@@ -222,6 +225,60 @@ void DeprecatedEnableFeatureWithParam(const base::Feature& feature,
kFakeTrialName, kFakeTrialGroupName, param_values, command_line); kFakeTrialName, kFakeTrialGroupName, param_values, command_line);
} }
namespace {
// Helper class for CreateAndAttachInnerContents.
//
// TODO(lfg): https://crbug.com/821187 Inner webcontentses currently require
// supplying a BrowserPluginGuestDelegate; however, the oopif architecture
// doesn't really require it. Refactor this so that we can create an inner
// contents without any of the guest machinery.
class InnerWebContentsHelper : public WebContentsObserver,
public BrowserPluginGuestDelegate {
public:
explicit InnerWebContentsHelper(WebContents* outer_contents)
: WebContentsObserver(), outer_contents_(outer_contents) {}
~InnerWebContentsHelper() override = default;
// BrowserPluginGuestDelegate:
WebContents* GetOwnerWebContents() const override { return outer_contents_; }
// WebContentsObserver:
void WebContentsDestroyed() override { delete this; }
void SetInnerWebContents(WebContents* inner_web_contents) {
Observe(inner_web_contents);
}
private:
WebContents* outer_contents_;
DISALLOW_COPY_AND_ASSIGN(InnerWebContentsHelper);
};
} // namespace
WebContents* CreateAndAttachInnerContents(RenderFrameHost* rfh) {
WebContents* outer_contents =
static_cast<RenderFrameHostImpl*>(rfh)->delegate()->GetAsWebContents();
if (!outer_contents)
return nullptr;
auto guest_delegate =
std::make_unique<InnerWebContentsHelper>(outer_contents);
WebContents::CreateParams inner_params(outer_contents->GetBrowserContext());
inner_params.guest_delegate = guest_delegate.get();
WebContents* inner_contents = WebContents::Create(inner_params);
// Attach. |inner_contents| becomes owned by |outer_contents|.
inner_contents->AttachToOuterWebContentsFrame(outer_contents, rfh);
// |guest_delegate| becomes owned by |inner_contents|.
guest_delegate.release()->SetInnerWebContents(inner_contents);
return inner_contents;
}
MessageLoopRunner::MessageLoopRunner(QuitMode quit_mode) MessageLoopRunner::MessageLoopRunner(QuitMode quit_mode)
: quit_mode_(quit_mode), loop_running_(false), quit_closure_called_(false) { : quit_mode_(quit_mode), loop_running_(false), quit_closure_called_(false) {
} }
......
...@@ -101,6 +101,15 @@ void DeprecatedEnableFeatureWithParam(const base::Feature& feature, ...@@ -101,6 +101,15 @@ void DeprecatedEnableFeatureWithParam(const base::Feature& feature,
const std::string& param_value, const std::string& param_value,
base::CommandLine* command_line); base::CommandLine* command_line);
// Creates a WebContents and attaches it as an inner WebContents, replacing
// |rfh| in the frame tree. |rfh| should not be a main frame (in a browser test,
// it should be an <iframe>). Delegate interfaces are mocked out.
//
// Returns a pointer to the inner WebContents, which is now owned by the outer
// WebContents. The caller should be careful when retaining the pointer, as the
// inner WebContents will be deleted if the frame it's attached to goes away.
WebContents* CreateAndAttachInnerContents(RenderFrameHost* rfh);
// Helper class to Run and Quit the message loop. Run and Quit can only happen // Helper class to Run and Quit the message loop. Run and Quit can only happen
// once per instance. Make a new instance for each use. Calling Quit after Run // once per instance. Make a new instance for each use. Calling Quit after Run
// has returned is safe and has no effect. // has returned is safe and has no effect.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment