diff options
author | clamy@chromium.org <clamy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-28 01:52:24 +0000 |
---|---|---|
committer | clamy@chromium.org <clamy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-28 01:52:24 +0000 |
commit | 21b41c7e89c864e8bd5da8e4f81c38043eba83c5 (patch) | |
tree | 7be7e64a9a8e63ea75984ce7799ac615c0b1196b | |
parent | 82590814af33f91394e590df8a2b14c208563989 (diff) | |
download | chromium_src-21b41c7e89c864e8bd5da8e4f81c38043eba83c5.zip chromium_src-21b41c7e89c864e8bd5da8e4f81c38043eba83c5.tar.gz chromium_src-21b41c7e89c864e8bd5da8e4f81c38043eba83c5.tar.bz2 |
Revert "Revert 249676 "Have the unload event execute in background on cr...""
This fixes a bug in the original CL which may have caused a crash in
ProfileDestroyer.
BUG=343002,323528,342361
Review URL: https://codereview.chromium.org/180993003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@254007 0039d316-1c4b-4281-b951-d872f2087c98
22 files changed, 648 insertions, 201 deletions
diff --git a/chrome/browser/renderer_host/web_cache_manager_browsertest.cc b/chrome/browser/renderer_host/web_cache_manager_browsertest.cc index a917ed5..7caee2f 100644 --- a/chrome/browser/renderer_host/web_cache_manager_browsertest.cc +++ b/chrome/browser/renderer_host/web_cache_manager_browsertest.cc @@ -57,10 +57,13 @@ IN_PROC_BROWSER_TEST_F(WebCacheManagerBrowserTest, CrashOnceOnly) { ui_test_utils::NavigateToURL(browser(), url); - EXPECT_EQ( - WebCacheManager::GetInstance()->active_renderers_.size(), 1U); + // Depending on the speed of execution of the unload event, we may have one or + // two active renderers at that point (one executing the unload event in + // background). + EXPECT_GE(WebCacheManager::GetInstance()->active_renderers_.size(), 1U); + EXPECT_LE(WebCacheManager::GetInstance()->active_renderers_.size(), 2U); EXPECT_EQ( WebCacheManager::GetInstance()->inactive_renderers_.size(), 0U); - EXPECT_EQ( - WebCacheManager::GetInstance()->stats_.size(), 1U); + EXPECT_GE(WebCacheManager::GetInstance()->stats_.size(), 1U); + EXPECT_LE(WebCacheManager::GetInstance()->stats_.size(), 2U); } diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc index bd52e2b..23fc768 100644 --- a/content/browser/frame_host/frame_tree.cc +++ b/content/browser/frame_host/frame_tree.cc @@ -168,7 +168,17 @@ RenderViewHostImpl* FrameTree::CreateRenderViewHostForMainFrame( DCHECK(main_frame_routing_id != MSG_ROUTING_NONE); RenderViewHostMap::iterator iter = render_view_host_map_.find(site_instance->GetId()); - CHECK(iter == render_view_host_map_.end()); + if (iter != render_view_host_map_.end()) { + // If a RenderViewHost is pending shutdown for this |site_instance|, put it + // in the map of RenderViewHosts pending shutdown. Otherwise there should + // not be a RenderViewHost for the SiteInstance. + CHECK_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, + iter->second->rvh_state()); + render_view_host_pending_shutdown_map_.insert( + std::pair<int, RenderViewHostImpl*>(site_instance->GetId(), + iter->second)); + render_view_host_map_.erase(iter); + } RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( RenderViewHostFactory::Create(site_instance, render_view_delegate_, @@ -178,8 +188,7 @@ RenderViewHostImpl* FrameTree::CreateRenderViewHostForMainFrame( swapped_out, hidden)); - render_view_host_map_[site_instance->GetId()] = - RenderViewHostRefCount(rvh, 0); + render_view_host_map_[site_instance->GetId()] = rvh; return rvh; } @@ -190,8 +199,7 @@ RenderViewHostImpl* FrameTree::GetRenderViewHostForSubFrame( // TODO(creis): Mirror the frame tree so this check can't fail. if (iter == render_view_host_map_.end()) return NULL; - RenderViewHostRefCount rvh_refcount = iter->second; - return rvh_refcount.first; + return iter->second; } void FrameTree::RegisterRenderFrameHost( @@ -202,26 +210,51 @@ void FrameTree::RegisterRenderFrameHost( render_view_host_map_.find(site_instance->GetId()); CHECK(iter != render_view_host_map_.end()); - // Increment the refcount. - CHECK_GE(iter->second.second, 0); - iter->second.second++; + iter->second->increment_ref_count(); } void FrameTree::UnregisterRenderFrameHost( RenderFrameHostImpl* render_frame_host) { SiteInstance* site_instance = render_frame_host->render_view_host()->GetSiteInstance(); + int32 site_instance_id = site_instance->GetId(); RenderViewHostMap::iterator iter = - render_view_host_map_.find(site_instance->GetId()); - CHECK(iter != render_view_host_map_.end()); - - // Decrement the refcount and shutdown the RenderViewHost if no one else is - // using it. - CHECK_GT(iter->second.second, 0); - iter->second.second--; - if (iter->second.second == 0) { - iter->second.first->Shutdown(); - render_view_host_map_.erase(iter); + render_view_host_map_.find(site_instance_id); + if (iter != render_view_host_map_.end() && + iter->second == render_frame_host->render_view_host()) { + // Decrement the refcount and shutdown the RenderViewHost if no one else is + // using it. + CHECK_GT(iter->second->ref_count(), 0); + iter->second->decrement_ref_count(); + if (iter->second->ref_count() == 0) { + iter->second->Shutdown(); + render_view_host_map_.erase(iter); + } + } else { + // The RenderViewHost should be in the list of RenderViewHosts pending + // shutdown. + bool render_view_host_found = false; + std::pair<RenderViewHostMultiMap::iterator, + RenderViewHostMultiMap::iterator> result = + render_view_host_pending_shutdown_map_.equal_range(site_instance_id); + for (RenderViewHostMultiMap::iterator multi_iter = result.first; + multi_iter != result.second; + ++multi_iter) { + if (multi_iter->second != render_frame_host->render_view_host()) + continue; + render_view_host_found = true; + RenderViewHostImpl* rvh = multi_iter->second; + // Decrement the refcount and shutdown the RenderViewHost if no one else + // is using it. + CHECK_GT(rvh->ref_count(), 0); + rvh->decrement_ref_count(); + if (rvh->ref_count() == 0) { + rvh->Shutdown(); + render_view_host_pending_shutdown_map_.erase(multi_iter); + } + break; + } + CHECK(render_view_host_found); } } diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h index 8e18956..1a43618 100644 --- a/content/browser/frame_host/frame_tree.h +++ b/content/browser/frame_host/frame_tree.h @@ -116,8 +116,8 @@ class CONTENT_EXPORT FrameTree { void UnregisterRenderFrameHost(RenderFrameHostImpl* render_frame_host); private: - typedef std::pair<RenderViewHostImpl*, int> RenderViewHostRefCount; - typedef base::hash_map<int, RenderViewHostRefCount> RenderViewHostMap; + typedef base::hash_map<int, RenderViewHostImpl*> RenderViewHostMap; + typedef std::multimap<int, RenderViewHostImpl*> RenderViewHostMultiMap; // These delegates are installed into all the RenderViewHosts and // RenderFrameHosts that we create. @@ -126,15 +126,23 @@ class CONTENT_EXPORT FrameTree { RenderWidgetHostDelegate* render_widget_delegate_; RenderFrameHostManager::Delegate* manager_delegate_; - // Map of SiteInstance ID to a (RenderViewHost, refcount) pair. This allows - // us to look up the RenderViewHost for a given SiteInstance when creating - // RenderFrameHosts, and it allows us to call Shutdown on the RenderViewHost - // and remove it from the map when no more RenderFrameHosts are using it. + // Map of SiteInstance ID to a RenderViewHost. This allows us to look up the + // RenderViewHost for a given SiteInstance when creating RenderFrameHosts. + // Combined with the refcount on RenderViewHost, this allows us to call + // Shutdown on the RenderViewHost and remove it from the map when no more + // RenderFrameHosts are using it. // // Must be declared before |root_| so that it is deleted afterward. Otherwise // the map will be cleared before we delete the RenderFrameHosts in the tree. RenderViewHostMap render_view_host_map_; + // Map of SiteInstance ID to RenderViewHosts that are pending shutdown. The + // renderers of these RVH are currently executing the unload event in + // background. When the SwapOutACK is received, they will be deleted. In the + // meantime, they are kept in this map, as they should not be reused (part of + // their state is already gone away). + RenderViewHostMultiMap render_view_host_pending_shutdown_map_; + scoped_ptr<FrameTreeNode> root_; base::Callback<void(RenderViewHostImpl*, int)> on_frame_removed_; diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc index f41b5e8..6a2a9a2 100644 --- a/content/browser/frame_host/navigation_controller_impl_unittest.cc +++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc @@ -1095,6 +1095,7 @@ TEST_F(NavigationControllerTest, LoadURL_WithBindings) { contents()->GetPendingRenderViewHost()->AllowBindings(1); static_cast<TestRenderViewHost*>( contents()->GetPendingRenderViewHost())->SendNavigate(1, url2); + orig_rvh->OnSwappedOut(false); // The second load should be committed, and bindings should be remembered. EXPECT_EQ(controller.GetEntryCount(), 2); diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 4ff41aa..97305f3 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc @@ -243,7 +243,7 @@ void RenderFrameHostImpl::OnNavigate(const IPC::Message& msg) { // unload request. It will either respond to the unload request soon or our // timer will expire. Either way, we should ignore this message, because we // have already committed to closing this renderer. - if (render_view_host_->is_waiting_for_unload_ack_) + if (render_view_host_->IsWaitingForUnloadACK()) return; RenderProcessHost* process = GetProcess(); @@ -331,6 +331,10 @@ void RenderFrameHostImpl::OnContextMenu(const ContextMenuParams& params) { delegate_->ShowContextMenu(this, validated_params); } +void RenderFrameHostImpl::SetPendingShutdown(const base::Closure& on_swap_out) { + render_view_host_->SetPendingShutdown(on_swap_out); +} + bool RenderFrameHostImpl::CanCommitURL(const GURL& url) { // TODO(creis): We should also check for WebUI pages here. Also, when the // out-of-process iframes implementation is ready, we should check for diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index cf58b87..12a0059 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h @@ -7,6 +7,7 @@ #include <string> +#include "base/callback.h" #include "base/compiler_specific.h" #include "base/strings/string16.h" #include "content/common/content_export.h" @@ -82,6 +83,10 @@ class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost { is_swapped_out_ = is_swapped_out; } + // Sets the RVH for |this| as pending shutdown. |on_swap_out| will be called + // when the SwapOutACK is received. + void SetPendingShutdown(const base::Closure& on_swap_out); + // TODO(nasko): This method is public so RenderViewHostImpl::Navigate can // call it directly. It should be made private once Navigate moves here. void OnDidStartLoading(); diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc index e69abd9..1887c68 100644 --- a/content/browser/frame_host/render_frame_host_manager.cc +++ b/content/browser/frame_host/render_frame_host_manager.cc @@ -57,6 +57,11 @@ RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams( RenderFrameHostManager::PendingNavigationParams::~PendingNavigationParams() {} +bool RenderFrameHostManager::ClearRFHsPendingShutdown(FrameTreeNode* node) { + node->render_manager()->pending_delete_hosts_.clear(); + return true; +} + RenderFrameHostManager::RenderFrameHostManager( FrameTreeNode* frame_tree_node, RenderFrameHostDelegate* render_frame_delegate, @@ -72,7 +77,8 @@ RenderFrameHostManager::RenderFrameHostManager( render_frame_host_(NULL), pending_render_frame_host_(NULL), interstitial_page_(NULL), - cross_process_frame_connector_(NULL) {} + cross_process_frame_connector_(NULL), + weak_factory_(this) {} RenderFrameHostManager::~RenderFrameHostManager() { if (pending_render_frame_host_) @@ -243,7 +249,7 @@ bool RenderFrameHostManager::ShouldCloseTabOnUnresponsiveRenderer() { // If the tab becomes unresponsive during {before}unload while doing a // cross-site navigation, proceed with the navigation. (This assumes that // the pending RenderFrameHost is still responsive.) - if (render_frame_host_->render_view_host()->is_waiting_for_unload_ack()) { + if (render_frame_host_->render_view_host()->IsWaitingForUnloadACK()) { // The request has been started and paused while we're waiting for the // unload handler to finish. We'll pretend that it did. The pending // renderer will then be swapped in as part of the usual DidNavigate logic. @@ -564,6 +570,15 @@ void RenderFrameHostManager::SwapOutOldPage() { } } +void RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance( + int32 site_instance_id, + RenderFrameHostImpl* rfh) { + RFHPendingDeleteMap::iterator iter = + pending_delete_hosts_.find(site_instance_id); + if (iter != pending_delete_hosts_.end() && iter->second.get() == rfh) + pending_delete_hosts_.erase(site_instance_id); +} + void RenderFrameHostManager::Observe( int type, const NotificationSource& source, @@ -585,8 +600,30 @@ bool RenderFrameHostManager::ClearSwappedOutRFHsInSiteInstance( FrameTreeNode* node) { RenderFrameHostMap::iterator iter = node->render_manager()->swapped_out_hosts_.find(site_instance_id); - if (iter != node->render_manager()->swapped_out_hosts_.end()) - delete iter->second; + if (iter != node->render_manager()->swapped_out_hosts_.end()) { + RenderFrameHostImpl* swapped_out_rfh = iter->second; + // If the RVH is pending swap out, it needs to switch state to + // pending shutdown. Otherwise it is deleted. + if (swapped_out_rfh->render_view_host()->rvh_state() == + RenderViewHostImpl::STATE_PENDING_SWAP_OUT) { + swapped_out_rfh->SetPendingShutdown(base::Bind( + &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance, + node->render_manager()->weak_factory_.GetWeakPtr(), + site_instance_id, + swapped_out_rfh)); + RFHPendingDeleteMap::iterator pending_delete_iter = + node->render_manager()->pending_delete_hosts_.find(site_instance_id); + if (pending_delete_iter == + node->render_manager()->pending_delete_hosts_.end() || + pending_delete_iter->second.get() != iter->second) { + node->render_manager()->pending_delete_hosts_[site_instance_id] = + linked_ptr<RenderFrameHostImpl>(swapped_out_rfh); + } + } else { + delete swapped_out_rfh; + } + node->render_manager()->swapped_out_hosts_.erase(site_instance_id); + } return true; } @@ -969,7 +1006,7 @@ bool RenderFrameHostManager::InitRenderView(RenderViewHost* render_view_host, // process unless it's swapped out. RenderViewHostImpl* rvh_impl = static_cast<RenderViewHostImpl*>(render_view_host); - if (!rvh_impl->is_swapped_out()) { + if (!rvh_impl->IsSwappedOut()) { CHECK(!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( render_view_host->GetProcess()->GetID())); } @@ -1048,10 +1085,16 @@ void RenderFrameHostManager::CommitPending() { // If the old view is live and top-level, hide it now that the new one is // visible. + int32 old_site_instance_id = + old_render_frame_host->render_view_host()->GetSiteInstance()->GetId(); if (old_render_frame_host->render_view_host()->GetView()) { if (is_main_frame) { old_render_frame_host->render_view_host()->GetView()->Hide(); - old_render_frame_host->render_view_host()->WasSwappedOut(); + old_render_frame_host->render_view_host()->WasSwappedOut(base::Bind( + &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance, + weak_factory_.GetWeakPtr(), + old_site_instance_id, + old_render_frame_host)); } else { // TODO(creis): We'll need to set this back to false if we navigate back. old_render_frame_host->set_swapped_out(true); @@ -1085,18 +1128,17 @@ void RenderFrameHostManager::CommitPending() { if (old_render_frame_host->render_view_host()->IsRenderViewLive()) { // If the old RFH is live, we are swapping it out and should keep track of - // it in case we navigate back to it. + // it in case we navigate back to it, or it is waiting for the unload event + // to execute in the background. // TODO(creis): Swap out the subframe in --site-per-process. if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) DCHECK(old_render_frame_host->is_swapped_out() || - old_render_frame_host->render_view_host()->is_swapped_out()); - + !RenderViewHostImpl::IsRVHStateActive( + old_render_frame_host->render_view_host()->rvh_state())); // Temp fix for http://crbug.com/90867 until we do a better cleanup to make // sure we don't get different rvh instances for the same site instance // in the same rvhmgr. // TODO(creis): Clean this up. - int32 old_site_instance_id = - old_render_frame_host->render_view_host()->GetSiteInstance()->GetId(); RenderFrameHostMap::iterator iter = swapped_out_hosts_.find(old_site_instance_id); if (iter != swapped_out_hosts_.end() && @@ -1104,7 +1146,23 @@ void RenderFrameHostManager::CommitPending() { // Delete the RFH that will be replaced in the map to avoid a leak. delete iter->second; } - swapped_out_hosts_[old_site_instance_id] = old_render_frame_host; + // If the RenderViewHost backing the RenderFrameHost is pending shutdown, + // the RenderFrameHost should be put in the map of RenderFrameHosts pending + // shutdown. Otherwise, it is stored in the map of swapped out + // RenderFrameHosts. + if (old_render_frame_host->render_view_host()->rvh_state() == + RenderViewHostImpl::STATE_PENDING_SHUTDOWN) { + swapped_out_hosts_.erase(old_site_instance_id); + RFHPendingDeleteMap::iterator pending_delete_iter = + pending_delete_hosts_.find(old_site_instance_id); + if (pending_delete_iter == pending_delete_hosts_.end() || + pending_delete_iter->second.get() != old_render_frame_host) { + pending_delete_hosts_[old_site_instance_id] = + linked_ptr<RenderFrameHostImpl>(old_render_frame_host); + } + } else { + swapped_out_hosts_[old_site_instance_id] = old_render_frame_host; + } // If there are no active views in this SiteInstance, it means that // this RFH was the last active one in the SiteInstance. Now that we @@ -1147,7 +1205,6 @@ void RenderFrameHostManager::ShutdownRenderFrameHostsInSiteInstance( tree->ForEach(base::Bind( &RenderFrameHostManager::ClearSwappedOutRFHsInSiteInstance, site_instance_id)); - // rvh is now deleted. } } } diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h index 39b6b9f..f1cc81c 100644 --- a/content/browser/frame_host/render_frame_host_manager.h +++ b/content/browser/frame_host/render_frame_host_manager.h @@ -12,6 +12,7 @@ #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/site_instance_impl.h" #include "content/common/content_export.h" +#include "content/public/browser/global_request_id.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/common/referrer.h" @@ -113,6 +114,11 @@ class CONTENT_EXPORT RenderFrameHostManager virtual ~Delegate() {} }; + // Used with FrameTree::ForEach to delete RenderFrameHosts pending shutdown + // from a FrameTreeNode's RenderFrameHostManager. Used during destruction of + // WebContentsImpl. + static bool ClearRFHsPendingShutdown(FrameTreeNode* node); + // All three delegate pointers must be non-NULL and are not owned by this // class. They must outlive this class. The RenderViewHostDelegate and // RenderWidgetHostDelegate are what will be installed into all @@ -270,6 +276,10 @@ class CONTENT_EXPORT RenderFrameHostManager // to a new process after this completes or times out. void SwapOutOldPage(); + // Deletes a RenderFrameHost that was pending shutdown. + void ClearPendingShutdownRFHForSiteInstance(int32 site_instance_id, + RenderFrameHostImpl* rfh); + private: friend class RenderFrameHostManagerTest; friend class TestWebContents; @@ -440,6 +450,11 @@ class CONTENT_EXPORT RenderFrameHostManager typedef base::hash_map<int32, RenderFrameHostImpl*> RenderFrameHostMap; RenderFrameHostMap swapped_out_hosts_; + // A map of RenderFrameHosts pending shutdown. + typedef base::hash_map<int32, linked_ptr<RenderFrameHostImpl> > + RFHPendingDeleteMap; + RFHPendingDeleteMap pending_delete_hosts_; + // The intersitial page currently shown if any, not own by this class // (the InterstitialPage is self-owned, it deletes itself when hidden). InterstitialPageImpl* interstitial_page_; @@ -453,6 +468,8 @@ class CONTENT_EXPORT RenderFrameHostManager // process as its parent. CrossProcessFrameConnector* cross_process_frame_connector_; + base::WeakPtrFactory<RenderFrameHostManager> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManager); }; diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc index 10f14ae..2a82834 100644 --- a/content/browser/frame_host/render_frame_host_manager_unittest.cc +++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc @@ -172,10 +172,10 @@ class RenderFrameHostManagerTest // BeforeUnload finishes. ntp_rvh->SendShouldCloseACK(true); - // Assume SwapOutACK times out, so the dest_rvh proceeds and commits. dest_rvh->SendNavigate(101, kDestUrl); + ntp_rvh->OnSwappedOut(false); - EXPECT_TRUE(ntp_rvh->is_swapped_out()); + EXPECT_TRUE(ntp_rvh->IsSwappedOut()); return ntp_rvh; } @@ -299,7 +299,7 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) { // The old renderer, being slow, now updates the title. It should be filtered // out and not take effect. - EXPECT_TRUE(ntp_rvh->is_swapped_out()); + EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, ntp_rvh->rvh_state()); EXPECT_TRUE(ntp_rvh->OnMessageReceived( ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction))); EXPECT_EQ(dest_title, contents()->GetTitle()); @@ -364,7 +364,7 @@ TEST_F(RenderFrameHostManagerTest, WhiteListDidActivateAcceleratedCompositing) { // widgets. TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) { TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); - EXPECT_TRUE(swapped_out_rvh->is_swapped_out()); + EXPECT_TRUE(swapped_out_rvh->IsSwappedOut()); scoped_ptr<RenderWidgetHostIterator> widgets( RenderWidgetHost::GetRenderWidgetHosts()); @@ -374,7 +374,8 @@ TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) { RenderWidgetHost* widget = widgets->GetNextHost(); EXPECT_FALSE(widgets->GetNextHost()); RenderViewHost* rvh = RenderViewHost::From(widget); - EXPECT_FALSE(static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + static_cast<RenderViewHostImpl*>(rvh)->rvh_state()); } // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of @@ -385,7 +386,7 @@ TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) { TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) { TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); - EXPECT_TRUE(swapped_out_rvh->is_swapped_out()); + EXPECT_TRUE(swapped_out_rvh->IsSwappedOut()); scoped_ptr<RenderWidgetHostIterator> widgets( RenderWidgetHost::GetRenderWidgetHosts()); @@ -771,7 +772,7 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) { // CrossSiteResourceHandler::StartCrossSiteTransition triggers a // call of RenderFrameHostManager::SwapOutOldPage before // RenderFrameHostManager::DidNavigateMainFrame is called. - // The RVH is not swapped out until the commit. + // The RVH is swapped out after receiving the unload ack. manager->SwapOutOldPage(); EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( ViewMsg_SwapOut::ID)); @@ -799,9 +800,7 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) { EXPECT_NE(host3, host); EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id); - // Navigations in the new RVH should be suspended, which is ok because the - // old RVH is not yet swapped out and can respond to a second beforeunload - // request. + // Navigations in the new RVH should be suspended. EXPECT_TRUE(static_cast<RenderViewHostImpl*>( host3->render_view_host())->are_navigations_suspended()); EXPECT_EQ(host, manager->current_frame_host()); @@ -815,7 +814,6 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) { // CrossSiteResourceHandler::StartCrossSiteTransition triggers a // call of RenderFrameHostManager::SwapOutOldPage before // RenderFrameHostManager::DidNavigateMainFrame is called. - // The RVH is not swapped out until the commit. manager->SwapOutOldPage(); EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( ViewMsg_SwapOut::ID)); @@ -1032,14 +1030,12 @@ TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) { contents()->ProceedWithCrossSiteNavigation(); EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack()); rvh2->SwapOut(); - EXPECT_TRUE(rvh2->is_waiting_for_unload_ack()); + EXPECT_TRUE(rvh2->IsWaitingForUnloadACK()); - // The back navigation commits. We should proactively clear the - // is_waiting_for_unload_ack state to be safe. + // The back navigation commits. const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry(); rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL()); - EXPECT_TRUE(rvh2->is_swapped_out()); - EXPECT_FALSE(rvh2->is_waiting_for_unload_ack()); + EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh2->rvh_state()); // We should be able to navigate forward. contents()->GetController().GoForward(); @@ -1047,8 +1043,10 @@ TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) { const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry(); rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL()); EXPECT_EQ(rvh2, rvh()); - EXPECT_FALSE(rvh2->is_swapped_out()); - EXPECT_TRUE(rvh1->is_swapped_out()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); + EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state()); + rvh1->OnSwappedOut(false); + EXPECT_TRUE(rvh1->IsSwappedOut()); } // Test that we create swapped out RVHs for the opener chain when navigating an @@ -1095,13 +1093,13 @@ TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRVHs) { TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh)); - EXPECT_TRUE(opener1_rvh->is_swapped_out()); + EXPECT_TRUE(opener1_rvh->IsSwappedOut()); // Ensure a swapped out RVH is created in the second opener tab. TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>( opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh)); - EXPECT_TRUE(opener2_rvh->is_swapped_out()); + EXPECT_TRUE(opener2_rvh->IsSwappedOut()); // Navigate to a cross-BrowsingInstance URL. contents()->NavigateAndCommit(kChromeUrl); @@ -1203,7 +1201,7 @@ TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) { TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh)); - EXPECT_TRUE(opener1_rvh->is_swapped_out()); + EXPECT_TRUE(opener1_rvh->IsSwappedOut()); // Ensure the new RVH has WebUI bindings. EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); @@ -1349,4 +1347,228 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) { EXPECT_EQ(host, manager->current_frame_host()); } +// This checks that the given RVH has been properly deleted. +class RenderViewHostDestructionObserver : public WebContentsObserver { + public: + RenderViewHostDestructionObserver(RenderViewHost* render_view_host) + : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)), + render_view_host_(render_view_host), + rvh_deleted_(false) {} + + bool rvh_deleted() { return rvh_deleted_; } + + virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE { + if (render_view_host == render_view_host_) + rvh_deleted_ = true; + } + + private: + RenderViewHost* render_view_host_; + bool rvh_deleted_; + + DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver); +}; + +// Tests that the RenderViewHost is properly deleted when the SwapOutACK is +// received before the new page commits. +TEST_F(RenderFrameHostManagerTest, + SwapOutACKBeforeNewPageCommitsLeadsToDeletion) { + const GURL kUrl1("http://www.google.com/"); + const GURL kUrl2("http://www.chromium.org/"); + + // Navigate to the first page. + contents()->NavigateAndCommit(kUrl1); + TestRenderViewHost* rvh1 = test_rvh(); + RenderViewHostDestructionObserver destruction_observer(rvh1); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + + // Navigate to new site, simulating onbeforeunload approval. + controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); + base::TimeTicks now = base::TimeTicks::Now(); + rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); + EXPECT_TRUE(contents()->cross_navigation_pending()); + TestRenderViewHost* rvh2 = + static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); + + // Simulate rvh2's response, which leads to an unload request being sent to + // rvh1. + std::vector<GURL> url_chain; + url_chain.push_back(GURL()); + contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( + rvh2, GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), + url_chain, Referrer(), PAGE_TRANSITION_TYPED, 1, false); + EXPECT_TRUE(contents()->cross_navigation_pending()); + EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, + rvh1->rvh_state()); + + // Simulate the swap out ack. + rvh1->OnSwappedOut(false); + EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state()); + + // The new page commits. + contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + EXPECT_FALSE(contents()->cross_navigation_pending()); + EXPECT_EQ(rvh2, rvh()); + EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); + + // rvh1 should have been deleted. + EXPECT_TRUE(destruction_observer.rvh_deleted()); + rvh1 = NULL; +} + +// Tests that the RenderViewHost is properly swapped out when the SwapOutACK is +// received before the new page commits. +TEST_F(RenderFrameHostManagerTest, + SwapOutACKBeforeNewPageCommitsLeadsToSwapOut) { + const GURL kUrl1("http://www.google.com/"); + const GURL kUrl2("http://www.chromium.org/"); + + // Navigate to the first page. + contents()->NavigateAndCommit(kUrl1); + TestRenderViewHost* rvh1 = test_rvh(); + RenderViewHostDestructionObserver destruction_observer(rvh1); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + + // Increment the number of active views in SiteInstanceImpl so that rvh2 is + // not deleted on swap out. + static_cast<SiteInstanceImpl*>( + rvh1->GetSiteInstance())->increment_active_view_count(); + + // Navigate to new site, simulating onbeforeunload approval. + controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); + base::TimeTicks now = base::TimeTicks::Now(); + rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); + EXPECT_TRUE(contents()->cross_navigation_pending()); + TestRenderViewHost* rvh2 = + static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); + + // Simulate rvh2's response, which leads to an unload request being sent to + // rvh1. + std::vector<GURL> url_chain; + url_chain.push_back(GURL()); + contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( + rvh2, GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), + url_chain, Referrer(), PAGE_TRANSITION_TYPED, 1, false); + EXPECT_TRUE(contents()->cross_navigation_pending()); + EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, + rvh1->rvh_state()); + + // Simulate the swap out ack. + rvh1->OnSwappedOut(false); + EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state()); + + // The new page commits. + contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + EXPECT_FALSE(contents()->cross_navigation_pending()); + EXPECT_EQ(rvh2, rvh()); + EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); + + // rvh1 should be swapped out. + EXPECT_FALSE(destruction_observer.rvh_deleted()); + EXPECT_TRUE(rvh1->IsSwappedOut()); +} + +// Tests that the RenderViewHost is properly deleted when the new +// page commits before the swap out ack is received. +TEST_F(RenderFrameHostManagerTest, + NewPageCommitsBeforeSwapOutACKLeadsToDeletion) { + const GURL kUrl1("http://www.google.com/"); + const GURL kUrl2("http://www.chromium.org/"); + + // Navigate to the first page. + contents()->NavigateAndCommit(kUrl1); + TestRenderViewHost* rvh1 = test_rvh(); + RenderViewHostDestructionObserver destruction_observer(rvh1); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + + // Navigate to new site, simulating onbeforeunload approval. + controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); + base::TimeTicks now = base::TimeTicks::Now(); + rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); + EXPECT_TRUE(contents()->cross_navigation_pending()); + TestRenderViewHost* rvh2 = + static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); + + // Simulate rvh2's response, which leads to an unload request being sent to + // rvh1. + std::vector<GURL> url_chain; + url_chain.push_back(GURL()); + contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( + rvh2, GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), + url_chain, Referrer(), PAGE_TRANSITION_TYPED, 1, false); + EXPECT_TRUE(contents()->cross_navigation_pending()); + EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, + rvh1->rvh_state()); + + // The new page commits. + contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + EXPECT_FALSE(contents()->cross_navigation_pending()); + EXPECT_EQ(rvh2, rvh()); + EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); + EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state()); + + // Simulate the swap out ack. + rvh1->OnSwappedOut(false); + + // rvh1 should have been deleted. + EXPECT_TRUE(destruction_observer.rvh_deleted()); + rvh1 = NULL; +} + +// Tests that the RenderViewHost is properly swapped out when the new page +// commits before the swap out ack is received. +TEST_F(RenderFrameHostManagerTest, + NewPageCommitsBeforeSwapOutACKLeadsToSwapOut) { + const GURL kUrl1("http://www.google.com/"); + const GURL kUrl2("http://www.chromium.org/"); + + // Navigate to the first page. + contents()->NavigateAndCommit(kUrl1); + TestRenderViewHost* rvh1 = test_rvh(); + RenderViewHostDestructionObserver destruction_observer(rvh1); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + + // Increment the number of active views in SiteInstanceImpl so that rvh1 is + // not deleted on swap out. + static_cast<SiteInstanceImpl*>( + rvh1->GetSiteInstance())->increment_active_view_count(); + + // Navigate to new site, simulating onbeforeunload approval. + controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); + base::TimeTicks now = base::TimeTicks::Now(); + rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); + EXPECT_TRUE(contents()->cross_navigation_pending()); + TestRenderViewHost* rvh2 = + static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); + + // Simulate rvh2's response, which leads to an unload request being sent to + // rvh1. + std::vector<GURL> url_chain; + url_chain.push_back(GURL()); + contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( + rvh2, GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), + url_chain, Referrer(), PAGE_TRANSITION_TYPED, 1, false); + EXPECT_TRUE(contents()->cross_navigation_pending()); + EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, + rvh1->rvh_state()); + + // The new page commits. + contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + EXPECT_FALSE(contents()->cross_navigation_pending()); + EXPECT_EQ(rvh2, rvh()); + EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); + EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state()); + + // Simulate the swap out ack. + rvh1->OnSwappedOut(false); + + // rvh1 should be swapped out. + EXPECT_FALSE(destruction_observer.rvh_deleted()); + EXPECT_TRUE(rvh1->IsSwappedOut()); +} + } // namespace content diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index b2e5db6..d2fbe9a 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h @@ -17,7 +17,6 @@ #include "content/browser/geolocation/geolocation_dispatcher_host.h" #include "content/browser/power_monitor_message_broadcaster.h" #include "content/common/content_export.h" -#include "content/public/browser/global_request_id.h" #include "content/public/browser/gpu_data_manager_observer.h" #include "content/public/browser/render_process_host.h" #include "ipc/ipc_channel_proxy.h" @@ -128,6 +127,8 @@ class CONTENT_EXPORT RenderProcessHostImpl virtual void SetWebRtcLogMessageCallback( base::Callback<void(const std::string&)> callback) OVERRIDE; #endif + virtual void ResumeDeferredNavigation(const GlobalRequestID& request_id) + OVERRIDE; // IPC::Sender via RenderProcessHost. virtual bool Send(IPC::Message* msg) OVERRIDE; @@ -142,10 +143,6 @@ class CONTENT_EXPORT RenderProcessHostImpl scoped_refptr<AudioRendererHost> audio_renderer_host() const; - // Tells the ResourceDispatcherHost to resume a deferred navigation without - // transferring it to a new renderer process. - void ResumeDeferredNavigation(const GlobalRequestID& request_id); - // Call this function when it is evident that the child process is actively // performing some operation, for example if we just received an IPC message. void mark_child_process_activity_time() { diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index 05fb1aa..513c924 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -36,6 +36,7 @@ #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/renderer_host/cross_site_transferring_request.h" #include "content/browser/renderer_host/dip_util.h" +#include "content/browser/renderer_host/input/timeout_monitor.h" #include "content/browser/renderer_host/media/audio_renderer_host.h" #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_view_host_delegate.h" @@ -151,6 +152,16 @@ void DismissVirtualKeyboardTask() { // RenderViewHost, public: // static +bool RenderViewHostImpl::IsRVHStateActive(RenderViewHostImplState rvh_state) { + if (rvh_state == STATE_DEFAULT || + rvh_state == STATE_WAITING_FOR_UNLOAD_ACK || + rvh_state == STATE_WAITING_FOR_COMMIT || + rvh_state == STATE_WAITING_FOR_CLOSE) + return true; + return false; +} + +// static RenderViewHost* RenderViewHost::FromID(int render_process_id, int render_view_id) { return RenderViewHostImpl::FromID(render_process_id, render_view_id); @@ -187,29 +198,32 @@ RenderViewHostImpl::RenderViewHostImpl( instance->GetProcess(), routing_id, hidden), + frames_ref_count_(0), delegate_(delegate), instance_(static_cast<SiteInstanceImpl*>(instance)), waiting_for_drag_context_response_(false), enabled_bindings_(0), navigations_suspended_(false), has_accessed_initial_document_(false), - is_swapped_out_(swapped_out), main_frame_routing_id_(main_frame_routing_id), is_waiting_for_beforeunload_ack_(false), - is_waiting_for_unload_ack_(false), - has_timed_out_on_unload_(false), unload_ack_is_for_cross_site_transition_(false), are_javascript_messages_suppressed_(false), sudden_termination_allowed_(false), render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING), - virtual_keyboard_requested_(false) { + virtual_keyboard_requested_(false), + weak_factory_(this) { DCHECK(instance_.get()); CHECK(delegate_); // http://crbug.com/82827 GetProcess()->EnableSendQueue(); - if (!swapped_out) + if (swapped_out) { + rvh_state_ = STATE_SWAPPED_OUT; + } else { + rvh_state_ = STATE_DEFAULT; instance_->increment_active_view_count(); + } if (ResourceDispatcherHostImpl::Get()) { BrowserThread::PostTask( @@ -222,6 +236,9 @@ RenderViewHostImpl::RenderViewHostImpl( #if defined(OS_ANDROID) media_player_manager_.reset(BrowserMediaPlayerManager::Create(this)); #endif + + unload_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind( + &RenderViewHostImpl::OnSwappedOut, weak_factory_.GetWeakPtr(), true))); } RenderViewHostImpl::~RenderViewHostImpl() { @@ -241,7 +258,7 @@ RenderViewHostImpl::~RenderViewHostImpl() { // If this was swapped out, it already decremented the active view // count of the SiteInstance it belongs to. - if (!is_swapped_out_) + if (IsRVHStateActive(rvh_state_)) instance_->decrement_active_view_count(); } @@ -292,7 +309,7 @@ bool RenderViewHostImpl::CreateRenderView( params.frame_name = frame_name; // Ensure the RenderView sets its opener correctly. params.opener_route_id = opener_route_id; - params.swapped_out = is_swapped_out_; + params.swapped_out = !IsRVHStateActive(rvh_state_); params.hidden = is_hidden(); params.next_page_id = next_page_id; GetWebScreenInfo(¶ms.screen_info); @@ -576,7 +593,7 @@ void RenderViewHostImpl::Navigate(const ViewMsg_Navigate_Params& params) { } else { // Get back to a clean state, in case we start a new navigation without // completing a RVH swap or unload handler. - SetSwappedOut(false); + SetState(STATE_DEFAULT); Send(new ViewMsg_Navigate(GetRoutingID(), params)); } @@ -622,7 +639,7 @@ void RenderViewHostImpl::SetNavigationsSuspended( // There's navigation message params waiting to be sent. Now that we're not // suspended anymore, resume navigation by sending them. If we were swapped // out, we should also stop filtering out the IPC messages now. - SetSwappedOut(false); + SetState(STATE_DEFAULT); DCHECK(!proceed_time.is_null()); suspended_nav_params_->browser_navigation_start = proceed_time; @@ -705,22 +722,14 @@ void RenderViewHostImpl::SuppressDialogsUntilSwapOut() { } void RenderViewHostImpl::SwapOut() { - // This will be set back to false in OnSwapOutACK, just before we replace - // this RVH with the pending RVH. - is_waiting_for_unload_ack_ = true; - // Start the hang monitor in case the renderer hangs in the unload handler. - // Increment the in-flight event count, to ensure that input events won't - // cancel the timeout timer. - increment_in_flight_event_count(); - StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); + SetState(STATE_WAITING_FOR_UNLOAD_ACK); + unload_event_monitor_timeout_->Start( + base::TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); if (IsRenderViewLive()) { Send(new ViewMsg_SwapOut(GetRoutingID())); - } else { - // This RenderViewHost doesn't have a live renderer, so just skip the unload - // event. - OnSwappedOut(true); } + delegate_->SwappedOut(this); } void RenderViewHostImpl::OnSwapOutACK() { @@ -728,36 +737,11 @@ void RenderViewHostImpl::OnSwapOutACK() { } void RenderViewHostImpl::OnSwappedOut(bool timed_out) { - // Stop the hang monitor now that the unload handler has finished. - decrement_in_flight_event_count(); - StopHangMonitorTimeout(); - is_waiting_for_unload_ack_ = false; - has_timed_out_on_unload_ = timed_out; - delegate_->SwappedOut(this); -} - -void RenderViewHostImpl::WasSwappedOut() { - // Don't bother reporting hung state anymore. - StopHangMonitorTimeout(); - - // If we have timed out on running the unload handler, we consider - // the process hung and we should terminate it if there are no other tabs - // using the process. If there are other views using this process, the - // unresponsive renderer timeout will catch it. - bool hung = has_timed_out_on_unload_; - - // Now that we're no longer the active RVH in the tab, start filtering out - // most IPC messages. Usually the renderer will have stopped sending - // messages as of OnSwapOutACK. However, we may have timed out waiting - // for that message, and additional IPC messages may keep streaming in. - // We filter them out, as long as that won't cause problems (e.g., we - // still allow synchronous messages through). - SetSwappedOut(true); - - // If we are not running the renderer in process and no other tab is using - // the hung process, consider it eligible to be killed, assuming it is a real - // process (unit tests don't have real processes). - if (hung) { + // Ignore spurious swap out ack. + if (!IsWaitingForUnloadACK()) + return; + unload_event_monitor_timeout_->Stop(); + if (timed_out) { base::ProcessHandle process_handle = GetProcess()->GetHandle(); int views = 0; @@ -794,13 +778,49 @@ void RenderViewHostImpl::WasSwappedOut() { } } - // Inform the renderer that it can exit if no one else is using it. + switch (rvh_state_) { + case STATE_WAITING_FOR_UNLOAD_ACK: + SetState(STATE_WAITING_FOR_COMMIT); + break; + case STATE_PENDING_SWAP_OUT: + SetState(STATE_SWAPPED_OUT); + break; + case STATE_PENDING_SHUTDOWN: + DCHECK(!pending_shutdown_on_swap_out_.is_null()); + pending_shutdown_on_swap_out_.Run(); + break; + default: + NOTREACHED(); + } +} + +void RenderViewHostImpl::WasSwappedOut( + const base::Closure& pending_delete_on_swap_out) { Send(new ViewMsg_WasSwappedOut(GetRoutingID())); + if (rvh_state_ == STATE_WAITING_FOR_UNLOAD_ACK) { + SetState(STATE_PENDING_SWAP_OUT); + if (!instance_->active_view_count()) + SetPendingShutdown(pending_delete_on_swap_out); + } else if (rvh_state_ == STATE_WAITING_FOR_COMMIT) { + SetState(STATE_SWAPPED_OUT); + } else if (rvh_state_ == STATE_DEFAULT) { + // When the RenderView is not live, the RenderFrameHostManager will call + // CommitPending directly, without calling SwapOut on the old RVH. This will + // cause WasSwappedOut to be called directly on the live old RVH. + DCHECK(!IsRenderViewLive()); + SetState(STATE_SWAPPED_OUT); + } else { + NOTREACHED(); + } +} + +void RenderViewHostImpl::SetPendingShutdown(const base::Closure& on_swap_out) { + pending_shutdown_on_swap_out_ = on_swap_out; + SetState(STATE_PENDING_SHUTDOWN); } void RenderViewHostImpl::ClosePage() { - // Start the hang monitor in case the renderer hangs in the unload handler. - is_waiting_for_unload_ack_ = true; + SetState(STATE_WAITING_FOR_CLOSE); StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); if (IsRenderViewLive()) { @@ -827,7 +847,6 @@ void RenderViewHostImpl::ClosePage() { void RenderViewHostImpl::ClosePageIgnoringUnloadEvents() { StopHangMonitorTimeout(); is_waiting_for_beforeunload_ack_ = false; - is_waiting_for_unload_ack_ = false; sudden_termination_allowed_ = true; delegate_->Close(this); @@ -1000,8 +1019,7 @@ void RenderViewHostImpl::JavaScriptDialogClosed( bool success, const base::string16& user_input) { GetProcess()->SetIgnoreInputEvents(false); - bool is_waiting = - is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_; + bool is_waiting = is_waiting_for_beforeunload_ack_ || IsWaitingForUnloadACK(); // If we are executing as part of (before)unload event handling, we don't // want to use the regular hung_renderer_delay_ms_ if the user has agreed to @@ -1024,7 +1042,7 @@ void RenderViewHostImpl::JavaScriptDialogClosed( // correctly while waiting for a response. if (is_waiting && are_javascript_messages_suppressed_) delegate_->RendererUnresponsive( - this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_); + this, is_waiting_for_beforeunload_ack_, IsWaitingForUnloadACK()); } void RenderViewHostImpl::DragSourceEndedAt( @@ -1180,7 +1198,7 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) { // Filter out most IPC messages if this renderer is swapped out. // We still want to handle certain ACKs to keep our state consistent. - if (is_swapped_out_) { + if (IsSwappedOut()) { if (!SwappedOutMessages::CanHandleWhileSwappedOut(msg)) { // If this is a synchronous message and we decided not to handle it, // we must send an error reply, or else the renderer will be stuck @@ -1322,7 +1340,7 @@ void RenderViewHostImpl::OnShowView(int route_id, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture) { - if (!is_swapped_out_) { + if (IsRVHStateActive(rvh_state_)) { delegate_->ShowCreatedWindow( route_id, disposition, initial_pos, user_gesture); } @@ -1331,13 +1349,13 @@ void RenderViewHostImpl::OnShowView(int route_id, void RenderViewHostImpl::OnShowWidget(int route_id, const gfx::Rect& initial_pos) { - if (!is_swapped_out_) + if (IsRVHStateActive(rvh_state_)) delegate_->ShowCreatedWidget(route_id, initial_pos); Send(new ViewMsg_Move_ACK(route_id)); } void RenderViewHostImpl::OnShowFullscreenWidget(int route_id) { - if (!is_swapped_out_) + if (IsRVHStateActive(rvh_state_)) delegate_->ShowCreatedFullscreenWidget(route_id); Send(new ViewMsg_Move_ACK(route_id)); } @@ -1409,7 +1427,7 @@ void RenderViewHostImpl::OnUpdateEncoding(const std::string& encoding_name) { } void RenderViewHostImpl::OnUpdateTargetURL(int32 page_id, const GURL& url) { - if (!is_swapped_out_) + if (IsRVHStateActive(rvh_state_)) delegate_->UpdateTargetURL(page_id, url); // Send a notification back to the renderer that we are ready to @@ -1430,7 +1448,7 @@ void RenderViewHostImpl::OnClose() { } void RenderViewHostImpl::OnRequestMove(const gfx::Rect& pos) { - if (!is_swapped_out_) + if (IsRVHStateActive(rvh_state_)) delegate_->RequestMove(pos); Send(new ViewMsg_Move_ACK(GetRoutingID())); } @@ -1659,7 +1677,7 @@ void RenderViewHostImpl::OnShouldCloseACK( // If this renderer navigated while the beforeunload request was in flight, we // may have cleared this state in OnNavigate, in which case we can ignore // this message. - if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_) + if (!is_waiting_for_beforeunload_ack_ || rvh_state_ != STATE_DEFAULT) return; is_waiting_for_beforeunload_ack_ = false; @@ -1702,7 +1720,7 @@ void RenderViewHostImpl::OnClosePageACK() { void RenderViewHostImpl::NotifyRendererUnresponsive() { delegate_->RendererUnresponsive( - this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_); + this, is_waiting_for_beforeunload_ack_, IsWaitingForUnloadACK()); } void RenderViewHostImpl::NotifyRendererResponsive() { @@ -1807,6 +1825,13 @@ void RenderViewHostImpl::ToggleSpeechInput() { Send(new InputTagSpeechMsg_ToggleSpeechInput(GetRoutingID())); } +bool RenderViewHostImpl::IsWaitingForUnloadACK() const { + return rvh_state_ == STATE_WAITING_FOR_UNLOAD_ACK || + rvh_state_ == STATE_WAITING_FOR_CLOSE || + rvh_state_ == STATE_PENDING_SHUTDOWN || + rvh_state_ == STATE_PENDING_SWAP_OUT; +} + void RenderViewHostImpl::ExitFullscreen() { RejectMouseLockOrUnlockIfNecessary(); // Notify delegate_ and renderer of fullscreen state change. @@ -1819,7 +1844,7 @@ WebPreferences RenderViewHostImpl::GetWebkitPreferences() { void RenderViewHostImpl::DisownOpener() { // This should only be called when swapped out. - DCHECK(is_swapped_out_); + DCHECK(IsSwappedOut()); Send(new ViewMsg_DisownOpener(GetRoutingID())); } @@ -1901,7 +1926,7 @@ void RenderViewHostImpl::NotifyMoveOrResizeStarted() { void RenderViewHostImpl::OnAccessibilityEvents( const std::vector<AccessibilityHostMsg_EventParams>& params) { if ((accessibility_mode() & AccessibilityModeFlagPlatform) && view_ && - !is_swapped_out_) { + IsRVHStateActive(rvh_state_)) { view_->CreateBrowserAccessibilityManagerIfNeeded(); BrowserAccessibilityManager* manager = view_->GetBrowserAccessibilityManager(); @@ -1933,7 +1958,7 @@ void RenderViewHostImpl::OnAccessibilityEvents( void RenderViewHostImpl::OnAccessibilityLocationChanges( const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) { - if (view_ && !is_swapped_out_) { + if (view_ && IsRVHStateActive(rvh_state_)) { view_->CreateBrowserAccessibilityManagerIfNeeded(); BrowserAccessibilityManager* manager = view_->GetBrowserAccessibilityManager(); @@ -2029,22 +2054,25 @@ void RenderViewHostImpl::OnShowPopup( } #endif -void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) { +void RenderViewHostImpl::SetState(RenderViewHostImplState rvh_state) { // We update the number of RenderViews in a SiteInstance when the - // swapped out status of this RenderView gets flipped. - if (is_swapped_out_ && !is_swapped_out) + // swapped out status of this RenderView gets flipped to/from live. + if (!IsRVHStateActive(rvh_state_) && IsRVHStateActive(rvh_state)) instance_->increment_active_view_count(); - else if (!is_swapped_out_ && is_swapped_out) + else if (IsRVHStateActive(rvh_state_) && !IsRVHStateActive(rvh_state)) instance_->decrement_active_view_count(); - is_swapped_out_ = is_swapped_out; + // Whenever we change the RVH state to and from live or swapped out state, we + // should not be waiting for beforeunload or unload acks. We clear them here + // to be safe, since they can cause navigations to be ignored in OnNavigate. + if (rvh_state == STATE_DEFAULT || + rvh_state == STATE_SWAPPED_OUT || + rvh_state_ == STATE_DEFAULT || + rvh_state_ == STATE_SWAPPED_OUT) { + is_waiting_for_beforeunload_ack_ = false; + } + rvh_state_ = rvh_state; - // Whenever we change swap out state, we should not be waiting for - // beforeunload or unload acks. We clear them here to be safe, since they - // can cause navigations to be ignored in OnNavigate. - is_waiting_for_beforeunload_ack_ = false; - is_waiting_for_unload_ack_ = false; - has_timed_out_on_unload_ = false; } bool RenderViewHostImpl::CanAccessFilesOfPageState( diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h index 9c883fc..7158869 100644 --- a/content/browser/renderer_host/render_view_host_impl.h +++ b/content/browser/renderer_host/render_view_host_impl.h @@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "base/callback.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -65,6 +66,7 @@ class RenderWidgetHostDelegate; class SessionStorageNamespace; class SessionStorageNamespaceImpl; class TestRenderViewHost; +class TimeoutMonitor; struct FileChooserParams; struct Referrer; struct ShowDesktopNotificationHostMsgParams; @@ -99,6 +101,39 @@ class CONTENT_EXPORT RenderViewHostImpl : public RenderViewHost, public RenderWidgetHostImpl { public: + // Keeps track of the state of the RenderViewHostImpl, particularly with + // respect to swap out. + enum RenderViewHostImplState { + // The standard state for a RVH handling the communication with a + // RenderView. + STATE_DEFAULT = 0, + // The RVH has sent the SwapOut request to the renderer, but has not + // received the SwapOutACK yet. The new page has not been committed yet + // either. + STATE_WAITING_FOR_UNLOAD_ACK, + // The RVH received the SwapOutACK from the RenderView, but the new page has + // not been committed yet. + STATE_WAITING_FOR_COMMIT, + // The RVH is waiting for the CloseACK from the RenderView. + STATE_WAITING_FOR_CLOSE, + // The RVH has not received the SwapOutACK yet, but the new page has + // committed in a different RVH. The number of active views of the RVH + // SiteInstanceImpl is not zero. Upon reception of the SwapOutACK, the RVH + // will be swapped out. + STATE_PENDING_SWAP_OUT, + // The RVH has not received the SwapOutACK yet, but the new page has + // committed in a different RVH. The number of active views of the RVH + // SiteInstanceImpl is zero. Upon reception of the SwapOutACK, the RVH will + // be shutdown. + STATE_PENDING_SHUTDOWN, + // The RVH is swapped out, and it is being used as a placeholder to allow + // for cross-process communication. + STATE_SWAPPED_OUT, + }; + // Helper function to determine whether the RVH state should contribute to the + // number of active views of a SiteInstance or not. + static bool IsRVHStateActive(RenderViewHostImplState rvh_state); + // Convenience function, just like RenderViewHost::FromID. static RenderViewHostImpl* FromID(int render_process_id, int render_view_id); @@ -280,7 +315,10 @@ class CONTENT_EXPORT RenderViewHostImpl // Whether this RenderViewHost has been swapped out to be displayed by a // different process. - bool is_swapped_out() const { return is_swapped_out_; } + bool IsSwappedOut() const { return rvh_state_ == STATE_SWAPPED_OUT; } + + // The current state of this RVH. + RenderViewHostImplState rvh_state() const { return rvh_state_; } // Called on the pending RenderViewHost when the network response is ready to // commit. We should ensure that the old RenderViewHost runs its unload @@ -313,11 +351,15 @@ class CONTENT_EXPORT RenderViewHostImpl // out. void OnSwappedOut(bool timed_out); - // Called to notify the renderer that it has been visibly swapped out and - // replaced by another RenderViewHost, after an earlier call to SwapOut. - // It is now safe for the process to exit if there are no other active - // RenderViews. - void WasSwappedOut(); + // Called when the RenderFrameHostManager has swapped in a new + // RenderFrameHost. Should |this| RVH switch to the pending shutdown state, + // |pending_delete_on_swap_out| will be executed upon reception of the + // SwapOutACK, or when the unload timer times out. + void WasSwappedOut(const base::Closure& pending_delete_on_swap_out); + + // Set |this| as pending shutdown. |on_swap_out| will be called + // when the SwapOutACK is received, or when the unload timer times out. + void SetPendingShutdown(const base::Closure& on_swap_out); // Close the page ignoring whether it has unload events registers. // This is called after the beforeunload and unload events have fired @@ -448,9 +490,8 @@ class CONTENT_EXPORT RenderViewHostImpl return is_waiting_for_beforeunload_ack_; } - bool is_waiting_for_unload_ack() { - return is_waiting_for_unload_ack_; - } + // Whether the RVH is waiting for the unload ack from the renderer. + bool IsWaitingForUnloadACK() const; // Update the FrameTree to use this RenderViewHost's main frame // RenderFrameHost. Called when the RenderViewHost is committed. @@ -468,6 +509,18 @@ class CONTENT_EXPORT RenderViewHostImpl bool main_frame, const GURL& url); + // Increases the refcounting on this RVH. This is done by the FrameTree on + // creation of a RenderFrameHost. + void increment_ref_count() { ++frames_ref_count_; } + + // Decreases the refcounting on this RVH. This is done by the FrameTree on + // destruction of a RenderFrameHost. + void decrement_ref_count() { --frames_ref_count_; } + + // Returns the refcount on this RVH, that is the number of RenderFrameHosts + // currently using it. + int ref_count() { return frames_ref_count_; } + // NOTE: Do not add functions that just send an IPC message that are called in // one or two places. Have the caller send the IPC message directly (unless // the caller places are in different platforms, in which case it's better @@ -582,12 +635,15 @@ class CONTENT_EXPORT RenderViewHostImpl FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, BasicRenderFrameHost); FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, RoutingIdSane); - // Sets whether this RenderViewHost is swapped out in favor of another, - // and clears any waiting state that is no longer relevant. - void SetSwappedOut(bool is_swapped_out); + // Updates the state of this RenderViewHost and clears any waiting state + // that is no longer relevant. + void SetState(RenderViewHostImplState rvh_state); bool CanAccessFilesOfPageState(const PageState& state) const; + // The number of RenderFrameHosts which have a reference to this RVH. + int frames_ref_count_; + // Our delegate, which wants to know about changes in the RenderView. RenderViewHostDelegate* delegate_; @@ -622,29 +678,23 @@ class CONTENT_EXPORT RenderViewHostImpl // first commit. bool has_accessed_initial_document_; - // Whether this RenderViewHost is currently swapped out, such that the view is - // being rendered by another process. - bool is_swapped_out_; + // The current state of this RVH. + RenderViewHostImplState rvh_state_; // Routing ID for the main frame's RenderFrameHost. int main_frame_routing_id_; // Set to true when there is a pending ViewMsg_ShouldClose message. This // ensures we don't spam the renderer with multiple beforeunload requests. - // When either this value or is_waiting_for_unload_ack_ is true, the value of + // When either this value or IsWaitingForUnloadACK is true, the value of // unload_ack_is_for_cross_site_transition_ indicates whether this is for a // cross-site transition or a tab close attempt. + // TODO(clamy): Remove this boolean and add one more state to the state + // machine. bool is_waiting_for_beforeunload_ack_; - // Set to true when there is a pending ViewMsg_Close message. Also see - // is_waiting_for_beforeunload_ack_, unload_ack_is_for_cross_site_transition_. - bool is_waiting_for_unload_ack_; - - // Set to true when waiting for ViewHostMsg_SwapOut_ACK has timed out. - bool has_timed_out_on_unload_; - // Valid only when is_waiting_for_beforeunload_ack_ or - // is_waiting_for_unload_ack_ is true. This tells us if the unload request + // IsWaitingForUnloadACK is true. This tells us if the unload request // is for closing the entire tab ( = false), or only this RenderViewHost in // the case of a cross-site transition ( = true). bool unload_ack_is_for_cross_site_transition_; @@ -679,6 +729,17 @@ class CONTENT_EXPORT RenderViewHostImpl scoped_ptr<BrowserMediaPlayerManager> media_player_manager_; #endif + // Used to swap out or shutdown this RVH when the unload event is taking too + // long to execute, depending on the number of active views in the + // SiteInstance. + scoped_ptr<TimeoutMonitor> unload_event_monitor_timeout_; + + // Called after receiving the SwapOutACK when the RVH is in state pending + // shutdown. Also called if the unload timer times out. + base::Closure pending_shutdown_on_swap_out_; + + base::WeakPtrFactory<RenderViewHostImpl> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(RenderViewHostImpl); }; diff --git a/content/browser/renderer_host/render_view_host_unittest.cc b/content/browser/renderer_host/render_view_host_unittest.cc index 73060c3..f2f1d97 100644 --- a/content/browser/renderer_host/render_view_host_unittest.cc +++ b/content/browser/renderer_host/render_view_host_unittest.cc @@ -72,10 +72,9 @@ TEST_F(RenderViewHostTest, CreateFullscreenWidget) { test_rvh()->CreateNewFullscreenWidget(routing_id); } -// Makes sure that RenderViewHost::is_waiting_for_unload_ack_ is false when -// reloading a page. If is_waiting_for_unload_ack_ is not false when reloading -// the contents may get closed out even though the user pressed the reload -// button. +// Makes sure that the RenderViewHost is not waiting for an unload ack when +// reloading a page. If this is not the case, when reloading, the contents may +// get closed out even though the user pressed the reload button. TEST_F(RenderViewHostTest, ResetUnloadOnReload) { const GURL url1("http://foo1"); const GURL url2("http://foo2"); @@ -85,12 +84,11 @@ TEST_F(RenderViewHostTest, ResetUnloadOnReload) { // . go to a page. // . go to a new page, preferably one that takes a while to resolve, such // as one on a site that doesn't exist. - // . After this step is_waiting_for_unload_ack_ has been set to true on - // the first RVH. + // . After this step IsWaitingForUnloadACK returns true on the first RVH. // . click stop before the page has been commited. // . click reload. - // . is_waiting_for_unload_ack_ is still true, and the if the hang monitor - // fires the contents gets closed. + // . IsWaitingForUnloadACK still returns true, and if the hang monitor fires + // the contents gets closed. NavigateAndCommit(url1); controller().LoadURL( @@ -101,7 +99,7 @@ TEST_F(RenderViewHostTest, ResetUnloadOnReload) { test_rvh()->SendShouldCloseACK(true); contents()->Stop(); controller().Reload(false); - EXPECT_FALSE(test_rvh()->is_waiting_for_unload_ack()); + EXPECT_FALSE(test_rvh()->IsWaitingForUnloadACK()); } // Ensure we do not grant bindings to a process shared with unprivileged views. diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index b4f54df..8529331 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -313,7 +313,8 @@ scoped_ptr<RenderWidgetHostIterator> RenderWidgetHost::GetRenderWidgetHosts() { // Add only active RenderViewHosts. RenderViewHost* rvh = RenderViewHost::From(widget); - if (!static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out()) + if (RenderViewHostImpl::IsRVHStateActive( + static_cast<RenderViewHostImpl*>(rvh)->rvh_state())) hosts->Add(widget); } diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 2095af8..f517908 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -327,6 +327,11 @@ WebContentsImpl::WebContentsImpl( WebContentsImpl::~WebContentsImpl() { is_being_destroyed_ = true; + // Delete all RFH pending shutdown, which will lead the corresponding RVH to + // shutdown and be deleted as well. + frame_tree_.ForEach( + base::Bind(&RenderFrameHostManager::ClearRFHsPendingShutdown)); + ClearAllPowerSaveBlockers(); for (std::set<RenderWidgetHostImpl*>::iterator iter = @@ -2779,7 +2784,7 @@ void WebContentsImpl::RenderViewCreated(RenderViewHost* render_view_host) { // Don't send notifications if we are just creating a swapped-out RVH for // the opener chain. These won't be used for view-source or WebUI, so it's // ok to return early. - if (static_cast<RenderViewHostImpl*>(render_view_host)->is_swapped_out()) + if (static_cast<RenderViewHostImpl*>(render_view_host)->IsSwappedOut()) return; if (delegate_) @@ -3061,7 +3066,7 @@ void WebContentsImpl::RequestOpenURL(RenderViewHost* rvh, bool user_gesture) { // If this came from a swapped out RenderViewHost, we only allow the request // if we are still in the same BrowsingInstance. - if (static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out() && + if (static_cast<RenderViewHostImpl*>(rvh)->IsSwappedOut() && !rvh->GetSiteInstance()->IsRelatedSiteInstance(GetSiteInstance())) { return; } @@ -3232,7 +3237,7 @@ void WebContentsImpl::RunJavaScriptMessage( // showing an interstitial as it's shown over the previous page and we don't // want the hidden page's dialogs to interfere with the interstitial. bool suppress_this_message = - static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out() || + static_cast<RenderViewHostImpl*>(rvh)->IsSwappedOut() || ShowingInterstitialPage() || !delegate_ || delegate_->ShouldSuppressDialogs() || @@ -3277,7 +3282,7 @@ void WebContentsImpl::RunBeforeUnloadConfirm(RenderViewHost* rvh, delegate_->WillRunBeforeUnloadConfirm(); bool suppress_this_message = - rvhi->is_swapped_out() || + rvhi->rvh_state() != RenderViewHostImpl::STATE_DEFAULT || !delegate_ || delegate_->ShouldSuppressDialogs() || !delegate_->GetJavaScriptDialogManager(); diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc index 2d9259f..635543d 100644 --- a/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/content/browser/web_contents/web_contents_impl_unittest.cc @@ -513,6 +513,7 @@ TEST_F(WebContentsImplTest, CrossSiteBoundaries) { EXPECT_TRUE(contents()->GetRenderManagerForTesting()-> IsOnSwappedOutList(pending_rfh)); EXPECT_EQ(pending_rvh_delete_count, 0); + pending_rvh->OnSwappedOut(false); // Close contents and ensure RVHs are deleted. DeleteContents(); @@ -710,6 +711,7 @@ TEST_F(WebContentsImplTest, NavigateDoesNotUseUpSiteInstance) { EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList( orig_rfh)); EXPECT_EQ(orig_rvh_delete_count, 0); + orig_rvh->OnSwappedOut(false); // Close contents and ensure RVHs are deleted. DeleteContents(); @@ -1138,7 +1140,7 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) { EXPECT_TRUE(contents()->cross_navigation_pending()); // Simulate swap out message when the response arrives. - orig_rvh->set_is_swapped_out(true); + orig_rvh->OnSwappedOut(false); // Suppose the navigation doesn't get a chance to commit, and the user // navigates in the current RVH's SiteInstance. @@ -1150,7 +1152,7 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) { SiteInstance* instance2 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); EXPECT_EQ(orig_rvh, rvh()); - EXPECT_FALSE(orig_rvh->is_swapped_out()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, orig_rvh->rvh_state()); EXPECT_EQ(instance1, instance2); EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); } diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h index b6ffaf9..a259f6d 100644 --- a/content/public/browser/render_process_host.h +++ b/content/public/browser/render_process_host.h @@ -29,6 +29,7 @@ class BrowserMessageFilter; class RenderProcessHostObserver; class RenderWidgetHost; class StoragePartition; +struct GlobalRequestID; typedef base::Thread* (*RendererMainThreadFactoryFunction)( const std::string& id); @@ -223,6 +224,10 @@ class CONTENT_EXPORT RenderProcessHost : public IPC::Sender, base::Callback<void(const std::string&)> callback) = 0; #endif + // Tells the ResourceDispatcherHost to resume a deferred navigation without + // transferring it to a new renderer process. + virtual void ResumeDeferredNavigation(const GlobalRequestID& request_id) = 0; + // Static management functions ----------------------------------------------- // Flag to run the renderer in process. This is primarily diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc index 5e67bf8..a2b058a 100644 --- a/content/public/test/mock_render_process_host.cc +++ b/content/public/test/mock_render_process_host.cc @@ -12,6 +12,7 @@ #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/common/child_process_host_impl.h" +#include "content/public/browser/global_request_id.h" #include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/storage_partition.h" @@ -263,6 +264,9 @@ void MockRenderProcessHost::SetWebRtcLogMessageCallback( } #endif +void MockRenderProcessHost::ResumeDeferredNavigation( + const GlobalRequestID& request_id) {} + bool MockRenderProcessHost::OnMessageReceived(const IPC::Message& msg) { IPC::Listener* listener = listeners_.Lookup(msg.routing_id()); if (listener) diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h index d3c9739..063cb8e 100644 --- a/content/public/test/mock_render_process_host.h +++ b/content/public/test/mock_render_process_host.h @@ -81,6 +81,8 @@ class MockRenderProcessHost : public RenderProcessHost { virtual void SetWebRtcLogMessageCallback( base::Callback<void(const std::string&)> callback) OVERRIDE; #endif + virtual void ResumeDeferredNavigation(const GlobalRequestID& request_id) + OVERRIDE; // IPC::Sender via RenderProcessHost. virtual bool Send(IPC::Message* msg) OVERRIDE; diff --git a/content/public/test/test_renderer_host.cc b/content/public/test/test_renderer_host.cc index 2c13db4..dc323ca 100644 --- a/content/public/test/test_renderer_host.cc +++ b/content/public/test/test_renderer_host.cc @@ -46,7 +46,8 @@ RenderViewHost* RenderViewHostTester::GetPendingForController( // static bool RenderViewHostTester::IsRenderViewHostSwappedOut(RenderViewHost* rvh) { - return static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out(); + return static_cast<RenderViewHostImpl*>(rvh)->rvh_state() == + RenderViewHostImpl::STATE_SWAPPED_OUT; } // static diff --git a/content/test/test_render_view_host.h b/content/test/test_render_view_host.h index cf963e4..985b4d9 100644 --- a/content/test/test_render_view_host.h +++ b/content/test/test_render_view_host.h @@ -285,16 +285,10 @@ class TestRenderViewHost return is_waiting_for_beforeunload_ack_; } - // Returns whether the RenderViewHost is currently waiting to hear the result - // of an unload handler from the renderer. - bool is_waiting_for_unload_ack() const { - return is_waiting_for_unload_ack_; - } - // Sets whether the RenderViewHost is currently swapped out, and thus // filtering messages from the renderer. - void set_is_swapped_out(bool is_swapped_out) { - is_swapped_out_ = is_swapped_out; + void set_rvh_state(RenderViewHostImplState rvh_state) { + rvh_state_ = rvh_state; } // If set, navigations will appear to have loaded through a proxy diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc index b7efad0..ec14917 100644 --- a/content/test/test_web_contents.cc +++ b/content/test/test_web_contents.cc @@ -151,12 +151,11 @@ void TestWebContents::CommitPendingNavigation() { page_id = GetMaxPageIDForSiteInstance(rvh->GetSiteInstance()) + 1; } - // Simulate the SwapOut_ACK that happens when we swap out the old - // RVH, before the navigation commits. This is needed when - // cross-site navigation happens (old_rvh != rvh). + rvh->SendNavigate(page_id, entry->GetURL()); + // Simulate the SwapOut_ACK. This is needed when cross-site navigation happens + // (old_rvh != rvh). if (old_rvh != rvh) static_cast<RenderViewHostImpl*>(old_rvh)->OnSwappedOut(false); - rvh->SendNavigate(page_id, entry->GetURL()); } void TestWebContents::ProceedWithCrossSiteNavigation() { |