render_frame_host_manager.cc revision 010d83a9304c5a91596085d917d248abff47903a
15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// found in the LICENSE file. 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/render_frame_host_manager.h" 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include <utility> 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/command_line.h" 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/debug/trace_event.h" 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/logging.h" 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/stl_util.h" 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/child_process_security_policy_impl.h" 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/devtools/render_view_devtools_agent_host.h" 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/cross_process_frame_connector.h" 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/cross_site_transferring_request.h" 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/debug_urls.h" 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/interstitial_page_impl.h" 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/navigation_controller_impl.h" 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/navigation_entry_impl.h" 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/navigator.h" 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/render_frame_host_factory.h" 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/render_frame_host_impl.h" 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/frame_host/render_frame_proxy_host.h" 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/renderer_host/render_process_host_impl.h" 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/renderer_host/render_view_host_factory.h" 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/renderer_host/render_view_host_impl.h" 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/renderer_host/render_widget_host_view_base.h" 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/site_instance_impl.h" 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/browser/webui/web_ui_controller_factory_registry.h" 315267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "content/browser/webui/web_ui_impl.h" 325267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "content/common/view_messages.h" 335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/public/browser/content_browser_client.h" 345267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "content/public/browser/notification_service.h" 355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/public/browser/notification_types.h" 3606f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles)#include "content/public/browser/render_widget_host_iterator.h" 375267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "content/public/browser/user_metrics.h" 385267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "content/public/browser/web_ui_controller.h" 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/public/common/content_switches.h" 4051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)#include "content/public/common/url_constants.h" 41e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 42e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)namespace content { 435267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) 445267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams( 455267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) const GlobalRequestID& global_request_id, 465267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request, 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const std::vector<GURL>& transfer_url_chain, 485267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) Referrer referrer, 495267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) PageTransition page_transition, 505267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) int render_frame_id, 515267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) bool should_replace_current_entry) 525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) : global_request_id(global_request_id), 535267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) cross_site_transferring_request(cross_site_transferring_request.Pass()), 545267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) transfer_url_chain(transfer_url_chain), 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) referrer(referrer), 565267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) page_transition(page_transition), 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) render_frame_id(render_frame_id), 5806f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) should_replace_current_entry(should_replace_current_entry) { 595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 6006f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) 61e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)RenderFrameHostManager::PendingNavigationParams::~PendingNavigationParams() {} 62e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles) 63e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)bool RenderFrameHostManager::ClearRFHsPendingShutdown(FrameTreeNode* node) { 645267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) node->render_manager()->pending_delete_hosts_.clear(); 655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return true; 665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 6751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) 685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RenderFrameHostManager::RenderFrameHostManager( 695267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) FrameTreeNode* frame_tree_node, 70 RenderFrameHostDelegate* render_frame_delegate, 71 RenderViewHostDelegate* render_view_delegate, 72 RenderWidgetHostDelegate* render_widget_delegate, 73 Delegate* delegate) 74 : frame_tree_node_(frame_tree_node), 75 delegate_(delegate), 76 cross_navigation_pending_(false), 77 render_frame_delegate_(render_frame_delegate), 78 render_view_delegate_(render_view_delegate), 79 render_widget_delegate_(render_widget_delegate), 80 interstitial_page_(NULL), 81 cross_process_frame_connector_(NULL), 82 weak_factory_(this) { 83 DCHECK(frame_tree_node_); 84} 85 86RenderFrameHostManager::~RenderFrameHostManager() { 87 if (pending_render_frame_host_) 88 CancelPending(); 89 90 if (cross_process_frame_connector_) 91 delete cross_process_frame_connector_; 92 93 // We should always have a current RenderFrameHost except in some tests. 94 render_frame_host_.reset(); 95 96 // Delete any swapped out RenderFrameHosts. 97 STLDeleteValues(&proxy_hosts_); 98} 99 100void RenderFrameHostManager::Init(BrowserContext* browser_context, 101 SiteInstance* site_instance, 102 int view_routing_id, 103 int frame_routing_id) { 104 // Create a RenderViewHost and RenderFrameHost, once we have an instance. It 105 // is important to immediately give this SiteInstance to a RenderViewHost so 106 // that the SiteInstance is ref counted. 107 if (!site_instance) 108 site_instance = SiteInstance::Create(browser_context); 109 110 render_frame_host_ = CreateRenderFrameHost(site_instance, 111 view_routing_id, 112 frame_routing_id, 113 false, 114 delegate_->IsHidden()); 115 116 // Keep track of renderer processes as they start to shut down or are 117 // crashed/killed. 118 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED, 119 NotificationService::AllSources()); 120 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSING, 121 NotificationService::AllSources()); 122} 123 124RenderViewHostImpl* RenderFrameHostManager::current_host() const { 125 if (!render_frame_host_) 126 return NULL; 127 return render_frame_host_->render_view_host(); 128} 129 130RenderViewHostImpl* RenderFrameHostManager::pending_render_view_host() const { 131 if (!pending_render_frame_host_) 132 return NULL; 133 return pending_render_frame_host_->render_view_host(); 134} 135 136RenderWidgetHostView* RenderFrameHostManager::GetRenderWidgetHostView() const { 137 if (interstitial_page_) 138 return interstitial_page_->GetView(); 139 if (!render_frame_host_) 140 return NULL; 141 return render_frame_host_->render_view_host()->GetView(); 142} 143 144void RenderFrameHostManager::SetPendingWebUI(const NavigationEntryImpl& entry) { 145 pending_web_ui_.reset( 146 delegate_->CreateWebUIForRenderManager(entry.GetURL())); 147 pending_and_current_web_ui_.reset(); 148 149 // If we have assigned (zero or more) bindings to this NavigationEntry in the 150 // past, make sure we're not granting it different bindings than it had 151 // before. If so, note it and don't give it any bindings, to avoid a 152 // potential privilege escalation. 153 if (pending_web_ui_.get() && 154 entry.bindings() != NavigationEntryImpl::kInvalidBindings && 155 pending_web_ui_->GetBindings() != entry.bindings()) { 156 RecordAction( 157 base::UserMetricsAction("ProcessSwapBindingsMismatch_RVHM")); 158 pending_web_ui_.reset(); 159 } 160} 161 162RenderFrameHostImpl* RenderFrameHostManager::Navigate( 163 const NavigationEntryImpl& entry) { 164 TRACE_EVENT0("browser", "RenderFrameHostManager:Navigate"); 165 // Create a pending RenderFrameHost to use for the navigation. 166 RenderFrameHostImpl* dest_render_frame_host = UpdateStateForNavigate(entry); 167 if (!dest_render_frame_host) 168 return NULL; // We weren't able to create a pending render frame host. 169 170 // If the current render_frame_host_ isn't live, we should create it so 171 // that we don't show a sad tab while the dest_render_frame_host fetches 172 // its first page. (Bug 1145340) 173 if (dest_render_frame_host != render_frame_host_ && 174 !render_frame_host_->render_view_host()->IsRenderViewLive()) { 175 // Note: we don't call InitRenderView here because we are navigating away 176 // soon anyway, and we don't have the NavigationEntry for this host. 177 delegate_->CreateRenderViewForRenderManager( 178 render_frame_host_->render_view_host(), MSG_ROUTING_NONE, NULL); 179 } 180 181 // If the renderer crashed, then try to create a new one to satisfy this 182 // navigation request. 183 if (!dest_render_frame_host->render_view_host()->IsRenderViewLive()) { 184 // Recreate the opener chain. 185 int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager( 186 dest_render_frame_host->GetSiteInstance()); 187 if (!InitRenderView(dest_render_frame_host->render_view_host(), 188 opener_route_id)) 189 return NULL; 190 191 // Now that we've created a new renderer, be sure to hide it if it isn't 192 // our primary one. Otherwise, we might crash if we try to call Show() 193 // on it later. 194 if (dest_render_frame_host != render_frame_host_ && 195 dest_render_frame_host->render_view_host()->GetView()) { 196 dest_render_frame_host->render_view_host()->GetView()->Hide(); 197 } else if (frame_tree_node_->IsMainFrame()) { 198 // This is our primary renderer, notify here as we won't be calling 199 // CommitPending (which does the notify). We only do this for top-level 200 // frames. 201 delegate_->NotifySwappedFromRenderManager( 202 NULL, render_frame_host_->render_view_host()); 203 } 204 } 205 206 // If entry includes the request ID of a request that is being transferred, 207 // the destination render frame will take ownership, so release ownership of 208 // the request. 209 if (pending_nav_params_ && 210 pending_nav_params_->global_request_id == 211 entry.transferred_global_request_id()) { 212 pending_nav_params_->cross_site_transferring_request->ReleaseRequest(); 213 } 214 215 return dest_render_frame_host; 216} 217 218void RenderFrameHostManager::Stop() { 219 render_frame_host_->render_view_host()->Stop(); 220 221 // If we are cross-navigating, we should stop the pending renderers. This 222 // will lead to a DidFailProvisionalLoad, which will properly destroy them. 223 if (cross_navigation_pending_) { 224 pending_render_frame_host_->render_view_host()->Send(new ViewMsg_Stop( 225 pending_render_frame_host_->render_view_host()->GetRoutingID())); 226 } 227} 228 229void RenderFrameHostManager::SetIsLoading(bool is_loading) { 230 render_frame_host_->render_view_host()->SetIsLoading(is_loading); 231 if (pending_render_frame_host_) 232 pending_render_frame_host_->render_view_host()->SetIsLoading(is_loading); 233} 234 235bool RenderFrameHostManager::ShouldCloseTabOnUnresponsiveRenderer() { 236 if (!cross_navigation_pending_) 237 return true; 238 239 // We should always have a pending RFH when there's a cross-process navigation 240 // in progress. Sanity check this for http://crbug.com/276333. 241 CHECK(pending_render_frame_host_); 242 243 // If the tab becomes unresponsive during {before}unload while doing a 244 // cross-site navigation, proceed with the navigation. (This assumes that 245 // the pending RenderFrameHost is still responsive.) 246 if (render_frame_host_->render_view_host()->IsWaitingForUnloadACK()) { 247 // The request has been started and paused while we're waiting for the 248 // unload handler to finish. We'll pretend that it did. The pending 249 // renderer will then be swapped in as part of the usual DidNavigate logic. 250 // (If the unload handler later finishes, this call will be ignored because 251 // the pending_nav_params_ state will already be cleaned up.) 252 current_host()->OnSwappedOut(true); 253 } else if (render_frame_host_->render_view_host()-> 254 is_waiting_for_beforeunload_ack()) { 255 // Haven't gotten around to starting the request, because we're still 256 // waiting for the beforeunload handler to finish. We'll pretend that it 257 // did finish, to let the navigation proceed. Note that there's a danger 258 // that the beforeunload handler will later finish and possibly return 259 // false (meaning the navigation should not proceed), but we'll ignore it 260 // in this case because it took too long. 261 if (pending_render_frame_host_->render_view_host()-> 262 are_navigations_suspended()) { 263 pending_render_frame_host_->render_view_host()->SetNavigationsSuspended( 264 false, base::TimeTicks::Now()); 265 } 266 } 267 return false; 268} 269 270void RenderFrameHostManager::OnBeforeUnloadACK( 271 bool for_cross_site_transition, 272 bool proceed, 273 const base::TimeTicks& proceed_time) { 274 if (for_cross_site_transition) { 275 // Ignore if we're not in a cross-site navigation. 276 if (!cross_navigation_pending_) 277 return; 278 279 if (proceed) { 280 // Ok to unload the current page, so proceed with the cross-site 281 // navigation. Note that if navigations are not currently suspended, it 282 // might be because the renderer was deemed unresponsive and this call was 283 // already made by ShouldCloseTabOnUnresponsiveRenderer. In that case, it 284 // is ok to do nothing here. 285 if (pending_render_frame_host_ && 286 pending_render_frame_host_->render_view_host()-> 287 are_navigations_suspended()) { 288 pending_render_frame_host_->render_view_host()-> 289 SetNavigationsSuspended(false, proceed_time); 290 } 291 } else { 292 // Current page says to cancel. 293 CancelPending(); 294 cross_navigation_pending_ = false; 295 } 296 } else { 297 // Non-cross site transition means closing the entire tab. 298 bool proceed_to_fire_unload; 299 delegate_->BeforeUnloadFiredFromRenderManager(proceed, proceed_time, 300 &proceed_to_fire_unload); 301 302 if (proceed_to_fire_unload) { 303 // If we're about to close the tab and there's a pending RFH, cancel it. 304 // Otherwise, if the navigation in the pending RFH completes before the 305 // close in the current RFH, we'll lose the tab close. 306 if (pending_render_frame_host_) { 307 CancelPending(); 308 cross_navigation_pending_ = false; 309 } 310 311 // This is not a cross-site navigation, the tab is being closed. 312 render_frame_host_->render_view_host()->ClosePage(); 313 } 314 } 315} 316 317void RenderFrameHostManager::OnCrossSiteResponse( 318 RenderFrameHostImpl* pending_render_frame_host, 319 const GlobalRequestID& global_request_id, 320 scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request, 321 const std::vector<GURL>& transfer_url_chain, 322 const Referrer& referrer, 323 PageTransition page_transition, 324 bool should_replace_current_entry) { 325 // This should be called either when the pending RFH is ready to commit or 326 // when we realize that the current RFH's request requires a transfer. 327 DCHECK(pending_render_frame_host == pending_render_frame_host_ || 328 pending_render_frame_host == render_frame_host_); 329 330 // TODO(creis): Eventually we will want to check all navigation responses 331 // here, but currently we pass information for a transfer if 332 // ShouldSwapProcessesForRedirect returned true in the network stack. 333 // In that case, we should set up a transfer after the unload handler runs. 334 // If |cross_site_transferring_request| is NULL, we will just run the unload 335 // handler and resume. 336 pending_nav_params_.reset(new PendingNavigationParams( 337 global_request_id, cross_site_transferring_request.Pass(), 338 transfer_url_chain, referrer, page_transition, 339 pending_render_frame_host->GetRoutingID(), 340 should_replace_current_entry)); 341 342 // Run the unload handler of the current page. 343 SwapOutOldPage(); 344} 345 346void RenderFrameHostManager::SwappedOut( 347 RenderFrameHostImpl* render_frame_host) { 348 // Make sure this is from our current RFH, and that we have a pending 349 // navigation from OnCrossSiteResponse. (There may be no pending navigation 350 // for data URLs that don't make network requests, for example.) If not, 351 // just return early and ignore. 352 if (render_frame_host != render_frame_host_ || !pending_nav_params_.get()) { 353 pending_nav_params_.reset(); 354 return; 355 } 356 357 // Now that the unload handler has run, we need to either initiate the 358 // pending transfer (if there is one) or resume the paused response (if not). 359 // TODO(creis): The blank swapped out page is visible during this time, but 360 // we can shorten this by delivering the response directly, rather than 361 // forcing an identical request to be made. 362 if (pending_nav_params_->cross_site_transferring_request) { 363 // Sanity check that the params are for the correct frame and process. 364 // These should match the RenderFrameHost that made the request. 365 // If it started as a cross-process navigation via OpenURL, this is the 366 // pending one. If it wasn't cross-process until the transfer, this is the 367 // current one. 368 int render_frame_id = pending_render_frame_host_ ? 369 pending_render_frame_host_->GetRoutingID() : 370 render_frame_host_->GetRoutingID(); 371 DCHECK_EQ(render_frame_id, pending_nav_params_->render_frame_id); 372 int process_id = pending_render_frame_host_ ? 373 pending_render_frame_host_->GetProcess()->GetID() : 374 render_frame_host_->GetProcess()->GetID(); 375 DCHECK_EQ(process_id, pending_nav_params_->global_request_id.child_id); 376 377 // Treat the last URL in the chain as the destination and the remainder as 378 // the redirect chain. 379 CHECK(pending_nav_params_->transfer_url_chain.size()); 380 GURL transfer_url = pending_nav_params_->transfer_url_chain.back(); 381 pending_nav_params_->transfer_url_chain.pop_back(); 382 383 // We don't know whether the original request had |user_action| set to true. 384 // However, since we force the navigation to be in the current tab, it 385 // doesn't matter. 386 render_frame_host->frame_tree_node()->navigator()->RequestTransferURL( 387 render_frame_host, 388 transfer_url, 389 pending_nav_params_->transfer_url_chain, 390 pending_nav_params_->referrer, 391 pending_nav_params_->page_transition, 392 CURRENT_TAB, 393 pending_nav_params_->global_request_id, 394 pending_nav_params_->should_replace_current_entry, 395 true); 396 } else if (pending_render_frame_host_) { 397 RenderProcessHostImpl* pending_process = 398 static_cast<RenderProcessHostImpl*>( 399 pending_render_frame_host_->GetProcess()); 400 pending_process->ResumeDeferredNavigation( 401 pending_nav_params_->global_request_id); 402 } 403 pending_nav_params_.reset(); 404} 405 406void RenderFrameHostManager::DidNavigateFrame( 407 RenderFrameHostImpl* render_frame_host) { 408 if (!cross_navigation_pending_) { 409 DCHECK(!pending_render_frame_host_); 410 411 // We should only hear this from our current renderer. 412 DCHECK_EQ(render_frame_host_, render_frame_host); 413 414 // Even when there is no pending RVH, there may be a pending Web UI. 415 if (pending_web_ui()) 416 CommitPending(); 417 return; 418 } 419 420 if (render_frame_host == pending_render_frame_host_) { 421 // The pending cross-site navigation completed, so show the renderer. 422 // If it committed without sending network requests (e.g., data URLs), 423 // then we still need to swap out the old RFH first and run its unload 424 // handler, only if it hasn't happened yet. OK for that to happen in the 425 // background. 426 if (pending_render_frame_host_->render_view_host()-> 427 HasPendingCrossSiteRequest() && 428 pending_render_frame_host_->render_view_host()->rvh_state() == 429 RenderViewHostImpl::STATE_DEFAULT) { 430 SwapOutOldPage(); 431 } 432 433 CommitPending(); 434 cross_navigation_pending_ = false; 435 } else if (render_frame_host == render_frame_host_) { 436 // A navigation in the original page has taken place. Cancel the pending 437 // one. 438 CancelPending(); 439 cross_navigation_pending_ = false; 440 } else { 441 // No one else should be sending us DidNavigate in this state. 442 DCHECK(false); 443 } 444} 445 446// TODO(creis): Take in RenderFrameHost instead, since frames can have openers. 447void RenderFrameHostManager::DidDisownOpener(RenderViewHost* render_view_host) { 448 // Notify all swapped out hosts, including the pending RVH. 449 for (RenderFrameProxyHostMap::iterator iter = proxy_hosts_.begin(); 450 iter != proxy_hosts_.end(); 451 ++iter) { 452 DCHECK_NE(iter->second->GetSiteInstance(), 453 current_frame_host()->GetSiteInstance()); 454 iter->second->render_view_host()->DisownOpener(); 455 } 456} 457 458void RenderFrameHostManager::RendererProcessClosing( 459 RenderProcessHost* render_process_host) { 460 // Remove any swapped out RVHs from this process, so that we don't try to 461 // swap them back in while the process is exiting. Start by finding them, 462 // since there could be more than one. 463 std::list<int> ids_to_remove; 464 for (RenderFrameProxyHostMap::iterator iter = proxy_hosts_.begin(); 465 iter != proxy_hosts_.end(); 466 ++iter) { 467 if (iter->second->GetProcess() == render_process_host) 468 ids_to_remove.push_back(iter->first); 469 } 470 471 // Now delete them. 472 while (!ids_to_remove.empty()) { 473 delete proxy_hosts_[ids_to_remove.back()]; 474 proxy_hosts_.erase(ids_to_remove.back()); 475 ids_to_remove.pop_back(); 476 } 477} 478 479void RenderFrameHostManager::SwapOutOldPage() { 480 // Should only see this while we have a pending renderer or transfer. 481 CHECK(cross_navigation_pending_ || pending_nav_params_.get()); 482 483 // Tell the renderer to suppress any further modal dialogs so that we can swap 484 // it out. This must be done before canceling any current dialog, in case 485 // there is a loop creating additional dialogs. 486 // TODO(creis): Handle modal dialogs in subframe processes. 487 render_frame_host_->render_view_host()->SuppressDialogsUntilSwapOut(); 488 489 // Now close any modal dialogs that would prevent us from swapping out. This 490 // must be done separately from SwapOut, so that the PageGroupLoadDeferrer is 491 // no longer on the stack when we send the SwapOut message. 492 delegate_->CancelModalDialogsForRenderManager(); 493 494 if (!frame_tree_node_->IsMainFrame()) { 495 // The RenderFrameHost being swapped out becomes the proxy for this 496 // frame in its parent's process. CrossProcessFrameConnector 497 // initialization only needs to happen on an initial cross-process 498 // navigation, when the RenderFrame leaves the same process as its parent. 499 // The same CrossProcessFrameConnector is used for subsequent cross- 500 // process navigations, but it will be destroyed if the Frame is 501 // navigated back to the same site instance as its parent. 502 // TODO(kenrb): This will change when RenderFrameProxyHost is created. 503 if (!cross_process_frame_connector_) { 504 cross_process_frame_connector_ = 505 new CrossProcessFrameConnector(render_frame_host_.get()); 506 } 507 } 508 509 // Tell the old frame it is being swapped out. This will fire the unload 510 // handler in the background (without firing the beforeunload handler a second 511 // time). When the navigation completes, we will send a message to the 512 // ResourceDispatcherHost, allowing the pending RVH's response to resume. 513 render_frame_host_->SwapOut(); 514 515 // ResourceDispatcherHost has told us to run the onunload handler, which 516 // means it is not a download or unsafe page, and we are going to perform the 517 // navigation. Thus, we no longer need to remember that the RenderFrameHost 518 // is part of a pending cross-site request. 519 if (pending_render_frame_host_) { 520 pending_render_frame_host_->render_view_host()-> 521 SetHasPendingCrossSiteRequest(false); 522 } 523} 524 525void RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance( 526 int32 site_instance_id, 527 RenderFrameHostImpl* rfh) { 528 RFHPendingDeleteMap::iterator iter = 529 pending_delete_hosts_.find(site_instance_id); 530 if (iter != pending_delete_hosts_.end() && iter->second.get() == rfh) 531 pending_delete_hosts_.erase(site_instance_id); 532} 533 534void RenderFrameHostManager::Observe( 535 int type, 536 const NotificationSource& source, 537 const NotificationDetails& details) { 538 switch (type) { 539 case NOTIFICATION_RENDERER_PROCESS_CLOSED: 540 case NOTIFICATION_RENDERER_PROCESS_CLOSING: 541 RendererProcessClosing( 542 Source<RenderProcessHost>(source).ptr()); 543 break; 544 545 default: 546 NOTREACHED(); 547 } 548} 549 550bool RenderFrameHostManager::ClearProxiesInSiteInstance( 551 int32 site_instance_id, 552 FrameTreeNode* node) { 553 RenderFrameProxyHostMap::iterator iter = 554 node->render_manager()->proxy_hosts_.find(site_instance_id); 555 if (iter != node->render_manager()->proxy_hosts_.end()) { 556 RenderFrameProxyHost* proxy = iter->second; 557 // If the RVH is pending swap out, it needs to switch state to 558 // pending shutdown. Otherwise it is deleted. 559 if (proxy->render_view_host()->rvh_state() == 560 RenderViewHostImpl::STATE_PENDING_SWAP_OUT) { 561 scoped_ptr<RenderFrameHostImpl> swapped_out_rfh = proxy->PassFrameHost(); 562 563 swapped_out_rfh->SetPendingShutdown(base::Bind( 564 &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance, 565 node->render_manager()->weak_factory_.GetWeakPtr(), 566 site_instance_id, 567 swapped_out_rfh.get())); 568 RFHPendingDeleteMap::iterator pending_delete_iter = 569 node->render_manager()->pending_delete_hosts_.find(site_instance_id); 570 if (pending_delete_iter == 571 node->render_manager()->pending_delete_hosts_.end() || 572 pending_delete_iter->second.get() != swapped_out_rfh) { 573 node->render_manager()->pending_delete_hosts_[site_instance_id] = 574 linked_ptr<RenderFrameHostImpl>(swapped_out_rfh.release()); 575 } 576 } else { 577 delete proxy; 578 } 579 node->render_manager()->proxy_hosts_.erase(site_instance_id); 580 } 581 582 return true; 583} 584 585bool RenderFrameHostManager::ShouldTransitionCrossSite() { 586 // False in the single-process mode, as it makes RVHs to accumulate 587 // in swapped_out_hosts_. 588 // True if we are using process-per-site-instance (default) or 589 // process-per-site (kProcessPerSite). 590 return 591 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) && 592 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab); 593} 594 595bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation( 596 const NavigationEntry* current_entry, 597 const NavigationEntryImpl* new_entry) const { 598 DCHECK(new_entry); 599 600 // If new_entry already has a SiteInstance, assume it is correct. We only 601 // need to force a swap if it is in a different BrowsingInstance. 602 if (new_entry->site_instance()) { 603 return !new_entry->site_instance()->IsRelatedSiteInstance( 604 render_frame_host_->GetSiteInstance()); 605 } 606 607 // Check for reasons to swap processes even if we are in a process model that 608 // doesn't usually swap (e.g., process-per-tab). Any time we return true, 609 // the new_entry will be rendered in a new SiteInstance AND BrowsingInstance. 610 611 // We use the effective URL here, since that's what is used in the 612 // SiteInstance's site and when we later call IsSameWebSite. If there is no 613 // current_entry, check the current SiteInstance's site, which might already 614 // be committed to a Web UI URL (such as the NTP). 615 BrowserContext* browser_context = 616 delegate_->GetControllerForRenderManager().GetBrowserContext(); 617 const GURL& current_url = (current_entry) ? 618 SiteInstanceImpl::GetEffectiveURL(browser_context, 619 current_entry->GetURL()) : 620 render_frame_host_->GetSiteInstance()->GetSiteURL(); 621 const GURL& new_url = SiteInstanceImpl::GetEffectiveURL(browser_context, 622 new_entry->GetURL()); 623 624 // Don't force a new BrowsingInstance for debug URLs that are handled in the 625 // renderer process, like javascript: or chrome://crash. 626 if (IsRendererDebugURL(new_url)) 627 return false; 628 629 // For security, we should transition between processes when one is a Web UI 630 // page and one isn't. 631 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL( 632 browser_context, current_url)) { 633 // If so, force a swap if destination is not an acceptable URL for Web UI. 634 // Here, data URLs are never allowed. 635 if (!WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI( 636 browser_context, new_url)) { 637 return true; 638 } 639 } else { 640 // Force a swap if it's a Web UI URL. 641 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL( 642 browser_context, new_url)) { 643 return true; 644 } 645 } 646 647 // Check with the content client as well. Important to pass current_url here, 648 // which uses the SiteInstance's site if there is no current_entry. 649 if (GetContentClient()->browser()->ShouldSwapBrowsingInstancesForNavigation( 650 render_frame_host_->GetSiteInstance(), 651 current_url, new_url)) { 652 return true; 653 } 654 655 // We can't switch a RenderView between view source and non-view source mode 656 // without screwing up the session history sometimes (when navigating between 657 // "view-source:http://foo.com/" and "http://foo.com/", Blink doesn't treat 658 // it as a new navigation). So require a BrowsingInstance switch. 659 if (current_entry && 660 current_entry->IsViewSourceMode() != new_entry->IsViewSourceMode()) 661 return true; 662 663 return false; 664} 665 666bool RenderFrameHostManager::ShouldReuseWebUI( 667 const NavigationEntry* current_entry, 668 const NavigationEntryImpl* new_entry) const { 669 NavigationControllerImpl& controller = 670 delegate_->GetControllerForRenderManager(); 671 return current_entry && web_ui_.get() && 672 (WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType( 673 controller.GetBrowserContext(), current_entry->GetURL()) == 674 WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType( 675 controller.GetBrowserContext(), new_entry->GetURL())); 676} 677 678SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry( 679 const NavigationEntryImpl& entry, 680 SiteInstance* current_instance, 681 bool force_browsing_instance_swap) { 682 // Determine which SiteInstance to use for navigating to |entry|. 683 const GURL& dest_url = entry.GetURL(); 684 NavigationControllerImpl& controller = 685 delegate_->GetControllerForRenderManager(); 686 BrowserContext* browser_context = controller.GetBrowserContext(); 687 688 // If the entry has an instance already we should use it. 689 if (entry.site_instance()) { 690 // If we are forcing a swap, this should be in a different BrowsingInstance. 691 if (force_browsing_instance_swap) { 692 CHECK(!entry.site_instance()->IsRelatedSiteInstance( 693 render_frame_host_->GetSiteInstance())); 694 } 695 return entry.site_instance(); 696 } 697 698 // If a swap is required, we need to force the SiteInstance AND 699 // BrowsingInstance to be different ones, using CreateForURL. 700 if (force_browsing_instance_swap) 701 return SiteInstance::CreateForURL(browser_context, dest_url); 702 703 // (UGLY) HEURISTIC, process-per-site only: 704 // 705 // If this navigation is generated, then it probably corresponds to a search 706 // query. Given that search results typically lead to users navigating to 707 // other sites, we don't really want to use the search engine hostname to 708 // determine the site instance for this navigation. 709 // 710 // NOTE: This can be removed once we have a way to transition between 711 // RenderViews in response to a link click. 712 // 713 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerSite) && 714 PageTransitionCoreTypeIs(entry.GetTransitionType(), 715 PAGE_TRANSITION_GENERATED)) { 716 return current_instance; 717 } 718 719 SiteInstanceImpl* current_site_instance = 720 static_cast<SiteInstanceImpl*>(current_instance); 721 722 // If we haven't used our SiteInstance (and thus RVH) yet, then we can use it 723 // for this entry. We won't commit the SiteInstance to this site until the 724 // navigation commits (in DidNavigate), unless the navigation entry was 725 // restored or it's a Web UI as described below. 726 if (!current_site_instance->HasSite()) { 727 // If we've already created a SiteInstance for our destination, we don't 728 // want to use this unused SiteInstance; use the existing one. (We don't 729 // do this check if the current_instance has a site, because for now, we 730 // want to compare against the current URL and not the SiteInstance's site. 731 // In this case, there is no current URL, so comparing against the site is 732 // ok. See additional comments below.) 733 // 734 // Also, if the URL should use process-per-site mode and there is an 735 // existing process for the site, we should use it. We can call 736 // GetRelatedSiteInstance() for this, which will eagerly set the site and 737 // thus use the correct process. 738 bool use_process_per_site = 739 RenderProcessHost::ShouldUseProcessPerSite(browser_context, dest_url) && 740 RenderProcessHostImpl::GetProcessHostForSite(browser_context, dest_url); 741 if (current_site_instance->HasRelatedSiteInstance(dest_url) || 742 use_process_per_site) { 743 return current_site_instance->GetRelatedSiteInstance(dest_url); 744 } 745 746 // For extensions, Web UI URLs (such as the new tab page), and apps we do 747 // not want to use the current_instance if it has no site, since it will 748 // have a RenderProcessHost of PRIV_NORMAL. Create a new SiteInstance for 749 // this URL instead (with the correct process type). 750 if (current_site_instance->HasWrongProcessForURL(dest_url)) 751 return current_site_instance->GetRelatedSiteInstance(dest_url); 752 753 // View-source URLs must use a new SiteInstance and BrowsingInstance. 754 // TODO(nasko): This is the same condition as later in the function. This 755 // should be taken into account when refactoring this method as part of 756 // http://crbug.com/123007. 757 if (entry.IsViewSourceMode()) 758 return SiteInstance::CreateForURL(browser_context, dest_url); 759 760 // If we are navigating from a blank SiteInstance to a WebUI, make sure we 761 // create a new SiteInstance. 762 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL( 763 browser_context, dest_url)) { 764 return SiteInstance::CreateForURL(browser_context, dest_url); 765 } 766 767 // Normally the "site" on the SiteInstance is set lazily when the load 768 // actually commits. This is to support better process sharing in case 769 // the site redirects to some other site: we want to use the destination 770 // site in the site instance. 771 // 772 // In the case of session restore, as it loads all the pages immediately 773 // we need to set the site first, otherwise after a restore none of the 774 // pages would share renderers in process-per-site. 775 if (entry.restore_type() != NavigationEntryImpl::RESTORE_NONE) 776 current_site_instance->SetSite(dest_url); 777 778 return current_site_instance; 779 } 780 781 // Otherwise, only create a new SiteInstance for a cross-site navigation. 782 783 // TODO(creis): Once we intercept links and script-based navigations, we 784 // will be able to enforce that all entries in a SiteInstance actually have 785 // the same site, and it will be safe to compare the URL against the 786 // SiteInstance's site, as follows: 787 // const GURL& current_url = current_instance->site(); 788 // For now, though, we're in a hybrid model where you only switch 789 // SiteInstances if you type in a cross-site URL. This means we have to 790 // compare the entry's URL to the last committed entry's URL. 791 NavigationEntry* current_entry = controller.GetLastCommittedEntry(); 792 if (interstitial_page_) { 793 // The interstitial is currently the last committed entry, but we want to 794 // compare against the last non-interstitial entry. 795 current_entry = controller.GetEntryAtOffset(-1); 796 } 797 // If there is no last non-interstitial entry (and current_instance already 798 // has a site), then we must have been opened from another tab. We want 799 // to compare against the URL of the page that opened us, but we can't 800 // get to it directly. The best we can do is check against the site of 801 // the SiteInstance. This will be correct when we intercept links and 802 // script-based navigations, but for now, it could place some pages in a 803 // new process unnecessarily. We should only hit this case if a page tries 804 // to open a new tab to an interstitial-inducing URL, and then navigates 805 // the page to a different same-site URL. (This seems very unlikely in 806 // practice.) 807 const GURL& current_url = (current_entry) ? current_entry->GetURL() : 808 current_instance->GetSiteURL(); 809 810 // View-source URLs must use a new SiteInstance and BrowsingInstance. 811 // We don't need a swap when going from view-source to a debug URL like 812 // chrome://crash, however. 813 // TODO(creis): Refactor this method so this duplicated code isn't needed. 814 // See http://crbug.com/123007. 815 if (current_entry && 816 current_entry->IsViewSourceMode() != entry.IsViewSourceMode() && 817 !IsRendererDebugURL(dest_url)) { 818 return SiteInstance::CreateForURL(browser_context, dest_url); 819 } 820 821 // Use the current SiteInstance for same site navigations, as long as the 822 // process type is correct. (The URL may have been installed as an app since 823 // the last time we visited it.) 824 if (SiteInstance::IsSameWebSite(browser_context, current_url, dest_url) && 825 !current_site_instance->HasWrongProcessForURL(dest_url)) { 826 return current_instance; 827 } 828 829 // Start the new renderer in a new SiteInstance, but in the current 830 // BrowsingInstance. It is important to immediately give this new 831 // SiteInstance to a RenderViewHost (if it is different than our current 832 // SiteInstance), so that it is ref counted. This will happen in 833 // CreateRenderView. 834 return current_instance->GetRelatedSiteInstance(dest_url); 835} 836 837scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::CreateRenderFrameHost( 838 SiteInstance* site_instance, 839 int view_routing_id, 840 int frame_routing_id, 841 bool swapped_out, 842 bool hidden) { 843 if (frame_routing_id == MSG_ROUTING_NONE) 844 frame_routing_id = site_instance->GetProcess()->GetNextRoutingID(); 845 846 // Create a RVH for main frames, or find the existing one for subframes. 847 FrameTree* frame_tree = frame_tree_node_->frame_tree(); 848 RenderViewHostImpl* render_view_host = NULL; 849 if (frame_tree_node_->IsMainFrame()) { 850 render_view_host = frame_tree->CreateRenderViewHostForMainFrame( 851 site_instance, view_routing_id, frame_routing_id, swapped_out, hidden); 852 } else { 853 render_view_host = frame_tree->GetRenderViewHostForSubFrame(site_instance); 854 855 // If we haven't found a RVH for a subframe RFH, it's because we currently 856 // do not create top-level RFHs for pending subframe navigations. Create 857 // the RVH here for now. 858 // TODO(creis): Mirror the frame tree so this check isn't necessary. 859 if (!render_view_host) { 860 render_view_host = frame_tree->CreateRenderViewHostForMainFrame( 861 site_instance, view_routing_id, frame_routing_id, swapped_out, 862 hidden); 863 } 864 } 865 866 // TODO(creis): Pass hidden to RFH. 867 scoped_ptr<RenderFrameHostImpl> render_frame_host = 868 make_scoped_ptr(RenderFrameHostFactory::Create(render_view_host, 869 render_frame_delegate_, 870 frame_tree, 871 frame_tree_node_, 872 frame_routing_id, 873 swapped_out).release()); 874 return render_frame_host.Pass(); 875} 876 877int RenderFrameHostManager::CreateRenderFrame( 878 SiteInstance* instance, 879 int opener_route_id, 880 bool swapped_out, 881 bool hidden) { 882 CHECK(instance); 883 DCHECK(!swapped_out || hidden); // Swapped out views should always be hidden. 884 885 scoped_ptr<RenderFrameHostImpl> new_render_frame_host; 886 int routing_id = MSG_ROUTING_NONE; 887 888 // We are creating a pending or swapped out RFH here. We should never create 889 // it in the same SiteInstance as our current RFH. 890 CHECK_NE(render_frame_host_->GetSiteInstance(), instance); 891 892 // Check if we've already created an RFH for this SiteInstance. If so, try 893 // to re-use the existing one, which has already been initialized. We'll 894 // remove it from the list of swapped out hosts if it commits. 895 RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(instance); 896 897 FrameTreeNode* parent_node = frame_tree_node_->parent(); 898 899 if (proxy) { 900 routing_id = proxy->render_view_host()->GetRoutingID(); 901 // Delete the existing RenderFrameProxyHost, but reuse the RenderFrameHost. 902 // Prevent the process from exiting while we're trying to use it. 903 if (!swapped_out) { 904 new_render_frame_host = proxy->PassFrameHost(); 905 new_render_frame_host->GetProcess()->AddPendingView(); 906 907 proxy_hosts_.erase(instance->GetId()); 908 delete proxy; 909 } else { 910 // Detect if this is a cross-process child frame that is navigating 911 // back to the same SiteInstance as its parent. 912 if (parent_node && cross_process_frame_connector_ && 913 render_frame_host_->GetSiteInstance() == parent_node-> 914 render_manager()->current_frame_host()->GetSiteInstance()) { 915 delete cross_process_frame_connector_; 916 cross_process_frame_connector_ = NULL; 917 } 918 } 919 } else { 920 // Create a new RenderFrameHost if we don't find an existing one. 921 new_render_frame_host = CreateRenderFrameHost( 922 instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE, swapped_out, hidden); 923 RenderViewHostImpl* render_view_host = 924 new_render_frame_host->render_view_host(); 925 926 // Prevent the process from exiting while we're trying to navigate in it. 927 // Otherwise, if the new RFH is swapped out already, store it. 928 if (!swapped_out) { 929 new_render_frame_host->GetProcess()->AddPendingView(); 930 } else { 931 proxy_hosts_[instance->GetId()] = new RenderFrameProxyHost( 932 new_render_frame_host.Pass()); 933 } 934 935 bool success = InitRenderView(render_view_host, opener_route_id); 936 if (success && frame_tree_node_->IsMainFrame()) { 937 // Don't show the main frame's view until we get a DidNavigate from it. 938 render_view_host->GetView()->Hide(); 939 } else if (!swapped_out && pending_render_frame_host_) { 940 CancelPending(); 941 } 942 routing_id = render_view_host->GetRoutingID(); 943 } 944 945 // Use this as our new pending RFH if it isn't swapped out. 946 if (!swapped_out) 947 pending_render_frame_host_ = new_render_frame_host.Pass(); 948 949 return routing_id; 950} 951 952bool RenderFrameHostManager::InitRenderView(RenderViewHost* render_view_host, 953 int opener_route_id) { 954 // We may have initialized this RenderViewHost for another RenderFrameHost. 955 if (render_view_host->IsRenderViewLive()) 956 return true; 957 958 // If the pending navigation is to a WebUI and the RenderView is not in a 959 // guest process, tell the RenderViewHost about any bindings it will need 960 // enabled. 961 if (pending_web_ui() && !render_view_host->GetProcess()->IsGuest()) { 962 render_view_host->AllowBindings(pending_web_ui()->GetBindings()); 963 } else { 964 // Ensure that we don't create an unprivileged RenderView in a WebUI-enabled 965 // process unless it's swapped out. 966 RenderViewHostImpl* rvh_impl = 967 static_cast<RenderViewHostImpl*>(render_view_host); 968 if (!rvh_impl->IsSwappedOut()) { 969 CHECK(!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( 970 render_view_host->GetProcess()->GetID())); 971 } 972 } 973 974 return delegate_->CreateRenderViewForRenderManager( 975 render_view_host, opener_route_id, cross_process_frame_connector_); 976} 977 978void RenderFrameHostManager::CommitPending() { 979 // First check whether we're going to want to focus the location bar after 980 // this commit. We do this now because the navigation hasn't formally 981 // committed yet, so if we've already cleared |pending_web_ui_| the call chain 982 // this triggers won't be able to figure out what's going on. 983 bool will_focus_location_bar = delegate_->FocusLocationBarByDefault(); 984 985 // We expect SwapOutOldPage to have canceled any modal dialogs and told the 986 // renderer to suppress any further dialogs until it is swapped out. However, 987 // crash reports indicate that it's still possible for modal dialogs to exist 988 // at this point, which poses a risk if we delete their RenderViewHost below. 989 // Cancel them again to be safe. http://crbug.com/324320. 990 delegate_->CancelModalDialogsForRenderManager(); 991 992 // Next commit the Web UI, if any. Either replace |web_ui_| with 993 // |pending_web_ui_|, or clear |web_ui_| if there is no pending WebUI, or 994 // leave |web_ui_| as is if reusing it. 995 DCHECK(!(pending_web_ui_.get() && pending_and_current_web_ui_.get())); 996 if (pending_web_ui_) { 997 web_ui_.reset(pending_web_ui_.release()); 998 } else if (!pending_and_current_web_ui_.get()) { 999 web_ui_.reset(); 1000 } else { 1001 DCHECK_EQ(pending_and_current_web_ui_.get(), web_ui_.get()); 1002 pending_and_current_web_ui_.reset(); 1003 } 1004 1005 // It's possible for the pending_render_frame_host_ to be NULL when we aren't 1006 // crossing process boundaries. If so, we just needed to handle the Web UI 1007 // committing above and we're done. 1008 if (!pending_render_frame_host_) { 1009 if (will_focus_location_bar) 1010 delegate_->SetFocusToLocationBar(false); 1011 return; 1012 } 1013 1014 // Remember if the page was focused so we can focus the new renderer in 1015 // that case. 1016 bool focus_render_view = !will_focus_location_bar && 1017 render_frame_host_->render_view_host()->GetView() && 1018 render_frame_host_->render_view_host()->GetView()->HasFocus(); 1019 1020 // TODO(creis): As long as show/hide are on RVH, we don't want to do them for 1021 // subframe navigations or they'll interfere with the top-level page. 1022 bool is_main_frame = frame_tree_node_->IsMainFrame(); 1023 1024 // Swap in the pending frame and make it active. Also ensure the FrameTree 1025 // stays in sync. 1026 scoped_ptr<RenderFrameHostImpl> old_render_frame_host = 1027 render_frame_host_.Pass(); 1028 render_frame_host_ = pending_render_frame_host_.Pass(); 1029 if (is_main_frame) 1030 render_frame_host_->render_view_host()->AttachToFrameTree(); 1031 1032 // The process will no longer try to exit, so we can decrement the count. 1033 render_frame_host_->GetProcess()->RemovePendingView(); 1034 1035 // If the view is gone, then this RenderViewHost died while it was hidden. 1036 // We ignored the RenderProcessGone call at the time, so we should send it now 1037 // to make sure the sad tab shows up, etc. 1038 if (!render_frame_host_->render_view_host()->GetView()) { 1039 delegate_->RenderProcessGoneFromRenderManager( 1040 render_frame_host_->render_view_host()); 1041 } else if (!delegate_->IsHidden()) { 1042 render_frame_host_->render_view_host()->GetView()->Show(); 1043 } 1044 1045 // If the old view is live and top-level, hide it now that the new one is 1046 // visible. 1047 int32 old_site_instance_id = 1048 old_render_frame_host->GetSiteInstance()->GetId(); 1049 if (old_render_frame_host->render_view_host()->GetView()) { 1050 if (is_main_frame) { 1051 old_render_frame_host->render_view_host()->GetView()->Hide(); 1052 old_render_frame_host->render_view_host()->WasSwappedOut(base::Bind( 1053 &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance, 1054 weak_factory_.GetWeakPtr(), 1055 old_site_instance_id, 1056 old_render_frame_host.get())); 1057 } else { 1058 // TODO(creis): We'll need to set this back to false if we navigate back. 1059 old_render_frame_host->set_swapped_out(true); 1060 } 1061 } 1062 1063 // Make sure the size is up to date. (Fix for bug 1079768.) 1064 delegate_->UpdateRenderViewSizeForRenderManager(); 1065 1066 if (will_focus_location_bar) { 1067 delegate_->SetFocusToLocationBar(false); 1068 } else if (focus_render_view && 1069 render_frame_host_->render_view_host()->GetView()) { 1070 render_frame_host_->render_view_host()->GetView()->Focus(); 1071 } 1072 1073 // Notify that we've swapped RenderFrameHosts. We do this before shutting down 1074 // the RFH so that we can clean up RendererResources related to the RFH first. 1075 // TODO(creis): Only do this on top-level RFHs for now, and later update it to 1076 // pass the RFHs. 1077 if (is_main_frame) { 1078 delegate_->NotifySwappedFromRenderManager( 1079 old_render_frame_host->render_view_host(), 1080 render_frame_host_->render_view_host()); 1081 } 1082 1083 // If the old RFH is not live, just return as there is no work to do. 1084 if (!old_render_frame_host->render_view_host()->IsRenderViewLive()) { 1085 return; 1086 } 1087 1088 // If the old RFH is live, we are swapping it out and should keep track of 1089 // it in case we navigate back to it, or it is waiting for the unload event 1090 // to execute in the background. 1091 // TODO(creis): Swap out the subframe in --site-per-process. 1092 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) 1093 DCHECK(old_render_frame_host->is_swapped_out() || 1094 !RenderViewHostImpl::IsRVHStateActive( 1095 old_render_frame_host->render_view_host()->rvh_state())); 1096 // Temp fix for http://crbug.com/90867 until we do a better cleanup to make 1097 // sure we don't get different rvh instances for the same site instance 1098 // in the same rvhmgr. 1099 // TODO(creis): Clean this up. 1100 RenderFrameProxyHostMap::iterator iter = 1101 proxy_hosts_.find(old_site_instance_id); 1102 if (iter != proxy_hosts_.end() && 1103 iter->second->render_frame_host() != old_render_frame_host) { 1104 // Delete the proxy that will be replaced in the map to avoid a leak. 1105 delete iter->second; 1106 } 1107 1108 // If the RenderViewHost backing the RenderFrameHost is pending shutdown, 1109 // the RenderFrameHost should be put in the map of RenderFrameHosts pending 1110 // shutdown. Otherwise, it is stored in the map of proxy hosts. 1111 if (old_render_frame_host->render_view_host()->rvh_state() == 1112 RenderViewHostImpl::STATE_PENDING_SHUTDOWN) { 1113 proxy_hosts_.erase(old_site_instance_id); 1114 RFHPendingDeleteMap::iterator pending_delete_iter = 1115 pending_delete_hosts_.find(old_site_instance_id); 1116 if (pending_delete_iter == pending_delete_hosts_.end() || 1117 pending_delete_iter->second.get() != old_render_frame_host) { 1118 pending_delete_hosts_[old_site_instance_id] = 1119 linked_ptr<RenderFrameHostImpl>(old_render_frame_host.release()); 1120 } 1121 } else { 1122 // If there are no active views in this SiteInstance, it means that 1123 // this RFH was the last active one in the SiteInstance. Now that we 1124 // know that all RFHs are swapped out, we can delete all the RFHs and RVHs 1125 // in this SiteInstance. We do this after ensuring the RFH is on the 1126 // swapped out list to simplify the deletion. 1127 if (!static_cast<SiteInstanceImpl*>( 1128 old_render_frame_host->GetSiteInstance())->active_view_count()) { 1129 old_render_frame_host.reset(); 1130 ShutdownRenderFrameHostsInSiteInstance(old_site_instance_id); 1131 } else { 1132 proxy_hosts_[old_site_instance_id] = new RenderFrameProxyHost( 1133 old_render_frame_host.Pass()); 1134 } 1135 } 1136} 1137 1138void RenderFrameHostManager::ShutdownRenderFrameHostsInSiteInstance( 1139 int32 site_instance_id) { 1140 // First remove any swapped out RFH for this SiteInstance from our own list. 1141 ClearProxiesInSiteInstance(site_instance_id, frame_tree_node_); 1142 1143 // Use the safe RenderWidgetHost iterator for now to find all RenderViewHosts 1144 // in the SiteInstance, then tell their respective FrameTrees to remove all 1145 // RenderFrameProxyHosts corresponding to them. 1146 // TODO(creis): Replace this with a RenderFrameHostIterator that protects 1147 // against use-after-frees if a later element is deleted before getting to it. 1148 scoped_ptr<RenderWidgetHostIterator> widgets( 1149 RenderWidgetHostImpl::GetAllRenderWidgetHosts()); 1150 while (RenderWidgetHost* widget = widgets->GetNextHost()) { 1151 if (!widget->IsRenderView()) 1152 continue; 1153 RenderViewHostImpl* rvh = 1154 static_cast<RenderViewHostImpl*>(RenderViewHost::From(widget)); 1155 if (site_instance_id == rvh->GetSiteInstance()->GetId()) { 1156 // This deletes all RenderFrameHosts using the |rvh|, which then causes 1157 // |rvh| to Shutdown. 1158 FrameTree* tree = rvh->GetDelegate()->GetFrameTree(); 1159 tree->ForEach(base::Bind( 1160 &RenderFrameHostManager::ClearProxiesInSiteInstance, 1161 site_instance_id)); 1162 } 1163 } 1164} 1165 1166RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( 1167 const NavigationEntryImpl& entry) { 1168 // If we are currently navigating cross-process, we want to get back to normal 1169 // and then navigate as usual. 1170 if (cross_navigation_pending_) { 1171 if (pending_render_frame_host_) 1172 CancelPending(); 1173 cross_navigation_pending_ = false; 1174 } 1175 1176 // render_frame_host_'s SiteInstance and new_instance will not be deleted 1177 // before the end of this method, so we don't have to worry about their ref 1178 // counts dropping to zero. 1179 SiteInstance* current_instance = render_frame_host_->GetSiteInstance(); 1180 SiteInstance* new_instance = current_instance; 1181 1182 // We do not currently swap processes for navigations in webview tag guests. 1183 bool is_guest_scheme = current_instance->GetSiteURL().SchemeIs(kGuestScheme); 1184 1185 // Determine if we need a new BrowsingInstance for this entry. If true, this 1186 // implies that it will get a new SiteInstance (and likely process), and that 1187 // other tabs in the current BrowsingInstance will be unable to script it. 1188 // This is used for cases that require a process swap even in the 1189 // process-per-tab model, such as WebUI pages. 1190 const NavigationEntry* current_entry = 1191 delegate_->GetLastCommittedNavigationEntryForRenderManager(); 1192 bool force_swap = !is_guest_scheme && 1193 ShouldSwapBrowsingInstancesForNavigation(current_entry, &entry); 1194 if (!is_guest_scheme && (ShouldTransitionCrossSite() || force_swap)) 1195 new_instance = GetSiteInstanceForEntry(entry, current_instance, force_swap); 1196 1197 // If force_swap is true, we must use a different SiteInstance. If we didn't, 1198 // we would have two RenderFrameHosts in the same SiteInstance and the same 1199 // frame, resulting in page_id conflicts for their NavigationEntries. 1200 if (force_swap) 1201 CHECK_NE(new_instance, current_instance); 1202 1203 if (new_instance != current_instance) { 1204 // New SiteInstance: create a pending RFH to navigate. 1205 DCHECK(!cross_navigation_pending_); 1206 1207 // This will possibly create (set to NULL) a Web UI object for the pending 1208 // page. We'll use this later to give the page special access. This must 1209 // happen before the new renderer is created below so it will get bindings. 1210 // It must also happen after the above conditional call to CancelPending(), 1211 // otherwise CancelPending may clear the pending_web_ui_ and the page will 1212 // not have its bindings set appropriately. 1213 SetPendingWebUI(entry); 1214 1215 // Ensure that we have created RFHs for the new RFH's opener chain if 1216 // we are staying in the same BrowsingInstance. This allows the pending RFH 1217 // to send cross-process script calls to its opener(s). 1218 int opener_route_id = MSG_ROUTING_NONE; 1219 if (new_instance->IsRelatedSiteInstance(current_instance)) { 1220 opener_route_id = 1221 delegate_->CreateOpenerRenderViewsForRenderManager(new_instance); 1222 } 1223 1224 // Create a non-swapped-out pending RFH with the given opener and navigate 1225 // it. 1226 int route_id = CreateRenderFrame(new_instance, opener_route_id, false, 1227 delegate_->IsHidden()); 1228 if (route_id == MSG_ROUTING_NONE) 1229 return NULL; 1230 1231 // Check if our current RFH is live before we set up a transition. 1232 if (!render_frame_host_->render_view_host()->IsRenderViewLive()) { 1233 if (!cross_navigation_pending_) { 1234 // The current RFH is not live. There's no reason to sit around with a 1235 // sad tab or a newly created RFH while we wait for the pending RFH to 1236 // navigate. Just switch to the pending RFH now and go back to non 1237 // cross-navigating (Note that we don't care about on{before}unload 1238 // handlers if the current RFH isn't live.) 1239 CommitPending(); 1240 return render_frame_host_.get(); 1241 } else { 1242 NOTREACHED(); 1243 return render_frame_host_.get(); 1244 } 1245 } 1246 // Otherwise, it's safe to treat this as a pending cross-site transition. 1247 1248 // We need to wait until the beforeunload handler has run, unless we are 1249 // transferring an existing request (in which case it has already run). 1250 // Suspend the new render view (i.e., don't let it send the cross-site 1251 // Navigate message) until we hear back from the old renderer's 1252 // beforeunload handler. If the handler returns false, we'll have to 1253 // cancel the request. 1254 DCHECK(!pending_render_frame_host_->render_view_host()-> 1255 are_navigations_suspended()); 1256 bool is_transfer = 1257 entry.transferred_global_request_id() != GlobalRequestID(); 1258 if (is_transfer) { 1259 // We don't need to stop the old renderer or run beforeunload/unload 1260 // handlers, because those have already been done. 1261 DCHECK(pending_nav_params_->global_request_id == 1262 entry.transferred_global_request_id()); 1263 } else { 1264 // Also make sure the old render view stops, in case a load is in 1265 // progress. (We don't want to do this for transfers, since it will 1266 // interrupt the transfer with an unexpected DidStopLoading.) 1267 render_frame_host_->render_view_host()->Send(new ViewMsg_Stop( 1268 render_frame_host_->render_view_host()->GetRoutingID())); 1269 1270 pending_render_frame_host_->render_view_host()->SetNavigationsSuspended( 1271 true, base::TimeTicks()); 1272 1273 // Tell the CrossSiteRequestManager that this RVH has a pending cross-site 1274 // request, so that ResourceDispatcherHost will know to tell us to run the 1275 // old page's unload handler before it sends the response. 1276 // TODO(creis): This needs to be on the RFH. 1277 pending_render_frame_host_->render_view_host()-> 1278 SetHasPendingCrossSiteRequest(true); 1279 } 1280 1281 // We now have a pending RFH. 1282 DCHECK(!cross_navigation_pending_); 1283 cross_navigation_pending_ = true; 1284 1285 // Unless we are transferring an existing request, we should now 1286 // tell the old render view to run its beforeunload handler, since it 1287 // doesn't otherwise know that the cross-site request is happening. This 1288 // will trigger a call to OnBeforeUnloadACK with the reply. 1289 if (!is_transfer) 1290 render_frame_host_->DispatchBeforeUnload(true); 1291 1292 return pending_render_frame_host_.get(); 1293 } 1294 1295 // Otherwise the same SiteInstance can be used. Navigate render_frame_host_. 1296 DCHECK(!cross_navigation_pending_); 1297 if (ShouldReuseWebUI(current_entry, &entry)) { 1298 pending_web_ui_.reset(); 1299 pending_and_current_web_ui_ = web_ui_->AsWeakPtr(); 1300 } else { 1301 SetPendingWebUI(entry); 1302 1303 // Make sure the new RenderViewHost has the right bindings. 1304 if (pending_web_ui() && !render_frame_host_->GetProcess()->IsGuest()) { 1305 render_frame_host_->render_view_host()->AllowBindings( 1306 pending_web_ui()->GetBindings()); 1307 } 1308 } 1309 1310 if (pending_web_ui() && 1311 render_frame_host_->render_view_host()->IsRenderViewLive()) { 1312 pending_web_ui()->GetController()->RenderViewReused( 1313 render_frame_host_->render_view_host()); 1314 } 1315 1316 // The renderer can exit view source mode when any error or cancellation 1317 // happen. We must overwrite to recover the mode. 1318 if (entry.IsViewSourceMode()) { 1319 render_frame_host_->render_view_host()->Send( 1320 new ViewMsg_EnableViewSourceMode( 1321 render_frame_host_->render_view_host()->GetRoutingID())); 1322 } 1323 1324 return render_frame_host_.get(); 1325} 1326 1327void RenderFrameHostManager::CancelPending() { 1328 scoped_ptr<RenderFrameHostImpl> pending_render_frame_host = 1329 pending_render_frame_host_.Pass(); 1330 1331 RenderViewDevToolsAgentHost::OnCancelPendingNavigation( 1332 pending_render_frame_host->render_view_host(), 1333 render_frame_host_->render_view_host()); 1334 1335 // We no longer need to prevent the process from exiting. 1336 pending_render_frame_host->GetProcess()->RemovePendingView(); 1337 1338 // If the SiteInstance for the pending RFH is being used by others, don't 1339 // delete the RFH, just swap it out and it can be reused at a later point. 1340 SiteInstanceImpl* site_instance = static_cast<SiteInstanceImpl*>( 1341 pending_render_frame_host->GetSiteInstance()); 1342 if (site_instance->active_view_count() > 1) { 1343 // Any currently suspended navigations are no longer needed. 1344 pending_render_frame_host->render_view_host()->CancelSuspendedNavigations(); 1345 1346 pending_render_frame_host->SwapOut(); 1347 1348 proxy_hosts_[site_instance->GetId()] = new RenderFrameProxyHost( 1349 pending_render_frame_host.Pass()); 1350 } else { 1351 // We won't be coming back, so delete this one. 1352 pending_render_frame_host.reset(); 1353 } 1354 1355 pending_web_ui_.reset(); 1356 pending_and_current_web_ui_.reset(); 1357} 1358 1359bool RenderFrameHostManager::IsRVHOnSwappedOutList( 1360 RenderViewHostImpl* rvh) const { 1361 RenderFrameProxyHost* proxy = GetRenderFrameProxyHost( 1362 rvh->GetSiteInstance()); 1363 if (!proxy) 1364 return false; 1365 return IsOnSwappedOutList(proxy->render_frame_host()); 1366} 1367 1368bool RenderFrameHostManager::IsOnSwappedOutList( 1369 RenderFrameHostImpl* rfh) const { 1370 if (!rfh->GetSiteInstance()) 1371 return false; 1372 1373 RenderFrameProxyHostMap::const_iterator iter = proxy_hosts_.find( 1374 rfh->GetSiteInstance()->GetId()); 1375 if (iter == proxy_hosts_.end()) 1376 return false; 1377 1378 return iter->second->render_frame_host() == rfh; 1379} 1380 1381RenderViewHostImpl* RenderFrameHostManager::GetSwappedOutRenderViewHost( 1382 SiteInstance* instance) const { 1383 RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(instance); 1384 if (proxy) 1385 return proxy->render_view_host(); 1386 return NULL; 1387} 1388 1389RenderFrameProxyHost* RenderFrameHostManager::GetRenderFrameProxyHost( 1390 SiteInstance* instance) const { 1391 RenderFrameProxyHostMap::const_iterator iter = 1392 proxy_hosts_.find(instance->GetId()); 1393 if (iter != proxy_hosts_.end()) 1394 return iter->second; 1395 1396 return NULL; 1397} 1398 1399} // namespace content 1400