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