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