summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorclamy@chromium.org <clamy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-28 01:52:24 +0000
committerclamy@chromium.org <clamy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-28 01:52:24 +0000
commit21b41c7e89c864e8bd5da8e4f81c38043eba83c5 (patch)
tree7be7e64a9a8e63ea75984ce7799ac615c0b1196b
parent82590814af33f91394e590df8a2b14c208563989 (diff)
downloadchromium_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
-rw-r--r--chrome/browser/renderer_host/web_cache_manager_browsertest.cc11
-rw-r--r--content/browser/frame_host/frame_tree.cc69
-rw-r--r--content/browser/frame_host/frame_tree.h20
-rw-r--r--content/browser/frame_host/navigation_controller_impl_unittest.cc1
-rw-r--r--content/browser/frame_host/render_frame_host_impl.cc6
-rw-r--r--content/browser/frame_host/render_frame_host_impl.h5
-rw-r--r--content/browser/frame_host/render_frame_host_manager.cc83
-rw-r--r--content/browser/frame_host/render_frame_host_manager.h17
-rw-r--r--content/browser/frame_host/render_frame_host_manager_unittest.cc264
-rw-r--r--content/browser/renderer_host/render_process_host_impl.h7
-rw-r--r--content/browser/renderer_host/render_view_host_impl.cc188
-rw-r--r--content/browser/renderer_host/render_view_host_impl.h109
-rw-r--r--content/browser/renderer_host/render_view_host_unittest.cc16
-rw-r--r--content/browser/renderer_host/render_widget_host_impl.cc3
-rw-r--r--content/browser/web_contents/web_contents_impl.cc13
-rw-r--r--content/browser/web_contents/web_contents_impl_unittest.cc6
-rw-r--r--content/public/browser/render_process_host.h5
-rw-r--r--content/public/test/mock_render_process_host.cc4
-rw-r--r--content/public/test/mock_render_process_host.h2
-rw-r--r--content/public/test/test_renderer_host.cc3
-rw-r--r--content/test/test_render_view_host.h10
-rw-r--r--content/test/test_web_contents.cc7
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(&params.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() {