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/bind.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/chrome_notification_types.h" 14 #include "chrome/browser/history/history_tab_helper.h" 15 #include "chrome/browser/prerender/prerender_field_trial.h" 16 #include "chrome/browser/prerender/prerender_final_status.h" 17 #include "chrome/browser/prerender/prerender_handle.h" 18 #include "chrome/browser/prerender/prerender_manager.h" 19 #include "chrome/browser/prerender/prerender_manager_factory.h" 20 #include "chrome/browser/prerender/prerender_resource_throttle.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/tab_helpers.h" 25 #include "chrome/browser/ui/web_contents_sizer.h" 26 #include "chrome/common/prerender_messages.h" 27 #include "chrome/common/render_messages.h" 28 #include "chrome/common/url_constants.h" 29 #include "components/history/core/browser/history_types.h" 30 #include "content/public/browser/browser_child_process_host.h" 31 #include "content/public/browser/browser_thread.h" 32 #include "content/public/browser/notification_service.h" 33 #include "content/public/browser/render_frame_host.h" 34 #include "content/public/browser/render_process_host.h" 35 #include "content/public/browser/render_view_host.h" 36 #include "content/public/browser/resource_request_details.h" 37 #include "content/public/browser/session_storage_namespace.h" 38 #include "content/public/browser/web_contents.h" 39 #include "content/public/browser/web_contents_delegate.h" 40 #include "content/public/common/frame_navigate_params.h" 41 #include "net/url_request/url_request_context_getter.h" 42 #include "ui/base/page_transition_types.h" 43 #include "ui/gfx/rect.h" 44 45 using content::BrowserThread; 46 using content::DownloadItem; 47 using content::OpenURLParams; 48 using content::RenderViewHost; 49 using content::ResourceRedirectDetails; 50 using content::ResourceType; 51 using content::SessionStorageNamespace; 52 using content::WebContents; 53 54 namespace prerender { 55 56 namespace { 57 58 // Internal cookie event. 59 // Whenever a prerender interacts with the cookie store, either sending 60 // existing cookies that existed before the prerender started, or when a cookie 61 // is changed, we record these events for histogramming purposes. 62 enum InternalCookieEvent { 63 INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND = 0, 64 INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE = 1, 65 INTERNAL_COOKIE_EVENT_OTHER_SEND = 2, 66 INTERNAL_COOKIE_EVENT_OTHER_CHANGE = 3, 67 INTERNAL_COOKIE_EVENT_MAX 68 }; 69 70 // Indicates whether existing cookies were sent, and if they were third party 71 // cookies, and whether they were for blocking resources. 72 // Each value may be inclusive of previous values. We only care about the 73 // value with the highest index that has ever occurred in the course of a 74 // prerender. 75 enum CookieSendType { 76 COOKIE_SEND_TYPE_NONE = 0, 77 COOKIE_SEND_TYPE_FIRST_PARTY = 1, 78 COOKIE_SEND_TYPE_THIRD_PARTY = 2, 79 COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE = 3, 80 COOKIE_SEND_TYPE_MAX 81 }; 82 83 void ResumeThrottles( 84 std::vector<base::WeakPtr<PrerenderResourceThrottle> > throttles) { 85 for (size_t i = 0; i < throttles.size(); i++) { 86 if (throttles[i]) 87 throttles[i]->Resume(); 88 } 89 } 90 91 } // namespace 92 93 // static 94 const int PrerenderContents::kNumCookieStatuses = 95 (1 << INTERNAL_COOKIE_EVENT_MAX); 96 97 // static 98 const int PrerenderContents::kNumCookieSendTypes = COOKIE_SEND_TYPE_MAX; 99 100 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory { 101 public: 102 virtual PrerenderContents* CreatePrerenderContents( 103 PrerenderManager* prerender_manager, Profile* profile, 104 const GURL& url, const content::Referrer& referrer, 105 Origin origin, uint8 experiment_id) OVERRIDE { 106 return new PrerenderContents(prerender_manager, profile, 107 url, referrer, origin, experiment_id); 108 } 109 }; 110 111 // WebContentsDelegateImpl ----------------------------------------------------- 112 113 class PrerenderContents::WebContentsDelegateImpl 114 : public content::WebContentsDelegate { 115 public: 116 explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents) 117 : prerender_contents_(prerender_contents) { 118 } 119 120 // content::WebContentsDelegate implementation: 121 virtual WebContents* OpenURLFromTab(WebContents* source, 122 const OpenURLParams& params) OVERRIDE { 123 // |OpenURLFromTab| is typically called when a frame performs a navigation 124 // that requires the browser to perform the transition instead of WebKit. 125 // Examples include prerendering a site that redirects to an app URL, 126 // or if --enable-strict-site-isolation is specified and the prerendered 127 // frame redirects to a different origin. 128 // TODO(cbentzel): Consider supporting this if it is a common case during 129 // prerenders. 130 prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL); 131 return NULL; 132 } 133 134 virtual void CloseContents(content::WebContents* contents) OVERRIDE { 135 prerender_contents_->Destroy(FINAL_STATUS_CLOSED); 136 } 137 138 virtual void CanDownload( 139 RenderViewHost* render_view_host, 140 const GURL& url, 141 const std::string& request_method, 142 const base::Callback<void(bool)>& callback) OVERRIDE { 143 prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD); 144 // Cancel the download. 145 callback.Run(false); 146 } 147 148 virtual bool ShouldCreateWebContents( 149 WebContents* web_contents, 150 int route_id, 151 WindowContainerType window_container_type, 152 const base::string16& frame_name, 153 const GURL& target_url, 154 const std::string& partition_id, 155 SessionStorageNamespace* session_storage_namespace) OVERRIDE { 156 // Since we don't want to permit child windows that would have a 157 // window.opener property, terminate prerendering. 158 prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW); 159 // Cancel the popup. 160 return false; 161 } 162 163 virtual bool OnGoToEntryOffset(int offset) OVERRIDE { 164 // This isn't allowed because the history merge operation 165 // does not work if there are renderer issued challenges. 166 // TODO(cbentzel): Cancel in this case? May not need to do 167 // since render-issued offset navigations are not guaranteed, 168 // but indicates that the page cares about the history. 169 return false; 170 } 171 172 virtual bool ShouldSuppressDialogs() OVERRIDE { 173 // We still want to show the user the message when they navigate to this 174 // page, so cancel this prerender. 175 prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT); 176 // Always suppress JavaScript messages if they're triggered by a page being 177 // prerendered. 178 return true; 179 } 180 181 virtual void RegisterProtocolHandler(WebContents* web_contents, 182 const std::string& protocol, 183 const GURL& url, 184 bool user_gesture) OVERRIDE { 185 // TODO(mmenke): Consider supporting this if it is a common case during 186 // prerenders. 187 prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER); 188 } 189 190 virtual gfx::Size GetSizeForNewRenderView( 191 WebContents* web_contents) const OVERRIDE { 192 // Have to set the size of the RenderView on initialization to be sure it is 193 // set before the RenderView is hidden on all platforms (esp. Android). 194 return prerender_contents_->size_; 195 } 196 197 private: 198 PrerenderContents* prerender_contents_; 199 }; 200 201 void PrerenderContents::Observer::OnPrerenderStopLoading( 202 PrerenderContents* contents) { 203 } 204 205 void PrerenderContents::Observer::OnPrerenderDomContentLoaded( 206 PrerenderContents* contents) { 207 } 208 209 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement( 210 PrerenderContents* contents, PrerenderContents* replacement) { 211 } 212 213 PrerenderContents::Observer::Observer() { 214 } 215 216 PrerenderContents::Observer::~Observer() { 217 } 218 219 PrerenderContents::PrerenderContents( 220 PrerenderManager* prerender_manager, 221 Profile* profile, 222 const GURL& url, 223 const content::Referrer& referrer, 224 Origin origin, 225 uint8 experiment_id) 226 : prerendering_has_started_(false), 227 session_storage_namespace_id_(-1), 228 prerender_manager_(prerender_manager), 229 prerender_url_(url), 230 referrer_(referrer), 231 profile_(profile), 232 page_id_(0), 233 has_stopped_loading_(false), 234 has_finished_loading_(false), 235 final_status_(FINAL_STATUS_MAX), 236 match_complete_status_(MATCH_COMPLETE_DEFAULT), 237 prerendering_has_been_cancelled_(false), 238 child_id_(-1), 239 route_id_(-1), 240 origin_(origin), 241 experiment_id_(experiment_id), 242 creator_child_id_(-1), 243 cookie_status_(0), 244 cookie_send_type_(COOKIE_SEND_TYPE_NONE), 245 network_bytes_(0) { 246 DCHECK(prerender_manager != NULL); 247 } 248 249 PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() { 250 PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents( 251 prerender_url(), referrer(), origin(), experiment_id()); 252 253 new_contents->load_start_time_ = load_start_time_; 254 new_contents->session_storage_namespace_id_ = session_storage_namespace_id_; 255 new_contents->set_match_complete_status( 256 PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING); 257 258 const bool did_init = new_contents->Init(); 259 DCHECK(did_init); 260 DCHECK_EQ(alias_urls_.front(), new_contents->alias_urls_.front()); 261 DCHECK_EQ(1u, new_contents->alias_urls_.size()); 262 new_contents->alias_urls_ = alias_urls_; 263 // Erase all but the first alias URL; the replacement has adopted the 264 // remainder without increasing the renderer-side reference count. 265 alias_urls_.resize(1); 266 new_contents->set_match_complete_status( 267 PrerenderContents::MATCH_COMPLETE_REPLACEMENT); 268 NotifyPrerenderCreatedMatchCompleteReplacement(new_contents); 269 return new_contents; 270 } 271 272 bool PrerenderContents::Init() { 273 return AddAliasURL(prerender_url_); 274 } 275 276 // static 277 PrerenderContents::Factory* PrerenderContents::CreateFactory() { 278 return new PrerenderContentsFactoryImpl(); 279 } 280 281 // static 282 PrerenderContents* PrerenderContents::FromWebContents( 283 content::WebContents* web_contents) { 284 if (!web_contents) 285 return NULL; 286 PrerenderManager* prerender_manager = PrerenderManagerFactory::GetForProfile( 287 Profile::FromBrowserContext(web_contents->GetBrowserContext())); 288 if (!prerender_manager) 289 return NULL; 290 return prerender_manager->GetPrerenderContents(web_contents); 291 } 292 293 void PrerenderContents::StartPrerendering( 294 int creator_child_id, 295 const gfx::Size& size, 296 SessionStorageNamespace* session_storage_namespace, 297 net::URLRequestContextGetter* request_context) { 298 DCHECK(profile_ != NULL); 299 DCHECK(!size.IsEmpty()); 300 DCHECK(!prerendering_has_started_); 301 DCHECK(prerender_contents_.get() == NULL); 302 DCHECK_EQ(-1, creator_child_id_); 303 DCHECK(size_.IsEmpty()); 304 DCHECK_EQ(1U, alias_urls_.size()); 305 306 creator_child_id_ = creator_child_id; 307 session_storage_namespace_id_ = session_storage_namespace->id(); 308 size_ = size; 309 310 DCHECK(load_start_time_.is_null()); 311 load_start_time_ = base::TimeTicks::Now(); 312 start_time_ = base::Time::Now(); 313 314 // Everything after this point sets up the WebContents object and associated 315 // RenderView for the prerender page. Don't do this for members of the 316 // control group. 317 if (prerender_manager_->IsControlGroup(experiment_id())) 318 return; 319 320 if (origin_ == ORIGIN_LOCAL_PREDICTOR && 321 IsLocalPredictorPrerenderAlwaysControlEnabled()) { 322 return; 323 } 324 325 prerendering_has_started_ = true; 326 327 alias_session_storage_namespace = session_storage_namespace->CreateAlias(); 328 prerender_contents_.reset( 329 CreateWebContents(alias_session_storage_namespace.get())); 330 TabHelpers::AttachTabHelpers(prerender_contents_.get()); 331 content::WebContentsObserver::Observe(prerender_contents_.get()); 332 333 web_contents_delegate_.reset(new WebContentsDelegateImpl(this)); 334 prerender_contents_.get()->SetDelegate(web_contents_delegate_.get()); 335 // Set the size of the prerender WebContents. 336 ResizeWebContents(prerender_contents_.get(), size_); 337 338 child_id_ = GetRenderViewHost()->GetProcess()->GetID(); 339 route_id_ = GetRenderViewHost()->GetRoutingID(); 340 341 // Log transactions to see if we could merge session storage namespaces in 342 // the event of a mismatch. 343 alias_session_storage_namespace->AddTransactionLogProcessId(child_id_); 344 345 // Add the RenderProcessHost to the Prerender Manager. 346 prerender_manager()->AddPrerenderProcessHost( 347 GetRenderViewHost()->GetProcess()); 348 349 // In the prerender tracker, create a Prerender Cookie Store to keep track of 350 // cookie changes performed by the prerender. Once the prerender is shown, 351 // the cookie changes will be committed to the actual cookie store, 352 // otherwise, they will be discarded. 353 // If |request_context| is NULL, the feature must be disabled, so the 354 // operation will not be performed. 355 if (request_context) { 356 BrowserThread::PostTask( 357 BrowserThread::IO, FROM_HERE, 358 base::Bind(&PrerenderTracker::AddPrerenderCookieStoreOnIOThread, 359 base::Unretained(prerender_manager()->prerender_tracker()), 360 GetRenderViewHost()->GetProcess()->GetID(), 361 make_scoped_refptr(request_context), 362 base::Bind(&PrerenderContents::Destroy, 363 AsWeakPtr(), 364 FINAL_STATUS_COOKIE_CONFLICT))); 365 } 366 367 NotifyPrerenderStart(); 368 369 // Close ourselves when the application is shutting down. 370 notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, 371 content::NotificationService::AllSources()); 372 373 // Register to inform new RenderViews that we're prerendering. 374 notification_registrar_.Add( 375 this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, 376 content::Source<WebContents>(prerender_contents_.get())); 377 378 // Transfer over the user agent override. 379 prerender_contents_.get()->SetUserAgentOverride( 380 prerender_manager_->config().user_agent_override); 381 382 content::NavigationController::LoadURLParams load_url_params( 383 prerender_url_); 384 load_url_params.referrer = referrer_; 385 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK; 386 if (origin_ == ORIGIN_OMNIBOX) { 387 load_url_params.transition_type = ui::PageTransitionFromInt( 388 ui::PAGE_TRANSITION_TYPED | 389 ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); 390 } else if (origin_ == ORIGIN_INSTANT) { 391 load_url_params.transition_type = ui::PageTransitionFromInt( 392 ui::PAGE_TRANSITION_GENERATED | 393 ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); 394 } 395 load_url_params.override_user_agent = 396 prerender_manager_->config().is_overriding_user_agent ? 397 content::NavigationController::UA_OVERRIDE_TRUE : 398 content::NavigationController::UA_OVERRIDE_FALSE; 399 prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params); 400 } 401 402 bool PrerenderContents::GetChildId(int* child_id) const { 403 CHECK(child_id); 404 DCHECK_GE(child_id_, -1); 405 *child_id = child_id_; 406 return child_id_ != -1; 407 } 408 409 bool PrerenderContents::GetRouteId(int* route_id) const { 410 CHECK(route_id); 411 DCHECK_GE(route_id_, -1); 412 *route_id = route_id_; 413 return route_id_ != -1; 414 } 415 416 void PrerenderContents::SetFinalStatus(FinalStatus final_status) { 417 DCHECK_GE(final_status, FINAL_STATUS_USED); 418 DCHECK_LT(final_status, FINAL_STATUS_MAX); 419 420 DCHECK_EQ(FINAL_STATUS_MAX, final_status_); 421 422 final_status_ = final_status; 423 } 424 425 PrerenderContents::~PrerenderContents() { 426 DCHECK_NE(FINAL_STATUS_MAX, final_status()); 427 DCHECK( 428 prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED); 429 DCHECK_NE(ORIGIN_MAX, origin()); 430 // Since a lot of prerenders terminate before any meaningful cookie action 431 // would have happened, only record the cookie status for prerenders who 432 // were used, cancelled, or timed out. 433 if (prerendering_has_started_ && final_status() == FINAL_STATUS_USED) { 434 prerender_manager_->RecordCookieStatus(origin(), experiment_id(), 435 cookie_status_); 436 prerender_manager_->RecordCookieSendType(origin(), experiment_id(), 437 cookie_send_type_); 438 } 439 prerender_manager_->RecordFinalStatusWithMatchCompleteStatus( 440 origin(), experiment_id(), match_complete_status(), final_status()); 441 442 bool used = final_status() == FINAL_STATUS_USED || 443 final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED; 444 prerender_manager_->RecordNetworkBytes(origin(), used, network_bytes_); 445 446 // Broadcast the removal of aliases. 447 for (content::RenderProcessHost::iterator host_iterator = 448 content::RenderProcessHost::AllHostsIterator(); 449 !host_iterator.IsAtEnd(); 450 host_iterator.Advance()) { 451 content::RenderProcessHost* host = host_iterator.GetCurrentValue(); 452 host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_)); 453 } 454 455 // If we still have a WebContents, clean up anything we need to and then 456 // destroy it. 457 if (prerender_contents_.get()) 458 delete ReleasePrerenderContents(); 459 } 460 461 void PrerenderContents::AddObserver(Observer* observer) { 462 DCHECK_EQ(FINAL_STATUS_MAX, final_status_); 463 observer_list_.AddObserver(observer); 464 } 465 466 void PrerenderContents::RemoveObserver(Observer* observer) { 467 observer_list_.RemoveObserver(observer); 468 } 469 470 void PrerenderContents::Observe(int type, 471 const content::NotificationSource& source, 472 const content::NotificationDetails& details) { 473 switch (type) { 474 // TODO(davidben): Try to remove this in favor of relying on 475 // FINAL_STATUS_PROFILE_DESTROYED. 476 case chrome::NOTIFICATION_APP_TERMINATING: 477 Destroy(FINAL_STATUS_APP_TERMINATING); 478 return; 479 480 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { 481 if (prerender_contents_.get()) { 482 DCHECK_EQ(content::Source<WebContents>(source).ptr(), 483 prerender_contents_.get()); 484 485 content::Details<RenderViewHost> new_render_view_host(details); 486 OnRenderViewHostCreated(new_render_view_host.ptr()); 487 488 // Make sure the size of the RenderViewHost has been passed to the new 489 // RenderView. Otherwise, the size may not be sent until the 490 // RenderViewReady event makes it from the render process to the UI 491 // thread of the browser process. When the RenderView receives its 492 // size, is also sets itself to be visible, which would then break the 493 // visibility API. 494 new_render_view_host->WasResized(); 495 prerender_contents_->WasHidden(); 496 } 497 break; 498 } 499 500 default: 501 NOTREACHED() << "Unexpected notification sent."; 502 break; 503 } 504 } 505 506 void PrerenderContents::OnRenderViewHostCreated( 507 RenderViewHost* new_render_view_host) { 508 } 509 510 WebContents* PrerenderContents::CreateWebContents( 511 SessionStorageNamespace* session_storage_namespace) { 512 // TODO(ajwong): Remove the temporary map once prerendering is aware of 513 // multiple session storage namespaces per tab. 514 content::SessionStorageNamespaceMap session_storage_namespace_map; 515 session_storage_namespace_map[std::string()] = session_storage_namespace; 516 return WebContents::CreateWithSessionStorage( 517 WebContents::CreateParams(profile_), session_storage_namespace_map); 518 } 519 520 void PrerenderContents::NotifyPrerenderStart() { 521 DCHECK_EQ(FINAL_STATUS_MAX, final_status_); 522 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this)); 523 } 524 525 void PrerenderContents::NotifyPrerenderStopLoading() { 526 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this)); 527 } 528 529 void PrerenderContents::NotifyPrerenderDomContentLoaded() { 530 FOR_EACH_OBSERVER(Observer, observer_list_, 531 OnPrerenderDomContentLoaded(this)); 532 } 533 534 void PrerenderContents::NotifyPrerenderStop() { 535 DCHECK_NE(FINAL_STATUS_MAX, final_status_); 536 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this)); 537 observer_list_.Clear(); 538 } 539 540 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement( 541 PrerenderContents* replacement) { 542 FOR_EACH_OBSERVER(Observer, observer_list_, 543 OnPrerenderCreatedMatchCompleteReplacement(this, 544 replacement)); 545 } 546 547 bool PrerenderContents::OnMessageReceived(const IPC::Message& message) { 548 bool handled = true; 549 // The following messages we do want to consume. 550 IPC_BEGIN_MESSAGE_MAP(PrerenderContents, message) 551 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting, 552 OnCancelPrerenderForPrinting) 553 IPC_MESSAGE_UNHANDLED(handled = false) 554 IPC_END_MESSAGE_MAP() 555 556 return handled; 557 } 558 559 bool PrerenderContents::CheckURL(const GURL& url) { 560 if (!url.SchemeIsHTTPOrHTTPS()) { 561 DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_); 562 Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME); 563 return false; 564 } 565 if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING && 566 prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) { 567 Destroy(FINAL_STATUS_RECENTLY_VISITED); 568 return false; 569 } 570 return true; 571 } 572 573 bool PrerenderContents::AddAliasURL(const GURL& url) { 574 if (!CheckURL(url)) 575 return false; 576 577 alias_urls_.push_back(url); 578 579 for (content::RenderProcessHost::iterator host_iterator = 580 content::RenderProcessHost::AllHostsIterator(); 581 !host_iterator.IsAtEnd(); 582 host_iterator.Advance()) { 583 content::RenderProcessHost* host = host_iterator.GetCurrentValue(); 584 host->Send(new PrerenderMsg_OnPrerenderAddAlias(url)); 585 } 586 587 return true; 588 } 589 590 bool PrerenderContents::Matches( 591 const GURL& url, 592 const SessionStorageNamespace* session_storage_namespace) const { 593 if (session_storage_namespace && 594 session_storage_namespace_id_ != session_storage_namespace->id()) { 595 return false; 596 } 597 return std::count_if(alias_urls_.begin(), alias_urls_.end(), 598 std::bind2nd(std::equal_to<GURL>(), url)) != 0; 599 } 600 601 void PrerenderContents::RenderProcessGone(base::TerminationStatus status) { 602 Destroy(FINAL_STATUS_RENDERER_CRASHED); 603 } 604 605 void PrerenderContents::RenderFrameCreated( 606 content::RenderFrameHost* render_frame_host) { 607 // When a new RenderFrame is created for a prerendering WebContents, tell the 608 // new RenderFrame it's being used for prerendering before any navigations 609 // occur. Note that this is always triggered before the first navigation, so 610 // there's no need to send the message just after the WebContents is created. 611 render_frame_host->Send(new PrerenderMsg_SetIsPrerendering( 612 render_frame_host->GetRoutingID(), true)); 613 } 614 615 void PrerenderContents::DidStopLoading( 616 content::RenderViewHost* render_view_host) { 617 has_stopped_loading_ = true; 618 NotifyPrerenderStopLoading(); 619 } 620 621 void PrerenderContents::DocumentLoadedInFrame( 622 content::RenderFrameHost* render_frame_host) { 623 if (!render_frame_host->GetParent()) 624 NotifyPrerenderDomContentLoaded(); 625 } 626 627 void PrerenderContents::DidStartProvisionalLoadForFrame( 628 content::RenderFrameHost* render_frame_host, 629 const GURL& validated_url, 630 bool is_error_page, 631 bool is_iframe_srcdoc) { 632 if (!render_frame_host->GetParent()) { 633 if (!CheckURL(validated_url)) 634 return; 635 636 // Usually, this event fires if the user clicks or enters a new URL. 637 // Neither of these can happen in the case of an invisible prerender. 638 // So the cause is: Some JavaScript caused a new URL to be loaded. In that 639 // case, the spinner would start again in the browser, so we must reset 640 // has_stopped_loading_ so that the spinner won't be stopped. 641 has_stopped_loading_ = false; 642 has_finished_loading_ = false; 643 } 644 } 645 646 void PrerenderContents::DidFinishLoad( 647 content::RenderFrameHost* render_frame_host, 648 const GURL& validated_url) { 649 if (!render_frame_host->GetParent()) 650 has_finished_loading_ = true; 651 } 652 653 void PrerenderContents::DidNavigateMainFrame( 654 const content::LoadCommittedDetails& details, 655 const content::FrameNavigateParams& params) { 656 // If the prerender made a second navigation entry, abort the prerender. This 657 // avoids having to correctly implement a complex history merging case (this 658 // interacts with location.replace) and correctly synchronize with the 659 // renderer. The final status may be monitored to see we need to revisit this 660 // decision. This does not affect client redirects as those do not push new 661 // history entries. (Calls to location.replace, navigations before onload, and 662 // <meta http-equiv=refresh> with timeouts under 1 second do not create 663 // entries in Blink.) 664 if (prerender_contents_->GetController().GetEntryCount() > 1) { 665 Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY); 666 return; 667 } 668 669 // Add each redirect as an alias. |params.url| is included in 670 // |params.redirects|. 671 // 672 // TODO(davidben): We do not correctly patch up history for renderer-initated 673 // navigations which add history entries. http://crbug.com/305660. 674 for (size_t i = 0; i < params.redirects.size(); i++) { 675 if (!AddAliasURL(params.redirects[i])) 676 return; 677 } 678 } 679 680 void PrerenderContents::DidGetRedirectForResourceRequest( 681 RenderViewHost* render_view_host, 682 const content::ResourceRedirectDetails& details) { 683 // DidGetRedirectForResourceRequest can come for any resource on a page. If 684 // it's a redirect on the top-level resource, the name needs to be remembered 685 // for future matching, and if it redirects to an https resource, it needs to 686 // be canceled. If a subresource is redirected, nothing changes. 687 if (details.resource_type != content::RESOURCE_TYPE_MAIN_FRAME) 688 return; 689 CheckURL(details.new_url); 690 } 691 692 void PrerenderContents::Destroy(FinalStatus final_status) { 693 DCHECK_NE(final_status, FINAL_STATUS_USED); 694 695 if (prerendering_has_been_cancelled_) 696 return; 697 698 SetFinalStatus(final_status); 699 700 prerendering_has_been_cancelled_ = true; 701 prerender_manager_->AddToHistory(this); 702 prerender_manager_->MoveEntryToPendingDelete(this, final_status); 703 704 // Note that if this PrerenderContents was made into a MatchComplete 705 // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will 706 // not reach the PrerenderHandle. Rather 707 // OnPrerenderCreatedMatchCompleteReplacement will propogate that 708 // information to the referer. 709 if (!prerender_manager_->IsControlGroup(experiment_id()) && 710 (prerendering_has_started() || 711 match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) { 712 NotifyPrerenderStop(); 713 } 714 } 715 716 base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() { 717 if (process_metrics_.get() == NULL) { 718 // If a PrenderContents hasn't started prerending, don't be fully formed. 719 if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess()) 720 return NULL; 721 base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle(); 722 if (handle == base::kNullProcessHandle) 723 return NULL; 724 #if !defined(OS_MACOSX) 725 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle)); 726 #else 727 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics( 728 handle, 729 content::BrowserChildProcessHost::GetPortProvider())); 730 #endif 731 } 732 733 return process_metrics_.get(); 734 } 735 736 void PrerenderContents::DestroyWhenUsingTooManyResources() { 737 base::ProcessMetrics* metrics = MaybeGetProcessMetrics(); 738 if (metrics == NULL) 739 return; 740 741 size_t private_bytes, shared_bytes; 742 if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) && 743 private_bytes > prerender_manager_->config().max_bytes) { 744 Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED); 745 } 746 } 747 748 WebContents* PrerenderContents::ReleasePrerenderContents() { 749 prerender_contents_->SetDelegate(NULL); 750 content::WebContentsObserver::Observe(NULL); 751 if (alias_session_storage_namespace.get()) 752 alias_session_storage_namespace->RemoveTransactionLogProcessId(child_id_); 753 return prerender_contents_.release(); 754 } 755 756 RenderViewHost* PrerenderContents::GetRenderViewHostMutable() { 757 return const_cast<RenderViewHost*>(GetRenderViewHost()); 758 } 759 760 const RenderViewHost* PrerenderContents::GetRenderViewHost() const { 761 if (!prerender_contents_.get()) 762 return NULL; 763 return prerender_contents_->GetRenderViewHost(); 764 } 765 766 void PrerenderContents::DidNavigate( 767 const history::HistoryAddPageArgs& add_page_args) { 768 add_page_vector_.push_back(add_page_args); 769 } 770 771 void PrerenderContents::CommitHistory(WebContents* tab) { 772 HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab); 773 for (size_t i = 0; i < add_page_vector_.size(); ++i) 774 history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]); 775 } 776 777 base::Value* PrerenderContents::GetAsValue() const { 778 if (!prerender_contents_.get()) 779 return NULL; 780 base::DictionaryValue* dict_value = new base::DictionaryValue(); 781 dict_value->SetString("url", prerender_url_.spec()); 782 base::TimeTicks current_time = base::TimeTicks::Now(); 783 base::TimeDelta duration = current_time - load_start_time_; 784 dict_value->SetInteger("duration", duration.InSeconds()); 785 dict_value->SetBoolean("is_loaded", prerender_contents_ && 786 !prerender_contents_->IsLoading()); 787 return dict_value; 788 } 789 790 bool PrerenderContents::IsCrossSiteNavigationPending() const { 791 if (!prerender_contents_) 792 return false; 793 return (prerender_contents_->GetSiteInstance() != 794 prerender_contents_->GetPendingSiteInstance()); 795 } 796 797 void PrerenderContents::PrepareForUse() { 798 SetFinalStatus(FINAL_STATUS_USED); 799 800 if (prerender_contents_.get()) { 801 prerender_contents_->SendToAllFrames( 802 new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE, false)); 803 } 804 805 NotifyPrerenderStop(); 806 807 BrowserThread::PostTask( 808 BrowserThread::IO, 809 FROM_HERE, 810 base::Bind(&ResumeThrottles, resource_throttles_)); 811 resource_throttles_.clear(); 812 } 813 814 SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const { 815 if (!prerender_contents()) 816 return NULL; 817 return prerender_contents()->GetController(). 818 GetDefaultSessionStorageNamespace(); 819 } 820 821 void PrerenderContents::OnCancelPrerenderForPrinting() { 822 Destroy(FINAL_STATUS_WINDOW_PRINT); 823 } 824 825 void PrerenderContents::RecordCookieEvent(CookieEvent event, 826 bool is_main_frame_http_request, 827 bool is_third_party_cookie, 828 bool is_for_blocking_resource, 829 base::Time earliest_create_date) { 830 // We don't care about sent cookies that were created after this prerender 831 // started. 832 // The reason is that for the purpose of the histograms emitted, we only care 833 // about cookies that existed before the prerender was started, but not 834 // about cookies that were created as part of the prerender. Using the 835 // earliest creation timestamp of all cookies provided by the cookie monster 836 // is a heuristic that yields the desired result pretty closely. 837 // In particular, we pretend no other WebContents make changes to the cookies 838 // relevant to the prerender, which may not actually always be the case, but 839 // hopefully most of the times. 840 if (event == COOKIE_EVENT_SEND && earliest_create_date > start_time_) 841 return; 842 843 InternalCookieEvent internal_event = INTERNAL_COOKIE_EVENT_MAX; 844 845 if (is_main_frame_http_request) { 846 if (event == COOKIE_EVENT_SEND) { 847 internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND; 848 } else { 849 internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE; 850 } 851 } else { 852 if (event == COOKIE_EVENT_SEND) { 853 internal_event = INTERNAL_COOKIE_EVENT_OTHER_SEND; 854 } else { 855 internal_event = INTERNAL_COOKIE_EVENT_OTHER_CHANGE; 856 } 857 } 858 859 DCHECK_GE(internal_event, 0); 860 DCHECK_LT(internal_event, INTERNAL_COOKIE_EVENT_MAX); 861 862 cookie_status_ |= (1 << internal_event); 863 864 DCHECK_GE(cookie_status_, 0); 865 DCHECK_LT(cookie_status_, kNumCookieStatuses); 866 867 CookieSendType send_type = COOKIE_SEND_TYPE_NONE; 868 if (event == COOKIE_EVENT_SEND) { 869 if (!is_third_party_cookie) { 870 send_type = COOKIE_SEND_TYPE_FIRST_PARTY; 871 } else { 872 if (is_for_blocking_resource) { 873 send_type = COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE; 874 } else { 875 send_type = COOKIE_SEND_TYPE_THIRD_PARTY; 876 } 877 } 878 } 879 DCHECK_GE(send_type, 0); 880 DCHECK_LT(send_type, COOKIE_SEND_TYPE_MAX); 881 882 if (cookie_send_type_ < send_type) 883 cookie_send_type_ = send_type; 884 } 885 886 void PrerenderContents::AddResourceThrottle( 887 const base::WeakPtr<PrerenderResourceThrottle>& throttle) { 888 resource_throttles_.push_back(throttle); 889 } 890 891 void PrerenderContents::AddNetworkBytes(int64 bytes) { 892 network_bytes_ += bytes; 893 } 894 895 } // namespace prerender 896