Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(304)

Side by Side Diff: content/browser/web_contents/render_view_host_manager.cc

Issue 49823002: Move navigation and frame tree classes to a new frame_host/ directory. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixing gyp and adding TODO. Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/web_contents/render_view_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/devtools/render_view_devtools_agent_host.h"
13 #include "content/browser/renderer_host/render_process_host_impl.h"
14 #include "content/browser/renderer_host/render_view_host_factory.h"
15 #include "content/browser/renderer_host/render_view_host_impl.h"
16 #include "content/browser/site_instance_impl.h"
17 #include "content/browser/web_contents/interstitial_page_impl.h"
18 #include "content/browser/web_contents/navigation_controller_impl.h"
19 #include "content/browser/web_contents/navigation_entry_impl.h"
20 #include "content/browser/webui/web_ui_controller_factory_registry.h"
21 #include "content/browser/webui/web_ui_impl.h"
22 #include "content/common/view_messages.h"
23 #include "content/port/browser/render_widget_host_view_port.h"
24 #include "content/public/browser/content_browser_client.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/browser/render_widget_host_iterator.h"
28 #include "content/public/browser/user_metrics.h"
29 #include "content/public/browser/web_contents_view.h"
30 #include "content/public/browser/web_ui_controller.h"
31 #include "content/public/common/content_switches.h"
32 #include "content/public/common/url_constants.h"
33
34 namespace content {
35
36 RenderViewHostManager::PendingNavigationParams::PendingNavigationParams()
37 : is_transfer(false), frame_id(-1) {
38 }
39
40 RenderViewHostManager::PendingNavigationParams::PendingNavigationParams(
41 const GlobalRequestID& global_request_id,
42 bool is_transfer,
43 const GURL& transfer_url,
44 Referrer referrer,
45 int64 frame_id)
46 : global_request_id(global_request_id),
47 is_transfer(is_transfer),
48 transfer_url(transfer_url),
49 referrer(referrer),
50 frame_id(frame_id) {
51 }
52
53 RenderViewHostManager::RenderViewHostManager(
54 RenderViewHostDelegate* render_view_delegate,
55 RenderWidgetHostDelegate* render_widget_delegate,
56 Delegate* delegate)
57 : delegate_(delegate),
58 cross_navigation_pending_(false),
59 render_view_delegate_(render_view_delegate),
60 render_widget_delegate_(render_widget_delegate),
61 render_view_host_(NULL),
62 pending_render_view_host_(NULL),
63 interstitial_page_(NULL) {
64 }
65
66 RenderViewHostManager::~RenderViewHostManager() {
67 if (pending_render_view_host_)
68 CancelPending();
69
70 // We should always have a main RenderViewHost except in some tests.
71 RenderViewHostImpl* render_view_host = render_view_host_;
72 render_view_host_ = NULL;
73 if (render_view_host)
74 render_view_host->Shutdown();
75
76 // Shut down any swapped out RenderViewHosts.
77 for (RenderViewHostMap::iterator iter = swapped_out_hosts_.begin();
78 iter != swapped_out_hosts_.end();
79 ++iter) {
80 iter->second->Shutdown();
81 }
82 }
83
84 void RenderViewHostManager::Init(BrowserContext* browser_context,
85 SiteInstance* site_instance,
86 int routing_id,
87 int main_frame_routing_id) {
88 // Create a RenderViewHost, once we have an instance. It is important to
89 // immediately give this SiteInstance to a RenderViewHost so that it is
90 // ref counted.
91 if (!site_instance)
92 site_instance = SiteInstance::Create(browser_context);
93 render_view_host_ = static_cast<RenderViewHostImpl*>(
94 RenderViewHostFactory::Create(
95 site_instance, render_view_delegate_, render_widget_delegate_,
96 routing_id, main_frame_routing_id, false,
97 delegate_->IsHidden()));
98 render_view_host_->AttachToFrameTree();
99
100 // Keep track of renderer processes as they start to shut down or are
101 // crashed/killed.
102 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
103 NotificationService::AllSources());
104 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSING,
105 NotificationService::AllSources());
106 }
107
108 RenderViewHostImpl* RenderViewHostManager::current_host() const {
109 return render_view_host_;
110 }
111
112 RenderViewHostImpl* RenderViewHostManager::pending_render_view_host() const {
113 return pending_render_view_host_;
114 }
115
116 RenderWidgetHostView* RenderViewHostManager::GetRenderWidgetHostView() const {
117 if (interstitial_page_)
118 return interstitial_page_->GetView();
119 if (!render_view_host_)
120 return NULL;
121 return render_view_host_->GetView();
122 }
123
124 void RenderViewHostManager::SetPendingWebUI(const NavigationEntryImpl& entry) {
125 pending_web_ui_.reset(
126 delegate_->CreateWebUIForRenderManager(entry.GetURL()));
127 pending_and_current_web_ui_.reset();
128
129 // If we have assigned (zero or more) bindings to this NavigationEntry in the
130 // past, make sure we're not granting it different bindings than it had
131 // before. If so, note it and don't give it any bindings, to avoid a
132 // potential privilege escalation.
133 if (pending_web_ui_.get() &&
134 entry.bindings() != NavigationEntryImpl::kInvalidBindings &&
135 pending_web_ui_->GetBindings() != entry.bindings()) {
136 RecordAction(UserMetricsAction("ProcessSwapBindingsMismatch_RVHM"));
137 pending_web_ui_.reset();
138 }
139 }
140
141 RenderViewHostImpl* RenderViewHostManager::Navigate(
142 const NavigationEntryImpl& entry) {
143 TRACE_EVENT0("browser", "RenderViewHostManager:Navigate");
144 // Create a pending RenderViewHost. It will give us the one we should use
145 RenderViewHostImpl* dest_render_view_host =
146 static_cast<RenderViewHostImpl*>(UpdateRendererStateForNavigate(entry));
147 if (!dest_render_view_host)
148 return NULL; // We weren't able to create a pending render view host.
149
150 // If the current render_view_host_ isn't live, we should create it so
151 // that we don't show a sad tab while the dest_render_view_host fetches
152 // its first page. (Bug 1145340)
153 if (dest_render_view_host != render_view_host_ &&
154 !render_view_host_->IsRenderViewLive()) {
155 // Note: we don't call InitRenderView here because we are navigating away
156 // soon anyway, and we don't have the NavigationEntry for this host.
157 delegate_->CreateRenderViewForRenderManager(render_view_host_,
158 MSG_ROUTING_NONE);
159 }
160
161 // If the renderer crashed, then try to create a new one to satisfy this
162 // navigation request.
163 if (!dest_render_view_host->IsRenderViewLive()) {
164 // Recreate the opener chain.
165 int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager(
166 dest_render_view_host->GetSiteInstance());
167 if (!InitRenderView(dest_render_view_host, opener_route_id))
168 return NULL;
169
170 // Now that we've created a new renderer, be sure to hide it if it isn't
171 // our primary one. Otherwise, we might crash if we try to call Show()
172 // on it later.
173 if (dest_render_view_host != render_view_host_ &&
174 dest_render_view_host->GetView()) {
175 dest_render_view_host->GetView()->Hide();
176 } else {
177 // This is our primary renderer, notify here as we won't be calling
178 // CommitPending (which does the notify).
179 delegate_->NotifySwappedFromRenderManager(NULL, render_view_host_);
180 }
181 }
182
183 return dest_render_view_host;
184 }
185
186 void RenderViewHostManager::Stop() {
187 render_view_host_->Stop();
188
189 // If we are cross-navigating, we should stop the pending renderers. This
190 // will lead to a DidFailProvisionalLoad, which will properly destroy them.
191 if (cross_navigation_pending_) {
192 pending_render_view_host_->Send(
193 new ViewMsg_Stop(pending_render_view_host_->GetRoutingID()));
194 }
195 }
196
197 void RenderViewHostManager::SetIsLoading(bool is_loading) {
198 render_view_host_->SetIsLoading(is_loading);
199 if (pending_render_view_host_)
200 pending_render_view_host_->SetIsLoading(is_loading);
201 }
202
203 bool RenderViewHostManager::ShouldCloseTabOnUnresponsiveRenderer() {
204 if (!cross_navigation_pending_)
205 return true;
206
207 // We should always have a pending RVH when there's a cross-process navigation
208 // in progress. Sanity check this for http://crbug.com/276333.
209 CHECK(pending_render_view_host_);
210
211 // If the tab becomes unresponsive during {before}unload while doing a
212 // cross-site navigation, proceed with the navigation. (This assumes that
213 // the pending RenderViewHost is still responsive.)
214 if (render_view_host_->is_waiting_for_unload_ack()) {
215 // The request has been started and paused while we're waiting for the
216 // unload handler to finish. We'll pretend that it did. The pending
217 // renderer will then be swapped in as part of the usual DidNavigate logic.
218 // (If the unload handler later finishes, this call will be ignored because
219 // the pending_nav_params_ state will already be cleaned up.)
220 current_host()->OnSwappedOut(true);
221 } else if (render_view_host_->is_waiting_for_beforeunload_ack()) {
222 // Haven't gotten around to starting the request, because we're still
223 // waiting for the beforeunload handler to finish. We'll pretend that it
224 // did finish, to let the navigation proceed. Note that there's a danger
225 // that the beforeunload handler will later finish and possibly return
226 // false (meaning the navigation should not proceed), but we'll ignore it
227 // in this case because it took too long.
228 if (pending_render_view_host_->are_navigations_suspended())
229 pending_render_view_host_->SetNavigationsSuspended(
230 false, base::TimeTicks::Now());
231 }
232 return false;
233 }
234
235 void RenderViewHostManager::SwappedOut(RenderViewHost* render_view_host) {
236 // Make sure this is from our current RVH, and that we have a pending
237 // navigation from OnCrossSiteResponse. (There may be no pending navigation
238 // for data URLs that don't make network requests, for example.) If not,
239 // just return early and ignore.
240 if (render_view_host != render_view_host_ || !pending_nav_params_.get()) {
241 pending_nav_params_.reset();
242 return;
243 }
244
245 // Now that the unload handler has run, we need to either initiate the
246 // pending transfer (if there is one) or resume the paused response (if not).
247 // TODO(creis): The blank swapped out page is visible during this time, but
248 // we can shorten this by delivering the response directly, rather than
249 // forcing an identical request to be made.
250 if (pending_nav_params_->is_transfer) {
251 // We don't know whether the original request had |user_action| set to true.
252 // However, since we force the navigation to be in the current tab, it
253 // doesn't matter.
254 render_view_host->GetDelegate()->RequestTransferURL(
255 pending_nav_params_->transfer_url,
256 pending_nav_params_->referrer,
257 CURRENT_TAB,
258 pending_nav_params_->frame_id,
259 pending_nav_params_->global_request_id,
260 false,
261 true);
262 } else if (pending_render_view_host_) {
263 RenderProcessHostImpl* pending_process =
264 static_cast<RenderProcessHostImpl*>(
265 pending_render_view_host_->GetProcess());
266 pending_process->ResumeDeferredNavigation(
267 pending_nav_params_->global_request_id);
268 }
269 pending_nav_params_.reset();
270 }
271
272 void RenderViewHostManager::DidNavigateMainFrame(
273 RenderViewHost* render_view_host) {
274 if (!cross_navigation_pending_) {
275 DCHECK(!pending_render_view_host_);
276
277 // We should only hear this from our current renderer.
278 DCHECK(render_view_host == render_view_host_);
279
280 // Even when there is no pending RVH, there may be a pending Web UI.
281 if (pending_web_ui())
282 CommitPending();
283 return;
284 }
285
286 if (render_view_host == pending_render_view_host_) {
287 // The pending cross-site navigation completed, so show the renderer.
288 // If it committed without sending network requests (e.g., data URLs),
289 // then we still need to swap out the old RVH first and run its unload
290 // handler. OK for that to happen in the background.
291 if (pending_render_view_host_->HasPendingCrossSiteRequest())
292 SwapOutOldPage();
293
294 CommitPending();
295 cross_navigation_pending_ = false;
296 } else if (render_view_host == render_view_host_) {
297 // A navigation in the original page has taken place. Cancel the pending
298 // one.
299 CancelPending();
300 cross_navigation_pending_ = false;
301 } else {
302 // No one else should be sending us DidNavigate in this state.
303 DCHECK(false);
304 }
305 }
306
307 void RenderViewHostManager::DidDisownOpener(RenderViewHost* render_view_host) {
308 // Notify all swapped out hosts, including the pending RVH.
309 for (RenderViewHostMap::iterator iter = swapped_out_hosts_.begin();
310 iter != swapped_out_hosts_.end();
311 ++iter) {
312 DCHECK_NE(iter->second->GetSiteInstance(),
313 current_host()->GetSiteInstance());
314 iter->second->DisownOpener();
315 }
316 }
317
318 void RenderViewHostManager::RendererAbortedProvisionalLoad(
319 RenderViewHost* render_view_host) {
320 // We used to cancel the pending renderer here for cross-site downloads.
321 // However, it's not safe to do that because the download logic repeatedly
322 // looks for this WebContents based on a render view ID. Instead, we just
323 // leave the pending renderer around until the next navigation event
324 // (Navigate, DidNavigate, etc), which will clean it up properly.
325 // TODO(creis): All of this will go away when we move the cross-site logic
326 // to ResourceDispatcherHost, so that we intercept responses rather than
327 // navigation events. (That's necessary to support onunload anyway.) Once
328 // we've made that change, we won't create a pending renderer until we know
329 // the response is not a download.
330 }
331
332 void RenderViewHostManager::RendererProcessClosing(
333 RenderProcessHost* render_process_host) {
334 // Remove any swapped out RVHs from this process, so that we don't try to
335 // swap them back in while the process is exiting. Start by finding them,
336 // since there could be more than one.
337 std::list<int> ids_to_remove;
338 for (RenderViewHostMap::iterator iter = swapped_out_hosts_.begin();
339 iter != swapped_out_hosts_.end();
340 ++iter) {
341 if (iter->second->GetProcess() == render_process_host)
342 ids_to_remove.push_back(iter->first);
343 }
344
345 // Now delete them.
346 while (!ids_to_remove.empty()) {
347 swapped_out_hosts_[ids_to_remove.back()]->Shutdown();
348 swapped_out_hosts_.erase(ids_to_remove.back());
349 ids_to_remove.pop_back();
350 }
351 }
352
353 void RenderViewHostManager::ShouldClosePage(
354 bool for_cross_site_transition,
355 bool proceed,
356 const base::TimeTicks& proceed_time) {
357 if (for_cross_site_transition) {
358 // Ignore if we're not in a cross-site navigation.
359 if (!cross_navigation_pending_)
360 return;
361
362 if (proceed) {
363 // Ok to unload the current page, so proceed with the cross-site
364 // navigation. Note that if navigations are not currently suspended, it
365 // might be because the renderer was deemed unresponsive and this call was
366 // already made by ShouldCloseTabOnUnresponsiveRenderer. In that case, it
367 // is ok to do nothing here.
368 if (pending_render_view_host_ &&
369 pending_render_view_host_->are_navigations_suspended()) {
370 pending_render_view_host_->SetNavigationsSuspended(false, proceed_time);
371 }
372 } else {
373 // Current page says to cancel.
374 CancelPending();
375 cross_navigation_pending_ = false;
376 }
377 } else {
378 // Non-cross site transition means closing the entire tab.
379 bool proceed_to_fire_unload;
380 delegate_->BeforeUnloadFiredFromRenderManager(proceed, proceed_time,
381 &proceed_to_fire_unload);
382
383 if (proceed_to_fire_unload) {
384 // If we're about to close the tab and there's a pending RVH, cancel it.
385 // Otherwise, if the navigation in the pending RVH completes before the
386 // close in the current RVH, we'll lose the tab close.
387 if (pending_render_view_host_) {
388 CancelPending();
389 cross_navigation_pending_ = false;
390 }
391
392 // This is not a cross-site navigation, the tab is being closed.
393 render_view_host_->ClosePage();
394 }
395 }
396 }
397
398 void RenderViewHostManager::OnCrossSiteResponse(
399 RenderViewHost* pending_render_view_host,
400 const GlobalRequestID& global_request_id,
401 bool is_transfer,
402 const GURL& transfer_url,
403 const Referrer& referrer,
404 int64 frame_id) {
405 // This should be called either when the pending RVH is ready to commit or
406 // when we realize that the current RVH's request requires a transfer.
407 DCHECK(
408 pending_render_view_host == pending_render_view_host_ ||
409 pending_render_view_host == render_view_host_);
410
411 // TODO(creis): Eventually we will want to check all navigation responses
412 // here, but currently we pass information for a transfer if
413 // ShouldSwapProcessesForRedirect returned true in the network stack.
414 // In that case, we should set up a transfer after the unload handler runs.
415 // If is_transfer is false, we will just run the unload handler and resume.
416 pending_nav_params_.reset(new PendingNavigationParams(
417 global_request_id, is_transfer, transfer_url, referrer, frame_id));
418
419 // Run the unload handler of the current page.
420 SwapOutOldPage();
421 }
422
423 void RenderViewHostManager::SwapOutOldPage() {
424 // Should only see this while we have a pending renderer or transfer.
425 CHECK(cross_navigation_pending_ || pending_nav_params_.get());
426
427 // First close any modal dialogs that would prevent us from swapping out.
428 delegate_->CancelModalDialogsForRenderManager();
429
430 // Tell the old renderer it is being swapped out. This will fire the unload
431 // handler (without firing the beforeunload handler a second time). When the
432 // unload handler finishes and the navigation completes, we will send a
433 // message to the ResourceDispatcherHost, allowing the pending RVH's response
434 // to resume.
435 render_view_host_->SwapOut();
436
437 // ResourceDispatcherHost has told us to run the onunload handler, which
438 // means it is not a download or unsafe page, and we are going to perform the
439 // navigation. Thus, we no longer need to remember that the RenderViewHost
440 // is part of a pending cross-site request.
441 if (pending_render_view_host_)
442 pending_render_view_host_->SetHasPendingCrossSiteRequest(false);
443 }
444
445 void RenderViewHostManager::Observe(
446 int type,
447 const NotificationSource& source,
448 const NotificationDetails& details) {
449 switch (type) {
450 case NOTIFICATION_RENDERER_PROCESS_CLOSED:
451 case NOTIFICATION_RENDERER_PROCESS_CLOSING:
452 RendererProcessClosing(
453 Source<RenderProcessHost>(source).ptr());
454 break;
455
456 default:
457 NOTREACHED();
458 }
459 }
460
461 bool RenderViewHostManager::ShouldTransitionCrossSite() {
462 // False in the single-process mode, as it makes RVHs to accumulate
463 // in swapped_out_hosts_.
464 // True if we are using process-per-site-instance (default) or
465 // process-per-site (kProcessPerSite).
466 return
467 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) &&
468 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab);
469 }
470
471 bool RenderViewHostManager::ShouldSwapProcessesForNavigation(
472 const NavigationEntry* curr_entry,
473 const NavigationEntryImpl* new_entry) const {
474 DCHECK(new_entry);
475
476 // Check for reasons to swap processes even if we are in a process model that
477 // doesn't usually swap (e.g., process-per-tab).
478
479 // For security, we should transition between processes when one is a Web UI
480 // page and one isn't. If there's no curr_entry, check the current RVH's
481 // site, which might already be committed to a Web UI URL (such as the NTP).
482 const GURL& current_url = (curr_entry) ? curr_entry->GetURL() :
483 render_view_host_->GetSiteInstance()->GetSiteURL();
484 BrowserContext* browser_context =
485 delegate_->GetControllerForRenderManager().GetBrowserContext();
486 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
487 browser_context, current_url)) {
488 // Force swap if it's not an acceptable URL for Web UI.
489 // Here, data URLs are never allowed.
490 if (!WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
491 browser_context, new_entry->GetURL(), false)) {
492 return true;
493 }
494 } else {
495 // Force swap if it's a Web UI URL.
496 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
497 browser_context, new_entry->GetURL())) {
498 return true;
499 }
500 }
501
502 if (GetContentClient()->browser()->ShouldSwapProcessesForNavigation(
503 render_view_host_->GetSiteInstance(),
504 curr_entry ? curr_entry->GetURL() : GURL(),
505 new_entry->GetURL())) {
506 return true;
507 }
508
509 if (!curr_entry)
510 return false;
511
512 // We can't switch a RenderView between view source and non-view source mode
513 // without screwing up the session history sometimes (when navigating between
514 // "view-source:http://foo.com/" and "http://foo.com/", WebKit doesn't treat
515 // it as a new navigation). So require a view switch.
516 if (curr_entry->IsViewSourceMode() != new_entry->IsViewSourceMode())
517 return true;
518
519 return false;
520 }
521
522 bool RenderViewHostManager::ShouldReuseWebUI(
523 const NavigationEntry* curr_entry,
524 const NavigationEntryImpl* new_entry) const {
525 NavigationControllerImpl& controller =
526 delegate_->GetControllerForRenderManager();
527 return curr_entry && web_ui_.get() &&
528 (WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(
529 controller.GetBrowserContext(), curr_entry->GetURL()) ==
530 WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(
531 controller.GetBrowserContext(), new_entry->GetURL()));
532 }
533
534 SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry(
535 const NavigationEntryImpl& entry,
536 SiteInstance* curr_instance) {
537 // NOTE: This is only called when ShouldTransitionCrossSite is true.
538
539 const GURL& dest_url = entry.GetURL();
540 NavigationControllerImpl& controller =
541 delegate_->GetControllerForRenderManager();
542 BrowserContext* browser_context = controller.GetBrowserContext();
543
544 // If the entry has an instance already we should use it.
545 if (entry.site_instance())
546 return entry.site_instance();
547
548 // (UGLY) HEURISTIC, process-per-site only:
549 //
550 // If this navigation is generated, then it probably corresponds to a search
551 // query. Given that search results typically lead to users navigating to
552 // other sites, we don't really want to use the search engine hostname to
553 // determine the site instance for this navigation.
554 //
555 // NOTE: This can be removed once we have a way to transition between
556 // RenderViews in response to a link click.
557 //
558 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerSite) &&
559 PageTransitionCoreTypeIs(entry.GetTransitionType(),
560 PAGE_TRANSITION_GENERATED)) {
561 return curr_instance;
562 }
563
564 SiteInstanceImpl* curr_site_instance =
565 static_cast<SiteInstanceImpl*>(curr_instance);
566
567 // If we haven't used our SiteInstance (and thus RVH) yet, then we can use it
568 // for this entry. We won't commit the SiteInstance to this site until the
569 // navigation commits (in DidNavigate), unless the navigation entry was
570 // restored or it's a Web UI as described below.
571 if (!curr_site_instance->HasSite()) {
572 // If we've already created a SiteInstance for our destination, we don't
573 // want to use this unused SiteInstance; use the existing one. (We don't
574 // do this check if the curr_instance has a site, because for now, we want
575 // to compare against the current URL and not the SiteInstance's site. In
576 // this case, there is no current URL, so comparing against the site is ok.
577 // See additional comments below.)
578 //
579 // Also, if the URL should use process-per-site mode and there is an
580 // existing process for the site, we should use it. We can call
581 // GetRelatedSiteInstance() for this, which will eagerly set the site and
582 // thus use the correct process.
583 bool use_process_per_site =
584 RenderProcessHost::ShouldUseProcessPerSite(browser_context, dest_url) &&
585 RenderProcessHostImpl::GetProcessHostForSite(browser_context, dest_url);
586 if (curr_site_instance->HasRelatedSiteInstance(dest_url) ||
587 use_process_per_site) {
588 return curr_site_instance->GetRelatedSiteInstance(dest_url);
589 }
590
591 // For extensions, Web UI URLs (such as the new tab page), and apps we do
592 // not want to use the curr_instance if it has no site, since it will have a
593 // RenderProcessHost of PRIV_NORMAL. Create a new SiteInstance for this
594 // URL instead (with the correct process type).
595 if (curr_site_instance->HasWrongProcessForURL(dest_url))
596 return curr_site_instance->GetRelatedSiteInstance(dest_url);
597
598 // View-source URLs must use a new SiteInstance and BrowsingInstance.
599 // TODO(nasko): This is the same condition as later in the function. This
600 // should be taken into account when refactoring this method as part of
601 // http://crbug.com/123007.
602 if (entry.IsViewSourceMode())
603 return SiteInstance::CreateForURL(browser_context, dest_url);
604
605 // If we are navigating from a blank SiteInstance to a WebUI, make sure we
606 // create a new SiteInstance.
607 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
608 browser_context, dest_url)) {
609 return SiteInstance::CreateForURL(browser_context, dest_url);
610 }
611
612 // Normally the "site" on the SiteInstance is set lazily when the load
613 // actually commits. This is to support better process sharing in case
614 // the site redirects to some other site: we want to use the destination
615 // site in the site instance.
616 //
617 // In the case of session restore, as it loads all the pages immediately
618 // we need to set the site first, otherwise after a restore none of the
619 // pages would share renderers in process-per-site.
620 if (entry.restore_type() != NavigationEntryImpl::RESTORE_NONE)
621 curr_site_instance->SetSite(dest_url);
622
623 return curr_site_instance;
624 }
625
626 // Otherwise, only create a new SiteInstance for cross-site navigation.
627
628 // TODO(creis): Once we intercept links and script-based navigations, we
629 // will be able to enforce that all entries in a SiteInstance actually have
630 // the same site, and it will be safe to compare the URL against the
631 // SiteInstance's site, as follows:
632 // const GURL& current_url = curr_instance->site();
633 // For now, though, we're in a hybrid model where you only switch
634 // SiteInstances if you type in a cross-site URL. This means we have to
635 // compare the entry's URL to the last committed entry's URL.
636 NavigationEntry* curr_entry = controller.GetLastCommittedEntry();
637 if (interstitial_page_) {
638 // The interstitial is currently the last committed entry, but we want to
639 // compare against the last non-interstitial entry.
640 curr_entry = controller.GetEntryAtOffset(-1);
641 }
642 // If there is no last non-interstitial entry (and curr_instance already
643 // has a site), then we must have been opened from another tab. We want
644 // to compare against the URL of the page that opened us, but we can't
645 // get to it directly. The best we can do is check against the site of
646 // the SiteInstance. This will be correct when we intercept links and
647 // script-based navigations, but for now, it could place some pages in a
648 // new process unnecessarily. We should only hit this case if a page tries
649 // to open a new tab to an interstitial-inducing URL, and then navigates
650 // the page to a different same-site URL. (This seems very unlikely in
651 // practice.)
652 const GURL& current_url = (curr_entry) ? curr_entry->GetURL() :
653 curr_instance->GetSiteURL();
654
655 // View-source URLs must use a new SiteInstance and BrowsingInstance.
656 // TODO(creis): Refactor this method so this duplicated code isn't needed.
657 // See http://crbug.com/123007.
658 if (curr_entry &&
659 curr_entry->IsViewSourceMode() != entry.IsViewSourceMode()) {
660 return SiteInstance::CreateForURL(browser_context, dest_url);
661 }
662
663 // Use the current SiteInstance for same site navigations, as long as the
664 // process type is correct. (The URL may have been installed as an app since
665 // the last time we visited it.)
666 if (SiteInstance::IsSameWebSite(browser_context, current_url, dest_url) &&
667 !static_cast<SiteInstanceImpl*>(curr_instance)->HasWrongProcessForURL(
668 dest_url)) {
669 return curr_instance;
670 } else if (ShouldSwapProcessesForNavigation(curr_entry, &entry)) {
671 // When we're swapping, we need to force the site instance AND browsing
672 // instance to be different ones. This addresses special cases where we use
673 // a single BrowsingInstance for all pages of a certain type (e.g., New Tab
674 // Pages), keeping them in the same process. When you navigate away from
675 // that page, we want to explicity ignore that BrowsingInstance and group
676 // this page into the appropriate SiteInstance for its URL.
677 return SiteInstance::CreateForURL(browser_context, dest_url);
678 } else {
679 // Start the new renderer in a new SiteInstance, but in the current
680 // BrowsingInstance. It is important to immediately give this new
681 // SiteInstance to a RenderViewHost (if it is different than our current
682 // SiteInstance), so that it is ref counted. This will happen in
683 // CreateRenderView.
684 return curr_instance->GetRelatedSiteInstance(dest_url);
685 }
686 }
687
688 int RenderViewHostManager::CreateRenderView(
689 SiteInstance* instance,
690 int opener_route_id,
691 bool swapped_out,
692 bool hidden) {
693 CHECK(instance);
694 DCHECK(!swapped_out || hidden); // Swapped out views should always be hidden.
695
696 // Check if we've already created an RVH for this SiteInstance. If so, try
697 // to re-use the existing one, which has already been initialized. We'll
698 // remove it from the list of swapped out hosts if it commits.
699 RenderViewHostImpl* new_render_view_host = static_cast<RenderViewHostImpl*>(
700 GetSwappedOutRenderViewHost(instance));
701 if (new_render_view_host) {
702 // Prevent the process from exiting while we're trying to use it.
703 if (!swapped_out)
704 new_render_view_host->GetProcess()->AddPendingView();
705 } else {
706 // Create a new RenderViewHost if we don't find an existing one.
707 new_render_view_host = static_cast<RenderViewHostImpl*>(
708 RenderViewHostFactory::Create(instance,
709 render_view_delegate_,
710 render_widget_delegate_,
711 MSG_ROUTING_NONE,
712 MSG_ROUTING_NONE,
713 swapped_out,
714 hidden));
715
716 // If the new RVH is swapped out already, store it. Otherwise prevent the
717 // process from exiting while we're trying to navigate in it.
718 if (swapped_out) {
719 swapped_out_hosts_[instance->GetId()] = new_render_view_host;
720 } else {
721 new_render_view_host->GetProcess()->AddPendingView();
722 }
723
724 bool success = InitRenderView(new_render_view_host, opener_route_id);
725 if (success) {
726 // Don't show the view until we get a DidNavigate from it.
727 new_render_view_host->GetView()->Hide();
728 } else if (!swapped_out) {
729 CancelPending();
730 }
731 }
732
733 // Use this as our new pending RVH if it isn't swapped out.
734 if (!swapped_out)
735 pending_render_view_host_ = new_render_view_host;
736
737 return new_render_view_host->GetRoutingID();
738 }
739
740 bool RenderViewHostManager::InitRenderView(RenderViewHost* render_view_host,
741 int opener_route_id) {
742 // If the pending navigation is to a WebUI and the RenderView is not in a
743 // guest process, tell the RenderView about any bindings it will need enabled.
744 if (pending_web_ui() && !render_view_host->GetProcess()->IsGuest())
745 render_view_host->AllowBindings(pending_web_ui()->GetBindings());
746
747 return delegate_->CreateRenderViewForRenderManager(render_view_host,
748 opener_route_id);
749 }
750
751 void RenderViewHostManager::CommitPending() {
752 // First check whether we're going to want to focus the location bar after
753 // this commit. We do this now because the navigation hasn't formally
754 // committed yet, so if we've already cleared |pending_web_ui_| the call chain
755 // this triggers won't be able to figure out what's going on.
756 bool will_focus_location_bar = delegate_->FocusLocationBarByDefault();
757
758 // Next commit the Web UI, if any. Either replace |web_ui_| with
759 // |pending_web_ui_|, or clear |web_ui_| if there is no pending WebUI, or
760 // leave |web_ui_| as is if reusing it.
761 DCHECK(!(pending_web_ui_.get() && pending_and_current_web_ui_.get()));
762 if (pending_web_ui_)
763 web_ui_.reset(pending_web_ui_.release());
764 else if (!pending_and_current_web_ui_.get())
765 web_ui_.reset();
766
767 // It's possible for the pending_render_view_host_ to be NULL when we aren't
768 // crossing process boundaries. If so, we just needed to handle the Web UI
769 // committing above and we're done.
770 if (!pending_render_view_host_) {
771 if (will_focus_location_bar)
772 delegate_->SetFocusToLocationBar(false);
773 return;
774 }
775
776 // Remember if the page was focused so we can focus the new renderer in
777 // that case.
778 bool focus_render_view = !will_focus_location_bar &&
779 render_view_host_->GetView() && render_view_host_->GetView()->HasFocus();
780
781 // Swap in the pending view and make it active. Also ensure the FrameTree
782 // stays in sync.
783 RenderViewHostImpl* old_render_view_host = render_view_host_;
784 render_view_host_ = pending_render_view_host_;
785 pending_render_view_host_ = NULL;
786 render_view_host_->AttachToFrameTree();
787
788 // The process will no longer try to exit, so we can decrement the count.
789 render_view_host_->GetProcess()->RemovePendingView();
790
791 // If the view is gone, then this RenderViewHost died while it was hidden.
792 // We ignored the RenderProcessGone call at the time, so we should send it now
793 // to make sure the sad tab shows up, etc.
794 if (!render_view_host_->GetView())
795 delegate_->RenderProcessGoneFromRenderManager(render_view_host_);
796 else if (!delegate_->IsHidden())
797 render_view_host_->GetView()->Show();
798
799 // Hide the old view now that the new one is visible.
800 if (old_render_view_host->GetView()) {
801 old_render_view_host->GetView()->Hide();
802 old_render_view_host->WasSwappedOut();
803 }
804
805 // Make sure the size is up to date. (Fix for bug 1079768.)
806 delegate_->UpdateRenderViewSizeForRenderManager();
807
808 if (will_focus_location_bar)
809 delegate_->SetFocusToLocationBar(false);
810 else if (focus_render_view && render_view_host_->GetView())
811 RenderWidgetHostViewPort::FromRWHV(render_view_host_->GetView())->Focus();
812
813 // Notify that we've swapped RenderViewHosts. We do this
814 // before shutting down the RVH so that we can clean up
815 // RendererResources related to the RVH first.
816 delegate_->NotifySwappedFromRenderManager(old_render_view_host,
817 render_view_host_);
818
819 // If the pending view was on the swapped out list, we can remove it.
820 swapped_out_hosts_.erase(render_view_host_->GetSiteInstance()->GetId());
821
822 // If there are no active RVHs in this SiteInstance, it means that
823 // this RVH was the last active one in the SiteInstance. Now that we
824 // know that all RVHs are swapped out, we can delete all the RVHs in
825 // this SiteInstance.
826 if (!static_cast<SiteInstanceImpl*>(old_render_view_host->GetSiteInstance())->
827 active_view_count()) {
828 ShutdownRenderViewHostsInSiteInstance(
829 old_render_view_host->GetSiteInstance()->GetId());
830 // This is deleted while cleaning up the SitaInstance's views.
831 old_render_view_host = NULL;
832 } else if (old_render_view_host->IsRenderViewLive()) {
833 // If the old RVH is live, we are swapping it out and should keep track of
834 // it in case we navigate back to it.
835 DCHECK(old_render_view_host->is_swapped_out());
836 // Temp fix for http://crbug.com/90867 until we do a better cleanup to make
837 // sure we don't get different rvh instances for the same site instance
838 // in the same rvhmgr.
839 // TODO(creis): Clean this up.
840 int32 old_site_instance_id =
841 old_render_view_host->GetSiteInstance()->GetId();
842 RenderViewHostMap::iterator iter =
843 swapped_out_hosts_.find(old_site_instance_id);
844 if (iter != swapped_out_hosts_.end() &&
845 iter->second != old_render_view_host) {
846 // Shutdown the RVH that will be replaced in the map to avoid a leak.
847 iter->second->Shutdown();
848 }
849 swapped_out_hosts_[old_site_instance_id] = old_render_view_host;
850 } else {
851 old_render_view_host->Shutdown();
852 old_render_view_host = NULL; // Shutdown() deletes it.
853 }
854 }
855
856 void RenderViewHostManager::ShutdownRenderViewHostsInSiteInstance(
857 int32 site_instance_id) {
858 // First remove any swapped out RVH for this SiteInstance from our
859 // list.
860 swapped_out_hosts_.erase(site_instance_id);
861
862 scoped_ptr<RenderWidgetHostIterator> widgets(
863 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
864 while (RenderWidgetHost* widget = widgets->GetNextHost()) {
865 if (!widget->IsRenderView())
866 continue;
867 RenderViewHostImpl* rvh =
868 static_cast<RenderViewHostImpl*>(RenderViewHost::From(widget));
869 if (site_instance_id == rvh->GetSiteInstance()->GetId())
870 rvh->Shutdown();
871 }
872 }
873
874 RenderViewHostImpl* RenderViewHostManager::UpdateRendererStateForNavigate(
875 const NavigationEntryImpl& entry) {
876 // If we are cross-navigating, then we want to get back to normal and navigate
877 // as usual.
878 if (cross_navigation_pending_) {
879 if (pending_render_view_host_)
880 CancelPending();
881 cross_navigation_pending_ = false;
882 }
883
884 // render_view_host_ will not be deleted before the end of this method, so we
885 // don't have to worry about this SiteInstance's ref count dropping to zero.
886 SiteInstance* curr_instance = render_view_host_->GetSiteInstance();
887
888 // Determine if we need a new SiteInstance for this entry.
889 // Again, new_instance won't be deleted before the end of this method, so it
890 // is safe to use a normal pointer here.
891 SiteInstance* new_instance = curr_instance;
892 const NavigationEntry* curr_entry =
893 delegate_->GetLastCommittedNavigationEntryForRenderManager();
894 bool is_guest_scheme = curr_instance->GetSiteURL().SchemeIs(kGuestScheme);
895 bool force_swap = ShouldSwapProcessesForNavigation(curr_entry, &entry);
896 if (!is_guest_scheme && (ShouldTransitionCrossSite() || force_swap))
897 new_instance = GetSiteInstanceForEntry(entry, curr_instance);
898
899 if (!is_guest_scheme && (new_instance != curr_instance || force_swap)) {
900 // New SiteInstance.
901 DCHECK(!cross_navigation_pending_);
902
903 // This will possibly create (set to NULL) a Web UI object for the pending
904 // page. We'll use this later to give the page special access. This must
905 // happen before the new renderer is created below so it will get bindings.
906 // It must also happen after the above conditional call to CancelPending(),
907 // otherwise CancelPending may clear the pending_web_ui_ and the page will
908 // not have its bindings set appropriately.
909 SetPendingWebUI(entry);
910
911 // Ensure that we have created RVHs for the new RVH's opener chain if
912 // we are staying in the same BrowsingInstance. This allows the pending RVH
913 // to send cross-process script calls to its opener(s).
914 int opener_route_id = MSG_ROUTING_NONE;
915 if (new_instance->IsRelatedSiteInstance(curr_instance)) {
916 opener_route_id =
917 delegate_->CreateOpenerRenderViewsForRenderManager(new_instance);
918 }
919
920 // Create a non-swapped-out pending RVH with the given opener and navigate
921 // it.
922 int route_id = CreateRenderView(new_instance, opener_route_id, false,
923 delegate_->IsHidden());
924 if (route_id == MSG_ROUTING_NONE)
925 return NULL;
926
927 // Check if our current RVH is live before we set up a transition.
928 if (!render_view_host_->IsRenderViewLive()) {
929 if (!cross_navigation_pending_) {
930 // The current RVH is not live. There's no reason to sit around with a
931 // sad tab or a newly created RVH while we wait for the pending RVH to
932 // navigate. Just switch to the pending RVH now and go back to non
933 // cross-navigating (Note that we don't care about on{before}unload
934 // handlers if the current RVH isn't live.)
935 CommitPending();
936 return render_view_host_;
937 } else {
938 NOTREACHED();
939 return render_view_host_;
940 }
941 }
942 // Otherwise, it's safe to treat this as a pending cross-site transition.
943
944 // We need to wait until the beforeunload handler has run, unless we are
945 // transferring an existing request (in which case it has already run).
946 // Suspend the new render view (i.e., don't let it send the cross-site
947 // Navigate message) until we hear back from the old renderer's
948 // beforeunload handler. If the handler returns false, we'll have to
949 // cancel the request.
950 DCHECK(!pending_render_view_host_->are_navigations_suspended());
951 bool is_transfer =
952 entry.transferred_global_request_id() != GlobalRequestID();
953 if (is_transfer) {
954 // We don't need to stop the old renderer or run beforeunload/unload
955 // handlers, because those have already been done.
956 DCHECK(pending_nav_params_->global_request_id ==
957 entry.transferred_global_request_id());
958 } else {
959 // Also make sure the old render view stops, in case a load is in
960 // progress. (We don't want to do this for transfers, since it will
961 // interrupt the transfer with an unexpected DidStopLoading.)
962 render_view_host_->Send(
963 new ViewMsg_Stop(render_view_host_->GetRoutingID()));
964
965 pending_render_view_host_->SetNavigationsSuspended(true,
966 base::TimeTicks());
967
968 // Tell the CrossSiteRequestManager that this RVH has a pending cross-site
969 // request, so that ResourceDispatcherHost will know to tell us to run the
970 // old page's unload handler before it sends the response.
971 pending_render_view_host_->SetHasPendingCrossSiteRequest(true);
972 }
973
974 // We now have a pending RVH.
975 DCHECK(!cross_navigation_pending_);
976 cross_navigation_pending_ = true;
977
978 // Unless we are transferring an existing request, we should now
979 // tell the old render view to run its beforeunload handler, since it
980 // doesn't otherwise know that the cross-site request is happening. This
981 // will trigger a call to ShouldClosePage with the reply.
982 if (!is_transfer)
983 render_view_host_->FirePageBeforeUnload(true);
984
985 return pending_render_view_host_;
986 } else {
987 if (ShouldReuseWebUI(curr_entry, &entry)) {
988 pending_web_ui_.reset();
989 pending_and_current_web_ui_ = web_ui_->AsWeakPtr();
990 } else {
991 SetPendingWebUI(entry);
992
993 // Make sure the new RenderViewHost has the right bindings.
994 if (pending_web_ui() && !render_view_host_->GetProcess()->IsGuest())
995 render_view_host_->AllowBindings(pending_web_ui()->GetBindings());
996 }
997
998 if (pending_web_ui() && render_view_host_->IsRenderViewLive())
999 pending_web_ui()->GetController()->RenderViewReused(render_view_host_);
1000
1001 // The renderer can exit view source mode when any error or cancellation
1002 // happen. We must overwrite to recover the mode.
1003 if (entry.IsViewSourceMode()) {
1004 render_view_host_->Send(
1005 new ViewMsg_EnableViewSourceMode(render_view_host_->GetRoutingID()));
1006 }
1007 }
1008
1009 // Same SiteInstance can be used. Navigate render_view_host_ if we are not
1010 // cross navigating.
1011 DCHECK(!cross_navigation_pending_);
1012 return render_view_host_;
1013 }
1014
1015 void RenderViewHostManager::CancelPending() {
1016 RenderViewHostImpl* pending_render_view_host = pending_render_view_host_;
1017 pending_render_view_host_ = NULL;
1018
1019 RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
1020 pending_render_view_host,
1021 render_view_host_);
1022
1023 // We no longer need to prevent the process from exiting.
1024 pending_render_view_host->GetProcess()->RemovePendingView();
1025
1026 // The pending RVH may already be on the swapped out list if we started to
1027 // swap it back in and then canceled. If so, make sure it gets swapped out
1028 // again. If it's not on the swapped out list (e.g., aborting a pending
1029 // load), then it's safe to shut down.
1030 if (IsOnSwappedOutList(pending_render_view_host)) {
1031 // Any currently suspended navigations are no longer needed.
1032 pending_render_view_host->CancelSuspendedNavigations();
1033
1034 pending_render_view_host->SwapOut();
1035 } else {
1036 // We won't be coming back, so shut this one down.
1037 pending_render_view_host->Shutdown();
1038 }
1039
1040 pending_web_ui_.reset();
1041 pending_and_current_web_ui_.reset();
1042 }
1043
1044 void RenderViewHostManager::RenderViewDeleted(RenderViewHost* rvh) {
1045 // We are doing this in order to work around and to track a crasher
1046 // (http://crbug.com/23411) where it seems that pending_render_view_host_ is
1047 // deleted (not sure from where) but not NULLed.
1048 if (rvh == pending_render_view_host_) {
1049 // If you hit this NOTREACHED, please report it in the following bug
1050 // http://crbug.com/23411 Make sure to include what you were doing when it
1051 // happened (navigating to a new page, closing a tab...) and if you can
1052 // reproduce.
1053 NOTREACHED();
1054 pending_render_view_host_ = NULL;
1055 }
1056
1057 // Make sure deleted RVHs are not kept in the swapped out list while we are
1058 // still alive. (If render_view_host_ is null, we're already being deleted.)
1059 if (!render_view_host_)
1060 return;
1061 // We can't look it up by SiteInstance ID, which may no longer be valid.
1062 for (RenderViewHostMap::iterator iter = swapped_out_hosts_.begin();
1063 iter != swapped_out_hosts_.end();
1064 ++iter) {
1065 if (iter->second == rvh) {
1066 swapped_out_hosts_.erase(iter);
1067 break;
1068 }
1069 }
1070 }
1071
1072 bool RenderViewHostManager::IsOnSwappedOutList(RenderViewHost* rvh) const {
1073 if (!rvh->GetSiteInstance())
1074 return false;
1075
1076 RenderViewHostMap::const_iterator iter = swapped_out_hosts_.find(
1077 rvh->GetSiteInstance()->GetId());
1078 if (iter == swapped_out_hosts_.end())
1079 return false;
1080
1081 return iter->second == rvh;
1082 }
1083
1084 RenderViewHostImpl* RenderViewHostManager::GetSwappedOutRenderViewHost(
1085 SiteInstance* instance) {
1086 RenderViewHostMap::iterator iter = swapped_out_hosts_.find(instance->GetId());
1087 if (iter != swapped_out_hosts_.end())
1088 return iter->second;
1089
1090 return NULL;
1091 }
1092
1093 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698