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_manager.h" 6 7 #include <algorithm> 8 #include <functional> 9 #include <string> 10 #include <vector> 11 12 #include "base/bind.h" 13 #include "base/bind_helpers.h" 14 #include "base/logging.h" 15 #include "base/memory/weak_ptr.h" 16 #include "base/metrics/histogram.h" 17 #include "base/prefs/pref_service.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "base/time/time.h" 20 #include "base/timer/elapsed_timer.h" 21 #include "base/values.h" 22 #include "chrome/browser/browser_process.h" 23 #include "chrome/browser/chrome_notification_types.h" 24 #include "chrome/browser/history/history_service_factory.h" 25 #include "chrome/browser/net/chrome_cookie_notification_details.h" 26 #include "chrome/browser/net/prediction_options.h" 27 #include "chrome/browser/predictors/predictor_database.h" 28 #include "chrome/browser/predictors/predictor_database_factory.h" 29 #include "chrome/browser/prerender/prerender_contents.h" 30 #include "chrome/browser/prerender/prerender_field_trial.h" 31 #include "chrome/browser/prerender/prerender_final_status.h" 32 #include "chrome/browser/prerender/prerender_handle.h" 33 #include "chrome/browser/prerender/prerender_histograms.h" 34 #include "chrome/browser/prerender/prerender_history.h" 35 #include "chrome/browser/prerender/prerender_local_predictor.h" 36 #include "chrome/browser/prerender/prerender_manager_factory.h" 37 #include "chrome/browser/prerender/prerender_tab_helper.h" 38 #include "chrome/browser/prerender/prerender_tracker.h" 39 #include "chrome/browser/prerender/prerender_util.h" 40 #include "chrome/browser/profiles/profile.h" 41 #include "chrome/browser/search/search.h" 42 #include "chrome/browser/tab_contents/tab_util.h" 43 #include "chrome/browser/ui/browser_navigator.h" 44 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" 45 #include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h" 46 #include "chrome/common/chrome_switches.h" 47 #include "chrome/common/pref_names.h" 48 #include "chrome/common/prerender_messages.h" 49 #include "chrome/common/prerender_types.h" 50 #include "content/public/browser/browser_thread.h" 51 #include "content/public/browser/devtools_agent_host.h" 52 #include "content/public/browser/navigation_controller.h" 53 #include "content/public/browser/notification_service.h" 54 #include "content/public/browser/notification_source.h" 55 #include "content/public/browser/render_frame_host.h" 56 #include "content/public/browser/render_process_host.h" 57 #include "content/public/browser/render_view_host.h" 58 #include "content/public/browser/resource_request_details.h" 59 #include "content/public/browser/session_storage_namespace.h" 60 #include "content/public/browser/site_instance.h" 61 #include "content/public/browser/storage_partition.h" 62 #include "content/public/browser/web_contents.h" 63 #include "content/public/browser/web_contents_delegate.h" 64 #include "content/public/common/url_constants.h" 65 #include "extensions/common/constants.h" 66 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 67 #include "net/url_request/url_request_context.h" 68 #include "net/url_request/url_request_context_getter.h" 69 70 using content::BrowserThread; 71 using content::RenderViewHost; 72 using content::RenderFrameHost; 73 using content::SessionStorageNamespace; 74 using content::WebContents; 75 using predictors::LoggedInPredictorTable; 76 77 namespace prerender { 78 79 namespace { 80 81 // Time interval at which periodic cleanups are performed. 82 const int kPeriodicCleanupIntervalMs = 1000; 83 84 // Valid HTTP methods for prerendering. 85 const char* const kValidHttpMethods[] = { 86 "GET", 87 "HEAD", 88 "OPTIONS", 89 "POST", 90 "TRACE", 91 }; 92 93 // Length of prerender history, for display in chrome://net-internals 94 const int kHistoryLength = 100; 95 96 // Timeout, in ms, for a session storage namespace merge. 97 const int kSessionStorageNamespaceMergeTimeoutMs = 500; 98 99 // If true, all session storage merges hang indefinitely. 100 bool g_hang_session_storage_merges_for_testing = false; 101 102 // Indicates whether a Prerender has been cancelled such that we need 103 // a dummy replacement for the purpose of recording the correct PPLT for 104 // the Match Complete case. 105 // Traditionally, "Match" means that a prerendered page was actually visited & 106 // the prerender was used. Our goal is to have "Match" cases line up in the 107 // control group & the experiment group, so that we can make meaningful 108 // comparisons of improvements. However, in the control group, since we don't 109 // actually perform prerenders, many of the cancellation reasons cannot be 110 // detected. Therefore, in the Prerender group, when we cancel for one of these 111 // reasons, we keep track of a dummy Prerender representing what we would 112 // have in the control group. If that dummy prerender in the prerender group 113 // would then be swapped in (but isn't actually b/c it's a dummy), we record 114 // this as a MatchComplete. This allows us to compare MatchComplete's 115 // across Prerender & Control group which ideally should be lining up. 116 // This ensures that there is no bias in terms of the page load times 117 // of the pages forming the difference between the two sets. 118 119 bool NeedMatchCompleteDummyForFinalStatus(FinalStatus final_status) { 120 return final_status != FINAL_STATUS_USED && 121 final_status != FINAL_STATUS_TIMED_OUT && 122 final_status != FINAL_STATUS_MANAGER_SHUTDOWN && 123 final_status != FINAL_STATUS_PROFILE_DESTROYED && 124 final_status != FINAL_STATUS_APP_TERMINATING && 125 final_status != FINAL_STATUS_WINDOW_OPENER && 126 final_status != FINAL_STATUS_CACHE_OR_HISTORY_CLEARED && 127 final_status != FINAL_STATUS_CANCELLED && 128 final_status != FINAL_STATUS_DEVTOOLS_ATTACHED && 129 final_status != FINAL_STATUS_CROSS_SITE_NAVIGATION_PENDING && 130 final_status != FINAL_STATUS_PAGE_BEING_CAPTURED && 131 final_status != FINAL_STATUS_NAVIGATION_UNCOMMITTED && 132 final_status != FINAL_STATUS_NON_EMPTY_BROWSING_INSTANCE; 133 } 134 135 void CheckIfCookiesExistForDomainResultOnUIThread( 136 const net::CookieMonster::HasCookiesForETLDP1Callback& callback, 137 bool cookies_exist) { 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 139 callback.Run(cookies_exist); 140 } 141 142 void CheckIfCookiesExistForDomainResultOnIOThread( 143 const net::CookieMonster::HasCookiesForETLDP1Callback& callback, 144 bool cookies_exist) { 145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 146 BrowserThread::PostTask( 147 BrowserThread::UI, 148 FROM_HERE, 149 base::Bind(&CheckIfCookiesExistForDomainResultOnUIThread, 150 callback, 151 cookies_exist)); 152 } 153 154 void CheckIfCookiesExistForDomainOnIOThread( 155 net::URLRequestContextGetter* rq_context, 156 const std::string& domain_key, 157 const net::CookieMonster::HasCookiesForETLDP1Callback& callback) { 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 159 net::CookieStore* cookie_store = 160 rq_context->GetURLRequestContext()->cookie_store(); 161 cookie_store->GetCookieMonster()->HasCookiesForETLDP1Async( 162 domain_key, 163 base::Bind(&CheckIfCookiesExistForDomainResultOnIOThread, callback)); 164 } 165 166 } // namespace 167 168 class PrerenderManager::OnCloseWebContentsDeleter 169 : public content::WebContentsDelegate, 170 public base::SupportsWeakPtr< 171 PrerenderManager::OnCloseWebContentsDeleter> { 172 public: 173 OnCloseWebContentsDeleter(PrerenderManager* manager, 174 WebContents* tab) 175 : manager_(manager), 176 tab_(tab), 177 suppressed_dialog_(false) { 178 tab_->SetDelegate(this); 179 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, 180 base::Bind(&OnCloseWebContentsDeleter::ScheduleWebContentsForDeletion, 181 AsWeakPtr(), true), 182 base::TimeDelta::FromSeconds(kDeleteWithExtremePrejudiceSeconds)); 183 } 184 185 virtual void CloseContents(WebContents* source) OVERRIDE { 186 DCHECK_EQ(tab_, source); 187 ScheduleWebContentsForDeletion(false); 188 } 189 190 virtual void SwappedOut(WebContents* source) OVERRIDE { 191 DCHECK_EQ(tab_, source); 192 ScheduleWebContentsForDeletion(false); 193 } 194 195 virtual bool ShouldSuppressDialogs() OVERRIDE { 196 // Use this as a proxy for getting statistics on how often we fail to honor 197 // the beforeunload event. 198 suppressed_dialog_ = true; 199 return true; 200 } 201 202 private: 203 static const int kDeleteWithExtremePrejudiceSeconds = 3; 204 205 void ScheduleWebContentsForDeletion(bool timeout) { 206 UMA_HISTOGRAM_BOOLEAN("Prerender.TabContentsDeleterTimeout", timeout); 207 UMA_HISTOGRAM_BOOLEAN("Prerender.TabContentsDeleterSuppressedDialog", 208 suppressed_dialog_); 209 tab_->SetDelegate(NULL); 210 manager_->ScheduleDeleteOldWebContents(tab_.release(), this); 211 // |this| is deleted at this point. 212 } 213 214 PrerenderManager* manager_; 215 scoped_ptr<WebContents> tab_; 216 bool suppressed_dialog_; 217 218 DISALLOW_COPY_AND_ASSIGN(OnCloseWebContentsDeleter); 219 }; 220 221 // static 222 int PrerenderManager::prerenders_per_session_count_ = 0; 223 224 // static 225 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ = 226 PRERENDER_MODE_ENABLED; 227 228 struct PrerenderManager::NavigationRecord { 229 NavigationRecord(const GURL& url, base::TimeTicks time) 230 : url(url), 231 time(time) { 232 } 233 234 GURL url; 235 base::TimeTicks time; 236 }; 237 238 PrerenderManager::PrerenderManager(Profile* profile, 239 PrerenderTracker* prerender_tracker) 240 : profile_(profile), 241 prerender_tracker_(prerender_tracker), 242 prerender_contents_factory_(PrerenderContents::CreateFactory()), 243 last_prerender_start_time_(GetCurrentTimeTicks() - 244 base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs)), 245 prerender_history_(new PrerenderHistory(kHistoryLength)), 246 histograms_(new PrerenderHistograms()), 247 profile_network_bytes_(0), 248 last_recorded_profile_network_bytes_(0), 249 cookie_store_loaded_(false) { 250 // There are some assumptions that the PrerenderManager is on the UI thread. 251 // Any other checks simply make sure that the PrerenderManager is accessed on 252 // the same thread that it was created on. 253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 254 255 if (IsLocalPredictorEnabled()) 256 local_predictor_.reset(new PrerenderLocalPredictor(this)); 257 258 if (IsLoggedInPredictorEnabled() && !profile_->IsOffTheRecord()) { 259 predictors::PredictorDatabase* predictor_db = 260 predictors::PredictorDatabaseFactory::GetForProfile(profile); 261 if (predictor_db) { 262 logged_in_predictor_table_ = predictor_db->logged_in_table(); 263 scoped_ptr<LoggedInStateMap> new_state_map(new LoggedInStateMap); 264 LoggedInStateMap* new_state_map_ptr = new_state_map.get(); 265 BrowserThread::PostTaskAndReply( 266 BrowserThread::DB, FROM_HERE, 267 base::Bind(&LoggedInPredictorTable::GetAllData, 268 logged_in_predictor_table_, 269 new_state_map_ptr), 270 base::Bind(&PrerenderManager::LoggedInPredictorDataReceived, 271 AsWeakPtr(), 272 base::Passed(&new_state_map))); 273 } 274 } 275 276 // Certain experiments override our default config_ values. 277 switch (PrerenderManager::GetMode()) { 278 case PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP: 279 config_.max_link_concurrency = 4; 280 config_.max_link_concurrency_per_launcher = 2; 281 break; 282 case PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP: 283 config_.time_to_live = base::TimeDelta::FromMinutes(15); 284 break; 285 default: 286 break; 287 } 288 289 notification_registrar_.Add( 290 this, chrome::NOTIFICATION_COOKIE_CHANGED, 291 content::NotificationService::AllBrowserContextsAndSources()); 292 293 notification_registrar_.Add( 294 this, chrome::NOTIFICATION_PROFILE_DESTROYED, 295 content::Source<Profile>(profile_)); 296 297 MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this); 298 } 299 300 PrerenderManager::~PrerenderManager() { 301 MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this); 302 303 // The earlier call to KeyedService::Shutdown() should have 304 // emptied these vectors already. 305 DCHECK(active_prerenders_.empty()); 306 DCHECK(to_delete_prerenders_.empty()); 307 308 for (PrerenderProcessSet::const_iterator it = 309 prerender_process_hosts_.begin(); 310 it != prerender_process_hosts_.end(); 311 ++it) { 312 (*it)->RemoveObserver(this); 313 } 314 } 315 316 void PrerenderManager::Shutdown() { 317 DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN); 318 on_close_web_contents_deleters_.clear(); 319 // Must happen before |profile_| is set to NULL as 320 // |local_predictor_| accesses it. 321 if (local_predictor_) 322 local_predictor_->Shutdown(); 323 profile_ = NULL; 324 325 DCHECK(active_prerenders_.empty()); 326 } 327 328 PrerenderHandle* PrerenderManager::AddPrerenderFromLinkRelPrerender( 329 int process_id, 330 int route_id, 331 const GURL& url, 332 const uint32 rel_types, 333 const content::Referrer& referrer, 334 const gfx::Size& size) { 335 Origin origin = rel_types & PrerenderRelTypePrerender ? 336 ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN : 337 ORIGIN_LINK_REL_NEXT; 338 SessionStorageNamespace* session_storage_namespace = NULL; 339 // Unit tests pass in a process_id == -1. 340 if (process_id != -1) { 341 RenderViewHost* source_render_view_host = 342 RenderViewHost::FromID(process_id, route_id); 343 if (!source_render_view_host) 344 return NULL; 345 WebContents* source_web_contents = 346 WebContents::FromRenderViewHost(source_render_view_host); 347 if (!source_web_contents) 348 return NULL; 349 if (origin == ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN && 350 source_web_contents->GetURL().host() == url.host()) { 351 origin = ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN; 352 } 353 // TODO(ajwong): This does not correctly handle storage for isolated apps. 354 session_storage_namespace = 355 source_web_contents->GetController() 356 .GetDefaultSessionStorageNamespace(); 357 } 358 359 return AddPrerender(origin, process_id, url, referrer, size, 360 session_storage_namespace); 361 } 362 363 PrerenderHandle* PrerenderManager::AddPrerenderFromOmnibox( 364 const GURL& url, 365 SessionStorageNamespace* session_storage_namespace, 366 const gfx::Size& size) { 367 if (!IsOmniboxEnabled(profile_)) 368 return NULL; 369 return AddPrerender(ORIGIN_OMNIBOX, -1, url, content::Referrer(), size, 370 session_storage_namespace); 371 } 372 373 PrerenderHandle* PrerenderManager::AddPrerenderFromLocalPredictor( 374 const GURL& url, 375 SessionStorageNamespace* session_storage_namespace, 376 const gfx::Size& size) { 377 return AddPrerender(ORIGIN_LOCAL_PREDICTOR, -1, url, content::Referrer(), 378 size, session_storage_namespace); 379 } 380 381 PrerenderHandle* PrerenderManager::AddPrerenderFromExternalRequest( 382 const GURL& url, 383 const content::Referrer& referrer, 384 SessionStorageNamespace* session_storage_namespace, 385 const gfx::Size& size) { 386 return AddPrerender(ORIGIN_EXTERNAL_REQUEST, -1, url, referrer, size, 387 session_storage_namespace); 388 } 389 390 PrerenderHandle* PrerenderManager::AddPrerenderForInstant( 391 const GURL& url, 392 content::SessionStorageNamespace* session_storage_namespace, 393 const gfx::Size& size) { 394 DCHECK(chrome::ShouldPrefetchSearchResults()); 395 return AddPrerender(ORIGIN_INSTANT, -1, url, content::Referrer(), size, 396 session_storage_namespace); 397 } 398 399 void PrerenderManager::CancelAllPrerenders() { 400 DCHECK(CalledOnValidThread()); 401 while (!active_prerenders_.empty()) { 402 PrerenderContents* prerender_contents = 403 active_prerenders_.front()->contents(); 404 prerender_contents->Destroy(FINAL_STATUS_CANCELLED); 405 } 406 } 407 408 bool PrerenderManager::MaybeUsePrerenderedPage(const GURL& url, 409 chrome::NavigateParams* params) { 410 DCHECK(CalledOnValidThread()); 411 412 content::WebContents* web_contents = params->target_contents; 413 DCHECK(!IsWebContentsPrerendering(web_contents, NULL)); 414 415 // Don't prerender if the navigation involves some special parameters. 416 if (params->uses_post || !params->extra_headers.empty()) 417 return false; 418 419 DeleteOldEntries(); 420 to_delete_prerenders_.clear(); 421 422 // First, try to find prerender data with the correct session storage 423 // namespace. 424 // TODO(ajwong): This doesn't handle isolated apps correctly. 425 PrerenderData* prerender_data = FindPrerenderData( 426 url, 427 web_contents->GetController().GetDefaultSessionStorageNamespace()); 428 429 // If this failed, we may still find a prerender for the same URL, but a 430 // different session storage namespace. If we do, we might have to perform 431 // a merge. 432 if (!prerender_data) { 433 prerender_data = FindPrerenderData(url, NULL); 434 } else { 435 RecordEvent(prerender_data->contents(), 436 PRERENDER_EVENT_SWAPIN_CANDIDATE_NAMESPACE_MATCHES); 437 } 438 439 if (!prerender_data) 440 return false; 441 RecordEvent(prerender_data->contents(), PRERENDER_EVENT_SWAPIN_CANDIDATE); 442 DCHECK(prerender_data->contents()); 443 444 // If there is currently a merge pending for this prerender data, don't swap. 445 if (prerender_data->pending_swap()) 446 return false; 447 448 // Abort any existing pending swap on the target contents. 449 PrerenderData* pending_swap = 450 FindPrerenderDataForTargetContents(web_contents); 451 if (pending_swap) { 452 pending_swap->ClearPendingSwap(); 453 DCHECK(FindPrerenderDataForTargetContents(web_contents) == NULL); 454 } 455 456 RecordEvent(prerender_data->contents(), 457 PRERENDER_EVENT_SWAPIN_NO_MERGE_PENDING); 458 SessionStorageNamespace* target_namespace = 459 web_contents->GetController().GetDefaultSessionStorageNamespace(); 460 SessionStorageNamespace* prerender_namespace = 461 prerender_data->contents()->GetSessionStorageNamespace(); 462 // Only when actually prerendering is session storage namespace merging an 463 // issue. For the control group, it will be assumed that the merge succeeded. 464 if (prerender_namespace && prerender_namespace != target_namespace && 465 !prerender_namespace->IsAliasOf(target_namespace)) { 466 if (!ShouldMergeSessionStorageNamespaces()) { 467 RecordEvent(prerender_data->contents(), 468 PRERENDER_EVENT_SWAPIN_MERGING_DISABLED); 469 return false; 470 } 471 RecordEvent(prerender_data->contents(), 472 PRERENDER_EVENT_SWAPIN_ISSUING_MERGE); 473 prerender_data->set_pending_swap(new PendingSwap( 474 this, web_contents, prerender_data, url, 475 params->should_replace_current_entry)); 476 prerender_data->pending_swap()->BeginSwap(); 477 // Although this returns false, creating a PendingSwap registers with 478 // PrerenderTracker to throttle MAIN_FRAME navigations while the swap is 479 // pending. 480 return false; 481 } 482 483 // No need to merge; swap synchronously. 484 WebContents* new_web_contents = SwapInternal( 485 url, web_contents, prerender_data, 486 params->should_replace_current_entry); 487 if (!new_web_contents) 488 return false; 489 490 // Record the new target_contents for the callers. 491 params->target_contents = new_web_contents; 492 return true; 493 } 494 495 WebContents* PrerenderManager::SwapInternal( 496 const GURL& url, 497 WebContents* web_contents, 498 PrerenderData* prerender_data, 499 bool should_replace_current_entry) { 500 DCHECK(CalledOnValidThread()); 501 DCHECK(!IsWebContentsPrerendering(web_contents, NULL)); 502 503 // Only swap if the target WebContents has a CoreTabHelper delegate to swap 504 // out of it. For a normal WebContents, this is if it is in a TabStripModel. 505 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents); 506 if (!core_tab_helper || !core_tab_helper->delegate()) { 507 RecordEvent(prerender_data->contents(), PRERENDER_EVENT_SWAPIN_NO_DELEGATE); 508 return NULL; 509 } 510 511 PrerenderTabHelper* target_tab_helper = 512 PrerenderTabHelper::FromWebContents(web_contents); 513 if (!target_tab_helper) { 514 NOTREACHED(); 515 return NULL; 516 } 517 518 if (IsNoSwapInExperiment(prerender_data->contents()->experiment_id())) 519 return NULL; 520 521 if (WebContents* new_web_contents = 522 prerender_data->contents()->prerender_contents()) { 523 if (web_contents == new_web_contents) 524 return NULL; // Do not swap in to ourself. 525 526 // We cannot swap in if there is no last committed entry, because we would 527 // show a blank page under an existing entry from the current tab. Even if 528 // there is a pending entry, it may not commit. 529 // TODO(creis): If there is a pending navigation and no last committed 530 // entry, we might be able to transfer the network request instead. 531 if (!new_web_contents->GetController().CanPruneAllButLastCommitted()) { 532 // Abort this prerender so it is not used later. http://crbug.com/292121 533 prerender_data->contents()->Destroy(FINAL_STATUS_NAVIGATION_UNCOMMITTED); 534 return NULL; 535 } 536 } 537 538 // Do not swap if the target WebContents is not the only WebContents in its 539 // current BrowsingInstance. 540 if (web_contents->GetSiteInstance()->GetRelatedActiveContentsCount() != 1u) { 541 DCHECK_GT( 542 web_contents->GetSiteInstance()->GetRelatedActiveContentsCount(), 1u); 543 prerender_data->contents()->Destroy( 544 FINAL_STATUS_NON_EMPTY_BROWSING_INSTANCE); 545 return NULL; 546 } 547 548 // Do not use the prerendered version if there is an opener object. 549 if (web_contents->HasOpener()) { 550 prerender_data->contents()->Destroy(FINAL_STATUS_WINDOW_OPENER); 551 return NULL; 552 } 553 554 // Do not swap in the prerender if the current WebContents is being captured. 555 if (web_contents->GetCapturerCount() > 0) { 556 prerender_data->contents()->Destroy(FINAL_STATUS_PAGE_BEING_CAPTURED); 557 return NULL; 558 } 559 560 // If we are just in the control group (which can be detected by noticing 561 // that prerendering hasn't even started yet), record that |web_contents| now 562 // would be showing a prerendered contents, but otherwise, don't do anything. 563 if (!prerender_data->contents()->prerendering_has_started()) { 564 target_tab_helper->WouldHavePrerenderedNextLoad( 565 prerender_data->contents()->origin()); 566 prerender_data->contents()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED); 567 return NULL; 568 } 569 570 // Don't use prerendered pages if debugger is attached to the tab. 571 // See http://crbug.com/98541 572 if (content::DevToolsAgentHost::IsDebuggerAttached(web_contents)) { 573 DestroyAndMarkMatchCompleteAsUsed(prerender_data->contents(), 574 FINAL_STATUS_DEVTOOLS_ATTACHED); 575 return NULL; 576 } 577 578 // If the prerendered page is in the middle of a cross-site navigation, 579 // don't swap it in because there isn't a good way to merge histories. 580 if (prerender_data->contents()->IsCrossSiteNavigationPending()) { 581 DestroyAndMarkMatchCompleteAsUsed( 582 prerender_data->contents(), 583 FINAL_STATUS_CROSS_SITE_NAVIGATION_PENDING); 584 return NULL; 585 } 586 587 // For bookkeeping purposes, we need to mark this WebContents to 588 // reflect that it would have been prerendered. 589 if (GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP) { 590 target_tab_helper->WouldHavePrerenderedNextLoad( 591 prerender_data->contents()->origin()); 592 prerender_data->contents()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED); 593 return NULL; 594 } 595 596 // At this point, we've determined that we will use the prerender. 597 content::RenderProcessHost* process_host = 598 prerender_data->contents()->GetRenderViewHost()->GetProcess(); 599 prerender_process_hosts_.erase(process_host); 600 BrowserThread::PostTask( 601 BrowserThread::IO, FROM_HERE, 602 base::Bind(&PrerenderTracker::RemovePrerenderCookieStoreOnIOThread, 603 base::Unretained(prerender_tracker()), process_host->GetID(), 604 true)); 605 if (!prerender_data->contents()->load_start_time().is_null()) { 606 histograms_->RecordTimeUntilUsed( 607 prerender_data->contents()->origin(), 608 GetCurrentTimeTicks() - prerender_data->contents()->load_start_time()); 609 } 610 histograms_->RecordAbandonTimeUntilUsed( 611 prerender_data->contents()->origin(), 612 prerender_data->abandon_time().is_null() ? 613 base::TimeDelta() : 614 GetCurrentTimeTicks() - prerender_data->abandon_time()); 615 616 histograms_->RecordPerSessionCount(prerender_data->contents()->origin(), 617 ++prerenders_per_session_count_); 618 histograms_->RecordUsedPrerender(prerender_data->contents()->origin()); 619 620 if (prerender_data->pending_swap()) 621 prerender_data->pending_swap()->set_swap_successful(true); 622 ScopedVector<PrerenderData>::iterator to_erase = 623 FindIteratorForPrerenderContents(prerender_data->contents()); 624 DCHECK(active_prerenders_.end() != to_erase); 625 DCHECK_EQ(prerender_data, *to_erase); 626 scoped_ptr<PrerenderContents> 627 prerender_contents(prerender_data->ReleaseContents()); 628 active_prerenders_.erase(to_erase); 629 630 // Mark prerender as used. 631 prerender_contents->PrepareForUse(); 632 633 WebContents* new_web_contents = 634 prerender_contents->ReleasePrerenderContents(); 635 WebContents* old_web_contents = web_contents; 636 DCHECK(new_web_contents); 637 DCHECK(old_web_contents); 638 639 // Merge the browsing history. 640 new_web_contents->GetController().CopyStateFromAndPrune( 641 &old_web_contents->GetController(), 642 should_replace_current_entry); 643 CoreTabHelper::FromWebContents(old_web_contents)->delegate()-> 644 SwapTabContents(old_web_contents, 645 new_web_contents, 646 true, 647 prerender_contents->has_finished_loading()); 648 prerender_contents->CommitHistory(new_web_contents); 649 650 // Update PPLT metrics: 651 // If the tab has finished loading, record a PPLT of 0. 652 // If the tab is still loading, reset its start time to the current time. 653 PrerenderTabHelper* prerender_tab_helper = 654 PrerenderTabHelper::FromWebContents(new_web_contents); 655 DCHECK(prerender_tab_helper != NULL); 656 prerender_tab_helper->PrerenderSwappedIn(); 657 658 if (old_web_contents->NeedToFireBeforeUnload()) { 659 // Schedule the delete to occur after the tab has run its unload handlers. 660 // TODO(davidben): Honor the beforeunload event. http://crbug.com/304932 661 on_close_web_contents_deleters_.push_back( 662 new OnCloseWebContentsDeleter(this, old_web_contents)); 663 old_web_contents->DispatchBeforeUnload(false); 664 } else { 665 // No unload handler to run, so delete asap. 666 ScheduleDeleteOldWebContents(old_web_contents, NULL); 667 } 668 669 // TODO(cbentzel): Should prerender_contents move to the pending delete 670 // list, instead of deleting directly here? 671 AddToHistory(prerender_contents.get()); 672 RecordNavigation(url); 673 return new_web_contents; 674 } 675 676 void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry, 677 FinalStatus final_status) { 678 DCHECK(CalledOnValidThread()); 679 DCHECK(entry); 680 681 ScopedVector<PrerenderData>::iterator it = 682 FindIteratorForPrerenderContents(entry); 683 DCHECK(it != active_prerenders_.end()); 684 685 // If this PrerenderContents is being deleted due to a cancellation any time 686 // after the prerender has started then we need to create a dummy replacement 687 // for PPLT accounting purposes for the Match Complete group. This is the case 688 // if the cancellation is for any reason that would not occur in the control 689 // group case. 690 if (entry->prerendering_has_started() && 691 entry->match_complete_status() == 692 PrerenderContents::MATCH_COMPLETE_DEFAULT && 693 NeedMatchCompleteDummyForFinalStatus(final_status) && 694 ActuallyPrerendering() && 695 GetMode() == PRERENDER_MODE_EXPERIMENT_MATCH_COMPLETE_GROUP) { 696 // TODO(tburkard): I'd like to DCHECK that we are actually prerendering. 697 // However, what if new conditions are added and 698 // NeedMatchCompleteDummyForFinalStatus is not being updated. Not sure 699 // what's the best thing to do here. For now, I will just check whether 700 // we are actually prerendering. 701 (*it)->MakeIntoMatchCompleteReplacement(); 702 } else { 703 to_delete_prerenders_.push_back(*it); 704 (*it)->ClearPendingSwap(); 705 active_prerenders_.weak_erase(it); 706 } 707 708 // Destroy the old WebContents relatively promptly to reduce resource usage. 709 PostCleanupTask(); 710 } 711 712 void PrerenderManager::RecordPageLoadTimeNotSwappedIn( 713 Origin origin, 714 base::TimeDelta page_load_time, 715 const GURL& url) { 716 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 717 histograms_->RecordPageLoadTimeNotSwappedIn(origin, page_load_time, url); 718 } 719 720 void PrerenderManager::RecordPerceivedPageLoadTime( 721 Origin origin, 722 NavigationType navigation_type, 723 base::TimeDelta perceived_page_load_time, 724 double fraction_plt_elapsed_at_swap_in, 725 const GURL& url) { 726 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 727 if (!IsEnabled()) 728 return; 729 730 histograms_->RecordPerceivedPageLoadTime( 731 origin, perceived_page_load_time, navigation_type, url); 732 733 if (navigation_type == NAVIGATION_TYPE_PRERENDERED) { 734 histograms_->RecordPercentLoadDoneAtSwapin( 735 origin, fraction_plt_elapsed_at_swap_in); 736 } 737 if (local_predictor_) { 738 local_predictor_->OnPLTEventForURL(url, perceived_page_load_time); 739 } 740 } 741 742 // static 743 PrerenderManager::PrerenderManagerMode PrerenderManager::GetMode() { 744 return mode_; 745 } 746 747 // static 748 void PrerenderManager::SetMode(PrerenderManagerMode mode) { 749 mode_ = mode; 750 } 751 752 // static 753 const char* PrerenderManager::GetModeString() { 754 switch (mode_) { 755 case PRERENDER_MODE_DISABLED: 756 return "_Disabled"; 757 case PRERENDER_MODE_ENABLED: 758 case PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP: 759 return "_Enabled"; 760 case PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP: 761 return "_Control"; 762 case PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP: 763 return "_Multi"; 764 case PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP: 765 return "_15MinTTL"; 766 case PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP: 767 return "_NoUse"; 768 case PRERENDER_MODE_EXPERIMENT_MATCH_COMPLETE_GROUP: 769 return "_MatchComplete"; 770 case PRERENDER_MODE_MAX: 771 default: 772 NOTREACHED() << "Invalid PrerenderManager mode."; 773 break; 774 } 775 return ""; 776 } 777 778 // static 779 bool PrerenderManager::IsPrerenderingPossible() { 780 return GetMode() != PRERENDER_MODE_DISABLED; 781 } 782 783 // static 784 bool PrerenderManager::ActuallyPrerendering() { 785 return IsPrerenderingPossible() && !IsControlGroup(kNoExperiment); 786 } 787 788 // static 789 bool PrerenderManager::IsControlGroup(uint8 experiment_id) { 790 return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP || 791 IsControlGroupExperiment(experiment_id); 792 } 793 794 // static 795 bool PrerenderManager::IsNoUseGroup() { 796 return GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP; 797 } 798 799 bool PrerenderManager::IsWebContentsPrerendering( 800 const WebContents* web_contents, 801 Origin* origin) const { 802 DCHECK(CalledOnValidThread()); 803 if (PrerenderContents* prerender_contents = 804 GetPrerenderContents(web_contents)) { 805 if (origin) 806 *origin = prerender_contents->origin(); 807 return true; 808 } 809 return false; 810 } 811 812 bool PrerenderManager::HasPrerenderedUrl( 813 GURL url, 814 content::WebContents* web_contents) const { 815 content::SessionStorageNamespace* session_storage_namespace = web_contents-> 816 GetController().GetDefaultSessionStorageNamespace(); 817 818 for (ScopedVector<PrerenderData>::const_iterator it = 819 active_prerenders_.begin(); 820 it != active_prerenders_.end(); ++it) { 821 PrerenderContents* prerender_contents = (*it)->contents(); 822 if (prerender_contents->Matches(url, session_storage_namespace)) { 823 return true; 824 } 825 } 826 return false; 827 } 828 829 PrerenderContents* PrerenderManager::GetPrerenderContents( 830 const content::WebContents* web_contents) const { 831 DCHECK(CalledOnValidThread()); 832 for (ScopedVector<PrerenderData>::const_iterator it = 833 active_prerenders_.begin(); 834 it != active_prerenders_.end(); ++it) { 835 WebContents* prerender_web_contents = 836 (*it)->contents()->prerender_contents(); 837 if (prerender_web_contents == web_contents) { 838 return (*it)->contents(); 839 } 840 } 841 842 // Also check the pending-deletion list. If the prerender is in pending 843 // delete, anyone with a handle on the WebContents needs to know. 844 for (ScopedVector<PrerenderData>::const_iterator it = 845 to_delete_prerenders_.begin(); 846 it != to_delete_prerenders_.end(); ++it) { 847 WebContents* prerender_web_contents = 848 (*it)->contents()->prerender_contents(); 849 if (prerender_web_contents == web_contents) { 850 return (*it)->contents(); 851 } 852 } 853 return NULL; 854 } 855 856 PrerenderContents* PrerenderManager::GetPrerenderContentsForRoute( 857 int child_id, 858 int route_id) const { 859 content::WebContents* web_contents = 860 tab_util::GetWebContentsByID(child_id, route_id); 861 if (web_contents == NULL) 862 return NULL; 863 return GetPrerenderContents(web_contents); 864 } 865 866 const std::vector<WebContents*> 867 PrerenderManager::GetAllPrerenderingContents() const { 868 DCHECK(CalledOnValidThread()); 869 std::vector<WebContents*> result; 870 871 for (ScopedVector<PrerenderData>::const_iterator it = 872 active_prerenders_.begin(); 873 it != active_prerenders_.end(); ++it) { 874 if (WebContents* contents = (*it)->contents()->prerender_contents()) 875 result.push_back(contents); 876 } 877 878 return result; 879 } 880 881 bool PrerenderManager::HasRecentlyBeenNavigatedTo(Origin origin, 882 const GURL& url) { 883 DCHECK(CalledOnValidThread()); 884 885 CleanUpOldNavigations(); 886 std::list<NavigationRecord>::const_reverse_iterator end = navigations_.rend(); 887 for (std::list<NavigationRecord>::const_reverse_iterator it = 888 navigations_.rbegin(); 889 it != end; 890 ++it) { 891 if (it->url == url) { 892 base::TimeDelta delta = GetCurrentTimeTicks() - it->time; 893 histograms_->RecordTimeSinceLastRecentVisit(origin, delta); 894 return true; 895 } 896 } 897 898 return false; 899 } 900 901 // static 902 bool PrerenderManager::IsValidHttpMethod(const std::string& method) { 903 // method has been canonicalized to upper case at this point so we can just 904 // compare them. 905 DCHECK_EQ(method, StringToUpperASCII(method)); 906 for (size_t i = 0; i < arraysize(kValidHttpMethods); ++i) { 907 if (method.compare(kValidHttpMethods[i]) == 0) 908 return true; 909 } 910 911 return false; 912 } 913 914 // static 915 bool PrerenderManager::DoesURLHaveValidScheme(const GURL& url) { 916 return (url.SchemeIsHTTPOrHTTPS() || 917 url.SchemeIs(extensions::kExtensionScheme) || 918 url.SchemeIs("data")); 919 } 920 921 // static 922 bool PrerenderManager::DoesSubresourceURLHaveValidScheme(const GURL& url) { 923 return DoesURLHaveValidScheme(url) || url == GURL(url::kAboutBlankURL); 924 } 925 926 base::DictionaryValue* PrerenderManager::GetAsValue() const { 927 DCHECK(CalledOnValidThread()); 928 base::DictionaryValue* dict_value = new base::DictionaryValue(); 929 dict_value->Set("history", prerender_history_->GetEntriesAsValue()); 930 dict_value->Set("active", GetActivePrerendersAsValue()); 931 dict_value->SetBoolean("enabled", IsEnabled()); 932 dict_value->SetBoolean("omnibox_enabled", IsOmniboxEnabled(profile_)); 933 // If prerender is disabled via a flag this method is not even called. 934 std::string enabled_note; 935 if (IsControlGroup(kNoExperiment)) 936 enabled_note += "(Control group: Not actually prerendering) "; 937 if (IsNoUseGroup()) 938 enabled_note += "(No-use group: Not swapping in prerendered pages) "; 939 if (GetMode() == PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP) 940 enabled_note += 941 "(15 min TTL group: Extended prerender eviction to 15 mins) "; 942 dict_value->SetString("enabled_note", enabled_note); 943 return dict_value; 944 } 945 946 void PrerenderManager::ClearData(int clear_flags) { 947 DCHECK_GE(clear_flags, 0); 948 DCHECK_LT(clear_flags, CLEAR_MAX); 949 if (clear_flags & CLEAR_PRERENDER_CONTENTS) 950 DestroyAllContents(FINAL_STATUS_CACHE_OR_HISTORY_CLEARED); 951 // This has to be second, since destroying prerenders can add to the history. 952 if (clear_flags & CLEAR_PRERENDER_HISTORY) 953 prerender_history_->Clear(); 954 } 955 956 void PrerenderManager::RecordFinalStatusWithMatchCompleteStatus( 957 Origin origin, 958 uint8 experiment_id, 959 PrerenderContents::MatchCompleteStatus mc_status, 960 FinalStatus final_status) const { 961 histograms_->RecordFinalStatus(origin, 962 experiment_id, 963 mc_status, 964 final_status); 965 } 966 967 void PrerenderManager::RecordNavigation(const GURL& url) { 968 DCHECK(CalledOnValidThread()); 969 970 navigations_.push_back(NavigationRecord(url, GetCurrentTimeTicks())); 971 CleanUpOldNavigations(); 972 } 973 974 // protected 975 struct PrerenderManager::PrerenderData::OrderByExpiryTime { 976 bool operator()(const PrerenderData* a, const PrerenderData* b) const { 977 return a->expiry_time() < b->expiry_time(); 978 } 979 }; 980 981 PrerenderManager::PrerenderData::PrerenderData(PrerenderManager* manager, 982 PrerenderContents* contents, 983 base::TimeTicks expiry_time) 984 : manager_(manager), 985 contents_(contents), 986 handle_count_(0), 987 expiry_time_(expiry_time) { 988 DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_); 989 } 990 991 PrerenderManager::PrerenderData::~PrerenderData() { 992 } 993 994 void PrerenderManager::PrerenderData::MakeIntoMatchCompleteReplacement() { 995 DCHECK(contents_); 996 contents_->set_match_complete_status( 997 PrerenderContents::MATCH_COMPLETE_REPLACED); 998 PrerenderData* to_delete = new PrerenderData(manager_, contents_.release(), 999 expiry_time_); 1000 contents_.reset(to_delete->contents_->CreateMatchCompleteReplacement()); 1001 manager_->to_delete_prerenders_.push_back(to_delete); 1002 } 1003 1004 void PrerenderManager::PrerenderData::OnHandleCreated(PrerenderHandle* handle) { 1005 DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_); 1006 ++handle_count_; 1007 contents_->AddObserver(handle); 1008 } 1009 1010 void PrerenderManager::PrerenderData::OnHandleNavigatedAway( 1011 PrerenderHandle* handle) { 1012 DCHECK_LT(0, handle_count_); 1013 DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_); 1014 if (abandon_time_.is_null()) 1015 abandon_time_ = base::TimeTicks::Now(); 1016 // We intentionally don't decrement the handle count here, so that the 1017 // prerender won't be canceled until it times out. 1018 manager_->SourceNavigatedAway(this); 1019 } 1020 1021 void PrerenderManager::PrerenderData::OnHandleCanceled( 1022 PrerenderHandle* handle) { 1023 DCHECK_LT(0, handle_count_); 1024 DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_); 1025 1026 if (--handle_count_ == 0) { 1027 // This will eventually remove this object from active_prerenders_. 1028 contents_->Destroy(FINAL_STATUS_CANCELLED); 1029 } 1030 } 1031 1032 void PrerenderManager::PrerenderData::ClearPendingSwap() { 1033 pending_swap_.reset(NULL); 1034 } 1035 1036 PrerenderContents* PrerenderManager::PrerenderData::ReleaseContents() { 1037 return contents_.release(); 1038 } 1039 1040 PrerenderManager::PendingSwap::PendingSwap( 1041 PrerenderManager* manager, 1042 content::WebContents* target_contents, 1043 PrerenderData* prerender_data, 1044 const GURL& url, 1045 bool should_replace_current_entry) 1046 : content::WebContentsObserver(target_contents), 1047 manager_(manager), 1048 prerender_data_(prerender_data), 1049 url_(url), 1050 should_replace_current_entry_(should_replace_current_entry), 1051 start_time_(base::TimeTicks::Now()), 1052 seen_target_route_id_(false), 1053 swap_successful_(false), 1054 weak_factory_(this) { 1055 } 1056 1057 PrerenderManager::PendingSwap::~PendingSwap() { 1058 manager_->prerender_tracker()->RemovePrerenderPendingSwap( 1059 target_route_id_, swap_successful_); 1060 } 1061 1062 void PrerenderManager::PendingSwap::BeginSwap() { 1063 if (g_hang_session_storage_merges_for_testing) 1064 return; 1065 1066 SessionStorageNamespace* target_namespace = 1067 web_contents()->GetController().GetDefaultSessionStorageNamespace(); 1068 SessionStorageNamespace* prerender_namespace = 1069 prerender_data_->contents()->GetSessionStorageNamespace(); 1070 1071 prerender_namespace->Merge( 1072 true, prerender_data_->contents()->child_id(), 1073 target_namespace, 1074 base::Bind(&PrerenderManager::PendingSwap::OnMergeCompleted, 1075 weak_factory_.GetWeakPtr())); 1076 1077 merge_timeout_.Start( 1078 FROM_HERE, 1079 base::TimeDelta::FromMilliseconds( 1080 kSessionStorageNamespaceMergeTimeoutMs), 1081 this, &PrerenderManager::PendingSwap::OnMergeTimeout); 1082 } 1083 1084 void PrerenderManager::PendingSwap::AboutToNavigateRenderView( 1085 RenderViewHost* render_view_host) { 1086 if (seen_target_route_id_) { 1087 // A second navigation began browser-side. 1088 prerender_data_->ClearPendingSwap(); 1089 return; 1090 } 1091 1092 seen_target_route_id_ = true; 1093 target_route_id_ = PrerenderTracker::ChildRouteIdPair( 1094 render_view_host->GetMainFrame()->GetProcess()->GetID(), 1095 render_view_host->GetMainFrame()->GetRoutingID()); 1096 manager_->prerender_tracker()->AddPrerenderPendingSwap( 1097 target_route_id_, url_); 1098 } 1099 1100 void PrerenderManager::PendingSwap::DidStartProvisionalLoadForFrame( 1101 content::RenderFrameHost* render_frame_host, 1102 const GURL& validated_url, 1103 bool is_error_page, 1104 bool is_iframe_srcdoc) { 1105 if (render_frame_host->GetParent()) 1106 return; 1107 1108 // We must only cancel the pending swap if the url navigated to is not 1109 // the URL being attempted to be swapped in. That's because in the normal 1110 // flow, a ProvisionalChangeToMainFrameUrl will happen for the URL attempted 1111 // to be swapped in immediately after the pending swap has issued its merge. 1112 if (validated_url != url_) 1113 prerender_data_->ClearPendingSwap(); 1114 } 1115 1116 void PrerenderManager::PendingSwap::DidCommitProvisionalLoadForFrame( 1117 content::RenderFrameHost* render_frame_host, 1118 const GURL& validated_url, 1119 ui::PageTransition transition_type) { 1120 if (render_frame_host->GetParent()) 1121 return; 1122 prerender_data_->ClearPendingSwap(); 1123 } 1124 1125 void PrerenderManager::PendingSwap::DidFailProvisionalLoad( 1126 content::RenderFrameHost* render_frame_host, 1127 const GURL& validated_url, 1128 int error_code, 1129 const base::string16& error_description) { 1130 if (render_frame_host->GetParent()) 1131 return; 1132 prerender_data_->ClearPendingSwap(); 1133 } 1134 1135 void PrerenderManager::PendingSwap::WebContentsDestroyed() { 1136 prerender_data_->ClearPendingSwap(); 1137 } 1138 1139 void PrerenderManager::PendingSwap::RecordEvent(PrerenderEvent event) const { 1140 manager_->RecordEvent(prerender_data_->contents(), event); 1141 } 1142 1143 void PrerenderManager::PendingSwap::OnMergeCompleted( 1144 SessionStorageNamespace::MergeResult result) { 1145 UMA_HISTOGRAM_TIMES("Prerender.SessionStorageNamespaceMergeTime", 1146 base::TimeTicks::Now() - start_time_); 1147 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_MERGE_DONE); 1148 1149 // Log the exact merge result in a histogram. 1150 switch (result) { 1151 case SessionStorageNamespace::MERGE_RESULT_NAMESPACE_NOT_FOUND: 1152 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NAMESPACE_NOT_FOUND); 1153 break; 1154 case SessionStorageNamespace::MERGE_RESULT_NAMESPACE_NOT_ALIAS: 1155 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NAMESPACE_NOT_ALIAS); 1156 break; 1157 case SessionStorageNamespace::MERGE_RESULT_NOT_LOGGING: 1158 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NOT_LOGGING); 1159 break; 1160 case SessionStorageNamespace::MERGE_RESULT_NO_TRANSACTIONS: 1161 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NO_TRANSACTIONS); 1162 break; 1163 case SessionStorageNamespace::MERGE_RESULT_TOO_MANY_TRANSACTIONS: 1164 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_TOO_MANY_TRANSACTIONS); 1165 break; 1166 case SessionStorageNamespace::MERGE_RESULT_NOT_MERGEABLE: 1167 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NOT_MERGEABLE); 1168 break; 1169 case SessionStorageNamespace::MERGE_RESULT_MERGEABLE: 1170 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_MERGEABLE); 1171 break; 1172 default: 1173 NOTREACHED(); 1174 } 1175 1176 if (result != SessionStorageNamespace::MERGE_RESULT_MERGEABLE && 1177 result != SessionStorageNamespace::MERGE_RESULT_NO_TRANSACTIONS) { 1178 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_MERGE_FAILED); 1179 prerender_data_->ClearPendingSwap(); 1180 return; 1181 } 1182 1183 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_SWAPPING_IN); 1184 1185 // Note that SwapInternal will, on success, delete |prerender_data_| and 1186 // |this|. It will also delete |this| in some failure cases. Pass in a new 1187 // GURL object rather than a reference to |url_|. Also hold on to |manager_| 1188 // and |prerender_data_|. 1189 // 1190 // TODO(davidben): Can we make this less fragile? 1191 PrerenderManager* manager = manager_; 1192 PrerenderData* prerender_data = prerender_data_; 1193 WebContents* new_web_contents = 1194 manager_->SwapInternal(GURL(url_), 1195 web_contents(), 1196 prerender_data_, 1197 should_replace_current_entry_); 1198 if (!new_web_contents) { 1199 manager->RecordEvent(prerender_data->contents(), 1200 PRERENDER_EVENT_MERGE_RESULT_SWAPIN_FAILED); 1201 // Depending on whether SwapInternal called Destroy() or simply failed to 1202 // swap, |this| may or may not be deleted. Either way, if the swap failed, 1203 // |prerender_data| is deleted asynchronously, so this call is a no-op if 1204 // |this| is already gone. 1205 prerender_data->ClearPendingSwap(); 1206 } 1207 } 1208 1209 void PrerenderManager::PendingSwap::OnMergeTimeout() { 1210 RecordEvent(PRERENDER_EVENT_MERGE_RESULT_TIMED_OUT); 1211 prerender_data_->ClearPendingSwap(); 1212 } 1213 1214 void PrerenderManager::SetPrerenderContentsFactory( 1215 PrerenderContents::Factory* prerender_contents_factory) { 1216 DCHECK(CalledOnValidThread()); 1217 prerender_contents_factory_.reset(prerender_contents_factory); 1218 } 1219 1220 void PrerenderManager::SourceNavigatedAway(PrerenderData* prerender_data) { 1221 // The expiry time of our prerender data will likely change because of 1222 // this navigation. This requires a resort of active_prerenders_. 1223 ScopedVector<PrerenderData>::iterator it = 1224 std::find(active_prerenders_.begin(), active_prerenders_.end(), 1225 prerender_data); 1226 if (it == active_prerenders_.end()) 1227 return; 1228 1229 (*it)->set_expiry_time( 1230 std::min((*it)->expiry_time(), 1231 GetExpiryTimeForNavigatedAwayPrerender())); 1232 SortActivePrerenders(); 1233 } 1234 1235 net::URLRequestContextGetter* PrerenderManager::GetURLRequestContext() { 1236 return content::BrowserContext::GetDefaultStoragePartition(profile_)-> 1237 GetURLRequestContext(); 1238 } 1239 1240 1241 // private 1242 PrerenderHandle* PrerenderManager::AddPrerender( 1243 Origin origin, 1244 int process_id, 1245 const GURL& url_arg, 1246 const content::Referrer& referrer, 1247 const gfx::Size& size, 1248 SessionStorageNamespace* session_storage_namespace) { 1249 DCHECK(CalledOnValidThread()); 1250 1251 if (!IsEnabled()) 1252 return NULL; 1253 1254 if ((origin == ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN || 1255 origin == ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN) && 1256 IsGoogleSearchResultURL(referrer.url)) { 1257 origin = ORIGIN_GWS_PRERENDER; 1258 } 1259 1260 GURL url = url_arg; 1261 GURL alias_url; 1262 uint8 experiment = GetQueryStringBasedExperiment(url_arg); 1263 if (IsControlGroup(experiment) && 1264 MaybeGetQueryStringBasedAliasURL(url, &alias_url)) { 1265 url = alias_url; 1266 } 1267 1268 // From here on, we will record a FinalStatus so we need to register with the 1269 // histogram tracking. 1270 histograms_->RecordPrerender(origin, url_arg); 1271 1272 if (PrerenderData* preexisting_prerender_data = 1273 FindPrerenderData(url, session_storage_namespace)) { 1274 RecordFinalStatusWithoutCreatingPrerenderContents( 1275 url, origin, experiment, FINAL_STATUS_DUPLICATE); 1276 return new PrerenderHandle(preexisting_prerender_data); 1277 } 1278 1279 // Do not prerender if there are too many render processes, and we would 1280 // have to use an existing one. We do not want prerendering to happen in 1281 // a shared process, so that we can always reliably lower the CPU 1282 // priority for prerendering. 1283 // In single-process mode, ShouldTryToUseExistingProcessHost() always returns 1284 // true, so that case needs to be explicitly checked for. 1285 // TODO(tburkard): Figure out how to cancel prerendering in the opposite 1286 // case, when a new tab is added to a process used for prerendering. 1287 // TODO(ppi): Check whether there are usually enough render processes 1288 // available on Android. If not, kill an existing renderers so that we can 1289 // create a new one. 1290 if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost( 1291 profile_, url) && 1292 !content::RenderProcessHost::run_renderer_in_process()) { 1293 RecordFinalStatusWithoutCreatingPrerenderContents( 1294 url, origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES); 1295 return NULL; 1296 } 1297 1298 // Check if enough time has passed since the last prerender. 1299 if (!DoesRateLimitAllowPrerender(origin)) { 1300 // Cancel the prerender. We could add it to the pending prerender list but 1301 // this doesn't make sense as the next prerender request will be triggered 1302 // by a navigation and is unlikely to be the same site. 1303 RecordFinalStatusWithoutCreatingPrerenderContents( 1304 url, origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED); 1305 return NULL; 1306 } 1307 1308 if (IsPrerenderCookieStoreEnabled() && !cookie_store_loaded()) { 1309 // Only prerender if the cookie store for this profile has been loaded. 1310 // This is required by PrerenderCookieMonster. 1311 RecordFinalStatusWithoutCreatingPrerenderContents( 1312 url, origin, experiment, FINAL_STATUS_COOKIE_STORE_NOT_LOADED); 1313 return NULL; 1314 } 1315 1316 PrerenderContents* prerender_contents = CreatePrerenderContents( 1317 url, referrer, origin, experiment); 1318 DCHECK(prerender_contents); 1319 active_prerenders_.push_back( 1320 new PrerenderData(this, prerender_contents, 1321 GetExpiryTimeForNewPrerender(origin))); 1322 if (!prerender_contents->Init()) { 1323 DCHECK(active_prerenders_.end() == 1324 FindIteratorForPrerenderContents(prerender_contents)); 1325 return NULL; 1326 } 1327 1328 histograms_->RecordPrerenderStarted(origin); 1329 DCHECK(!prerender_contents->prerendering_has_started()); 1330 1331 PrerenderHandle* prerender_handle = 1332 new PrerenderHandle(active_prerenders_.back()); 1333 SortActivePrerenders(); 1334 1335 last_prerender_start_time_ = GetCurrentTimeTicks(); 1336 1337 gfx::Size contents_size = 1338 size.IsEmpty() ? config_.default_tab_bounds.size() : size; 1339 1340 net::URLRequestContextGetter* request_context = 1341 (IsPrerenderCookieStoreEnabled() ? GetURLRequestContext() : NULL); 1342 1343 prerender_contents->StartPrerendering(process_id, contents_size, 1344 session_storage_namespace, 1345 request_context); 1346 1347 DCHECK(IsControlGroup(experiment) || 1348 prerender_contents->prerendering_has_started() || 1349 (origin == ORIGIN_LOCAL_PREDICTOR && 1350 IsLocalPredictorPrerenderAlwaysControlEnabled())); 1351 1352 if (GetMode() == PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP) 1353 histograms_->RecordConcurrency(active_prerenders_.size()); 1354 1355 // Query the history to see if the URL being prerendered has ever been 1356 // visited before. 1357 HistoryService* history_service = HistoryServiceFactory::GetForProfile( 1358 profile_, Profile::EXPLICIT_ACCESS); 1359 if (history_service) { 1360 history_service->QueryURL( 1361 url, 1362 false, 1363 base::Bind(&PrerenderManager::OnHistoryServiceDidQueryURL, 1364 base::Unretained(this), 1365 origin, 1366 experiment), 1367 &query_url_tracker_); 1368 } 1369 1370 StartSchedulingPeriodicCleanups(); 1371 return prerender_handle; 1372 } 1373 1374 void PrerenderManager::StartSchedulingPeriodicCleanups() { 1375 DCHECK(CalledOnValidThread()); 1376 if (repeating_timer_.IsRunning()) 1377 return; 1378 repeating_timer_.Start(FROM_HERE, 1379 base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs), 1380 this, 1381 &PrerenderManager::PeriodicCleanup); 1382 } 1383 1384 void PrerenderManager::StopSchedulingPeriodicCleanups() { 1385 DCHECK(CalledOnValidThread()); 1386 repeating_timer_.Stop(); 1387 } 1388 1389 void PrerenderManager::PeriodicCleanup() { 1390 DCHECK(CalledOnValidThread()); 1391 1392 base::ElapsedTimer resource_timer; 1393 1394 // Grab a copy of the current PrerenderContents pointers, so that we 1395 // will not interfere with potential deletions of the list. 1396 std::vector<PrerenderContents*> 1397 prerender_contents(active_prerenders_.size()); 1398 std::transform(active_prerenders_.begin(), active_prerenders_.end(), 1399 prerender_contents.begin(), 1400 std::mem_fun(&PrerenderData::contents)); 1401 1402 // And now check for prerenders using too much memory. 1403 std::for_each(prerender_contents.begin(), prerender_contents.end(), 1404 std::mem_fun( 1405 &PrerenderContents::DestroyWhenUsingTooManyResources)); 1406 1407 // Measure how long the resource checks took. http://crbug.com/305419. 1408 UMA_HISTOGRAM_TIMES("Prerender.PeriodicCleanupResourceCheckTime", 1409 resource_timer.Elapsed()); 1410 1411 base::ElapsedTimer cleanup_timer; 1412 1413 // Perform deferred cleanup work. 1414 DeleteOldWebContents(); 1415 DeleteOldEntries(); 1416 if (active_prerenders_.empty()) 1417 StopSchedulingPeriodicCleanups(); 1418 1419 to_delete_prerenders_.clear(); 1420 1421 // Measure how long a the various cleanup tasks took. http://crbug.com/305419. 1422 UMA_HISTOGRAM_TIMES("Prerender.PeriodicCleanupDeleteContentsTime", 1423 cleanup_timer.Elapsed()); 1424 } 1425 1426 void PrerenderManager::PostCleanupTask() { 1427 DCHECK(CalledOnValidThread()); 1428 base::MessageLoop::current()->PostTask( 1429 FROM_HERE, 1430 base::Bind(&PrerenderManager::PeriodicCleanup, AsWeakPtr())); 1431 } 1432 1433 base::TimeTicks PrerenderManager::GetExpiryTimeForNewPrerender( 1434 Origin origin) const { 1435 base::TimeDelta ttl = config_.time_to_live; 1436 if (origin == ORIGIN_LOCAL_PREDICTOR) 1437 ttl = base::TimeDelta::FromSeconds(GetLocalPredictorTTLSeconds()); 1438 return GetCurrentTimeTicks() + ttl; 1439 } 1440 1441 base::TimeTicks PrerenderManager::GetExpiryTimeForNavigatedAwayPrerender() 1442 const { 1443 return GetCurrentTimeTicks() + config_.abandon_time_to_live; 1444 } 1445 1446 void PrerenderManager::DeleteOldEntries() { 1447 DCHECK(CalledOnValidThread()); 1448 while (!active_prerenders_.empty()) { 1449 PrerenderData* prerender_data = active_prerenders_.front(); 1450 DCHECK(prerender_data); 1451 DCHECK(prerender_data->contents()); 1452 1453 if (prerender_data->expiry_time() > GetCurrentTimeTicks()) 1454 return; 1455 prerender_data->contents()->Destroy(FINAL_STATUS_TIMED_OUT); 1456 } 1457 } 1458 1459 base::Time PrerenderManager::GetCurrentTime() const { 1460 return base::Time::Now(); 1461 } 1462 1463 base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const { 1464 return base::TimeTicks::Now(); 1465 } 1466 1467 PrerenderContents* PrerenderManager::CreatePrerenderContents( 1468 const GURL& url, 1469 const content::Referrer& referrer, 1470 Origin origin, 1471 uint8 experiment_id) { 1472 DCHECK(CalledOnValidThread()); 1473 return prerender_contents_factory_->CreatePrerenderContents( 1474 this, profile_, url, referrer, origin, experiment_id); 1475 } 1476 1477 void PrerenderManager::SortActivePrerenders() { 1478 std::sort(active_prerenders_.begin(), active_prerenders_.end(), 1479 PrerenderData::OrderByExpiryTime()); 1480 } 1481 1482 PrerenderManager::PrerenderData* PrerenderManager::FindPrerenderData( 1483 const GURL& url, 1484 const SessionStorageNamespace* session_storage_namespace) { 1485 for (ScopedVector<PrerenderData>::iterator it = active_prerenders_.begin(); 1486 it != active_prerenders_.end(); ++it) { 1487 if ((*it)->contents()->Matches(url, session_storage_namespace)) 1488 return *it; 1489 } 1490 return NULL; 1491 } 1492 1493 PrerenderManager::PrerenderData* 1494 PrerenderManager::FindPrerenderDataForTargetContents( 1495 WebContents* target_contents) { 1496 for (ScopedVector<PrerenderData>::iterator it = active_prerenders_.begin(); 1497 it != active_prerenders_.end(); ++it) { 1498 if ((*it)->pending_swap() && 1499 (*it)->pending_swap()->web_contents() == target_contents) 1500 return *it; 1501 } 1502 return NULL; 1503 } 1504 1505 ScopedVector<PrerenderManager::PrerenderData>::iterator 1506 PrerenderManager::FindIteratorForPrerenderContents( 1507 PrerenderContents* prerender_contents) { 1508 for (ScopedVector<PrerenderData>::iterator it = active_prerenders_.begin(); 1509 it != active_prerenders_.end(); ++it) { 1510 if (prerender_contents == (*it)->contents()) 1511 return it; 1512 } 1513 return active_prerenders_.end(); 1514 } 1515 1516 bool PrerenderManager::DoesRateLimitAllowPrerender(Origin origin) const { 1517 DCHECK(CalledOnValidThread()); 1518 base::TimeDelta elapsed_time = 1519 GetCurrentTimeTicks() - last_prerender_start_time_; 1520 histograms_->RecordTimeBetweenPrerenderRequests(origin, elapsed_time); 1521 if (!config_.rate_limit_enabled) 1522 return true; 1523 // The LocalPredictor may issue multiple prerenders simultaneously (if so 1524 // configured), so no throttling. 1525 if (origin == ORIGIN_LOCAL_PREDICTOR) 1526 return true; 1527 return elapsed_time >= 1528 base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs); 1529 } 1530 1531 void PrerenderManager::DeleteOldWebContents() { 1532 while (!old_web_contents_list_.empty()) { 1533 WebContents* web_contents = old_web_contents_list_.front(); 1534 old_web_contents_list_.pop_front(); 1535 // TODO(dominich): should we use Instant Unload Handler here? 1536 delete web_contents; 1537 } 1538 } 1539 1540 void PrerenderManager::CleanUpOldNavigations() { 1541 DCHECK(CalledOnValidThread()); 1542 1543 // Cutoff. Navigations before this cutoff can be discarded. 1544 base::TimeTicks cutoff = GetCurrentTimeTicks() - 1545 base::TimeDelta::FromMilliseconds(kNavigationRecordWindowMs); 1546 while (!navigations_.empty()) { 1547 if (navigations_.front().time > cutoff) 1548 break; 1549 navigations_.pop_front(); 1550 } 1551 } 1552 1553 void PrerenderManager::ScheduleDeleteOldWebContents( 1554 WebContents* tab, 1555 OnCloseWebContentsDeleter* deleter) { 1556 old_web_contents_list_.push_back(tab); 1557 PostCleanupTask(); 1558 1559 if (deleter) { 1560 ScopedVector<OnCloseWebContentsDeleter>::iterator i = std::find( 1561 on_close_web_contents_deleters_.begin(), 1562 on_close_web_contents_deleters_.end(), 1563 deleter); 1564 DCHECK(i != on_close_web_contents_deleters_.end()); 1565 on_close_web_contents_deleters_.erase(i); 1566 } 1567 } 1568 1569 void PrerenderManager::AddToHistory(PrerenderContents* contents) { 1570 PrerenderHistory::Entry entry(contents->prerender_url(), 1571 contents->final_status(), 1572 contents->origin(), 1573 base::Time::Now()); 1574 prerender_history_->AddEntry(entry); 1575 } 1576 1577 base::Value* PrerenderManager::GetActivePrerendersAsValue() const { 1578 base::ListValue* list_value = new base::ListValue(); 1579 for (ScopedVector<PrerenderData>::const_iterator it = 1580 active_prerenders_.begin(); 1581 it != active_prerenders_.end(); ++it) { 1582 if (base::Value* prerender_value = (*it)->contents()->GetAsValue()) 1583 list_value->Append(prerender_value); 1584 } 1585 return list_value; 1586 } 1587 1588 void PrerenderManager::DestroyAllContents(FinalStatus final_status) { 1589 DeleteOldWebContents(); 1590 while (!active_prerenders_.empty()) { 1591 PrerenderContents* contents = active_prerenders_.front()->contents(); 1592 contents->Destroy(final_status); 1593 } 1594 to_delete_prerenders_.clear(); 1595 } 1596 1597 void PrerenderManager::DestroyAndMarkMatchCompleteAsUsed( 1598 PrerenderContents* prerender_contents, 1599 FinalStatus final_status) { 1600 prerender_contents->set_match_complete_status( 1601 PrerenderContents::MATCH_COMPLETE_REPLACED); 1602 histograms_->RecordFinalStatus(prerender_contents->origin(), 1603 prerender_contents->experiment_id(), 1604 PrerenderContents::MATCH_COMPLETE_REPLACEMENT, 1605 FINAL_STATUS_WOULD_HAVE_BEEN_USED); 1606 prerender_contents->Destroy(final_status); 1607 } 1608 1609 void PrerenderManager::RecordFinalStatusWithoutCreatingPrerenderContents( 1610 const GURL& url, Origin origin, uint8 experiment_id, 1611 FinalStatus final_status) const { 1612 PrerenderHistory::Entry entry(url, final_status, origin, base::Time::Now()); 1613 prerender_history_->AddEntry(entry); 1614 RecordFinalStatusWithMatchCompleteStatus( 1615 origin, experiment_id, 1616 PrerenderContents::MATCH_COMPLETE_DEFAULT, 1617 final_status); 1618 } 1619 1620 void PrerenderManager::Observe(int type, 1621 const content::NotificationSource& source, 1622 const content::NotificationDetails& details) { 1623 switch (type) { 1624 case chrome::NOTIFICATION_COOKIE_CHANGED: { 1625 Profile* profile = content::Source<Profile>(source).ptr(); 1626 if (!profile || !profile_->IsSameProfile(profile) || 1627 profile->IsOffTheRecord()) { 1628 return; 1629 } 1630 CookieChanged(content::Details<ChromeCookieDetails>(details).ptr()); 1631 break; 1632 } 1633 case chrome::NOTIFICATION_PROFILE_DESTROYED: 1634 DestroyAllContents(FINAL_STATUS_PROFILE_DESTROYED); 1635 on_close_web_contents_deleters_.clear(); 1636 break; 1637 default: 1638 NOTREACHED() << "Unexpected notification sent."; 1639 break; 1640 } 1641 } 1642 1643 void PrerenderManager::OnCreatingAudioStream(int render_process_id, 1644 int render_frame_id) { 1645 content::RenderFrameHost* render_frame_host = 1646 content::RenderFrameHost::FromID(render_process_id, render_frame_id); 1647 WebContents* tab = WebContents::FromRenderFrameHost(render_frame_host); 1648 if (!tab) 1649 return; 1650 1651 PrerenderContents* prerender_contents = GetPrerenderContents(tab); 1652 if (!prerender_contents) 1653 return; 1654 1655 prerender_contents->Destroy(prerender::FINAL_STATUS_CREATING_AUDIO_STREAM); 1656 } 1657 1658 void PrerenderManager::RecordLikelyLoginOnURL(const GURL& url) { 1659 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1660 if (!url.SchemeIsHTTPOrHTTPS()) 1661 return; 1662 if (logged_in_predictor_table_.get()) { 1663 BrowserThread::PostTask( 1664 BrowserThread::DB, 1665 FROM_HERE, 1666 base::Bind(&LoggedInPredictorTable::AddDomainFromURL, 1667 logged_in_predictor_table_, 1668 url)); 1669 } 1670 std::string key = LoggedInPredictorTable::GetKey(url); 1671 if (!logged_in_state_.get()) 1672 return; 1673 if (logged_in_state_->count(key)) 1674 return; 1675 (*logged_in_state_)[key] = base::Time::Now().ToInternalValue(); 1676 } 1677 1678 void PrerenderManager::CheckIfLikelyLoggedInOnURL( 1679 const GURL& url, 1680 bool* lookup_result, 1681 bool* database_was_present, 1682 const base::Closure& result_cb) { 1683 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1684 if (!logged_in_predictor_table_.get()) { 1685 *database_was_present = false; 1686 *lookup_result = false; 1687 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, result_cb); 1688 return; 1689 } 1690 BrowserThread::PostTaskAndReply( 1691 BrowserThread::DB, FROM_HERE, 1692 base::Bind(&LoggedInPredictorTable::HasUserLoggedIn, 1693 logged_in_predictor_table_, 1694 url, 1695 lookup_result, 1696 database_was_present), 1697 result_cb); 1698 } 1699 1700 1701 void PrerenderManager::CookieChanged(ChromeCookieDetails* details) { 1702 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1703 1704 if (!logged_in_predictor_table_.get()) 1705 return; 1706 1707 // We only care when a cookie has been removed. 1708 if (!details->removed) 1709 return; 1710 1711 std::string domain_key = 1712 LoggedInPredictorTable::GetKeyFromDomain(details->cookie->Domain()); 1713 1714 // If we have no record of this domain as a potentially logged in domain, 1715 // nothing to do here. 1716 if (logged_in_state_.get() && logged_in_state_->count(domain_key) < 1) 1717 return; 1718 1719 net::URLRequestContextGetter* rq_context = profile_->GetRequestContext(); 1720 if (!rq_context) 1721 return; 1722 1723 BrowserThread::PostTask( 1724 BrowserThread::IO, FROM_HERE, 1725 base::Bind(&CheckIfCookiesExistForDomainOnIOThread, 1726 base::Unretained(rq_context), 1727 domain_key, 1728 base::Bind( 1729 &PrerenderManager::CookieChangedAnyCookiesLeftLookupResult, 1730 AsWeakPtr(), 1731 domain_key) 1732 )); 1733 } 1734 1735 void PrerenderManager::CookieChangedAnyCookiesLeftLookupResult( 1736 const std::string& domain_key, 1737 bool cookies_exist) { 1738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1739 1740 if (cookies_exist) 1741 return; 1742 1743 if (logged_in_predictor_table_.get()) { 1744 BrowserThread::PostTask(BrowserThread::DB, 1745 FROM_HERE, 1746 base::Bind(&LoggedInPredictorTable::DeleteDomain, 1747 logged_in_predictor_table_, 1748 domain_key)); 1749 } 1750 1751 if (logged_in_state_.get()) 1752 logged_in_state_->erase(domain_key); 1753 } 1754 1755 void PrerenderManager::LoggedInPredictorDataReceived( 1756 scoped_ptr<LoggedInStateMap> new_map) { 1757 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1758 logged_in_state_.swap(new_map); 1759 } 1760 1761 void PrerenderManager::RecordEvent(PrerenderContents* contents, 1762 PrerenderEvent event) const { 1763 if (!contents) 1764 histograms_->RecordEvent(ORIGIN_NONE, kNoExperiment, event); 1765 else 1766 histograms_->RecordEvent(contents->origin(), contents->experiment_id(), 1767 event); 1768 } 1769 1770 // static 1771 void PrerenderManager::RecordCookieEvent(int process_id, 1772 int frame_id, 1773 const GURL& url, 1774 const GURL& frame_url, 1775 bool is_for_blocking_resource, 1776 PrerenderContents::CookieEvent event, 1777 const net::CookieList* cookie_list) { 1778 RenderFrameHost* rfh = RenderFrameHost::FromID(process_id, frame_id); 1779 WebContents* web_contents = WebContents::FromRenderFrameHost(rfh); 1780 if (!web_contents) 1781 return; 1782 1783 bool is_main_frame = (rfh == web_contents->GetMainFrame()); 1784 1785 bool is_third_party_cookie = 1786 (!frame_url.is_empty() && 1787 !net::registry_controlled_domains::SameDomainOrHost( 1788 url, frame_url, 1789 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); 1790 1791 PrerenderContents* prerender_contents = 1792 PrerenderContents::FromWebContents(web_contents); 1793 1794 if (!prerender_contents) 1795 return; 1796 1797 base::Time earliest_create_date; 1798 if (event == PrerenderContents::COOKIE_EVENT_SEND) { 1799 if (!cookie_list || cookie_list->empty()) 1800 return; 1801 for (size_t i = 0; i < cookie_list->size(); i++) { 1802 if (earliest_create_date.is_null() || 1803 (*cookie_list)[i].CreationDate() < earliest_create_date) { 1804 earliest_create_date = (*cookie_list)[i].CreationDate(); 1805 } 1806 } 1807 } 1808 1809 prerender_contents->RecordCookieEvent(event, 1810 is_main_frame && url == frame_url, 1811 is_third_party_cookie, 1812 is_for_blocking_resource, 1813 earliest_create_date); 1814 } 1815 1816 void PrerenderManager::RecordCookieStatus(Origin origin, 1817 uint8 experiment_id, 1818 int cookie_status) const { 1819 histograms_->RecordCookieStatus(origin, experiment_id, cookie_status); 1820 } 1821 1822 void PrerenderManager::RecordCookieSendType(Origin origin, 1823 uint8 experiment_id, 1824 int cookie_send_type) const { 1825 histograms_->RecordCookieSendType(origin, experiment_id, cookie_send_type); 1826 } 1827 1828 void PrerenderManager::OnHistoryServiceDidQueryURL( 1829 Origin origin, 1830 uint8 experiment_id, 1831 bool success, 1832 const history::URLRow& url_row, 1833 const history::VisitVector& /*visits*/) { 1834 histograms_->RecordPrerenderPageVisitedStatus(origin, experiment_id, success); 1835 } 1836 1837 // static 1838 void PrerenderManager::HangSessionStorageMergesForTesting() { 1839 g_hang_session_storage_merges_for_testing = true; 1840 } 1841 1842 void PrerenderManager::RecordNetworkBytes(Origin origin, 1843 bool used, 1844 int64 prerender_bytes) { 1845 if (!ActuallyPrerendering()) 1846 return; 1847 int64 recent_profile_bytes = 1848 profile_network_bytes_ - last_recorded_profile_network_bytes_; 1849 last_recorded_profile_network_bytes_ = profile_network_bytes_; 1850 DCHECK_GE(recent_profile_bytes, 0); 1851 histograms_->RecordNetworkBytes( 1852 origin, used, prerender_bytes, recent_profile_bytes); 1853 } 1854 1855 bool PrerenderManager::IsEnabled() const { 1856 DCHECK(CalledOnValidThread()); 1857 1858 return chrome_browser_net::CanPrefetchAndPrerenderUI(profile_->GetPrefs()); 1859 } 1860 1861 void PrerenderManager::AddProfileNetworkBytesIfEnabled(int64 bytes) { 1862 DCHECK_GE(bytes, 0); 1863 if (IsEnabled() && ActuallyPrerendering()) 1864 profile_network_bytes_ += bytes; 1865 } 1866 1867 void PrerenderManager::OnCookieStoreLoaded() { 1868 cookie_store_loaded_ = true; 1869 if (!on_cookie_store_loaded_cb_for_testing_.is_null()) 1870 on_cookie_store_loaded_cb_for_testing_.Run(); 1871 } 1872 1873 void PrerenderManager::AddPrerenderProcessHost( 1874 content::RenderProcessHost* process_host) { 1875 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1876 DCHECK(prerender_process_hosts_.find(process_host) == 1877 prerender_process_hosts_.end()); 1878 prerender_process_hosts_.insert(process_host); 1879 process_host->AddObserver(this); 1880 } 1881 1882 bool PrerenderManager::MayReuseProcessHost( 1883 content::RenderProcessHost* process_host) { 1884 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1885 // If prerender cookie stores are disabled, there is no need to require 1886 // isolated prerender processes. 1887 if (!IsPrerenderCookieStoreEnabled()) 1888 return true; 1889 return (prerender_process_hosts_.find(process_host) == 1890 prerender_process_hosts_.end()); 1891 } 1892 1893 void PrerenderManager::RenderProcessHostDestroyed( 1894 content::RenderProcessHost* host) { 1895 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1896 prerender_process_hosts_.erase(host); 1897 BrowserThread::PostTask( 1898 BrowserThread::IO, FROM_HERE, 1899 base::Bind(&PrerenderTracker::RemovePrerenderCookieStoreOnIOThread, 1900 base::Unretained(prerender_tracker()), host->GetID(), false)); 1901 } 1902 1903 } // namespace prerender 1904