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 "chrome/browser/prerender/prerender_contents.h" 6 7 #include <algorithm> 8 #include <functional> 9 #include <utility> 10 11 #include "base/strings/utf_string_conversions.h" 12 #include "chrome/browser/chrome_notification_types.h" 13 #include "chrome/browser/favicon/favicon_tab_helper.h" 14 #include "chrome/browser/history/history_tab_helper.h" 15 #include "chrome/browser/history/history_types.h" 16 #include "chrome/browser/prerender/prerender_field_trial.h" 17 #include "chrome/browser/prerender/prerender_final_status.h" 18 #include "chrome/browser/prerender/prerender_handle.h" 19 #include "chrome/browser/prerender/prerender_manager.h" 20 #include "chrome/browser/prerender/prerender_render_view_host_observer.h" 21 #include "chrome/browser/prerender/prerender_tracker.h" 22 #include "chrome/browser/profiles/profile.h" 23 #include "chrome/browser/ui/browser.h" 24 #include "chrome/browser/ui/browser_tab_contents.h" 25 #include "chrome/common/prerender_messages.h" 26 #include "chrome/common/url_constants.h" 27 #include "content/public/browser/browser_child_process_host.h" 28 #include "content/public/browser/notification_service.h" 29 #include "content/public/browser/render_process_host.h" 30 #include "content/public/browser/render_view_host.h" 31 #include "content/public/browser/resource_request_details.h" 32 #include "content/public/browser/session_storage_namespace.h" 33 #include "content/public/browser/web_contents.h" 34 #include "content/public/browser/web_contents_delegate.h" 35 #include "content/public/browser/web_contents_view.h" 36 #include "content/public/common/favicon_url.h" 37 #include "ui/gfx/rect.h" 38 39 using content::DownloadItem; 40 using content::OpenURLParams; 41 using content::RenderViewHost; 42 using content::ResourceRedirectDetails; 43 using content::SessionStorageNamespace; 44 using content::WebContents; 45 46 namespace prerender { 47 48 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory { 49 public: 50 virtual PrerenderContents* CreatePrerenderContents( 51 PrerenderManager* prerender_manager, Profile* profile, 52 const GURL& url, const content::Referrer& referrer, 53 Origin origin, uint8 experiment_id) OVERRIDE { 54 return new PrerenderContents(prerender_manager, profile, 55 url, referrer, origin, experiment_id); 56 } 57 }; 58 59 // WebContentsDelegateImpl ----------------------------------------------------- 60 61 class PrerenderContents::WebContentsDelegateImpl 62 : public content::WebContentsDelegate { 63 public: 64 explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents) 65 : prerender_contents_(prerender_contents) { 66 } 67 68 // content::WebContentsDelegate implementation: 69 virtual WebContents* OpenURLFromTab(WebContents* source, 70 const OpenURLParams& params) OVERRIDE { 71 // |OpenURLFromTab| is typically called when a frame performs a navigation 72 // that requires the browser to perform the transition instead of WebKit. 73 // Examples include prerendering a site that redirects to an app URL, 74 // or if --enable-strict-site-isolation is specified and the prerendered 75 // frame redirects to a different origin. 76 // TODO(cbentzel): Consider supporting this if it is a common case during 77 // prerenders. 78 prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL); 79 return NULL; 80 } 81 82 virtual void CanDownload( 83 RenderViewHost* render_view_host, 84 int request_id, 85 const std::string& request_method, 86 const base::Callback<void(bool)>& callback) OVERRIDE { 87 prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD); 88 // Cancel the download. 89 callback.Run(false); 90 } 91 92 virtual bool ShouldCreateWebContents( 93 WebContents* web_contents, 94 int route_id, 95 WindowContainerType window_container_type, 96 const string16& frame_name, 97 const GURL& target_url, 98 const content::Referrer& referrer, 99 WindowOpenDisposition disposition, 100 const WebKit::WebWindowFeatures& features, 101 bool user_gesture, 102 bool opener_suppressed) OVERRIDE { 103 // Since we don't want to permit child windows that would have a 104 // window.opener property, terminate prerendering. 105 prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW); 106 // Cancel the popup. 107 return false; 108 } 109 110 virtual bool OnGoToEntryOffset(int offset) OVERRIDE { 111 // This isn't allowed because the history merge operation 112 // does not work if there are renderer issued challenges. 113 // TODO(cbentzel): Cancel in this case? May not need to do 114 // since render-issued offset navigations are not guaranteed, 115 // but indicates that the page cares about the history. 116 return false; 117 } 118 119 virtual void JSOutOfMemory(WebContents* tab) OVERRIDE { 120 prerender_contents_->Destroy(FINAL_STATUS_JS_OUT_OF_MEMORY); 121 } 122 123 virtual bool ShouldSuppressDialogs() OVERRIDE { 124 // We still want to show the user the message when they navigate to this 125 // page, so cancel this prerender. 126 prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT); 127 // Always suppress JavaScript messages if they're triggered by a page being 128 // prerendered. 129 return true; 130 } 131 132 virtual void RegisterProtocolHandler(WebContents* web_contents, 133 const std::string& protocol, 134 const GURL& url, 135 const string16& title, 136 bool user_gesture) OVERRIDE { 137 // TODO(mmenke): Consider supporting this if it is a common case during 138 // prerenders. 139 prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER); 140 } 141 142 private: 143 PrerenderContents* prerender_contents_; 144 }; 145 146 void PrerenderContents::Observer::OnPrerenderStopLoading( 147 PrerenderContents* contents) { 148 } 149 150 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement( 151 PrerenderContents* contents, PrerenderContents* replacement) { 152 } 153 154 PrerenderContents::Observer::Observer() { 155 } 156 157 PrerenderContents::Observer::~Observer() { 158 } 159 160 PrerenderContents::PendingPrerenderInfo::PendingPrerenderInfo( 161 base::WeakPtr<PrerenderHandle> weak_prerender_handle, 162 Origin origin, 163 const GURL& url, 164 const content::Referrer& referrer, 165 const gfx::Size& size) 166 : weak_prerender_handle(weak_prerender_handle), 167 origin(origin), 168 url(url), 169 referrer(referrer), 170 size(size) { 171 } 172 173 PrerenderContents::PendingPrerenderInfo::~PendingPrerenderInfo() { 174 } 175 176 void PrerenderContents::AddPendingPrerender( 177 scoped_ptr<PendingPrerenderInfo> pending_prerender_info) { 178 pending_prerenders_.push_back(pending_prerender_info.release()); 179 } 180 181 void PrerenderContents::PrepareForUse() { 182 NotifyPrerenderStop(); 183 184 SessionStorageNamespace* session_storage_namespace = NULL; 185 if (prerender_contents_) { 186 // TODO(ajwong): This does not correctly handle storage for isolated apps. 187 session_storage_namespace = prerender_contents_-> 188 GetController().GetDefaultSessionStorageNamespace(); 189 } 190 prerender_manager_->StartPendingPrerenders( 191 child_id_, &pending_prerenders_, session_storage_namespace); 192 pending_prerenders_.clear(); 193 } 194 195 PrerenderContents::PrerenderContents( 196 PrerenderManager* prerender_manager, 197 Profile* profile, 198 const GURL& url, 199 const content::Referrer& referrer, 200 Origin origin, 201 uint8 experiment_id) 202 : prerendering_has_started_(false), 203 prerender_manager_(prerender_manager), 204 prerender_url_(url), 205 referrer_(referrer), 206 profile_(profile), 207 page_id_(0), 208 session_storage_namespace_id_(-1), 209 has_stopped_loading_(false), 210 has_finished_loading_(false), 211 final_status_(FINAL_STATUS_MAX), 212 match_complete_status_(MATCH_COMPLETE_DEFAULT), 213 prerendering_has_been_cancelled_(false), 214 child_id_(-1), 215 route_id_(-1), 216 origin_(origin), 217 experiment_id_(experiment_id), 218 creator_child_id_(-1) { 219 DCHECK(prerender_manager != NULL); 220 } 221 222 PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() { 223 PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents( 224 prerender_url(), referrer(), origin(), experiment_id()); 225 226 new_contents->load_start_time_ = load_start_time_; 227 new_contents->session_storage_namespace_id_ = session_storage_namespace_id_; 228 new_contents->set_match_complete_status( 229 PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING); 230 231 const bool did_init = new_contents->Init(); 232 DCHECK(did_init); 233 DCHECK_EQ(alias_urls_.front(), new_contents->alias_urls_.front()); 234 DCHECK_EQ(1u, new_contents->alias_urls_.size()); 235 new_contents->alias_urls_ = alias_urls_; 236 new_contents->set_match_complete_status( 237 PrerenderContents::MATCH_COMPLETE_REPLACEMENT); 238 NotifyPrerenderCreatedMatchCompleteReplacement(new_contents); 239 return new_contents; 240 } 241 242 bool PrerenderContents::Init() { 243 return AddAliasURL(prerender_url_); 244 } 245 246 // static 247 PrerenderContents::Factory* PrerenderContents::CreateFactory() { 248 return new PrerenderContentsFactoryImpl(); 249 } 250 251 void PrerenderContents::StartPrerendering( 252 int creator_child_id, 253 const gfx::Size& size, 254 SessionStorageNamespace* session_storage_namespace) { 255 DCHECK(profile_ != NULL); 256 DCHECK(!size.IsEmpty()); 257 DCHECK(!prerendering_has_started_); 258 DCHECK(prerender_contents_.get() == NULL); 259 DCHECK_EQ(-1, creator_child_id_); 260 DCHECK(size_.IsEmpty()); 261 DCHECK_EQ(1U, alias_urls_.size()); 262 263 creator_child_id_ = creator_child_id; 264 session_storage_namespace_id_ = session_storage_namespace->id(); 265 size_ = size; 266 267 DCHECK(load_start_time_.is_null()); 268 load_start_time_ = base::TimeTicks::Now(); 269 270 // Everything after this point sets up the WebContents object and associated 271 // RenderView for the prerender page. Don't do this for members of the 272 // control group. 273 if (prerender_manager_->IsControlGroup(experiment_id())) 274 return; 275 276 if (origin_ == ORIGIN_LOCAL_PREDICTOR && 277 IsLocalPredictorPrerenderAlwaysControlEnabled()) { 278 return; 279 } 280 281 prerendering_has_started_ = true; 282 283 prerender_contents_.reset(CreateWebContents(session_storage_namespace)); 284 BrowserTabContents::AttachTabHelpers(prerender_contents_.get()); 285 #if defined(OS_ANDROID) 286 // Delay icon fetching until the contents are getting swapped in 287 // to conserve network usage in mobile devices. 288 FaviconTabHelper::FromWebContents( 289 prerender_contents_.get())->set_should_fetch_icons(false); 290 #endif // defined(OS_ANDROID) 291 content::WebContentsObserver::Observe(prerender_contents_.get()); 292 293 web_contents_delegate_.reset(new WebContentsDelegateImpl(this)); 294 prerender_contents_.get()->SetDelegate(web_contents_delegate_.get()); 295 // Set the size of the prerender WebContents. 296 prerender_contents_->GetView()->SizeContents(size_); 297 298 // Register as an observer of the RenderViewHost so we get messages. 299 render_view_host_observer_.reset( 300 new PrerenderRenderViewHostObserver(this, GetRenderViewHostMutable())); 301 302 child_id_ = GetRenderViewHost()->GetProcess()->GetID(); 303 route_id_ = GetRenderViewHost()->GetRoutingID(); 304 305 // Register this with the ResourceDispatcherHost as a prerender 306 // RenderViewHost. This must be done before the Navigate message to catch all 307 // resource requests, but as it is on the same thread as the Navigate message 308 // (IO) there is no race condition. 309 AddObserver(prerender_manager()->prerender_tracker()); 310 NotifyPrerenderStart(); 311 312 // Close ourselves when the application is shutting down. 313 notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, 314 content::NotificationService::AllSources()); 315 316 // Register for our parent profile to shutdown, so we can shut ourselves down 317 // as well (should only be called for OTR profiles, as we should receive 318 // APP_TERMINATING before non-OTR profiles are destroyed). 319 // TODO(tburkard): figure out if this is needed. 320 notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, 321 content::Source<Profile>(profile_)); 322 323 // Register to inform new RenderViews that we're prerendering. 324 notification_registrar_.Add( 325 this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, 326 content::Source<WebContents>(prerender_contents_.get())); 327 328 // Register for redirect notifications sourced from |this|. 329 notification_registrar_.Add( 330 this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, 331 content::Source<WebContents>(prerender_contents_.get())); 332 333 // Transfer over the user agent override. 334 prerender_contents_.get()->SetUserAgentOverride( 335 prerender_manager_->config().user_agent_override); 336 337 content::NavigationController::LoadURLParams load_url_params( 338 prerender_url_); 339 load_url_params.referrer = referrer_; 340 load_url_params.transition_type = (origin_ == ORIGIN_OMNIBOX ? 341 content::PAGE_TRANSITION_TYPED : content::PAGE_TRANSITION_LINK); 342 load_url_params.override_user_agent = 343 prerender_manager_->config().is_overriding_user_agent ? 344 content::NavigationController::UA_OVERRIDE_TRUE : 345 content::NavigationController::UA_OVERRIDE_FALSE; 346 prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params); 347 } 348 349 bool PrerenderContents::GetChildId(int* child_id) const { 350 CHECK(child_id); 351 DCHECK_GE(child_id_, -1); 352 *child_id = child_id_; 353 return child_id_ != -1; 354 } 355 356 bool PrerenderContents::GetRouteId(int* route_id) const { 357 CHECK(route_id); 358 DCHECK_GE(route_id_, -1); 359 *route_id = route_id_; 360 return route_id_ != -1; 361 } 362 363 void PrerenderContents::SetFinalStatus(FinalStatus final_status) { 364 DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX); 365 DCHECK(final_status_ == FINAL_STATUS_MAX); 366 367 final_status_ = final_status; 368 } 369 370 PrerenderContents::~PrerenderContents() { 371 DCHECK_NE(FINAL_STATUS_MAX, final_status()); 372 DCHECK( 373 prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED); 374 DCHECK_NE(ORIGIN_MAX, origin()); 375 376 prerender_manager_->RecordFinalStatusWithMatchCompleteStatus( 377 origin(), experiment_id(), match_complete_status(), final_status()); 378 379 // Broadcast the removal of aliases. 380 for (content::RenderProcessHost::iterator host_iterator = 381 content::RenderProcessHost::AllHostsIterator(); 382 !host_iterator.IsAtEnd(); 383 host_iterator.Advance()) { 384 content::RenderProcessHost* host = host_iterator.GetCurrentValue(); 385 host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_)); 386 } 387 388 // If we still have a WebContents, clean up anything we need to and then 389 // destroy it. 390 if (prerender_contents_.get()) 391 delete ReleasePrerenderContents(); 392 } 393 394 void PrerenderContents::AddObserver(Observer* observer) { 395 DCHECK_EQ(FINAL_STATUS_MAX, final_status_); 396 observer_list_.AddObserver(observer); 397 } 398 399 void PrerenderContents::RemoveObserver(Observer* observer) { 400 observer_list_.RemoveObserver(observer); 401 } 402 403 void PrerenderContents::Observe(int type, 404 const content::NotificationSource& source, 405 const content::NotificationDetails& details) { 406 switch (type) { 407 case chrome::NOTIFICATION_PROFILE_DESTROYED: 408 Destroy(FINAL_STATUS_PROFILE_DESTROYED); 409 return; 410 411 case chrome::NOTIFICATION_APP_TERMINATING: 412 Destroy(FINAL_STATUS_APP_TERMINATING); 413 return; 414 415 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { 416 // RESOURCE_RECEIVED_REDIRECT can come for any resource on a page. 417 // If it's a redirect on the top-level resource, the name needs 418 // to be remembered for future matching, and if it redirects to 419 // an https resource, it needs to be canceled. If a subresource 420 // is redirected, nothing changes. 421 DCHECK_EQ(content::Source<WebContents>(source).ptr(), 422 prerender_contents_.get()); 423 ResourceRedirectDetails* resource_redirect_details = 424 content::Details<ResourceRedirectDetails>(details).ptr(); 425 CHECK(resource_redirect_details); 426 if (resource_redirect_details->resource_type == 427 ResourceType::MAIN_FRAME) { 428 if (!AddAliasURL(resource_redirect_details->new_url)) 429 return; 430 } 431 break; 432 } 433 434 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { 435 if (prerender_contents_.get()) { 436 DCHECK_EQ(content::Source<WebContents>(source).ptr(), 437 prerender_contents_.get()); 438 439 content::Details<RenderViewHost> new_render_view_host(details); 440 OnRenderViewHostCreated(new_render_view_host.ptr()); 441 442 // When a new RenderView is created for a prerendering WebContents, 443 // tell the new RenderView it's being used for prerendering before any 444 // navigations occur. Note that this is always triggered before the 445 // first navigation, so there's no need to send the message just after 446 // the WebContents is created. 447 new_render_view_host->Send( 448 new PrerenderMsg_SetIsPrerendering( 449 new_render_view_host->GetRoutingID(), 450 true)); 451 452 // Make sure the size of the RenderViewHost has been passed to the new 453 // RenderView. Otherwise, the size may not be sent until the 454 // RenderViewReady event makes it from the render process to the UI 455 // thread of the browser process. When the RenderView receives its 456 // size, is also sets itself to be visible, which would then break the 457 // visibility API. 458 new_render_view_host->WasResized(); 459 prerender_contents_->WasHidden(); 460 } 461 break; 462 } 463 464 default: 465 NOTREACHED() << "Unexpected notification sent."; 466 break; 467 } 468 } 469 470 void PrerenderContents::OnRenderViewHostCreated( 471 RenderViewHost* new_render_view_host) { 472 } 473 474 size_t PrerenderContents::pending_prerender_count() const { 475 return pending_prerenders_.size(); 476 } 477 478 WebContents* PrerenderContents::CreateWebContents( 479 SessionStorageNamespace* session_storage_namespace) { 480 // TODO(ajwong): Remove the temporary map once prerendering is aware of 481 // multiple session storage namespaces per tab. 482 content::SessionStorageNamespaceMap session_storage_namespace_map; 483 session_storage_namespace_map[std::string()] = session_storage_namespace; 484 return WebContents::CreateWithSessionStorage( 485 WebContents::CreateParams(profile_), session_storage_namespace_map); 486 } 487 488 void PrerenderContents::NotifyPrerenderStart() { 489 DCHECK_EQ(FINAL_STATUS_MAX, final_status_); 490 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this)); 491 } 492 493 void PrerenderContents::NotifyPrerenderStopLoading() { 494 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this)); 495 } 496 497 void PrerenderContents::NotifyPrerenderStop() { 498 DCHECK_NE(FINAL_STATUS_MAX, final_status_); 499 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this)); 500 observer_list_.Clear(); 501 } 502 503 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement( 504 PrerenderContents* replacement) { 505 FOR_EACH_OBSERVER(Observer, observer_list_, 506 OnPrerenderCreatedMatchCompleteReplacement(this, 507 replacement)); 508 } 509 510 void PrerenderContents::DidUpdateFaviconURL( 511 int32 page_id, 512 const std::vector<content::FaviconURL>& urls) { 513 VLOG(1) << "PrerenderContents::OnUpdateFaviconURL" << icon_url_; 514 for (std::vector<content::FaviconURL>::const_iterator it = urls.begin(); 515 it != urls.end(); ++it) { 516 if (it->icon_type == content::FaviconURL::FAVICON) { 517 icon_url_ = it->icon_url; 518 VLOG(1) << icon_url_; 519 return; 520 } 521 } 522 } 523 524 bool PrerenderContents::AddAliasURL(const GURL& url) { 525 const bool http = url.SchemeIs(chrome::kHttpScheme); 526 const bool https = url.SchemeIs(chrome::kHttpsScheme); 527 if (!http && !https) { 528 DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_); 529 Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME); 530 return false; 531 } 532 if (https && !prerender_manager_->config().https_allowed) { 533 DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_); 534 Destroy(FINAL_STATUS_HTTPS); 535 return false; 536 } 537 if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING && 538 prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) { 539 Destroy(FINAL_STATUS_RECENTLY_VISITED); 540 return false; 541 } 542 543 alias_urls_.push_back(url); 544 545 for (content::RenderProcessHost::iterator host_iterator = 546 content::RenderProcessHost::AllHostsIterator(); 547 !host_iterator.IsAtEnd(); 548 host_iterator.Advance()) { 549 content::RenderProcessHost* host = host_iterator.GetCurrentValue(); 550 host->Send(new PrerenderMsg_OnPrerenderAddAlias(url)); 551 } 552 553 return true; 554 } 555 556 bool PrerenderContents::Matches( 557 const GURL& url, 558 const SessionStorageNamespace* session_storage_namespace) const { 559 if (session_storage_namespace && 560 session_storage_namespace_id_ != session_storage_namespace->id()) { 561 return false; 562 } 563 return std::count_if(alias_urls_.begin(), alias_urls_.end(), 564 std::bind2nd(std::equal_to<GURL>(), url)) != 0; 565 } 566 567 void PrerenderContents::RenderProcessGone(base::TerminationStatus status) { 568 Destroy(FINAL_STATUS_RENDERER_CRASHED); 569 } 570 571 void PrerenderContents::DidStopLoading( 572 content::RenderViewHost* render_view_host) { 573 has_stopped_loading_ = true; 574 NotifyPrerenderStopLoading(); 575 } 576 577 void PrerenderContents::DidStartProvisionalLoadForFrame( 578 int64 frame_id, 579 int64 parent_frame_id, 580 bool is_main_frame, 581 const GURL& validated_url, 582 bool is_error_page, 583 bool is_iframe_srcdoc, 584 RenderViewHost* render_view_host) { 585 if (is_main_frame) { 586 if (!AddAliasURL(validated_url)) 587 return; 588 589 // Usually, this event fires if the user clicks or enters a new URL. 590 // Neither of these can happen in the case of an invisible prerender. 591 // So the cause is: Some JavaScript caused a new URL to be loaded. In that 592 // case, the spinner would start again in the browser, so we must reset 593 // has_stopped_loading_ so that the spinner won't be stopped. 594 has_stopped_loading_ = false; 595 has_finished_loading_ = false; 596 } 597 } 598 599 void PrerenderContents::DidFinishLoad(int64 frame_id, 600 const GURL& validated_url, 601 bool is_main_frame, 602 RenderViewHost* render_view_host) { 603 if (is_main_frame) 604 has_finished_loading_ = true; 605 } 606 607 void PrerenderContents::Destroy(FinalStatus final_status) { 608 DCHECK_NE(final_status, FINAL_STATUS_USED); 609 610 if (prerendering_has_been_cancelled_) 611 return; 612 613 if (child_id_ != -1 && route_id_ != -1) { 614 // Cancel the prerender in the PrerenderTracker. This is needed 615 // because destroy may be called directly from the UI thread without calling 616 // TryCancel(). This is difficult to completely avoid, since prerendering 617 // can be cancelled before a RenderView is created. 618 bool is_cancelled = prerender_manager()->prerender_tracker()->TryCancel( 619 child_id_, route_id_, final_status); 620 CHECK(is_cancelled); 621 622 // A different final status may have been set already from another thread. 623 // If so, use it instead. 624 if (!prerender_manager()->prerender_tracker()-> 625 GetFinalStatus(child_id_, route_id_, &final_status)) { 626 NOTREACHED(); 627 } 628 } 629 SetFinalStatus(final_status); 630 631 prerendering_has_been_cancelled_ = true; 632 prerender_manager_->AddToHistory(this); 633 prerender_manager_->MoveEntryToPendingDelete(this, final_status); 634 635 if (!prerender_manager_->IsControlGroup(experiment_id()) && 636 (prerendering_has_started() || 637 match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) { 638 NotifyPrerenderStop(); 639 } 640 641 // We may destroy the PrerenderContents before we have initialized the 642 // RenderViewHost. Otherwise set the Observer's PrerenderContents to NULL to 643 // avoid any more messages being sent. 644 if (render_view_host_observer_) 645 render_view_host_observer_->set_prerender_contents(NULL); 646 } 647 648 base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() { 649 if (process_metrics_.get() == NULL) { 650 // If a PrenderContents hasn't started prerending, don't be fully formed. 651 if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess()) 652 return NULL; 653 base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle(); 654 if (handle == base::kNullProcessHandle) 655 return NULL; 656 #if !defined(OS_MACOSX) 657 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle)); 658 #else 659 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics( 660 handle, 661 content::BrowserChildProcessHost::GetPortProvider())); 662 #endif 663 } 664 665 return process_metrics_.get(); 666 } 667 668 void PrerenderContents::DestroyWhenUsingTooManyResources() { 669 base::ProcessMetrics* metrics = MaybeGetProcessMetrics(); 670 if (metrics == NULL) 671 return; 672 673 size_t private_bytes, shared_bytes; 674 if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) && 675 private_bytes > prerender_manager_->config().max_bytes) { 676 Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED); 677 } 678 } 679 680 WebContents* PrerenderContents::ReleasePrerenderContents() { 681 prerender_contents_->SetDelegate(NULL); 682 render_view_host_observer_.reset(); 683 content::WebContentsObserver::Observe(NULL); 684 return prerender_contents_.release(); 685 } 686 687 RenderViewHost* PrerenderContents::GetRenderViewHostMutable() { 688 return const_cast<RenderViewHost*>(GetRenderViewHost()); 689 } 690 691 const RenderViewHost* PrerenderContents::GetRenderViewHost() const { 692 if (!prerender_contents_.get()) 693 return NULL; 694 return prerender_contents_->GetRenderViewHost(); 695 } 696 697 void PrerenderContents::DidNavigate( 698 const history::HistoryAddPageArgs& add_page_args) { 699 add_page_vector_.push_back(add_page_args); 700 } 701 702 void PrerenderContents::CommitHistory(WebContents* tab) { 703 HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab); 704 for (size_t i = 0; i < add_page_vector_.size(); ++i) 705 history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]); 706 } 707 708 Value* PrerenderContents::GetAsValue() const { 709 if (!prerender_contents_.get()) 710 return NULL; 711 DictionaryValue* dict_value = new DictionaryValue(); 712 dict_value->SetString("url", prerender_url_.spec()); 713 base::TimeTicks current_time = base::TimeTicks::Now(); 714 base::TimeDelta duration = current_time - load_start_time_; 715 dict_value->SetInteger("duration", duration.InSeconds()); 716 dict_value->SetBoolean("is_loaded", prerender_contents_ && 717 !prerender_contents_->IsLoading()); 718 return dict_value; 719 } 720 721 bool PrerenderContents::IsCrossSiteNavigationPending() const { 722 if (!prerender_contents_) 723 return false; 724 return (prerender_contents_->GetSiteInstance() != 725 prerender_contents_->GetPendingSiteInstance()); 726 } 727 728 729 } // namespace prerender 730