#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/devtools/render_view_devtools_agent_host.h"
#include "content/browser/frame_host/cross_process_frame_connector.h"
+#include "content/browser/frame_host/debug_urls.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/frame_host/render_frame_host_factory.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/cross_site_transferring_request.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
namespace content {
-RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams()
- : is_transfer(false), frame_id(-1), should_replace_current_entry(false) {
-}
-
RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams(
const GlobalRequestID& global_request_id,
- bool is_transfer,
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
const std::vector<GURL>& transfer_url_chain,
Referrer referrer,
PageTransition page_transition,
int64 frame_id,
bool should_replace_current_entry)
: global_request_id(global_request_id),
- is_transfer(is_transfer),
+ cross_site_transferring_request(cross_site_transferring_request.Pass()),
transfer_url_chain(transfer_url_chain),
referrer(referrer),
page_transition(page_transition),
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_)
}
}
+ // If entry includes the request ID of a request that is being transferred,
+ // the destination render frame will take ownership, so release ownership of
+ // the request.
+ if (pending_nav_params_ &&
+ pending_nav_params_->global_request_id ==
+ entry.transferred_global_request_id()) {
+ pending_nav_params_->cross_site_transferring_request->ReleaseRequest();
+ }
+
return dest_render_frame_host;
}
// 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.
// TODO(creis): The blank swapped out page is visible during this time, but
// we can shorten this by delivering the response directly, rather than
// forcing an identical request to be made.
- if (pending_nav_params_->is_transfer) {
+ if (pending_nav_params_->cross_site_transferring_request) {
// Treat the last URL in the chain as the destination and the remainder as
// the redirect chain.
CHECK(pending_nav_params_->transfer_url_chain.size());
// TODO(creis): The blank swapped out page is visible during this time, but
// we can shorten this by delivering the response directly, rather than
// forcing an identical request to be made.
- if (pending_nav_params_->is_transfer) {
+ if (pending_nav_params_->cross_site_transferring_request) {
// Treat the last URL in the chain as the destination and the remainder as
// the redirect chain.
CHECK(pending_nav_params_->transfer_url_chain.size());
void RenderFrameHostManager::OnCrossSiteResponse(
RenderViewHost* pending_render_view_host,
const GlobalRequestID& global_request_id,
- bool is_transfer,
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
const std::vector<GURL>& transfer_url_chain,
const Referrer& referrer,
PageTransition page_transition,
// here, but currently we pass information for a transfer if
// ShouldSwapProcessesForRedirect returned true in the network stack.
// In that case, we should set up a transfer after the unload handler runs.
- // If is_transfer is false, we will just run the unload handler and resume.
+ // If |cross_site_transferring_request| is NULL, we will just run the unload
+ // handler and resume.
pending_nav_params_.reset(new PendingNavigationParams(
- global_request_id, is_transfer, transfer_url_chain, referrer,
- page_transition, frame_id, should_replace_current_entry));
+ global_request_id, cross_site_transferring_request.Pass(),
+ transfer_url_chain, referrer, page_transition, frame_id,
+ should_replace_current_entry));
// Run the unload handler of the current page.
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,
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;
}
const GURL& new_url = SiteInstanceImpl::GetEffectiveURL(browser_context,
new_entry->GetURL());
+ // Don't force a new BrowsingInstance for debug URLs that are handled in the
+ // renderer process, like javascript: or chrome://crash.
+ if (IsRendererDebugURL(new_url))
+ return false;
+
// For security, we should transition between processes when one is a Web UI
// page and one isn't.
if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
current_instance->GetSiteURL();
// View-source URLs must use a new SiteInstance and BrowsingInstance.
+ // We don't need a swap when going from view-source to a debug URL like
+ // chrome://crash, however.
// TODO(creis): Refactor this method so this duplicated code isn't needed.
// See http://crbug.com/123007.
if (current_entry &&
- current_entry->IsViewSourceMode() != entry.IsViewSourceMode()) {
+ current_entry->IsViewSourceMode() != entry.IsViewSourceMode() &&
+ !IsRendererDebugURL(dest_url)) {
return SiteInstance::CreateForURL(browser_context, dest_url);
}
// 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()));
}
// |pending_web_ui_|, or clear |web_ui_| if there is no pending WebUI, or
// leave |web_ui_| as is if reusing it.
DCHECK(!(pending_web_ui_.get() && pending_and_current_web_ui_.get()));
- if (pending_web_ui_)
+ if (pending_web_ui_) {
web_ui_.reset(pending_web_ui_.release());
- else if (!pending_and_current_web_ui_.get())
+ } else if (!pending_and_current_web_ui_.get()) {
web_ui_.reset();
+ } else {
+ DCHECK_EQ(pending_and_current_web_ui_.get(), web_ui_.get());
+ pending_and_current_web_ui_.reset();
+ }
// It's possible for the pending_render_frame_host_ to be NULL when we aren't
// crossing process boundaries. If so, we just needed to handle the Web UI
// 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);
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() &&
// 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
tree->ForEach(base::Bind(
&RenderFrameHostManager::ClearSwappedOutRFHsInSiteInstance,
site_instance_id));
- // rvh is now deleted.
}
}
}