Index: content/browser/frame_host/render_frame_host_manager.cc |
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc |
index 51c9c241ec37466bb2fa073c3c32af10f25c6b61..4da4dc5bb8668f48db36827fd2afb092686954e5 100644 |
--- a/content/browser/frame_host/render_frame_host_manager.cc |
+++ b/content/browser/frame_host/render_frame_host_manager.cc |
@@ -60,7 +60,8 @@ RenderFrameHostManager::RenderFrameHostManager( |
render_frame_delegate_(render_frame_delegate), |
render_view_delegate_(render_view_delegate), |
render_widget_delegate_(render_widget_delegate), |
- interstitial_page_(NULL), |
+ interstitial_page_(nullptr), |
+ should_reuse_web_ui_(false), |
weak_factory_(this) { |
DCHECK(frame_tree_node_); |
} |
@@ -69,6 +70,11 @@ RenderFrameHostManager::~RenderFrameHostManager() { |
if (pending_render_frame_host_) |
UnsetPendingRenderFrameHost(); |
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kEnableBrowserSideNavigation)) { |
+ UnsetSpeculativeRenderFrameHost(); |
+ } |
+ |
if (render_frame_host_ && |
render_frame_host_->GetSiteInstance()->active_frame_count() <= 1U) { |
ShutdownRenderFrameProxyHostsInSiteInstance( |
@@ -292,6 +298,8 @@ void RenderFrameHostManager::OnBeforeUnloadACK( |
bool proceed, |
const base::TimeTicks& proceed_time) { |
if (for_cross_site_transition) { |
+ DCHECK(!base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kEnableBrowserSideNavigation)); |
// Ignore if we're not in a cross-site navigation. |
if (!cross_navigation_pending_) |
return; |
@@ -417,7 +425,26 @@ void RenderFrameHostManager::ClearNavigationTransitionData() { |
void RenderFrameHostManager::DidNavigateFrame( |
RenderFrameHostImpl* render_frame_host, |
- bool was_caused_by_user_gesture) { |
+ bool was_caused_by_user_gesture, |
+ bool was_within_same_page) { |
+ DCHECK(render_frame_host); |
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kEnableBrowserSideNavigation)) { |
+ if (render_frame_host == speculative_render_frame_host_.get()) { |
+ CommitPending(); |
+ } else if (render_frame_host == render_frame_host_.get()) { |
+ DCHECK(was_within_same_page || !speculative_render_frame_host_); |
+ // TODO(carlosk): if both |was_within_same_page| and |
+ // |was_caused_by_user_gesture| are true properly cancel a possible |
+ // ongoing navigation request. |
+ } else { |
+ // No one else should be sending us a DidNavigate in this state. |
+ DCHECK(false); |
+ } |
+ DCHECK(!speculative_render_frame_host_); |
+ return; |
+ } |
+ |
if (!cross_navigation_pending_) { |
DCHECK(!pending_render_frame_host_); |
@@ -439,7 +466,7 @@ void RenderFrameHostManager::DidNavigateFrame( |
// A navigation in the original page has taken place. Cancel the pending |
// one. Only do it for user gesture originated navigations to prevent |
// page doing any shenanigans to prevent user from navigating. |
- // See https://code.google.com/p/chromium/issues/detail?id=75195 |
+ // See https://crbug.com/75195. |
CancelPending(); |
cross_navigation_pending_ = false; |
} |
@@ -578,7 +605,10 @@ void RenderFrameHostManager::DiscardUnusedFrame( |
RenderFrameProxyHost* proxy = |
new RenderFrameProxyHost(site_instance, frame_tree_node_); |
proxy_hosts_[site_instance->GetId()] = proxy; |
- render_frame_host->SwapOut(proxy, false); |
+ |
+ if (!render_frame_host->is_swapped_out()) |
+ render_frame_host->SwapOut(proxy, false); |
+ |
if (frame_tree_node_->IsMainFrame()) |
proxy->TakeFrameHostOwnership(render_frame_host.Pass()); |
} else { |
@@ -623,35 +653,170 @@ void RenderFrameHostManager::ResetProxyHosts() { |
} |
// PlzNavigate |
+void RenderFrameHostManager::BeginNavigation( |
+ const FrameHostMsg_BeginNavigation_Params& params, |
+ const CommonNavigationParams& common_params) { |
+ CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kEnableBrowserSideNavigation)); |
+ // Cleans up any state in case there's an ongoing navigation. |
+ // TODO(carlosk): remove this cleanup here once we properly cancel ongoing |
+ // navigations. |
+ CleanUpNavigation(); |
+ |
+ SiteInstance* current_instance = render_frame_host_->GetSiteInstance(); |
+ // TODO(carlosk): Replace the default values with the right ones for |
+ // source_instance, dest_instance, dest_is_restore, dest_is_view_source_mode. |
+ scoped_refptr<SiteInstanceImpl> new_instance = static_cast<SiteInstanceImpl*>( |
+ GetSiteInstanceForNavigation(common_params.url, nullptr, nullptr, |
+ common_params.transition, false, false)); |
+ |
+ // Will keep the current RFH if its SiteInstance matches the new one from |
+ // the navigation or if this is a subframe navigation. If --site-per-process |
+ // is enabled the RFH is never kept when sites don't match. |
+ // TODO(carlosk): do not swap processes for renderer initiated navigations |
+ // (see crbug.com/440266). |
+ if (current_instance == new_instance.get() || |
+ (!frame_tree_node_->IsMainFrame() && |
+ !base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kSitePerProcess))) { |
+ // Make sure the current RFH is alive. |
+ if (!render_frame_host_->render_view_host()->IsRenderViewLive()) { |
+ // Recreate the opener chain. |
+ int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager( |
+ render_frame_host_->GetSiteInstance()); |
+ bool success = InitRenderView(render_frame_host_->render_view_host(), |
+ opener_route_id, MSG_ROUTING_NONE, |
+ frame_tree_node_->IsMainFrame()); |
+ DCHECK(success); |
+ } |
+ DCHECK(current_instance->GetProcess()->HasConnection()); |
+ return; |
+ } |
+ |
+ // Speculatively create a new RFH. |
+ // TODO(carlosk): enable bindings check below. |
+ bool success = CreateSpeculativeRenderFrameHost( |
+ common_params.url, current_instance, new_instance.get(), |
+ NavigationEntryImpl::kInvalidBindings); |
+ DCHECK(success); |
+ DCHECK(new_instance->GetProcess()->HasConnection()); |
+} |
+ |
+// PlzNavigate |
+bool RenderFrameHostManager::CreateSpeculativeRenderFrameHost( |
+ const GURL& url, |
+ SiteInstance* old_instance, |
+ SiteInstance* new_instance, |
+ int bindings) { |
+ CHECK(new_instance); |
+ CHECK_NE(old_instance, new_instance); |
+ |
+ const NavigationEntry* current_navigation_entry = |
+ delegate_->GetLastCommittedNavigationEntryForRenderManager(); |
+ scoped_ptr<WebUIImpl> new_web_ui; |
+ should_reuse_web_ui_ = ShouldReuseWebUI(current_navigation_entry, url); |
+ if (!should_reuse_web_ui_) |
+ new_web_ui = CreateWebUI(url, bindings); |
+ |
+ int opener_route_id = |
+ CreateOpenerRenderViewsIfNeeded(old_instance, new_instance); |
+ |
+ int create_render_frame_flags = 0; |
+ if (frame_tree_node_->IsMainFrame()) |
+ create_render_frame_flags |= CREATE_RF_FOR_MAIN_FRAME_NAVIGATION; |
+ if (delegate_->IsHidden()) |
+ create_render_frame_flags |= CREATE_RF_HIDDEN; |
+ scoped_ptr<RenderFrameHostImpl> new_render_frame_host = |
+ CreateRenderFrame(new_instance, new_web_ui.get(), opener_route_id, |
+ create_render_frame_flags, nullptr); |
+ if (!new_render_frame_host) { |
+ return false; |
+ } |
+ speculative_render_frame_host_.reset(new_render_frame_host.release()); |
+ speculative_web_ui_.reset(new_web_ui.release()); |
+ return true; |
+} |
+ |
+// PlzNavigate |
RenderFrameHostImpl* RenderFrameHostManager::GetFrameHostForNavigation( |
const GURL& url, |
ui::PageTransition transition) { |
CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( |
switches::kEnableBrowserSideNavigation)); |
- // TODO(clamy): When we handle renderer initiated navigations, make sure not |
- // to use a different process for subframes if --site-per-process is not |
- // enabled. |
- |
// Pick the right RenderFrameHost to commit the navigation. |
- // TODO(clamy): Replace the default values by the right ones. |
- RenderFrameHostImpl* render_frame_host = UpdateStateForNavigate( |
- url, nullptr, nullptr, transition, false, false, GlobalRequestID(), |
- NavigationEntryImpl::kInvalidBindings); |
+ RenderFrameHostImpl* rfh_for_navigation = nullptr; |
+ |
+ SiteInstance* current_instance = render_frame_host_->GetSiteInstance(); |
+ // TODO(clamy): Replace the default values with the right ones for |
+ // source_instance, dest_instance, dest_is_restore, dest_is_view_source_mode. |
+ scoped_refptr<SiteInstance> new_instance = GetSiteInstanceForNavigation( |
+ url, nullptr, nullptr, transition, false, false); |
+ |
+ // Will keep the current RFH if its SiteInstance matches the new one from |
+ // the navigation or if this is a subframe navigation. If --site-per-process |
+ // is enabled the RFH is never kept when sites don't match. |
+ // TODO(carlosk): do not swap processes for renderer initiated navigations |
+ // (see crbug.com/440266). |
+ if (current_instance == new_instance.get() || |
+ (!frame_tree_node_->IsMainFrame() && |
+ !base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kSitePerProcess))) { |
+ CleanUpNavigation(); |
+ rfh_for_navigation = render_frame_host_.get(); |
+ } else { |
+ // If the SiteInstance for the final URL doesn't match the one from the |
+ // speculatively created RenderFrameHost, create a new RenderFrameHost using |
+ // this new SiteInstance. |
+ if (!speculative_render_frame_host_ || |
+ speculative_render_frame_host_->GetSiteInstance() != |
+ new_instance.get()) { |
+ CleanUpNavigation(); |
+ // TODO(clamy): Replace the binding value with the right one. |
+ bool success = CreateSpeculativeRenderFrameHost( |
+ url, current_instance, new_instance.get(), |
+ NavigationEntryImpl::kInvalidBindings); |
+ CHECK(success); |
+ } |
+ DCHECK(speculative_render_frame_host_); |
+ rfh_for_navigation = speculative_render_frame_host_.get(); |
+ } |
+ DCHECK(rfh_for_navigation); |
- // If the renderer that needs to navigate is not live (it was just created or |
- // it crashed), initialize it. |
- if (!render_frame_host->render_view_host()->IsRenderViewLive()) { |
+ // If the renderer that needs to navigate is not live (it was just created |
+ // or it crashed), initialize it. |
+ if (!rfh_for_navigation->render_view_host()->IsRenderViewLive()) { |
// Recreate the opener chain. |
int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager( |
- render_frame_host->GetSiteInstance()); |
- if (!InitRenderView(render_frame_host->render_view_host(), |
- opener_route_id, |
- MSG_ROUTING_NONE, |
- frame_tree_node_->IsMainFrame())) { |
+ rfh_for_navigation->GetSiteInstance()); |
+ if (!InitRenderView(rfh_for_navigation->render_view_host(), opener_route_id, |
+ MSG_ROUTING_NONE, frame_tree_node_->IsMainFrame())) { |
return nullptr; |
} |
} |
- return render_frame_host; |
+ |
+ return rfh_for_navigation; |
+} |
+ |
+// PlzNavigate |
+void RenderFrameHostManager::CleanUpNavigation() { |
+ scoped_ptr<RenderFrameHostImpl> rfh = UnsetSpeculativeRenderFrameHost(); |
+ if (rfh) |
+ DiscardUnusedFrame(rfh.Pass()); |
+} |
+ |
+// PlzNavigate |
+scoped_ptr<RenderFrameHostImpl> |
+RenderFrameHostManager::UnsetSpeculativeRenderFrameHost() { |
+ CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kEnableBrowserSideNavigation)); |
+ if (speculative_web_ui_) |
+ speculative_web_ui_.reset(); |
+ should_reuse_web_ui_ = false; |
+ if (speculative_render_frame_host_) { |
+ speculative_render_frame_host_->GetProcess()->RemovePendingView(); |
+ return speculative_render_frame_host_.Pass(); |
+ } |
+ return nullptr; |
} |
void RenderFrameHostManager::OnDidStartLoading() { |
@@ -1094,7 +1259,7 @@ scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::CreateRenderFrameHost( |
// Create a RVH for main frames, or find the existing one for subframes. |
FrameTree* frame_tree = frame_tree_node_->frame_tree(); |
- RenderViewHostImpl* render_view_host = NULL; |
+ RenderViewHostImpl* render_view_host = nullptr; |
if (frame_tree_node_->IsMainFrame()) { |
render_view_host = frame_tree->CreateRenderViewHost( |
site_instance, view_routing_id, frame_routing_id, swapped_out, hidden); |
@@ -1133,8 +1298,8 @@ scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::CreateRenderFrame( |
if (view_routing_id_ptr) |
*view_routing_id_ptr = MSG_ROUTING_NONE; |
- // We are creating a pending or swapped out RFH here. We should never create |
- // it in the same SiteInstance as our current RFH. |
+ // We are creating a pending, speculative or swapped out RFH here. We should |
+ // never create it in the same SiteInstance as our current RFH. |
CHECK_NE(render_frame_host_->GetSiteInstance(), instance); |
// Check if we've already created an RFH for this SiteInstance. If so, try |
@@ -1313,29 +1478,40 @@ int RenderFrameHostManager::GetRoutingIdForSiteInstance( |
void RenderFrameHostManager::CommitPending() { |
TRACE_EVENT1("navigation", "RenderFrameHostManager::CommitPending", |
"FrameTreeNode id", frame_tree_node_->frame_tree_node_id()); |
+ bool browser_side_navigation = |
+ base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kEnableBrowserSideNavigation); |
// First check whether we're going to want to focus the location bar after |
// this commit. We do this now because the navigation hasn't formally |
// committed yet, so if we've already cleared |pending_web_ui_| the call chain |
// this triggers won't be able to figure out what's going on. |
bool will_focus_location_bar = delegate_->FocusLocationBarByDefault(); |
- // Next commit the Web UI, if any. Either replace |web_ui_| with |
- // |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_) { |
- web_ui_.reset(pending_web_ui_.release()); |
- } else if (!pending_and_current_web_ui_.get()) { |
- web_ui_.reset(); |
+ if (!browser_side_navigation) { |
+ DCHECK(!speculative_web_ui_); |
+ // Next commit the Web UI, if any. Either replace |web_ui_| with |
+ // |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_) { |
+ web_ui_.reset(pending_web_ui_.release()); |
+ } 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(); |
+ } |
} else { |
- DCHECK_EQ(pending_and_current_web_ui_.get(), web_ui_.get()); |
- pending_and_current_web_ui_.reset(); |
+ // PlzNavigate |
+ if (!should_reuse_web_ui_) |
+ web_ui_.reset(speculative_web_ui_.release()); |
+ DCHECK(!speculative_web_ui_); |
} |
- // 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 |
- // committing above and we're done. |
- if (!pending_render_frame_host_) { |
+ // It's possible for the pending_render_frame_host_ to be nullptr when we |
+ // aren't crossing process boundaries. If so, we just needed to handle the Web |
+ // UI committing above and we're done. |
+ if (!pending_render_frame_host_ && !speculative_render_frame_host_) { |
if (will_focus_location_bar) |
delegate_->SetFocusToLocationBar(false); |
return; |
@@ -1349,10 +1525,20 @@ void RenderFrameHostManager::CommitPending() { |
bool is_main_frame = frame_tree_node_->IsMainFrame(); |
- // Swap in the pending frame and make it active. Also ensure the FrameTree |
- // stays in sync. |
- scoped_ptr<RenderFrameHostImpl> old_render_frame_host = |
- SetRenderFrameHost(pending_render_frame_host_.Pass()); |
+ // Swap in the pending or speculative frame and make it active. Also ensure |
+ // the FrameTree stays in sync. |
+ scoped_ptr<RenderFrameHostImpl> old_render_frame_host; |
+ if (!browser_side_navigation) { |
+ DCHECK(!speculative_render_frame_host_); |
+ old_render_frame_host = |
+ SetRenderFrameHost(pending_render_frame_host_.Pass()); |
+ } else { |
+ // PlzNavigate |
+ DCHECK(speculative_render_frame_host_); |
+ old_render_frame_host = |
+ SetRenderFrameHost(speculative_render_frame_host_.Pass()); |
+ } |
+ |
if (is_main_frame) |
render_frame_host_->render_view_host()->AttachToFrameTree(); |
@@ -1498,17 +1684,17 @@ RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( |
// New SiteInstance: create a pending RFH to navigate. |
DCHECK(!cross_navigation_pending_); |
- // This will possibly create (set to NULL) a Web UI object for the pending |
- // page. We'll use this later to give the page special access. This must |
- // happen before the new renderer is created below so it will get bindings. |
- // It must also happen after the above conditional call to CancelPending(), |
- // otherwise CancelPending may clear the pending_web_ui_ and the page will |
- // not have its bindings set appropriately. |
+ // This will possibly create (set to nullptr) a Web UI object for the |
+ // pending page. We'll use this later to give the page special access. This |
+ // must happen before the new renderer is created below so it will get |
+ // bindings. It must also happen after the above conditional call to |
+ // CancelPending(), otherwise CancelPending may clear the pending_web_ui_ |
+ // and the page will not have its bindings set appropriately. |
SetPendingWebUI(dest_url, bindings); |
CreatePendingRenderFrameHost(current_instance, new_instance.get(), |
frame_tree_node_->IsMainFrame()); |
if (!pending_render_frame_host_.get()) { |
- return NULL; |
+ return nullptr; |
} |
// Check if our current RFH is live before we set up a transition. |
@@ -1532,14 +1718,6 @@ RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( |
DCHECK(!cross_navigation_pending_); |
cross_navigation_pending_ = true; |
- // PlzNavigate: There is no notion of transfer navigations, and the old |
- // renderer before unload handler has already run at that point, so return |
- // here. |
- if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kEnableBrowserSideNavigation)) { |
- return pending_render_frame_host_.get(); |
- } |
- |
// We need to wait until the beforeunload handler has run, unless we are |
// transferring an existing request (in which case it has already run). |
// Suspend the new render view (i.e., don't let it send the cross-site |