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 "apps/ui/web_contents_sizer.h" 12 #include "base/bind.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/browser/chrome_notification_types.h" 15 #include "chrome/browser/history/history_tab_helper.h" 16 #include "chrome/browser/history/history_types.h" 17 #include "chrome/browser/prerender/prerender_field_trial.h" 18 #include "chrome/browser/prerender/prerender_final_status.h" 19 #include "chrome/browser/prerender/prerender_handle.h" 20 #include "chrome/browser/prerender/prerender_manager.h" 21 #include "chrome/browser/prerender/prerender_manager_factory.h" 22 #include "chrome/browser/prerender/prerender_resource_throttle.h" 23 #include "chrome/browser/prerender/prerender_tracker.h" 24 #include "chrome/browser/profiles/profile.h" 25 #include "chrome/browser/ui/browser.h" 26 #include "chrome/browser/ui/tab_helpers.h" 27 #include "chrome/common/prerender_messages.h" 28 #include "chrome/common/render_messages.h" 29 #include "chrome/common/url_constants.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 "content/public/common/page_transition_types.h" 42 #include "net/url_request/url_request_context_getter.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::SessionStorageNamespace; 51 using content::WebContents; 52 53 namespace prerender { 54 55 namespace { 56 57 // Internal cookie event. 58 // Whenever a prerender interacts with the cookie store, either sending 59 // existing cookies that existed before the prerender started, or when a cookie 60 // is changed, we record these events for histogramming purposes. 61 enum InternalCookieEvent { 62 INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND = 0, 63 INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE = 1, 64 INTERNAL_COOKIE_EVENT_OTHER_SEND = 2, 65 INTERNAL_COOKIE_EVENT_OTHER_CHANGE = 3, 66 INTERNAL_COOKIE_EVENT_MAX 67 }; 68 69 // Indicates whether existing cookies were sent, and if they were third party 70 // cookies, and whether they were for blocking resources. 71 // Each value may be inclusive of previous values. We only care about the 72 // value with the highest index that has ever occurred in the course of a 73 // prerender. 74 enum CookieSendType { 75 COOKIE_SEND_TYPE_NONE = 0, 76 COOKIE_SEND_TYPE_FIRST_PARTY = 1, 77 COOKIE_SEND_TYPE_THIRD_PARTY = 2, 78 COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE = 3, 79 COOKIE_SEND_TYPE_MAX 80 }; 81 82 void ResumeThrottles( 83 std::vector<base::WeakPtr<PrerenderResourceThrottle> > throttles) { 84 for (size_t i = 0; i < throttles.size(); i++) { 85 if (throttles[i]) 86 throttles[i]->Resume(); 87 } 88 } 89 90 } // namespace 91 92 // static 93 const int PrerenderContents::kNumCookieStatuses = 94 (1 << INTERNAL_COOKIE_EVENT_MAX); 95 96 // static 97 const int PrerenderContents::kNumCookieSendTypes = COOKIE_SEND_TYPE_MAX; 98 99 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory { 100 public: 101 virtual PrerenderContents* CreatePrerenderContents( 102 PrerenderManager* prerender_manager, Profile* profile, 103 const GURL& url, const content::Referrer& referrer, 104 Origin origin, uint8 experiment_id) OVERRIDE { 105 return new PrerenderContents(prerender_manager, profile, 106 url, referrer, origin, experiment_id); 107 } 108 }; 109 110 // WebContentsDelegateImpl ----------------------------------------------------- 111 112 class PrerenderContents::WebContentsDelegateImpl 113 : public content::WebContentsDelegate { 114 public: 115 explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents) 116 : prerender_contents_(prerender_contents) { 117 } 118 119 // content::WebContentsDelegate implementation: 120 virtual WebContents* OpenURLFromTab(WebContents* source, 121 const OpenURLParams& params) OVERRIDE { 122 // |OpenURLFromTab| is typically called when a frame performs a navigation 123 // that requires the browser to perform the transition instead of WebKit. 124 // Examples include prerendering a site that redirects to an app URL, 125 // or if --enable-strict-site-isolation is specified and the prerendered 126 // frame redirects to a different origin. 127 // TODO(cbentzel): Consider supporting this if it is a common case during 128 // prerenders. 129 prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL); 130 return NULL; 131 } 132 133 virtual void CloseContents(content::WebContents* contents) OVERRIDE { 134 prerender_contents_->Destroy(FINAL_STATUS_CLOSED); 135 } 136 137 virtual void CanDownload( 138 RenderViewHost* render_view_host, 139 const GURL& url, 140 const std::string& request_method, 141 const base::Callback<void(bool)>& callback) OVERRIDE { 142 prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD); 143 // Cancel the download. 144 callback.Run(false); 145 } 146 147 virtual bool ShouldCreateWebContents( 148 WebContents* web_contents, 149 int route_id, 150 WindowContainerType window_container_type, 151 const base::string16& frame_name, 152 const GURL& target_url, 153 const std::string& partition_id, 154 SessionStorageNamespace* session_storage_namespace) OVERRIDE { 155 // Since we don't want to permit child windows that would have a 156 // window.opener property, terminate prerendering. 157 prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW); 158 // Cancel the popup. 159 return false; 160 } 161 162 virtual bool OnGoToEntryOffset(int offset) OVERRIDE { 163 // This isn't allowed because the history merge operation 164 // does not work if there are renderer issued challenges. 165 // TODO(cbentzel): Cancel in this case? May not need to do 166 // since render-issued offset navigations are not guaranteed, 167 // but indicates that the page cares about the history. 168 return false; 169 } 170 171 virtual bool ShouldSuppressDialogs() OVERRIDE { 172 // We still want to show the user the message when they navigate to this 173 // page, so cancel this prerender. 174 prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT); 175 // Always suppress JavaScript messages if they're triggered by a page being 176 // prerendered. 177 return true; 178 } 179 180 virtual void RegisterProtocolHandler(WebContents* web_contents, 181 const std::string& protocol, 182 const GURL& url, 183 bool user_gesture) OVERRIDE { 184 // TODO(mmenke): Consider supporting this if it is a common case during 185 // prerenders. 186 prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER); 187 } 188 189 virtual gfx::Size GetSizeForNewRenderView( 190 WebContents* web_contents) const OVERRIDE { 191 // Have to set the size of the RenderView on initialization to be sure it is 192 // set before the RenderView is hidden on all platforms (esp. Android). 193 return prerender_contents_->size_; 194 } 195 196 private: 197 PrerenderContents* prerender_contents_; 198 }; 199 200 void PrerenderContents::Observer::OnPrerenderStopLoading( 201 PrerenderContents* contents) { 202 } 203 204 void PrerenderContents::Observer::OnPrerenderDomContentLoaded( 205 PrerenderContents* contents) { 206 } 207 208 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement( 209 PrerenderContents* contents, PrerenderContents* replacement) { 210 } 211 212 PrerenderContents::Observer::Observer() { 213 } 214 215 PrerenderContents::Observer::~Observer() { 216 } 217 218 PrerenderContents::PrerenderContents( 219 PrerenderManager* prerender_manager, 220 Profile* profile, 221 const GURL& url, 222 const content::Referrer& referrer, 223 Origin origin, 224 uint8 experiment_id) 225 : prerendering_has_started_(false), 226 session_storage_namespace_id_(-1), 227 prerender_manager_(prerender_manager), 228 prerender_url_(url), 229 referrer_(referrer), 230 profile_(profile), 231 page_id_(0), 232 has_stopped_loading_(false), 233 has_finished_loading_(false), 234 final_status_(FINAL_STATUS_MAX), 235 match_complete_status_(MATCH_COMPLETE_DEFAULT), 236 prerendering_has_been_cancelled_(false), 237 child_id_(-1), 238 route_id_(-1), 239 origin_(origin), 240 experiment_id_(experiment_id), 241 creator_child_id_(-1), 242 main_frame_id_(0), 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 apps::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 = content::PAGE_TRANSITION_LINK; 386 if (origin_ == ORIGIN_OMNIBOX) { 387 load_url_params.transition_type = content::PageTransitionFromInt( 388 content::PAGE_TRANSITION_TYPED | 389 content::PAGE_TRANSITION_FROM_ADDRESS_BAR); 390 } else if (origin_ == ORIGIN_INSTANT) { 391 load_url_params.transition_type = content::PageTransitionFromInt( 392 content::PAGE_TRANSITION_GENERATED | 393 content::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 int64 frame_id, 623 RenderViewHost* render_view_host) { 624 if (frame_id == main_frame_id_) 625 NotifyPrerenderDomContentLoaded(); 626 } 627 628 void PrerenderContents::DidStartProvisionalLoadForFrame( 629 int64 frame_id, 630 int64 parent_frame_id, 631 bool is_main_frame, 632 const GURL& validated_url, 633 bool is_error_page, 634 bool is_iframe_srcdoc, 635 RenderViewHost* render_view_host) { 636 if (is_main_frame) { 637 if (!CheckURL(validated_url)) 638 return; 639 640 // Usually, this event fires if the user clicks or enters a new URL. 641 // Neither of these can happen in the case of an invisible prerender. 642 // So the cause is: Some JavaScript caused a new URL to be loaded. In that 643 // case, the spinner would start again in the browser, so we must reset 644 // has_stopped_loading_ so that the spinner won't be stopped. 645 has_stopped_loading_ = false; 646 has_finished_loading_ = false; 647 } 648 } 649 650 void PrerenderContents::DidCommitProvisionalLoadForFrame( 651 int64 frame_id, 652 const base::string16& frame_unique_name, 653 bool is_main_frame, 654 const GURL& url, 655 content::PageTransition transition_type, 656 RenderViewHost* render_view_host) { 657 if (is_main_frame) { 658 main_frame_id_ = frame_id; 659 } 660 } 661 662 void PrerenderContents::DidFinishLoad(int64 frame_id, 663 const GURL& validated_url, 664 bool is_main_frame, 665 RenderViewHost* render_view_host) { 666 if (is_main_frame) 667 has_finished_loading_ = true; 668 } 669 670 void PrerenderContents::DidNavigateMainFrame( 671 const content::LoadCommittedDetails& details, 672 const content::FrameNavigateParams& params) { 673 // If the prerender made a second navigation entry, abort the prerender. This 674 // avoids having to correctly implement a complex history merging case (this 675 // interacts with location.replace) and correctly synchronize with the 676 // renderer. The final status may be monitored to see we need to revisit this 677 // decision. This does not affect client redirects as those do not push new 678 // history entries. (Calls to location.replace, navigations before onload, and 679 // <meta http-equiv=refresh> with timeouts under 1 second do not create 680 // entries in Blink.) 681 if (prerender_contents_->GetController().GetEntryCount() > 1) { 682 Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY); 683 return; 684 } 685 686 // Add each redirect as an alias. |params.url| is included in 687 // |params.redirects|. 688 // 689 // TODO(davidben): We do not correctly patch up history for renderer-initated 690 // navigations which add history entries. http://crbug.com/305660. 691 for (size_t i = 0; i < params.redirects.size(); i++) { 692 if (!AddAliasURL(params.redirects[i])) 693 return; 694 } 695 } 696 697 void PrerenderContents::DidGetRedirectForResourceRequest( 698 RenderViewHost* render_view_host, 699 const content::ResourceRedirectDetails& details) { 700 // DidGetRedirectForResourceRequest can come for any resource on a page. If 701 // it's a redirect on the top-level resource, the name needs to be remembered 702 // for future matching, and if it redirects to an https resource, it needs to 703 // be canceled. If a subresource is redirected, nothing changes. 704 if (details.resource_type != ResourceType::MAIN_FRAME) 705 return; 706 CheckURL(details.new_url); 707 } 708 709 void PrerenderContents::Destroy(FinalStatus final_status) { 710 DCHECK_NE(final_status, FINAL_STATUS_USED); 711 712 if (prerendering_has_been_cancelled_) 713 return; 714 715 SetFinalStatus(final_status); 716 717 prerendering_has_been_cancelled_ = true; 718 prerender_manager_->AddToHistory(this); 719 prerender_manager_->MoveEntryToPendingDelete(this, final_status); 720 721 // Note that if this PrerenderContents was made into a MatchComplete 722 // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will 723 // not reach the PrerenderHandle. Rather 724 // OnPrerenderCreatedMatchCompleteReplacement will propogate that 725 // information to the referer. 726 if (!prerender_manager_->IsControlGroup(experiment_id()) && 727 (prerendering_has_started() || 728 match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) { 729 NotifyPrerenderStop(); 730 } 731 } 732 733 base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() { 734 if (process_metrics_.get() == NULL) { 735 // If a PrenderContents hasn't started prerending, don't be fully formed. 736 if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess()) 737 return NULL; 738 base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle(); 739 if (handle == base::kNullProcessHandle) 740 return NULL; 741 #if !defined(OS_MACOSX) 742 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle)); 743 #else 744 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics( 745 handle, 746 content::BrowserChildProcessHost::GetPortProvider())); 747 #endif 748 } 749 750 return process_metrics_.get(); 751 } 752 753 void PrerenderContents::DestroyWhenUsingTooManyResources() { 754 base::ProcessMetrics* metrics = MaybeGetProcessMetrics(); 755 if (metrics == NULL) 756 return; 757 758 size_t private_bytes, shared_bytes; 759 if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) && 760 private_bytes > prerender_manager_->config().max_bytes) { 761 Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED); 762 } 763 } 764 765 WebContents* PrerenderContents::ReleasePrerenderContents() { 766 prerender_contents_->SetDelegate(NULL); 767 content::WebContentsObserver::Observe(NULL); 768 if (alias_session_storage_namespace) 769 alias_session_storage_namespace->RemoveTransactionLogProcessId(child_id_); 770 return prerender_contents_.release(); 771 } 772 773 RenderViewHost* PrerenderContents::GetRenderViewHostMutable() { 774 return const_cast<RenderViewHost*>(GetRenderViewHost()); 775 } 776 777 const RenderViewHost* PrerenderContents::GetRenderViewHost() const { 778 if (!prerender_contents_.get()) 779 return NULL; 780 return prerender_contents_->GetRenderViewHost(); 781 } 782 783 void PrerenderContents::DidNavigate( 784 const history::HistoryAddPageArgs& add_page_args) { 785 add_page_vector_.push_back(add_page_args); 786 } 787 788 void PrerenderContents::CommitHistory(WebContents* tab) { 789 HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab); 790 for (size_t i = 0; i < add_page_vector_.size(); ++i) 791 history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]); 792 } 793 794 base::Value* PrerenderContents::GetAsValue() const { 795 if (!prerender_contents_.get()) 796 return NULL; 797 base::DictionaryValue* dict_value = new base::DictionaryValue(); 798 dict_value->SetString("url", prerender_url_.spec()); 799 base::TimeTicks current_time = base::TimeTicks::Now(); 800 base::TimeDelta duration = current_time - load_start_time_; 801 dict_value->SetInteger("duration", duration.InSeconds()); 802 dict_value->SetBoolean("is_loaded", prerender_contents_ && 803 !prerender_contents_->IsLoading()); 804 return dict_value; 805 } 806 807 bool PrerenderContents::IsCrossSiteNavigationPending() const { 808 if (!prerender_contents_) 809 return false; 810 return (prerender_contents_->GetSiteInstance() != 811 prerender_contents_->GetPendingSiteInstance()); 812 } 813 814 void PrerenderContents::PrepareForUse() { 815 SetFinalStatus(FINAL_STATUS_USED); 816 817 if (prerender_contents_.get()) { 818 prerender_contents_->SendToAllFrames( 819 new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE, false)); 820 } 821 822 NotifyPrerenderStop(); 823 824 BrowserThread::PostTask( 825 BrowserThread::IO, 826 FROM_HERE, 827 base::Bind(&ResumeThrottles, resource_throttles_)); 828 resource_throttles_.clear(); 829 } 830 831 SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const { 832 if (!prerender_contents()) 833 return NULL; 834 return prerender_contents()->GetController(). 835 GetDefaultSessionStorageNamespace(); 836 } 837 838 void PrerenderContents::OnCancelPrerenderForPrinting() { 839 Destroy(FINAL_STATUS_WINDOW_PRINT); 840 } 841 842 void PrerenderContents::RecordCookieEvent(CookieEvent event, 843 bool is_main_frame_http_request, 844 bool is_third_party_cookie, 845 bool is_for_blocking_resource, 846 base::Time earliest_create_date) { 847 // We don't care about sent cookies that were created after this prerender 848 // started. 849 // The reason is that for the purpose of the histograms emitted, we only care 850 // about cookies that existed before the prerender was started, but not 851 // about cookies that were created as part of the prerender. Using the 852 // earliest creation timestamp of all cookies provided by the cookie monster 853 // is a heuristic that yields the desired result pretty closely. 854 // In particular, we pretend no other WebContents make changes to the cookies 855 // relevant to the prerender, which may not actually always be the case, but 856 // hopefully most of the times. 857 if (event == COOKIE_EVENT_SEND && earliest_create_date > start_time_) 858 return; 859 860 InternalCookieEvent internal_event = INTERNAL_COOKIE_EVENT_MAX; 861 862 if (is_main_frame_http_request) { 863 if (event == COOKIE_EVENT_SEND) { 864 internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND; 865 } else { 866 internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE; 867 } 868 } else { 869 if (event == COOKIE_EVENT_SEND) { 870 internal_event = INTERNAL_COOKIE_EVENT_OTHER_SEND; 871 } else { 872 internal_event = INTERNAL_COOKIE_EVENT_OTHER_CHANGE; 873 } 874 } 875 876 DCHECK_GE(internal_event, 0); 877 DCHECK_LT(internal_event, INTERNAL_COOKIE_EVENT_MAX); 878 879 cookie_status_ |= (1 << internal_event); 880 881 DCHECK_GE(cookie_status_, 0); 882 DCHECK_LT(cookie_status_, kNumCookieStatuses); 883 884 CookieSendType send_type = COOKIE_SEND_TYPE_NONE; 885 if (event == COOKIE_EVENT_SEND) { 886 if (!is_third_party_cookie) { 887 send_type = COOKIE_SEND_TYPE_FIRST_PARTY; 888 } else { 889 if (is_for_blocking_resource) { 890 send_type = COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE; 891 } else { 892 send_type = COOKIE_SEND_TYPE_THIRD_PARTY; 893 } 894 } 895 } 896 DCHECK_GE(send_type, 0); 897 DCHECK_LT(send_type, COOKIE_SEND_TYPE_MAX); 898 899 if (cookie_send_type_ < send_type) 900 cookie_send_type_ = send_type; 901 } 902 903 void PrerenderContents::AddResourceThrottle( 904 const base::WeakPtr<PrerenderResourceThrottle>& throttle) { 905 resource_throttles_.push_back(throttle); 906 } 907 908 void PrerenderContents::AddNetworkBytes(int64 bytes) { 909 network_bytes_ += bytes; 910 } 911 912 } // namespace prerender 913