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 9973a8657557899523f41b4e45c838445fc7a3ce..12869aacf5fa7ac456503cc8f5a35ce877cfd76e 100644 |
--- a/content/browser/frame_host/render_frame_host_manager.cc |
+++ b/content/browser/frame_host/render_frame_host_manager.cc |
@@ -15,6 +15,7 @@ |
#include "content/browser/frame_host/cross_site_transferring_request.h" |
#include "content/browser/frame_host/debug_urls.h" |
#include "content/browser/frame_host/interstitial_page_impl.h" |
+#include "content/browser/frame_host/navigation_commit_info.h" |
#include "content/browser/frame_host/navigation_controller_impl.h" |
#include "content/browser/frame_host/navigation_entry_impl.h" |
#include "content/browser/frame_host/navigation_request.h" |
@@ -39,9 +40,39 @@ |
#include "content/public/browser/web_ui_controller.h" |
#include "content/public/common/content_switches.h" |
#include "content/public/common/url_constants.h" |
+#include "net/base/load_flags.h" |
namespace content { |
+namespace { |
+ |
+FrameHostMsg_BeginNavigation_Params BeginNavigationFromNavigate( |
+ const FrameMsg_Navigate_Params& navigate_params) { |
+ FrameHostMsg_BeginNavigation_Params begin_navigation_params; |
+ begin_navigation_params.method = navigate_params.is_post ? |
+ "POST" : "GET"; |
+ begin_navigation_params.url = navigate_params.url; |
+ begin_navigation_params.referrer = navigate_params.referrer.url; |
+ begin_navigation_params.referrer_policy = navigate_params.referrer.policy; |
+ |
+ // TODO(clamy): This should be modified to take into account caching policy |
+ // requirements (eg for POST reloads). |
+ begin_navigation_params.load_flags = |
+ net::LOAD_NORMAL | net::LOAD_ENABLE_LOAD_TIMING; |
+ |
+ // TODO(clamy): Post data from the browser should be put in the request body. |
+ |
+ begin_navigation_params.has_user_gesture = false; |
+ begin_navigation_params.transition_type = navigate_params.transition; |
+ begin_navigation_params.should_replace_current_entry = |
+ navigate_params.should_replace_current_entry; |
+ begin_navigation_params.allow_download = |
+ navigate_params.allow_download; |
+ return begin_navigation_params; |
+} |
+ |
+} // namespace |
+ |
RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams( |
const GlobalRequestID& global_request_id, |
scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request, |
@@ -180,43 +211,9 @@ RenderFrameHostImpl* RenderFrameHostManager::Navigate( |
if (!dest_render_frame_host) |
return NULL; // We weren't able to create a pending render frame host. |
- // If the current render_frame_host_ isn't live, we should create it so |
- // that we don't show a sad tab while the dest_render_frame_host fetches |
- // its first page. (Bug 1145340) |
- if (dest_render_frame_host != render_frame_host_ && |
- !render_frame_host_->render_view_host()->IsRenderViewLive()) { |
- // Note: we don't call InitRenderView here because we are navigating away |
- // soon anyway, and we don't have the NavigationEntry for this host. |
- delegate_->CreateRenderViewForRenderManager( |
- render_frame_host_->render_view_host(), MSG_ROUTING_NONE, |
- MSG_ROUTING_NONE, frame_tree_node_->IsMainFrame()); |
- } |
- |
- // If the renderer crashed, then try to create a new one to satisfy this |
- // navigation request. |
- if (!dest_render_frame_host->render_view_host()->IsRenderViewLive()) { |
- // Recreate the opener chain. |
- int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager( |
- dest_render_frame_host->GetSiteInstance()); |
- if (!InitRenderView(dest_render_frame_host->render_view_host(), |
- opener_route_id, |
- MSG_ROUTING_NONE, |
- frame_tree_node_->IsMainFrame())) |
- return NULL; |
- |
- // Now that we've created a new renderer, be sure to hide it if it isn't |
- // our primary one. Otherwise, we might crash if we try to call Show() |
- // on it later. |
- if (dest_render_frame_host != render_frame_host_ && |
- dest_render_frame_host->render_view_host()->GetView()) { |
- dest_render_frame_host->render_view_host()->GetView()->Hide(); |
- } else { |
- // Notify here as we won't be calling CommitPending (which does the |
- // notify). |
- delegate_->NotifySwappedFromRenderManager( |
- NULL, render_frame_host_.get(), frame_tree_node_->IsMainFrame()); |
- } |
- } |
+ // Initialize the destination RFH and the current RFH if they are not live. |
+ if (!InitRenderFrameHostsBeforeNavigation(dest_render_frame_host)) |
+ return NULL; |
// If entry includes the request ID of a request that is being transferred, |
// the destination render frame will take ownership, so release ownership of |
@@ -230,6 +227,20 @@ RenderFrameHostImpl* RenderFrameHostManager::Navigate( |
return dest_render_frame_host; |
} |
+bool RenderFrameHostManager::RequestNavigation( |
+ const NavigationEntryImpl& entry, |
+ const FrameMsg_Navigate_Params& navigate_params) { |
+ // TODO(clamy): replace RenderViewHost::IsRenderViewLive by |
+ // RenderFrameHost::IsLive. |
+ if (render_frame_host_->render_view_host()->IsRenderViewLive()) |
+ // TODO(clamy): send a RequestNavigation IPC. |
+ return true; |
+ |
+ // The navigation request is sent directly to the IO thread. |
+ OnBeginNavigation(BeginNavigationFromNavigate(navigate_params)); |
+ return true; |
+} |
+ |
void RenderFrameHostManager::Stop() { |
render_frame_host_->render_view_host()->Stop(); |
@@ -585,8 +596,73 @@ void RenderFrameHostManager::OnBeginNavigation( |
navigation_request_.reset( |
new NavigationRequest(info, frame_tree_node_->frame_tree_node_id())); |
navigation_request_->BeginNavigation(params.request_body); |
- // TODO(clamy): If we have no live RenderFrameHost to handle the request (eg |
- // cross-site navigation) spawn one speculatively here and keep track of it. |
+ |
+ // Create a new RFH for the navigation if necessary. |
+ RenderFrameHostImpl* dest_render_frame_host = render_frame_host_.get(); |
+ SiteInstance* current_instance = render_frame_host_->GetSiteInstance(); |
+ SiteInstance* new_instance = GetSiteInstanceForNavigation( |
+ *NavigationEntryImpl::FromNavigationEntry( |
+ frame_tree_node_->navigator()->GetController()->GetPendingEntry())); |
+ if (current_instance != new_instance) { |
+ dest_render_frame_host = CreateRenderFrameHostForCrossSite( |
+ current_instance, new_instance, true); |
+ DCHECK(dest_render_frame_host); |
+ } |
+ InitRenderFrameHostsBeforeNavigation(dest_render_frame_host); |
+} |
+ |
+void RenderFrameHostManager::CommitNavigation( |
+ const NavigationCommitInfo& info) { |
+ // Update the pending NavigationEntry for commit. |
+ // TODO(clamy): Check if some more state should be updated at that point. |
+ NavigationEntry* entry = |
+ frame_tree_node_->navigator()->GetController()->GetPendingEntry(); |
+ entry->SetURL(info.navigation_url); |
+ |
+ // Pick the right RenderFrameHost to commit the navigation. If the current RFH |
+ // is not suitable, a RFH may have been spawned speculatively to handle the |
+ // navigation. If there is none, or if it is not suitable, a new RFH needs to |
+ // be created. |
+ SiteInstance* current_instance = render_frame_host_->GetSiteInstance(); |
+ SiteInstance* speculative_instance = speculative_render_frame_host_.get() ? |
+ speculative_render_frame_host_->GetSiteInstance() : NULL; |
+ DCHECK(current_instance != speculative_instance); |
+ |
+ SiteInstance* new_instance = GetSiteInstanceForNavigation( |
+ *NavigationEntryImpl::FromNavigationEntry(entry)); |
+ RenderFrameHostImpl* rfh_to_navigate = NULL; |
+ DCHECK(!pending_render_frame_host_.get()); |
+ |
+ if (current_instance == new_instance) |
+ rfh_to_navigate = render_frame_host_.get(); |
+ |
+ if (speculative_instance) { |
+ if (speculative_instance == new_instance) { |
+ rfh_to_navigate = speculative_render_frame_host_.get(); |
+ } else { |
+ speculative_render_frame_host_.reset(); |
+ } |
+ } |
+ |
+ if (!rfh_to_navigate) { |
+ rfh_to_navigate = CreateRenderFrameHostForCrossSite( |
+ current_instance, new_instance, true); |
+ DCHECK(rfh_to_navigate); |
+ } |
+ |
+ // A WebUI renderer should never display a non WebUI url. |
+ frame_tree_node_->navigator()->CheckWebUIRendererDoesNotDisplayNormalURL( |
+ rfh_to_navigate, info.navigation_url); |
+ |
+ // TODO(clamy): the rfh_to_navigate should now send a commit IPC to the |
+ // renderer. |
+ // TODO(clamy): If the current RFH is not reused, then it should be swapped |
+ // out, and asked to run its unload handler. |
+ if (rfh_to_navigate != render_frame_host_.get()) |
+ SetRenderFrameHost(speculative_render_frame_host_.Pass()); |
+ DCHECK(!speculative_render_frame_host_.get()); |
+ |
+ navigation_request_.reset(); |
} |
void RenderFrameHostManager::Observe( |
@@ -900,6 +976,34 @@ SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry( |
return current_instance->GetRelatedSiteInstance(dest_url); |
} |
+RenderFrameHostImpl* RenderFrameHostManager::CreateRenderFrameHostForCrossSite( |
+ SiteInstance* old_instance, |
+ SiteInstance* new_instance, |
+ bool is_speculative) { |
+ // Ensure that we have created RFHs for the new RFH's opener chain if |
+ // we are staying in the same BrowsingInstance. This allows the pending RFH |
+ // to send cross-process script calls to its opener(s). |
+ int opener_route_id = MSG_ROUTING_NONE; |
+ if (new_instance->IsRelatedSiteInstance(old_instance)) { |
+ opener_route_id = |
+ delegate_->CreateOpenerRenderViewsForRenderManager(new_instance); |
+ } |
+ |
+ // Create a non-swapped-out speculative RFH with the given opener. |
+ int route_id = CreateRenderFrame(new_instance, opener_route_id, false, |
+ delegate_->IsHidden(), is_speculative); |
+ if (route_id == MSG_ROUTING_NONE) { |
+ if (is_speculative) |
+ speculative_render_frame_host_.reset(); |
+ else |
+ pending_render_frame_host_.reset(); |
+ return NULL; |
+ } |
+ if (is_speculative) |
+ return speculative_render_frame_host_.get(); |
+ return pending_render_frame_host_.get(); |
+} |
+ |
scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::CreateRenderFrameHost( |
SiteInstance* site_instance, |
int view_routing_id, |
@@ -944,9 +1048,11 @@ int RenderFrameHostManager::CreateRenderFrame( |
SiteInstance* instance, |
int opener_route_id, |
bool swapped_out, |
- bool hidden) { |
+ bool hidden, |
+ bool is_speculative) { |
CHECK(instance); |
DCHECK(!swapped_out || hidden); // Swapped out views should always be hidden. |
+ DCHECK(!(swapped_out && is_speculative)); |
scoped_ptr<RenderFrameHostImpl> new_render_frame_host; |
RenderFrameHostImpl* frame_to_announce = NULL; |
@@ -1020,8 +1126,12 @@ int RenderFrameHostManager::CreateRenderFrame( |
} |
// Use this as our new pending RFH if it isn't swapped out. |
- if (!swapped_out) |
- pending_render_frame_host_ = new_render_frame_host.Pass(); |
+ if (!swapped_out) { |
+ if (is_speculative) |
+ speculative_render_frame_host_ = new_render_frame_host.Pass(); |
+ else |
+ pending_render_frame_host_ = new_render_frame_host.Pass(); |
+ } |
// If a brand new RFH was created, announce it to observers. |
if (frame_to_announce) |
@@ -1058,6 +1168,77 @@ bool RenderFrameHostManager::InitRenderView(RenderViewHost* render_view_host, |
render_view_host, opener_route_id, proxy_routing_id, for_main_frame); |
} |
+bool RenderFrameHostManager::InitRenderFrameHostsBeforeNavigation( |
+ RenderFrameHostImpl* dest_render_frame_host) { |
+ // If the current render_frame_host_ isn't live, we should create it so |
+ // that we don't show a sad tab while the dest_render_frame_host fetches |
+ // its first page. (crbug.com/1145340) |
+ if (dest_render_frame_host != render_frame_host_ && |
+ !render_frame_host_->render_view_host()->IsRenderViewLive()) { |
+ // Note: we don't call InitRenderView here because we are navigating away |
+ // soon anyway, and we don't have the NavigationEntry for this host. |
+ delegate_->CreateRenderViewForRenderManager( |
+ render_frame_host_->render_view_host(), MSG_ROUTING_NONE, |
+ MSG_ROUTING_NONE, frame_tree_node_->IsMainFrame()); |
+ } |
+ |
+ // If the renderer crashed, then try to create a new one to satisfy this |
+ // navigation request. |
+ if (!dest_render_frame_host->render_view_host()->IsRenderViewLive()) { |
+ // Recreate the opener chain. |
+ int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager( |
+ dest_render_frame_host->GetSiteInstance()); |
+ if (!InitRenderView(dest_render_frame_host->render_view_host(), |
+ opener_route_id, |
+ MSG_ROUTING_NONE, |
+ frame_tree_node_->IsMainFrame())) |
+ return false; |
+ |
+ // Now that we've created a new renderer, be sure to hide it if it isn't |
+ // our primary one. Otherwise, we might crash if we try to call Show() |
+ // on it later. |
+ if (dest_render_frame_host != render_frame_host_ && |
+ dest_render_frame_host->render_view_host()->GetView()) { |
+ dest_render_frame_host->render_view_host()->GetView()->Hide(); |
+ } else { |
+ // Notify here as we won't be calling CommitPending (which does the |
+ // notify). |
+ delegate_->NotifySwappedFromRenderManager( |
+ NULL, render_frame_host_.get(), frame_tree_node_->IsMainFrame()); |
+ } |
+ } |
+ return true; |
+} |
+ |
+SiteInstance* RenderFrameHostManager::GetSiteInstanceForNavigation( |
+ const NavigationEntryImpl& entry) { |
+ SiteInstance* current_instance = render_frame_host_->GetSiteInstance(); |
+ SiteInstance* new_instance = current_instance; |
+ |
+ // We do not currently swap processes for navigations in webview tag guests. |
+ bool is_guest_scheme = current_instance->GetSiteURL().SchemeIs(kGuestScheme); |
+ |
+ // Determine if we need a new BrowsingInstance for this entry. If true, this |
+ // implies that it will get a new SiteInstance (and likely process), and that |
+ // other tabs in the current BrowsingInstance will be unable to script it. |
+ // This is used for cases that require a process swap even in the |
+ // process-per-tab model, such as WebUI pages. |
+ const NavigationEntry* current_entry = |
+ delegate_->GetLastCommittedNavigationEntryForRenderManager(); |
+ bool force_swap = !is_guest_scheme && |
+ ShouldSwapBrowsingInstancesForNavigation(current_entry, &entry); |
+ if (!is_guest_scheme && (ShouldTransitionCrossSite() || force_swap)) |
+ new_instance = GetSiteInstanceForEntry(entry, current_instance, force_swap); |
+ |
+ // If force_swap is true, we must use a different SiteInstance. If we didn't, |
+ // we would have two RenderFrameHosts in the same SiteInstance and the same |
+ // frame, resulting in page_id conflicts for their NavigationEntries. |
+ if (force_swap) |
+ CHECK_NE(new_instance, current_instance); |
+ |
+ return new_instance; |
+} |
+ |
void RenderFrameHostManager::CommitPending() { |
// 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 |
@@ -1266,28 +1447,9 @@ RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( |
// before the end of this method, so we don't have to worry about their ref |
// counts dropping to zero. |
SiteInstance* current_instance = render_frame_host_->GetSiteInstance(); |
- SiteInstance* new_instance = current_instance; |
- |
- // We do not currently swap processes for navigations in webview tag guests. |
- bool is_guest_scheme = current_instance->GetSiteURL().SchemeIs(kGuestScheme); |
- |
- // Determine if we need a new BrowsingInstance for this entry. If true, this |
- // implies that it will get a new SiteInstance (and likely process), and that |
- // other tabs in the current BrowsingInstance will be unable to script it. |
- // This is used for cases that require a process swap even in the |
- // process-per-tab model, such as WebUI pages. |
+ SiteInstance* new_instance = GetSiteInstanceForNavigation(entry); |
const NavigationEntry* current_entry = |
delegate_->GetLastCommittedNavigationEntryForRenderManager(); |
- bool force_swap = !is_guest_scheme && |
- ShouldSwapBrowsingInstancesForNavigation(current_entry, &entry); |
- if (!is_guest_scheme && (ShouldTransitionCrossSite() || force_swap)) |
- new_instance = GetSiteInstanceForEntry(entry, current_instance, force_swap); |
- |
- // If force_swap is true, we must use a different SiteInstance. If we didn't, |
- // we would have two RenderFrameHosts in the same SiteInstance and the same |
- // frame, resulting in page_id conflicts for their NavigationEntries. |
- if (force_swap) |
- CHECK_NE(new_instance, current_instance); |
if (new_instance != current_instance) { |
// New SiteInstance: create a pending RFH to navigate. |
@@ -1301,21 +1463,10 @@ RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( |
// not have its bindings set appropriately. |
SetPendingWebUI(entry); |
- // Ensure that we have created RFHs for the new RFH's opener chain if |
- // we are staying in the same BrowsingInstance. This allows the pending RFH |
- // to send cross-process script calls to its opener(s). |
- int opener_route_id = MSG_ROUTING_NONE; |
- if (new_instance->IsRelatedSiteInstance(current_instance)) { |
- opener_route_id = |
- delegate_->CreateOpenerRenderViewsForRenderManager(new_instance); |
- } |
- |
- // Create a non-swapped-out pending RFH with the given opener and navigate |
- // it. |
- int route_id = CreateRenderFrame(new_instance, opener_route_id, false, |
- delegate_->IsHidden()); |
- if (route_id == MSG_ROUTING_NONE) |
+ if (!CreateRenderFrameHostForCrossSite( |
+ current_instance, new_instance, false)) { |
return NULL; |
+ } |
// Check if our current RFH is live before we set up a transition. |
if (!render_frame_host_->render_view_host()->IsRenderViewLive()) { |