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