Commit 4562582b authored by Alex Moshchuk's avatar Alex Moshchuk Committed by Commit Bot

Prevent a late swapout ACK from marking a reused active RVH as inactive.

This CL fixes a bug where IsSwappedOut() always marked the RVH as
inactive and swapped-out.  This is incorrect in the case where the RVH
has been reused by another navigation that has already made it active.
The timing for that is hard to nail: usually, reusing a renderer
process for another navigation implies that the new navigation needs
to wait for a previous unload handler to finish running before a new
navigation can be started/committed.  However, if the navigation that
reuses the RVH is coming off of a cross-site sad tab page, it will
commit the pending RFH in the browser immediately, before the
navigation actually takes places in the renderer, which makes it
possible for a late swapout ACK from an unload handler to arrive after
CommitPending() has already marked the reused RVH as active.

Bug: 823567
Change-Id: I934ad70d3bd92d03f795291d603fb584f24b6c54
Reviewed-on: https://chromium-review.googlesource.com/971304
Commit-Queue: Alex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarLucas Gadani <lfg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#544801}
parent 08e4516b
......@@ -1903,11 +1903,11 @@ void RenderFrameHostImpl::OnSwappedOut() {
ClearAllWebUI();
// If this is a main frame RFH that's about to be deleted, update its RVH's
// swapped-out state here. https://crbug.com/505887
if (frame_tree_node_->IsMainFrame()) {
render_view_host_->set_is_active(false);
// swapped-out state here. https://crbug.com/505887. This should only be
// done if the RVH hasn't been already reused and marked as active by another
// navigation. See https://crbug.com/823567.
if (frame_tree_node_->IsMainFrame() && !render_view_host_->is_active())
render_view_host_->set_is_swapped_out(true);
}
bool deleted =
frame_tree_node_->render_manager()->DeleteFromPendingList(this);
......
......@@ -755,6 +755,8 @@ class CONTENT_EXPORT RenderFrameHostImpl
FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, FindImmediateLocalRoots);
FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
RenderViewHostIsNotReusedAfterDelayedSwapOutACK);
FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
RenderViewHostStaysActiveWithLateSwapoutACK);
FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
LoadEventForwardingWhilePendingDeletion);
FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
......
......@@ -10859,6 +10859,63 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SizeAvailableAfterCommit) {
EXPECT_GT(height, 0);
}
// Test that a late swapout ACK won't incorrectly mark RenderViewHost as
// inactive if it's already been reused and switched to active by another
// navigation. See https://crbug.com/823567.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
RenderViewHostStaysActiveWithLateSwapoutACK) {
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
// Open a popup and navigate it to a.com.
Shell* popup = OpenPopup(
shell(), embedded_test_server()->GetURL("a.com", "/title2.html"), "foo");
WebContentsImpl* popup_contents =
static_cast<WebContentsImpl*>(popup->web_contents());
RenderFrameHostImpl* rfh = popup_contents->GetMainFrame();
RenderViewHostImpl* rvh = rfh->render_view_host();
// Disable the swapout ACK and the swapout timer.
scoped_refptr<SwapoutACKMessageFilter> filter = new SwapoutACKMessageFilter();
rfh->GetProcess()->AddFilter(filter.get());
rfh->DisableSwapOutTimerForTesting();
// Navigate popup to b.com. Because there's an opener, the RVH for a.com
// stays around in swapped-out state.
EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(
popup, embedded_test_server()->GetURL("b.com", "/title3.html")));
EXPECT_FALSE(rvh->is_active());
// The old RenderFrameHost is now pending deletion.
ASSERT_TRUE(rfh->IsRenderFrameLive());
ASSERT_FALSE(rfh->is_active());
// Kill the b.com process.
RenderProcessHost* b_process = popup_contents->GetMainFrame()->GetProcess();
RenderProcessHostWatcher crash_observer(
b_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
b_process->Shutdown(0);
crash_observer.Wait();
// Go back in the popup from b.com to a.com/title2.html. Because the current
// b.com RFH is dead, the new RFH is committed right away (without waiting
// for renderer to commit), so that users don't need to look at the sad tab.
TestNavigationObserver back_observer(popup_contents);
popup_contents->GetController().GoBack();
// Pretend that the original RFH in a.com now finishes running its unload
// handler and sends the swapout ACK.
rfh->OnSwappedOut();
// Wait for the new a.com navigation to finish.
back_observer.Wait();
// The RVH for a.com should've been reused, and it should be active. Its
// main frame should've been updated to the RFH from the back navigation.
EXPECT_EQ(popup_contents->GetMainFrame()->render_view_host(), rvh);
EXPECT_TRUE(rvh->is_active());
EXPECT_EQ(rvh->GetMainFrame(), popup_contents->GetMainFrame());
}
// Check that when A opens a new window with B which embeds an A subframe, the
// subframe is visible and generates paint events. See
......
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