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 973f0b5f6fa16044c00660840fd25fb1e58e541f..7f9e98aef5651a524093721be9fe20d5ba632e4d 100644 |
| --- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc |
| +++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc |
| @@ -48,6 +48,7 @@ |
| #include "content/public/test/test_utils.h" |
| #include "content/shell/browser/shell.h" |
| #include "content/test/content_browser_test_utils_internal.h" |
| +#include "content/test/test_frame_navigation_observer.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/request_handler_util.h" |
| @@ -2357,6 +2358,137 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, |
| shell(), embedded_test_server()->GetURL("b.com", "/title3.html"))); |
| } |
| +// Ensure that we don't crash the renderer in CreateRenderView if a proxy goes |
| +// away between swapout and the next navigation. See https://crbug.com/581912. |
| +IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, |
| + CreateRenderViewAfterProcessKillAndClosedProxy) { |
| + StartEmbeddedServer(); |
| + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| + ->GetFrameTree() |
| + ->root(); |
| + |
| + // Give an initial page an unload handler that never completes. |
| + EXPECT_TRUE(NavigateToURL( |
| + shell(), embedded_test_server()->GetURL("a.com", "/title1.html"))); |
| + EXPECT_TRUE(ExecuteScript(root->current_frame_host(), |
| + "window.onunload=function(e){ while(1); };\n")); |
| + |
| + // Open a popup in the same process. |
| + Shell* new_shell = |
| + OpenPopup(shell()->web_contents(), GURL(url::kAboutBlankURL), "foo"); |
| + FrameTreeNode* popup_root = |
| + static_cast<WebContentsImpl*>(new_shell->web_contents()) |
| + ->GetFrameTree() |
| + ->root(); |
| + EXPECT_EQ(shell()->web_contents()->GetSiteInstance(), |
| + new_shell->web_contents()->GetSiteInstance()); |
| + |
| + // Navigate the first tab to a different site, and only wait for commit, not |
| + // load stop. |
| + RenderFrameHostImpl* rfh_a = root->current_frame_host(); |
| + SiteInstanceImpl* site_instance_a = rfh_a->GetSiteInstance(); |
| + TestFrameNavigationObserver commit_observer(root); |
| + shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title2.html")); |
| + commit_observer.WaitForCommit(); |
| + rfh_a->ResetSwapOutTimerForTesting(); |
| + EXPECT_NE(shell()->web_contents()->GetSiteInstance(), |
| + new_shell->web_contents()->GetSiteInstance()); |
| + EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site_instance_a)); |
| + |
| + // The previous RFH should still be pending deletion, as we wait for either |
| + // the SwapOut ACK or a timeout. |
| + ASSERT_TRUE(root->render_manager()->IsPendingDeletion(rfh_a)); |
| + |
| + // Kill the old process. |
| + RenderProcessHost* process = rfh_a->GetProcess(); |
| + RenderProcessHostWatcher crash_observer( |
| + process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| + process->Shutdown(0, false); |
| + crash_observer.Wait(); |
| + EXPECT_FALSE(popup_root->current_frame_host()->IsRenderFrameLive()); |
| + // |rfh_a| is now deleted, thanks to the bug fix. |
| + |
| + // Close the popup so there is no proxy for a.com in the original tab. |
| + new_shell->Close(); |
| + EXPECT_FALSE( |
| + root->render_manager()->GetRenderFrameProxyHost(site_instance_a)); |
| + |
| + // Go back in the main frame from b.com to a.com. In https://crbug.com/581912, |
| + // the browser process would crash here because there was no main frame |
| + // routing ID or proxy in RVHI::CreateRenderView. |
| + { |
| + TestNavigationObserver back_nav_load_observer(shell()->web_contents()); |
| + shell()->web_contents()->GetController().GoBack(); |
| + back_nav_load_observer.Wait(); |
| + } |
| +} |
| + |
| +// Ensure that we don't create in RenderViewImpl::Init if a proxy is created |
|
alexmos
2016/03/29 18:43:01
nit: word missing after "don't create"?
Charlie Reis
2016/03/29 20:29:02
Heh, s/create/crash/. Thanks for catching it.
|
| +// after swapout and before navigation. See https://crbug.com/544755. |
| +IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, |
| + RenderViewInitAfterNewProxyAndProcessKill) { |
| + StartEmbeddedServer(); |
| + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| + ->GetFrameTree() |
| + ->root(); |
| + |
| + // Give an initial page an unload handler that never completes. |
| + EXPECT_TRUE(NavigateToURL( |
| + shell(), embedded_test_server()->GetURL("a.com", "/title1.html"))); |
| + EXPECT_TRUE(ExecuteScript(root->current_frame_host(), |
| + "window.onunload=function(e){ while(1); };\n")); |
| + |
| + // Navigate the tab to a different site, and only wait for commit, not load |
| + // stop. |
| + RenderFrameHostImpl* rfh_a = root->current_frame_host(); |
| + SiteInstanceImpl* site_instance_a = rfh_a->GetSiteInstance(); |
| + TestFrameNavigationObserver commit_observer(root); |
| + shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title2.html")); |
| + commit_observer.WaitForCommit(); |
| + rfh_a->ResetSwapOutTimerForTesting(); |
| + EXPECT_NE(site_instance_a, shell()->web_contents()->GetSiteInstance()); |
| + |
| + // The previous RFH should still be pending deletion, as we wait for either |
| + // the SwapOut ACK or a timeout. |
| + ASSERT_TRUE(root->render_manager()->IsPendingDeletion(rfh_a)); |
|
alexmos
2016/03/29 18:43:02
Is it worth checking that the view is pending dele
Charlie Reis
2016/03/29 20:29:02
Good idea. Done. (I made them EXPECT rather than
|
| + |
| + // Open a popup in the new B process. |
| + Shell* new_shell = |
| + OpenPopup(shell()->web_contents(), GURL(url::kAboutBlankURL), "foo"); |
| + EXPECT_EQ(shell()->web_contents()->GetSiteInstance(), |
| + new_shell->web_contents()->GetSiteInstance()); |
| + |
| + // Navigate the popup to the original site, but don't wait for commit (which |
| + // won't happen). This creates a proxy in the original tab, alongside the |
| + // RFH and RVH pending deletion. |
| + new_shell->LoadURL(embedded_test_server()->GetURL("a.com", "/title2.html")); |
| + if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { |
|
alexmos
2016/03/29 18:43:02
Is this the right thing to use? (As opposed to Are
Charlie Reis
2016/03/29 20:29:02
Actually, I don't remember why I needed to guard t
|
| + EXPECT_TRUE( |
| + root->render_manager()->GetRenderFrameProxyHost(site_instance_a)); |
| + } |
| + |
| + // Kill the old process. |
| + RenderProcessHost* process = rfh_a->GetProcess(); |
| + RenderProcessHostWatcher crash_observer( |
| + process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| + process->Shutdown(0, false); |
| + crash_observer.Wait(); |
| + // |rfh_a| is now deleted, thanks to the bug fix. |
| + |
| + // Go back in the main frame from b.com to a.com. |
| + { |
| + TestNavigationObserver back_nav_load_observer(shell()->web_contents()); |
| + shell()->web_contents()->GetController().GoBack(); |
| + back_nav_load_observer.Wait(); |
| + } |
| + |
| + // In https://crbug.com/581912, the renderer process would crash here because |
| + // there was a frame, view, and proxy (and is_swapped_out was true). |
| + EXPECT_EQ(site_instance_a, root->current_frame_host()->GetSiteInstance()); |
| + EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); |
| + EXPECT_TRUE(new_shell->web_contents()->GetMainFrame()->IsRenderFrameLive()); |
| +} |
| + |
| // Ensure that we use the same pending RenderFrameHost if a second navigation to |
| // its site occurs before it commits. Otherwise the renderer process will have |
| // two competing pending RenderFrames that both try to swap with the same |