Chromium Code Reviews| Index: content/browser/frame_host/render_frame_host_manager_browsertest.cc |
| diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc |
| index 9de16f8e9c0be905594f840c66bddc33e4f83358..d0b696fda5576c77a2dc9f34f6f75251287032ac 100644 |
| --- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc |
| +++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc |
| @@ -35,6 +35,7 @@ |
| #include "content/shell/browser/shell.h" |
| #include "net/base/net_util.h" |
| #include "net/dns/mock_host_resolver.h" |
| +#include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/spawned_test_server/spawned_test_server.h" |
| using base::ASCIIToUTF16; |
| @@ -87,6 +88,13 @@ class RenderFrameHostManagerTest : public ContentBrowserTest { |
| foo_host_port_.set_host(foo_com_); |
| } |
| + void StartEmbeddedServer() { |
| + // Support multiple sites on the embedded test server. |
| + host_resolver()->AddRule("*", "127.0.0.1"); |
| + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| + SetupCrossSiteRedirector(embedded_test_server()); |
| + } |
| + |
| // Returns a URL on foo.com with the given path. |
| GURL GetCrossSiteURL(const std::string& path) { |
| GURL cross_site_url(test_server()->GetURL(path)); |
| @@ -1722,4 +1730,82 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, |
| shell()->web_contents()->GetRenderProcessHost()->GetID(), file)); |
| } |
| +// This class implements waiting for RenderFrameHost destruction. It relies on |
| +// the fact that RenderFrameDeleted event is fired when RenderFrameHost is |
| +// destroyed. |
| +// Note: RenderFrameDeleted is also fired when the process associated with the |
| +// RenderFrameHost crashes, so this cannot be used in cases where process dying |
| +// is expected. |
|
ncarter (slow)
2015/05/22 23:03:14
If you wanted to make it robust against this, you
ncarter (slow)
2015/05/22 23:04:29
[but I'm okay with just leaving the "Note:" too]
|
| +class RenderFrameHostDestructionObserver : public WebContentsObserver { |
|
ncarter (slow)
2015/05/22 23:03:14
This is pretty similar to https://code.google.com/
nasko
2015/05/22 23:46:49
Might be a good idea. I'd leave that for another C
ncarter (slow)
2015/05/23 00:33:36
Acknowledged.
|
| + public: |
| + explicit RenderFrameHostDestructionObserver(WebContents* web_contents) |
| + : WebContentsObserver(web_contents), |
| + message_loop_runner_(new MessageLoopRunner), |
| + started_watching(false) {} |
| + ~RenderFrameHostDestructionObserver() override {} |
| + |
| + void Observe(RenderFrameHost* rfh) { |
|
ncarter (slow)
2015/05/22 23:03:14
This shadows WebContentsObserver::Observe, which i
nasko
2015/05/22 23:46:50
Done.
|
| + started_watching = true; |
| + watched_render_frame_hosts_.insert(rfh); |
| + } |
| + |
| + void Wait() { |
| + if (!started_watching) |
| + return; |
| + message_loop_runner_->Run(); |
| + } |
| + |
| + private: |
| + // WebContentsObserver implementation: |
| + void RenderFrameDeleted(RenderFrameHost* rfh) override { |
|
ncarter (slow)
2015/05/22 23:03:14
This should be public.
nasko
2015/05/22 23:46:49
Why should it? I've changed it, but curious about
ncarter (slow)
2015/05/23 00:33:36
1. When you have something that's private in a der
nasko
2015/05/23 02:23:08
Acknowledged.
|
| + watched_render_frame_hosts_.erase(rfh); |
| + if (!started_watching) |
| + return; |
| + if (watched_render_frame_hosts_.size() == 0) { |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, message_loop_runner_->QuitClosure()); |
| + } |
| + } |
| + |
| + scoped_refptr<MessageLoopRunner> message_loop_runner_; |
| + bool started_watching; |
| + std::set<RenderFrameHost*> watched_render_frame_hosts_; |
| +}; |
| + |
| +// Ensures that no RenderFrameHost/RenderViewHost objects are leaked when |
| +// doing a simple cross-process navigation. |
| +IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, |
| + CleanupOnCrossProcessNavigation) { |
| + StartEmbeddedServer(); |
| + |
| + // Do an initial navigation and capture objects we expect to be cleaned up |
| + // on cross-process navigation. |
| + GURL start_url = embedded_test_server()->GetURL("/title1.html"); |
| + NavigateToURL(shell(), start_url); |
| + |
| + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| + ->GetFrameTree() |
| + ->root(); |
| + int32 orig_site_instance_id = |
| + root->current_frame_host()->GetSiteInstance()->GetId(); |
| + int initial_process_id = |
| + root->current_frame_host()->GetSiteInstance()->GetProcess()->GetID(); |
| + int initial_rfh_id = root->current_frame_host()->GetRoutingID(); |
| + int initial_rvh_id = |
| + root->current_frame_host()->render_view_host()->GetRoutingID(); |
| + |
| + // Navigate cross-process and ensure that cleanup is performed as expected. |
| + GURL cross_site_url = |
| + embedded_test_server()->GetURL("foo.com", "/title2.html"); |
| + RenderFrameHostDestructionObserver rfh_observer(shell()->web_contents()); |
| + rfh_observer.Observe(root->current_frame_host()); |
| + NavigateToURL(shell(), cross_site_url); |
| + rfh_observer.Wait(); |
| + |
| + EXPECT_NE(orig_site_instance_id, |
| + root->current_frame_host()->GetSiteInstance()->GetId()); |
| + EXPECT_FALSE(RenderFrameHost::FromID(initial_process_id, initial_rfh_id)); |
| + EXPECT_FALSE(RenderViewHost::FromID(initial_process_id, initial_rvh_id)); |
| +} |
| + |
| } // namespace content |