| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/prerender/prerender_manager.h" | 5 #include "chrome/browser/prerender/prerender_manager.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 160 DISALLOW_COPY_AND_ASSIGN(OnCloseTabContentsDeleter); | 160 DISALLOW_COPY_AND_ASSIGN(OnCloseTabContentsDeleter); |
| 161 }; | 161 }; |
| 162 | 162 |
| 163 // static | 163 // static |
| 164 int PrerenderManager::prerenders_per_session_count_ = 0; | 164 int PrerenderManager::prerenders_per_session_count_ = 0; |
| 165 | 165 |
| 166 // static | 166 // static |
| 167 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ = | 167 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ = |
| 168 PRERENDER_MODE_ENABLED; | 168 PRERENDER_MODE_ENABLED; |
| 169 | 169 |
| 170 // static | |
| 171 PrerenderManager::PrerenderManagerMode PrerenderManager::GetMode() { | |
| 172 return mode_; | |
| 173 } | |
| 174 | |
| 175 // static | |
| 176 void PrerenderManager::SetMode(PrerenderManagerMode mode) { | |
| 177 mode_ = mode; | |
| 178 } | |
| 179 | |
| 180 // static | |
| 181 bool PrerenderManager::IsPrerenderingPossible() { | |
| 182 return GetMode() == PRERENDER_MODE_ENABLED || | |
| 183 GetMode() == PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP || | |
| 184 GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP || | |
| 185 GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP; | |
| 186 } | |
| 187 | |
| 188 // static | |
| 189 bool PrerenderManager::ActuallyPrerendering() { | |
| 190 return IsPrerenderingPossible() && !IsControlGroup(); | |
| 191 } | |
| 192 | |
| 193 // static | |
| 194 bool PrerenderManager::IsControlGroup() { | |
| 195 return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP; | |
| 196 } | |
| 197 | |
| 198 // static | |
| 199 bool PrerenderManager::IsNoUseGroup() { | |
| 200 return GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP; | |
| 201 } | |
| 202 | |
| 203 // static | |
| 204 bool PrerenderManager::IsValidHttpMethod(const std::string& method) { | |
| 205 // method has been canonicalized to upper case at this point so we can just | |
| 206 // compare them. | |
| 207 DCHECK_EQ(method, StringToUpperASCII(method)); | |
| 208 for (size_t i = 0; i < arraysize(kValidHttpMethods); ++i) { | |
| 209 if (method.compare(kValidHttpMethods[i]) == 0) | |
| 210 return true; | |
| 211 } | |
| 212 | |
| 213 return false; | |
| 214 } | |
| 215 | |
| 216 struct PrerenderManager::PrerenderContentsData { | 170 struct PrerenderManager::PrerenderContentsData { |
| 217 PrerenderContents* contents_; | 171 PrerenderContents* contents_; |
| 218 base::Time start_time_; | 172 base::Time start_time_; |
| 219 PrerenderContentsData(PrerenderContents* contents, base::Time start_time) | 173 PrerenderContentsData(PrerenderContents* contents, base::Time start_time) |
| 220 : contents_(contents), | 174 : contents_(contents), |
| 221 start_time_(start_time) { | 175 start_time_(start_time) { |
| 222 CHECK(contents); | 176 CHECK(contents); |
| 223 } | 177 } |
| 224 }; | 178 }; |
| 225 | 179 |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 279 return profile_->GetTopSites(); | 233 return profile_->GetTopSites(); |
| 280 return NULL; | 234 return NULL; |
| 281 } | 235 } |
| 282 | 236 |
| 283 CancelableRequestConsumer topsites_consumer_; | 237 CancelableRequestConsumer topsites_consumer_; |
| 284 Profile* profile_; | 238 Profile* profile_; |
| 285 content::NotificationRegistrar registrar_; | 239 content::NotificationRegistrar registrar_; |
| 286 std::set<GURL> urls_; | 240 std::set<GURL> urls_; |
| 287 }; | 241 }; |
| 288 | 242 |
| 289 bool PrerenderManager::IsTopSite(const GURL& url) { | |
| 290 if (!most_visited_.get()) | |
| 291 most_visited_.reset(new MostVisitedSites(profile_)); | |
| 292 return most_visited_->IsTopSite(url); | |
| 293 } | |
| 294 | |
| 295 bool PrerenderManager::IsPendingEntry(const GURL& url) const { | |
| 296 DCHECK(CalledOnValidThread()); | |
| 297 for (std::list<PrerenderContentsData>::const_iterator it = | |
| 298 prerender_list_.begin(); | |
| 299 it != prerender_list_.end(); | |
| 300 ++it) { | |
| 301 if (it->contents_->IsPendingEntry(url)) | |
| 302 return true; | |
| 303 } | |
| 304 return false; | |
| 305 } | |
| 306 | |
| 307 PrerenderManager::PrerenderManager(Profile* profile, | 243 PrerenderManager::PrerenderManager(Profile* profile, |
| 308 PrerenderTracker* prerender_tracker) | 244 PrerenderTracker* prerender_tracker) |
| 309 : enabled_(true), | 245 : enabled_(true), |
| 310 profile_(profile), | 246 profile_(profile), |
| 311 prerender_tracker_(prerender_tracker), | 247 prerender_tracker_(prerender_tracker), |
| 312 prerender_contents_factory_(PrerenderContents::CreateFactory()), | 248 prerender_contents_factory_(PrerenderContents::CreateFactory()), |
| 313 last_prerender_start_time_(GetCurrentTimeTicks() - | 249 last_prerender_start_time_(GetCurrentTimeTicks() - |
| 314 base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs)), | 250 base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs)), |
| 315 weak_factory_(this), | 251 weak_factory_(this), |
| 316 prerender_history_(new PrerenderHistory(kHistoryLength)), | 252 prerender_history_(new PrerenderHistory(kHistoryLength)), |
| 317 histograms_(new PrerenderHistograms()) { | 253 histograms_(new PrerenderHistograms()) { |
| 318 // There are some assumptions that the PrerenderManager is on the UI thread. | 254 // There are some assumptions that the PrerenderManager is on the UI thread. |
| 319 // Any other checks simply make sure that the PrerenderManager is accessed on | 255 // Any other checks simply make sure that the PrerenderManager is accessed on |
| 320 // the same thread that it was created on. | 256 // the same thread that it was created on. |
| 321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 322 } | 258 } |
| 323 | 259 |
| 324 PrerenderManager::~PrerenderManager() { | 260 PrerenderManager::~PrerenderManager() { |
| 325 } | 261 } |
| 326 | 262 |
| 327 void PrerenderManager::Shutdown() { | 263 void PrerenderManager::Shutdown() { |
| 328 DoShutdown(); | 264 DoShutdown(); |
| 329 } | 265 } |
| 330 | 266 |
| 331 void PrerenderManager::SetPrerenderContentsFactory( | |
| 332 PrerenderContents::Factory* prerender_contents_factory) { | |
| 333 DCHECK(CalledOnValidThread()); | |
| 334 prerender_contents_factory_.reset(prerender_contents_factory); | |
| 335 } | |
| 336 | |
| 337 bool PrerenderManager::AddPrerenderFromLinkRelPrerender( | 267 bool PrerenderManager::AddPrerenderFromLinkRelPrerender( |
| 338 int process_id, | 268 int process_id, |
| 339 int route_id, | 269 int route_id, |
| 340 const GURL& url, | 270 const GURL& url, |
| 341 const content::Referrer& referrer) { | 271 const content::Referrer& referrer) { |
| 342 std::pair<int, int> child_route_id_pair = std::make_pair(process_id, | 272 std::pair<int, int> child_route_id_pair = std::make_pair(process_id, |
| 343 route_id); | 273 route_id); |
| 344 | 274 |
| 345 return AddPrerender(ORIGIN_LINK_REL_PRERENDER, child_route_id_pair, | 275 return AddPrerender(ORIGIN_LINK_REL_PRERENDER, child_route_id_pair, |
| 346 url, referrer, NULL); | 276 url, referrer, NULL); |
| 347 } | 277 } |
| 348 | 278 |
| 349 bool PrerenderManager::AddPrerenderFromOmnibox( | 279 bool PrerenderManager::AddPrerenderFromOmnibox( |
| 350 const GURL& url, | 280 const GURL& url, |
| 351 SessionStorageNamespace* session_storage_namespace) { | 281 SessionStorageNamespace* session_storage_namespace) { |
| 352 DCHECK(session_storage_namespace); | |
| 353 if (!IsOmniboxEnabled(profile_)) | 282 if (!IsOmniboxEnabled(profile_)) |
| 354 return false; | 283 return false; |
| 355 return AddPrerender(ORIGIN_OMNIBOX, std::make_pair(-1, -1), url, | 284 return AddPrerender(ORIGIN_OMNIBOX, std::make_pair(-1, -1), url, |
| 356 content::Referrer(), session_storage_namespace); | 285 content::Referrer(), session_storage_namespace); |
| 357 } | 286 } |
| 358 | 287 |
| 359 bool PrerenderManager::AddPrerender( | |
| 360 Origin origin, | |
| 361 const std::pair<int, int>& child_route_id_pair, | |
| 362 const GURL& url_arg, | |
| 363 const content::Referrer& referrer, | |
| 364 SessionStorageNamespace* session_storage_namespace) { | |
| 365 DCHECK(CalledOnValidThread()); | |
| 366 | |
| 367 if (origin == ORIGIN_LINK_REL_PRERENDER && | |
| 368 IsGoogleSearchResultURL(referrer.url)) { | |
| 369 origin = ORIGIN_GWS_PRERENDER; | |
| 370 } | |
| 371 | |
| 372 // If the referring page is prerendering, defer the prerender. | |
| 373 std::list<PrerenderContentsData>::iterator source_prerender = | |
| 374 FindPrerenderContentsForChildRouteIdPair(child_route_id_pair); | |
| 375 if (source_prerender != prerender_list_.end()) { | |
| 376 source_prerender->contents_->AddPendingPrerender( | |
| 377 origin, url_arg, referrer); | |
| 378 return true; | |
| 379 } | |
| 380 | |
| 381 DeleteOldEntries(); | |
| 382 DeletePendingDeleteEntries(); | |
| 383 | |
| 384 GURL url = url_arg; | |
| 385 GURL alias_url; | |
| 386 if (IsControlGroup() && MaybeGetQueryStringBasedAliasURL(url, &alias_url)) | |
| 387 url = alias_url; | |
| 388 | |
| 389 // From here on, we will record a FinalStatus so we need to register with the | |
| 390 // histogram tracking. | |
| 391 histograms_->RecordPrerender(origin, url_arg); | |
| 392 | |
| 393 uint8 experiment = GetQueryStringBasedExperiment(url_arg); | |
| 394 | |
| 395 if (FindEntry(url)) { | |
| 396 RecordFinalStatus(origin, experiment, FINAL_STATUS_DUPLICATE); | |
| 397 return false; | |
| 398 } | |
| 399 | |
| 400 // Do not prerender if there are too many render processes, and we would | |
| 401 // have to use an existing one. We do not want prerendering to happen in | |
| 402 // a shared process, so that we can always reliably lower the CPU | |
| 403 // priority for prerendering. | |
| 404 // In single-process mode, ShouldTryToUseExistingProcessHost() always returns | |
| 405 // true, so that case needs to be explicitly checked for. | |
| 406 // TODO(tburkard): Figure out how to cancel prerendering in the opposite | |
| 407 // case, when a new tab is added to a process used for prerendering. | |
| 408 if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost() && | |
| 409 !content::RenderProcessHost::run_renderer_in_process()) { | |
| 410 RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES); | |
| 411 return false; | |
| 412 } | |
| 413 | |
| 414 // Check if enough time has passed since the last prerender. | |
| 415 if (!DoesRateLimitAllowPrerender()) { | |
| 416 // Cancel the prerender. We could add it to the pending prerender list but | |
| 417 // this doesn't make sense as the next prerender request will be triggered | |
| 418 // by a navigation and is unlikely to be the same site. | |
| 419 RecordFinalStatus(origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED); | |
| 420 return false; | |
| 421 } | |
| 422 | |
| 423 RenderViewHost* source_render_view_host = NULL; | |
| 424 if (child_route_id_pair.first != -1) { | |
| 425 source_render_view_host = | |
| 426 RenderViewHost::FromID(child_route_id_pair.first, | |
| 427 child_route_id_pair.second); | |
| 428 // Don't prerender page if parent RenderViewHost no longer exists, or it has | |
| 429 // no view. The latter should only happen when the RenderView has closed. | |
| 430 if (!source_render_view_host || !source_render_view_host->view()) { | |
| 431 RecordFinalStatus(origin, experiment, | |
| 432 FINAL_STATUS_SOURCE_RENDER_VIEW_CLOSED); | |
| 433 return false; | |
| 434 } | |
| 435 } | |
| 436 | |
| 437 if (!session_storage_namespace && source_render_view_host) { | |
| 438 session_storage_namespace = | |
| 439 source_render_view_host->session_storage_namespace(); | |
| 440 } | |
| 441 | |
| 442 PrerenderContents* prerender_contents = CreatePrerenderContents( | |
| 443 url, referrer, origin, experiment); | |
| 444 if (!prerender_contents || !prerender_contents->Init()) | |
| 445 return false; | |
| 446 | |
| 447 histograms_->RecordPrerenderStarted(origin); | |
| 448 | |
| 449 // TODO(cbentzel): Move invalid checks here instead of PrerenderContents? | |
| 450 PrerenderContentsData data(prerender_contents, GetCurrentTime()); | |
| 451 | |
| 452 prerender_list_.push_back(data); | |
| 453 | |
| 454 if (!IsControlGroup()) { | |
| 455 last_prerender_start_time_ = GetCurrentTimeTicks(); | |
| 456 data.contents_->StartPrerendering(source_render_view_host, | |
| 457 session_storage_namespace); | |
| 458 } | |
| 459 while (prerender_list_.size() > config_.max_elements) { | |
| 460 data = prerender_list_.front(); | |
| 461 prerender_list_.pop_front(); | |
| 462 data.contents_->Destroy(FINAL_STATUS_EVICTED); | |
| 463 } | |
| 464 StartSchedulingPeriodicCleanups(); | |
| 465 return true; | |
| 466 } | |
| 467 | |
| 468 std::list<PrerenderManager::PrerenderContentsData>::iterator | |
| 469 PrerenderManager::FindPrerenderContentsForChildRouteIdPair( | |
| 470 const std::pair<int, int>& child_route_id_pair) { | |
| 471 std::list<PrerenderContentsData>::iterator it = prerender_list_.begin(); | |
| 472 for (; it != prerender_list_.end(); ++it) { | |
| 473 PrerenderContents* prerender_contents = it->contents_; | |
| 474 | |
| 475 int child_id; | |
| 476 int route_id; | |
| 477 bool has_child_id = prerender_contents->GetChildId(&child_id); | |
| 478 bool has_route_id = has_child_id && | |
| 479 prerender_contents->GetRouteId(&route_id); | |
| 480 | |
| 481 if (has_child_id && has_route_id && | |
| 482 child_id == child_route_id_pair.first && | |
| 483 route_id == child_route_id_pair.second) { | |
| 484 break; | |
| 485 } | |
| 486 } | |
| 487 return it; | |
| 488 } | |
| 489 | |
| 490 void PrerenderManager::DestroyPrerenderForRenderView( | 288 void PrerenderManager::DestroyPrerenderForRenderView( |
| 491 int process_id, int view_id, FinalStatus final_status) { | 289 int process_id, int view_id, FinalStatus final_status) { |
| 492 DCHECK(CalledOnValidThread()); | 290 DCHECK(CalledOnValidThread()); |
| 493 std::list<PrerenderContentsData>::iterator it = | 291 PrerenderContentsDataList::iterator it = |
| 494 FindPrerenderContentsForChildRouteIdPair( | 292 FindPrerenderContentsForChildRouteIdPair( |
| 495 std::make_pair(process_id, view_id)); | 293 std::make_pair(process_id, view_id)); |
| 496 if (it != prerender_list_.end()) { | 294 if (it != prerender_list_.end()) { |
| 497 PrerenderContents* prerender_contents = it->contents_; | 295 PrerenderContents* prerender_contents = it->contents_; |
| 498 prerender_contents->Destroy(final_status); | 296 prerender_contents->Destroy(final_status); |
| 499 } | 297 } |
| 500 } | 298 } |
| 501 | 299 |
| 502 void PrerenderManager::CancelAllPrerenders() { | 300 void PrerenderManager::CancelAllPrerenders() { |
| 503 DCHECK(CalledOnValidThread()); | 301 DCHECK(CalledOnValidThread()); |
| 504 while (!prerender_list_.empty()) { | 302 while (!prerender_list_.empty()) { |
| 505 PrerenderContentsData data = prerender_list_.front(); | 303 PrerenderContentsData data = prerender_list_.front(); |
| 506 DCHECK(data.contents_); | 304 DCHECK(data.contents_); |
| 507 data.contents_->Destroy(FINAL_STATUS_CANCELLED); | 305 data.contents_->Destroy(FINAL_STATUS_CANCELLED); |
| 508 } | 306 } |
| 509 } | 307 } |
| 510 | 308 |
| 511 void PrerenderManager::DeleteOldEntries() { | 309 void PrerenderManager::CancelOmniboxPrerenders() { |
| 512 DCHECK(CalledOnValidThread()); | 310 DCHECK(CalledOnValidThread()); |
| 513 while (!prerender_list_.empty()) { | 311 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); |
| 514 PrerenderContentsData data = prerender_list_.front(); | 312 it != prerender_list_.end(); ) { |
| 515 if (IsPrerenderElementFresh(data.start_time_)) | 313 PrerenderContentsDataList::iterator cur = it++; |
| 516 return; | 314 if (cur->contents_->origin() == ORIGIN_OMNIBOX) |
| 517 data.contents_->Destroy(FINAL_STATUS_TIMED_OUT); | 315 cur->contents_->Destroy(FINAL_STATUS_CANCELLED); |
| 518 } | 316 } |
| 519 MaybeStopSchedulingPeriodicCleanups(); | |
| 520 } | |
| 521 | |
| 522 PrerenderContents* PrerenderManager::GetEntryButNotSpecifiedWC( | |
| 523 const GURL& url, | |
| 524 WebContents* wc) { | |
| 525 DCHECK(CalledOnValidThread()); | |
| 526 DeleteOldEntries(); | |
| 527 DeletePendingDeleteEntries(); | |
| 528 for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin(); | |
| 529 it != prerender_list_.end(); | |
| 530 ++it) { | |
| 531 PrerenderContents* prerender_contents = it->contents_; | |
| 532 if (prerender_contents->MatchesURL(url, NULL)) { | |
| 533 if (!prerender_contents->prerender_contents() || | |
| 534 !wc || | |
| 535 prerender_contents->prerender_contents()->web_contents() != wc) { | |
| 536 prerender_list_.erase(it); | |
| 537 return prerender_contents; | |
| 538 } | |
| 539 } | |
| 540 } | |
| 541 // Entry not found. | |
| 542 return NULL; | |
| 543 } | |
| 544 | |
| 545 PrerenderContents* PrerenderManager::GetEntry(const GURL& url) { | |
| 546 return GetEntryButNotSpecifiedWC(url, NULL); | |
| 547 } | |
| 548 | |
| 549 void PrerenderManager::DestroyAndMarkMatchCompleteAsUsed( | |
| 550 PrerenderContents* prerender_contents, | |
| 551 FinalStatus final_status) { | |
| 552 prerender_contents->set_match_complete_status( | |
| 553 PrerenderContents::MATCH_COMPLETE_REPLACED); | |
| 554 histograms_->RecordFinalStatus(prerender_contents->origin(), | |
| 555 prerender_contents->experiment_id(), | |
| 556 PrerenderContents::MATCH_COMPLETE_REPLACEMENT, | |
| 557 FINAL_STATUS_WOULD_HAVE_BEEN_USED); | |
| 558 prerender_contents->Destroy(final_status); | |
| 559 } | 317 } |
| 560 | 318 |
| 561 bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, | 319 bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, |
| 562 const GURL& url, | 320 const GURL& url, |
| 563 const GURL& opener_url) { | 321 const GURL& opener_url) { |
| 564 DCHECK(CalledOnValidThread()); | 322 DCHECK(CalledOnValidThread()); |
| 565 RecordNavigation(url); | 323 RecordNavigation(url); |
| 566 | 324 |
| 567 scoped_ptr<PrerenderContents> prerender_contents( | 325 scoped_ptr<PrerenderContents> prerender_contents( |
| 568 GetEntryButNotSpecifiedWC(url, web_contents)); | 326 GetEntryButNotSpecifiedWC(url, web_contents)); |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 715 AddToHistory(prerender_contents.get()); | 473 AddToHistory(prerender_contents.get()); |
| 716 return true; | 474 return true; |
| 717 } | 475 } |
| 718 | 476 |
| 719 void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry, | 477 void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry, |
| 720 FinalStatus final_status) { | 478 FinalStatus final_status) { |
| 721 DCHECK(CalledOnValidThread()); | 479 DCHECK(CalledOnValidThread()); |
| 722 DCHECK(entry); | 480 DCHECK(entry); |
| 723 DCHECK(!IsPendingDelete(entry)); | 481 DCHECK(!IsPendingDelete(entry)); |
| 724 | 482 |
| 725 for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin(); | 483 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); |
| 726 it != prerender_list_.end(); | 484 it != prerender_list_.end(); |
| 727 ++it) { | 485 ++it) { |
| 728 if (it->contents_ == entry) { | 486 if (it->contents_ == entry) { |
| 729 bool swapped_in_dummy_replacement = false; | 487 bool swapped_in_dummy_replacement = false; |
| 730 | 488 |
| 731 // If this PrerenderContents is being deleted due to a cancellation, | 489 // If this PrerenderContents is being deleted due to a cancellation, |
| 732 // we need to create a dummy replacement for PPLT accounting purposes | 490 // we need to create a dummy replacement for PPLT accounting purposes |
| 733 // for the Match Complete group. | 491 // for the Match Complete group. |
| 734 // This is the case if the cancellation is for any reason that would not | 492 // This is the case if the cancellation is for any reason that would not |
| 735 // occur in the control group case. | 493 // occur in the control group case. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 767 } | 525 } |
| 768 } | 526 } |
| 769 AddToHistory(entry); | 527 AddToHistory(entry); |
| 770 pending_delete_list_.push_back(entry); | 528 pending_delete_list_.push_back(entry); |
| 771 | 529 |
| 772 // Destroy the old TabContents relatively promptly to reduce resource usage, | 530 // Destroy the old TabContents relatively promptly to reduce resource usage, |
| 773 // and in the case of HTML5 media, reduce the change of playing any sound. | 531 // and in the case of HTML5 media, reduce the change of playing any sound. |
| 774 PostCleanupTask(); | 532 PostCleanupTask(); |
| 775 } | 533 } |
| 776 | 534 |
| 777 base::Time PrerenderManager::GetCurrentTime() const { | |
| 778 return base::Time::Now(); | |
| 779 } | |
| 780 | |
| 781 base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const { | |
| 782 return base::TimeTicks::Now(); | |
| 783 } | |
| 784 | |
| 785 bool PrerenderManager::IsPrerenderElementFresh(const base::Time start) const { | |
| 786 DCHECK(CalledOnValidThread()); | |
| 787 base::Time now = GetCurrentTime(); | |
| 788 return (now - start < config_.max_age); | |
| 789 } | |
| 790 | |
| 791 PrerenderContents* PrerenderManager::CreatePrerenderContents( | |
| 792 const GURL& url, | |
| 793 const content::Referrer& referrer, | |
| 794 Origin origin, | |
| 795 uint8 experiment_id) { | |
| 796 DCHECK(CalledOnValidThread()); | |
| 797 return prerender_contents_factory_->CreatePrerenderContents( | |
| 798 this, prerender_tracker_, profile_, url, | |
| 799 referrer, origin, experiment_id); | |
| 800 } | |
| 801 | |
| 802 bool PrerenderManager::IsPendingDelete(PrerenderContents* entry) const { | |
| 803 DCHECK(CalledOnValidThread()); | |
| 804 for (std::list<PrerenderContents*>::const_iterator it = | |
| 805 pending_delete_list_.begin(); | |
| 806 it != pending_delete_list_.end(); | |
| 807 ++it) { | |
| 808 if (*it == entry) | |
| 809 return true; | |
| 810 } | |
| 811 | |
| 812 return false; | |
| 813 } | |
| 814 | |
| 815 void PrerenderManager::DeletePendingDeleteEntries() { | |
| 816 while (!pending_delete_list_.empty()) { | |
| 817 PrerenderContents* contents = pending_delete_list_.front(); | |
| 818 pending_delete_list_.pop_front(); | |
| 819 delete contents; | |
| 820 } | |
| 821 } | |
| 822 | |
| 823 // static | 535 // static |
| 824 void PrerenderManager::RecordPerceivedPageLoadTime( | 536 void PrerenderManager::RecordPerceivedPageLoadTime( |
| 825 base::TimeDelta perceived_page_load_time, | 537 base::TimeDelta perceived_page_load_time, |
| 826 WebContents* web_contents, | 538 WebContents* web_contents, |
| 827 const GURL& url) { | 539 const GURL& url) { |
| 828 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 540 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 829 PrerenderManager* prerender_manager = | 541 PrerenderManager* prerender_manager = |
| 830 PrerenderManagerFactory::GetForProfile( | 542 PrerenderManagerFactory::GetForProfile( |
| 831 Profile::FromBrowserContext(web_contents->GetBrowserContext())); | 543 Profile::FromBrowserContext(web_contents->GetBrowserContext())); |
| 832 if (!prerender_manager) | 544 if (!prerender_manager) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 854 return false; | 566 return false; |
| 855 } | 567 } |
| 856 return true; | 568 return true; |
| 857 } | 569 } |
| 858 | 570 |
| 859 void PrerenderManager::set_enabled(bool enabled) { | 571 void PrerenderManager::set_enabled(bool enabled) { |
| 860 DCHECK(CalledOnValidThread()); | 572 DCHECK(CalledOnValidThread()); |
| 861 enabled_ = enabled; | 573 enabled_ = enabled; |
| 862 } | 574 } |
| 863 | 575 |
| 864 void PrerenderManager::AddCondition(const PrerenderCondition* condition) { | 576 // static |
| 865 prerender_conditions_.push_back(condition); | 577 PrerenderManager::PrerenderManagerMode PrerenderManager::GetMode() { |
| 578 return mode_; |
| 866 } | 579 } |
| 867 | 580 |
| 868 PrerenderContents* PrerenderManager::FindEntry(const GURL& url) { | 581 // static |
| 869 DCHECK(CalledOnValidThread()); | 582 void PrerenderManager::SetMode(PrerenderManagerMode mode) { |
| 870 for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin(); | 583 mode_ = mode; |
| 871 it != prerender_list_.end(); | |
| 872 ++it) { | |
| 873 if (it->contents_->MatchesURL(url, NULL)) | |
| 874 return it->contents_; | |
| 875 } | |
| 876 // Entry not found. | |
| 877 return NULL; | |
| 878 } | 584 } |
| 879 | 585 |
| 880 void PrerenderManager::DoShutdown() { | 586 // static |
| 881 DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN); | 587 bool PrerenderManager::IsPrerenderingPossible() { |
| 882 STLDeleteElements(&prerender_conditions_); | 588 return GetMode() == PRERENDER_MODE_ENABLED || |
| 883 profile_ = NULL; | 589 GetMode() == PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP || |
| 590 GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP || |
| 591 GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP; |
| 884 } | 592 } |
| 885 | 593 |
| 886 bool PrerenderManager::DoesRateLimitAllowPrerender() const { | 594 // static |
| 887 DCHECK(CalledOnValidThread()); | 595 bool PrerenderManager::ActuallyPrerendering() { |
| 888 base::TimeDelta elapsed_time = | 596 return IsPrerenderingPossible() && !IsControlGroup(); |
| 889 GetCurrentTimeTicks() - last_prerender_start_time_; | |
| 890 histograms_->RecordTimeBetweenPrerenderRequests(elapsed_time); | |
| 891 if (!config_.rate_limit_enabled) | |
| 892 return true; | |
| 893 return elapsed_time > | |
| 894 base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs); | |
| 895 } | 597 } |
| 896 | 598 |
| 897 void PrerenderManager::StartSchedulingPeriodicCleanups() { | 599 // static |
| 898 DCHECK(CalledOnValidThread()); | 600 bool PrerenderManager::IsControlGroup() { |
| 899 if (repeating_timer_.IsRunning()) | 601 return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP; |
| 900 return; | |
| 901 repeating_timer_.Start(FROM_HERE, | |
| 902 base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs), | |
| 903 this, | |
| 904 &PrerenderManager::PeriodicCleanup); | |
| 905 } | 602 } |
| 906 | 603 |
| 907 void PrerenderManager::MaybeStopSchedulingPeriodicCleanups() { | 604 // static |
| 908 if (!prerender_list_.empty()) | 605 bool PrerenderManager::IsNoUseGroup() { |
| 909 return; | 606 return GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP; |
| 910 | |
| 911 DCHECK(CalledOnValidThread()); | |
| 912 repeating_timer_.Stop(); | |
| 913 } | |
| 914 | |
| 915 void PrerenderManager::DeleteOldTabContents() { | |
| 916 while (!old_tab_contents_list_.empty()) { | |
| 917 TabContentsWrapper* tab_contents = old_tab_contents_list_.front(); | |
| 918 old_tab_contents_list_.pop_front(); | |
| 919 // TODO(dominich): should we use Instant Unload Handler here? | |
| 920 delete tab_contents; | |
| 921 } | |
| 922 } | |
| 923 | |
| 924 bool PrerenderManager::IsOldRenderViewHost( | |
| 925 const RenderViewHost* render_view_host) const { | |
| 926 for (std::list<TabContentsWrapper*>::const_iterator it = | |
| 927 old_tab_contents_list_.begin(); | |
| 928 it != old_tab_contents_list_.end(); | |
| 929 ++it) { | |
| 930 if ((*it)->web_contents()->GetRenderViewHost() == render_view_host) | |
| 931 return true; | |
| 932 } | |
| 933 return false; | |
| 934 } | |
| 935 | |
| 936 void PrerenderManager::PeriodicCleanup() { | |
| 937 DCHECK(CalledOnValidThread()); | |
| 938 DeleteOldTabContents(); | |
| 939 DeleteOldEntries(); | |
| 940 | |
| 941 // Grab a copy of the current PrerenderContents pointers, so that we | |
| 942 // will not interfere with potential deletions of the list. | |
| 943 std::vector<PrerenderContents*> prerender_contents; | |
| 944 for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin(); | |
| 945 it != prerender_list_.end(); | |
| 946 ++it) { | |
| 947 DCHECK(it->contents_); | |
| 948 prerender_contents.push_back(it->contents_); | |
| 949 } | |
| 950 for (std::vector<PrerenderContents*>::iterator it = | |
| 951 prerender_contents.begin(); | |
| 952 it != prerender_contents.end(); | |
| 953 ++it) { | |
| 954 (*it)->DestroyWhenUsingTooManyResources(); | |
| 955 } | |
| 956 | |
| 957 DeletePendingDeleteEntries(); | |
| 958 } | |
| 959 | |
| 960 void PrerenderManager::PostCleanupTask() { | |
| 961 DCHECK(CalledOnValidThread()); | |
| 962 MessageLoop::current()->PostTask( | |
| 963 FROM_HERE, | |
| 964 base::Bind(&PrerenderManager::PeriodicCleanup, | |
| 965 weak_factory_.GetWeakPtr())); | |
| 966 } | 607 } |
| 967 | 608 |
| 968 bool PrerenderManager::IsWebContentsPrerendering( | 609 bool PrerenderManager::IsWebContentsPrerendering( |
| 969 WebContents* web_contents) const { | 610 WebContents* web_contents) const { |
| 970 DCHECK(CalledOnValidThread()); | 611 DCHECK(CalledOnValidThread()); |
| 971 for (std::list<PrerenderContentsData>::const_iterator it = | 612 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); |
| 972 prerender_list_.begin(); | |
| 973 it != prerender_list_.end(); | 613 it != prerender_list_.end(); |
| 974 ++it) { | 614 ++it) { |
| 975 TabContentsWrapper* prerender_tab_contents_wrapper = | 615 TabContentsWrapper* prerender_tab_contents_wrapper = |
| 976 it->contents_->prerender_contents(); | 616 it->contents_->prerender_contents(); |
| 977 if (prerender_tab_contents_wrapper && | 617 if (prerender_tab_contents_wrapper && |
| 978 prerender_tab_contents_wrapper->web_contents() == web_contents) { | 618 prerender_tab_contents_wrapper->web_contents() == web_contents) { |
| 979 return true; | 619 return true; |
| 980 } | 620 } |
| 981 } | 621 } |
| 982 | 622 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1018 DCHECK(CalledOnValidThread()); | 658 DCHECK(CalledOnValidThread()); |
| 1019 return prerendered_tab_contents_set_.count(web_contents) > 0; | 659 return prerendered_tab_contents_set_.count(web_contents) > 0; |
| 1020 } | 660 } |
| 1021 | 661 |
| 1022 bool PrerenderManager::WouldWebContentsBePrerendered( | 662 bool PrerenderManager::WouldWebContentsBePrerendered( |
| 1023 WebContents* web_contents) const { | 663 WebContents* web_contents) const { |
| 1024 DCHECK(CalledOnValidThread()); | 664 DCHECK(CalledOnValidThread()); |
| 1025 return would_be_prerendered_tab_contents_set_.count(web_contents) > 0; | 665 return would_be_prerendered_tab_contents_set_.count(web_contents) > 0; |
| 1026 } | 666 } |
| 1027 | 667 |
| 668 bool PrerenderManager::IsOldRenderViewHost( |
| 669 const RenderViewHost* render_view_host) const { |
| 670 for (std::list<TabContentsWrapper*>::const_iterator it = |
| 671 old_tab_contents_list_.begin(); |
| 672 it != old_tab_contents_list_.end(); |
| 673 ++it) { |
| 674 if ((*it)->web_contents()->GetRenderViewHost() == render_view_host) |
| 675 return true; |
| 676 } |
| 677 return false; |
| 678 } |
| 679 |
| 1028 bool PrerenderManager::HasRecentlyBeenNavigatedTo(const GURL& url) { | 680 bool PrerenderManager::HasRecentlyBeenNavigatedTo(const GURL& url) { |
| 1029 DCHECK(CalledOnValidThread()); | 681 DCHECK(CalledOnValidThread()); |
| 1030 | 682 |
| 1031 CleanUpOldNavigations(); | 683 CleanUpOldNavigations(); |
| 1032 for (std::list<NavigationRecord>::const_iterator it = navigations_.begin(); | 684 for (std::list<NavigationRecord>::const_iterator it = navigations_.begin(); |
| 1033 it != navigations_.end(); | 685 it != navigations_.end(); |
| 1034 ++it) { | 686 ++it) { |
| 1035 if (it->url_ == url) | 687 if (it->url_ == url) |
| 1036 return true; | 688 return true; |
| 1037 } | 689 } |
| 1038 | 690 |
| 1039 return false; | 691 return false; |
| 1040 } | 692 } |
| 1041 | 693 |
| 1042 void PrerenderManager::CleanUpOldNavigations() { | 694 // static |
| 1043 DCHECK(CalledOnValidThread()); | 695 bool PrerenderManager::IsValidHttpMethod(const std::string& method) { |
| 696 // method has been canonicalized to upper case at this point so we can just |
| 697 // compare them. |
| 698 DCHECK_EQ(method, StringToUpperASCII(method)); |
| 699 for (size_t i = 0; i < arraysize(kValidHttpMethods); ++i) { |
| 700 if (method.compare(kValidHttpMethods[i]) == 0) |
| 701 return true; |
| 702 } |
| 1044 | 703 |
| 1045 // Cutoff. Navigations before this cutoff can be discarded. | 704 return false; |
| 1046 base::TimeTicks cutoff = GetCurrentTimeTicks() - | |
| 1047 base::TimeDelta::FromMilliseconds(kNavigationRecordWindowMs); | |
| 1048 while (!navigations_.empty()) { | |
| 1049 if (navigations_.front().time_ > cutoff) | |
| 1050 break; | |
| 1051 navigations_.pop_front(); | |
| 1052 } | |
| 1053 } | |
| 1054 | |
| 1055 void PrerenderManager::ScheduleDeleteOldTabContents( | |
| 1056 TabContentsWrapper* tab, | |
| 1057 OnCloseTabContentsDeleter* deleter) { | |
| 1058 old_tab_contents_list_.push_back(tab); | |
| 1059 PostCleanupTask(); | |
| 1060 | |
| 1061 if (deleter) { | |
| 1062 ScopedVector<OnCloseTabContentsDeleter>::iterator i = std::find( | |
| 1063 on_close_tab_contents_deleters_.begin(), | |
| 1064 on_close_tab_contents_deleters_.end(), | |
| 1065 deleter); | |
| 1066 DCHECK(i != on_close_tab_contents_deleters_.end()); | |
| 1067 on_close_tab_contents_deleters_.erase(i); | |
| 1068 } | |
| 1069 } | 705 } |
| 1070 | 706 |
| 1071 DictionaryValue* PrerenderManager::GetAsValue() const { | 707 DictionaryValue* PrerenderManager::GetAsValue() const { |
| 1072 DCHECK(CalledOnValidThread()); | 708 DCHECK(CalledOnValidThread()); |
| 1073 DictionaryValue* dict_value = new DictionaryValue(); | 709 DictionaryValue* dict_value = new DictionaryValue(); |
| 1074 dict_value->Set("history", prerender_history_->GetEntriesAsValue()); | 710 dict_value->Set("history", prerender_history_->GetEntriesAsValue()); |
| 1075 dict_value->Set("active", GetActivePrerendersAsValue()); | 711 dict_value->Set("active", GetActivePrerendersAsValue()); |
| 1076 dict_value->SetBoolean("enabled", enabled_); | 712 dict_value->SetBoolean("enabled", enabled_); |
| 1077 dict_value->SetBoolean("omnibox_enabled", IsOmniboxEnabled(profile_)); | 713 dict_value->SetBoolean("omnibox_enabled", IsOmniboxEnabled(profile_)); |
| 1078 // If prerender is disabled via a flag this method is not even called. | 714 // If prerender is disabled via a flag this method is not even called. |
| 1079 if (IsControlGroup()) | 715 if (IsControlGroup()) |
| 1080 dict_value->SetString("disabled_reason", "(Disabled for testing)"); | 716 dict_value->SetString("disabled_reason", "(Disabled for testing)"); |
| 1081 if (IsNoUseGroup()) | 717 if (IsNoUseGroup()) |
| 1082 dict_value->SetString("disabled_reason", "(Not using prerendered pages)"); | 718 dict_value->SetString("disabled_reason", "(Not using prerendered pages)"); |
| 1083 return dict_value; | 719 return dict_value; |
| 1084 } | 720 } |
| 1085 | 721 |
| 1086 void PrerenderManager::ClearData(int clear_flags) { | 722 void PrerenderManager::ClearData(int clear_flags) { |
| 1087 DCHECK_GE(clear_flags, 0); | 723 DCHECK_GE(clear_flags, 0); |
| 1088 DCHECK_LT(clear_flags, CLEAR_MAX); | 724 DCHECK_LT(clear_flags, CLEAR_MAX); |
| 1089 if (clear_flags & CLEAR_PRERENDER_CONTENTS) | 725 if (clear_flags & CLEAR_PRERENDER_CONTENTS) |
| 1090 DestroyAllContents(FINAL_STATUS_CACHE_OR_HISTORY_CLEARED); | 726 DestroyAllContents(FINAL_STATUS_CACHE_OR_HISTORY_CLEARED); |
| 1091 // This has to be second, since destroying prerenders can add to the history. | 727 // This has to be second, since destroying prerenders can add to the history. |
| 1092 if (clear_flags & CLEAR_PRERENDER_HISTORY) | 728 if (clear_flags & CLEAR_PRERENDER_HISTORY) |
| 1093 prerender_history_->Clear(); | 729 prerender_history_->Clear(); |
| 1094 } | 730 } |
| 1095 | 731 |
| 1096 void PrerenderManager::AddToHistory(PrerenderContents* contents) { | |
| 1097 PrerenderHistory::Entry entry(contents->prerender_url(), | |
| 1098 contents->final_status(), | |
| 1099 contents->origin(), | |
| 1100 base::Time::Now()); | |
| 1101 prerender_history_->AddEntry(entry); | |
| 1102 } | |
| 1103 | |
| 1104 void PrerenderManager::RecordNavigation(const GURL& url) { | |
| 1105 DCHECK(CalledOnValidThread()); | |
| 1106 | |
| 1107 navigations_.push_back(NavigationRecord(url, GetCurrentTimeTicks())); | |
| 1108 CleanUpOldNavigations(); | |
| 1109 } | |
| 1110 | |
| 1111 Value* PrerenderManager::GetActivePrerendersAsValue() const { | |
| 1112 ListValue* list_value = new ListValue(); | |
| 1113 for (std::list<PrerenderContentsData>::const_iterator it = | |
| 1114 prerender_list_.begin(); | |
| 1115 it != prerender_list_.end(); | |
| 1116 ++it) { | |
| 1117 Value* prerender_value = it->contents_->GetAsValue(); | |
| 1118 if (!prerender_value) | |
| 1119 continue; | |
| 1120 list_value->Append(prerender_value); | |
| 1121 } | |
| 1122 return list_value; | |
| 1123 } | |
| 1124 | |
| 1125 void PrerenderManager::DestroyAllContents(FinalStatus final_status) { | |
| 1126 DeleteOldTabContents(); | |
| 1127 while (!prerender_list_.empty()) { | |
| 1128 PrerenderContentsData data = prerender_list_.front(); | |
| 1129 prerender_list_.pop_front(); | |
| 1130 data.contents_->Destroy(final_status); | |
| 1131 } | |
| 1132 DeletePendingDeleteEntries(); | |
| 1133 } | |
| 1134 | |
| 1135 void PrerenderManager::RecordFinalStatusWithMatchCompleteStatus( | 732 void PrerenderManager::RecordFinalStatusWithMatchCompleteStatus( |
| 1136 Origin origin, | 733 Origin origin, |
| 1137 uint8 experiment_id, | 734 uint8 experiment_id, |
| 1138 PrerenderContents::MatchCompleteStatus mc_status, | 735 PrerenderContents::MatchCompleteStatus mc_status, |
| 1139 FinalStatus final_status) const { | 736 FinalStatus final_status) const { |
| 1140 histograms_->RecordFinalStatus(origin, | 737 histograms_->RecordFinalStatus(origin, |
| 1141 experiment_id, | 738 experiment_id, |
| 1142 mc_status, | 739 mc_status, |
| 1143 final_status); | 740 final_status); |
| 1144 } | 741 } |
| 1145 | 742 |
| 743 void PrerenderManager::AddCondition(const PrerenderCondition* condition) { |
| 744 prerender_conditions_.push_back(condition); |
| 745 } |
| 746 |
| 747 bool PrerenderManager::IsTopSite(const GURL& url) { |
| 748 if (!most_visited_.get()) |
| 749 most_visited_.reset(new MostVisitedSites(profile_)); |
| 750 return most_visited_->IsTopSite(url); |
| 751 } |
| 752 |
| 753 bool PrerenderManager::IsPendingEntry(const GURL& url) const { |
| 754 DCHECK(CalledOnValidThread()); |
| 755 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); |
| 756 it != prerender_list_.end(); |
| 757 ++it) { |
| 758 if (it->contents_->IsPendingEntry(url)) |
| 759 return true; |
| 760 } |
| 761 return false; |
| 762 } |
| 763 |
| 764 bool PrerenderManager::IsPrerendering(const GURL& url) const { |
| 765 DCHECK(CalledOnValidThread()); |
| 766 return (FindEntry(url) != NULL); |
| 767 } |
| 768 |
| 769 // protected |
| 770 void PrerenderManager::SetPrerenderContentsFactory( |
| 771 PrerenderContents::Factory* prerender_contents_factory) { |
| 772 DCHECK(CalledOnValidThread()); |
| 773 prerender_contents_factory_.reset(prerender_contents_factory); |
| 774 } |
| 775 |
| 776 void PrerenderManager::DoShutdown() { |
| 777 DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN); |
| 778 STLDeleteElements(&prerender_conditions_); |
| 779 profile_ = NULL; |
| 780 } |
| 781 |
| 782 // private |
| 783 bool PrerenderManager::AddPrerender( |
| 784 Origin origin, |
| 785 const std::pair<int, int>& child_route_id_pair, |
| 786 const GURL& url_arg, |
| 787 const content::Referrer& referrer, |
| 788 SessionStorageNamespace* session_storage_namespace) { |
| 789 DCHECK(CalledOnValidThread()); |
| 790 |
| 791 if (origin == ORIGIN_LINK_REL_PRERENDER && |
| 792 IsGoogleSearchResultURL(referrer.url)) { |
| 793 origin = ORIGIN_GWS_PRERENDER; |
| 794 } |
| 795 |
| 796 // If the referring page is prerendering, defer the prerender. |
| 797 PrerenderContentsDataList::iterator source_prerender = |
| 798 FindPrerenderContentsForChildRouteIdPair(child_route_id_pair); |
| 799 if (source_prerender != prerender_list_.end()) { |
| 800 source_prerender->contents_->AddPendingPrerender( |
| 801 origin, url_arg, referrer); |
| 802 return true; |
| 803 } |
| 804 |
| 805 DeleteOldEntries(); |
| 806 DeletePendingDeleteEntries(); |
| 807 |
| 808 GURL url = url_arg; |
| 809 GURL alias_url; |
| 810 if (IsControlGroup() && MaybeGetQueryStringBasedAliasURL(url, &alias_url)) |
| 811 url = alias_url; |
| 812 |
| 813 // From here on, we will record a FinalStatus so we need to register with the |
| 814 // histogram tracking. |
| 815 histograms_->RecordPrerender(origin, url_arg); |
| 816 |
| 817 uint8 experiment = GetQueryStringBasedExperiment(url_arg); |
| 818 |
| 819 if (FindEntry(url)) { |
| 820 RecordFinalStatus(origin, experiment, FINAL_STATUS_DUPLICATE); |
| 821 return false; |
| 822 } |
| 823 |
| 824 // Do not prerender if there are too many render processes, and we would |
| 825 // have to use an existing one. We do not want prerendering to happen in |
| 826 // a shared process, so that we can always reliably lower the CPU |
| 827 // priority for prerendering. |
| 828 // In single-process mode, ShouldTryToUseExistingProcessHost() always returns |
| 829 // true, so that case needs to be explicitly checked for. |
| 830 // TODO(tburkard): Figure out how to cancel prerendering in the opposite |
| 831 // case, when a new tab is added to a process used for prerendering. |
| 832 if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost() && |
| 833 !content::RenderProcessHost::run_renderer_in_process()) { |
| 834 RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES); |
| 835 return false; |
| 836 } |
| 837 |
| 838 // Check if enough time has passed since the last prerender. |
| 839 if (!DoesRateLimitAllowPrerender()) { |
| 840 // Cancel the prerender. We could add it to the pending prerender list but |
| 841 // this doesn't make sense as the next prerender request will be triggered |
| 842 // by a navigation and is unlikely to be the same site. |
| 843 RecordFinalStatus(origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED); |
| 844 return false; |
| 845 } |
| 846 |
| 847 RenderViewHost* source_render_view_host = NULL; |
| 848 if (child_route_id_pair.first != -1) { |
| 849 source_render_view_host = |
| 850 RenderViewHost::FromID(child_route_id_pair.first, |
| 851 child_route_id_pair.second); |
| 852 // Don't prerender page if parent RenderViewHost no longer exists, or it has |
| 853 // no view. The latter should only happen when the RenderView has closed. |
| 854 if (!source_render_view_host || !source_render_view_host->view()) { |
| 855 RecordFinalStatus(origin, experiment, |
| 856 FINAL_STATUS_SOURCE_RENDER_VIEW_CLOSED); |
| 857 return false; |
| 858 } |
| 859 } |
| 860 |
| 861 if (!session_storage_namespace && source_render_view_host) { |
| 862 session_storage_namespace = |
| 863 source_render_view_host->session_storage_namespace(); |
| 864 } |
| 865 |
| 866 PrerenderContents* prerender_contents = CreatePrerenderContents( |
| 867 url, referrer, origin, experiment); |
| 868 if (!prerender_contents || !prerender_contents->Init()) |
| 869 return false; |
| 870 |
| 871 histograms_->RecordPrerenderStarted(origin); |
| 872 |
| 873 // TODO(cbentzel): Move invalid checks here instead of PrerenderContents? |
| 874 PrerenderContentsData data(prerender_contents, GetCurrentTime()); |
| 875 |
| 876 prerender_list_.push_back(data); |
| 877 |
| 878 if (!IsControlGroup()) { |
| 879 last_prerender_start_time_ = GetCurrentTimeTicks(); |
| 880 data.contents_->StartPrerendering(source_render_view_host, |
| 881 session_storage_namespace); |
| 882 } |
| 883 while (prerender_list_.size() > config_.max_elements) { |
| 884 data = prerender_list_.front(); |
| 885 prerender_list_.pop_front(); |
| 886 data.contents_->Destroy(FINAL_STATUS_EVICTED); |
| 887 } |
| 888 StartSchedulingPeriodicCleanups(); |
| 889 return true; |
| 890 } |
| 891 |
| 892 PrerenderContents* PrerenderManager::GetEntry(const GURL& url) { |
| 893 return GetEntryButNotSpecifiedWC(url, NULL); |
| 894 } |
| 895 |
| 896 PrerenderContents* PrerenderManager::GetEntryButNotSpecifiedWC( |
| 897 const GURL& url, |
| 898 WebContents* wc) { |
| 899 DCHECK(CalledOnValidThread()); |
| 900 DeleteOldEntries(); |
| 901 DeletePendingDeleteEntries(); |
| 902 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); |
| 903 it != prerender_list_.end(); |
| 904 ++it) { |
| 905 PrerenderContents* prerender_contents = it->contents_; |
| 906 if (prerender_contents->MatchesURL(url, NULL)) { |
| 907 if (!prerender_contents->prerender_contents() || |
| 908 !wc || |
| 909 prerender_contents->prerender_contents()->web_contents() != wc) { |
| 910 prerender_list_.erase(it); |
| 911 return prerender_contents; |
| 912 } |
| 913 } |
| 914 } |
| 915 // Entry not found. |
| 916 return NULL; |
| 917 } |
| 918 |
| 919 void PrerenderManager::StartSchedulingPeriodicCleanups() { |
| 920 DCHECK(CalledOnValidThread()); |
| 921 if (repeating_timer_.IsRunning()) |
| 922 return; |
| 923 repeating_timer_.Start(FROM_HERE, |
| 924 base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs), |
| 925 this, |
| 926 &PrerenderManager::PeriodicCleanup); |
| 927 } |
| 928 |
| 929 void PrerenderManager::MaybeStopSchedulingPeriodicCleanups() { |
| 930 if (!prerender_list_.empty()) |
| 931 return; |
| 932 |
| 933 DCHECK(CalledOnValidThread()); |
| 934 repeating_timer_.Stop(); |
| 935 } |
| 936 |
| 937 void PrerenderManager::PeriodicCleanup() { |
| 938 DCHECK(CalledOnValidThread()); |
| 939 DeleteOldTabContents(); |
| 940 DeleteOldEntries(); |
| 941 |
| 942 // Grab a copy of the current PrerenderContents pointers, so that we |
| 943 // will not interfere with potential deletions of the list. |
| 944 std::vector<PrerenderContents*> prerender_contents; |
| 945 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); |
| 946 it != prerender_list_.end(); |
| 947 ++it) { |
| 948 DCHECK(it->contents_); |
| 949 prerender_contents.push_back(it->contents_); |
| 950 } |
| 951 for (std::vector<PrerenderContents*>::iterator it = |
| 952 prerender_contents.begin(); |
| 953 it != prerender_contents.end(); |
| 954 ++it) { |
| 955 (*it)->DestroyWhenUsingTooManyResources(); |
| 956 } |
| 957 |
| 958 DeletePendingDeleteEntries(); |
| 959 } |
| 960 |
| 961 void PrerenderManager::PostCleanupTask() { |
| 962 DCHECK(CalledOnValidThread()); |
| 963 MessageLoop::current()->PostTask( |
| 964 FROM_HERE, |
| 965 base::Bind(&PrerenderManager::PeriodicCleanup, |
| 966 weak_factory_.GetWeakPtr())); |
| 967 } |
| 968 |
| 969 bool PrerenderManager::IsPrerenderElementFresh(const base::Time start) const { |
| 970 DCHECK(CalledOnValidThread()); |
| 971 base::Time now = GetCurrentTime(); |
| 972 return (now - start < config_.max_age); |
| 973 } |
| 974 |
| 975 void PrerenderManager::DeleteOldEntries() { |
| 976 DCHECK(CalledOnValidThread()); |
| 977 while (!prerender_list_.empty()) { |
| 978 PrerenderContentsData data = prerender_list_.front(); |
| 979 if (IsPrerenderElementFresh(data.start_time_)) |
| 980 return; |
| 981 data.contents_->Destroy(FINAL_STATUS_TIMED_OUT); |
| 982 } |
| 983 MaybeStopSchedulingPeriodicCleanups(); |
| 984 } |
| 985 |
| 986 base::Time PrerenderManager::GetCurrentTime() const { |
| 987 return base::Time::Now(); |
| 988 } |
| 989 |
| 990 base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const { |
| 991 return base::TimeTicks::Now(); |
| 992 } |
| 993 |
| 994 PrerenderContents* PrerenderManager::CreatePrerenderContents( |
| 995 const GURL& url, |
| 996 const content::Referrer& referrer, |
| 997 Origin origin, |
| 998 uint8 experiment_id) { |
| 999 DCHECK(CalledOnValidThread()); |
| 1000 return prerender_contents_factory_->CreatePrerenderContents( |
| 1001 this, prerender_tracker_, profile_, url, |
| 1002 referrer, origin, experiment_id); |
| 1003 } |
| 1004 |
| 1005 bool PrerenderManager::IsPendingDelete(PrerenderContents* entry) const { |
| 1006 DCHECK(CalledOnValidThread()); |
| 1007 for (std::list<PrerenderContents*>::const_iterator it = |
| 1008 pending_delete_list_.begin(); |
| 1009 it != pending_delete_list_.end(); |
| 1010 ++it) { |
| 1011 if (*it == entry) |
| 1012 return true; |
| 1013 } |
| 1014 |
| 1015 return false; |
| 1016 } |
| 1017 |
| 1018 void PrerenderManager::DeletePendingDeleteEntries() { |
| 1019 while (!pending_delete_list_.empty()) { |
| 1020 PrerenderContents* contents = pending_delete_list_.front(); |
| 1021 pending_delete_list_.pop_front(); |
| 1022 delete contents; |
| 1023 } |
| 1024 } |
| 1025 |
| 1026 PrerenderContents* PrerenderManager::FindEntry(const GURL& url) const { |
| 1027 DCHECK(CalledOnValidThread()); |
| 1028 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); |
| 1029 it != prerender_list_.end(); |
| 1030 ++it) { |
| 1031 if (it->contents_->MatchesURL(url, NULL)) |
| 1032 return it->contents_; |
| 1033 } |
| 1034 // Entry not found. |
| 1035 return NULL; |
| 1036 } |
| 1037 |
| 1038 std::list<PrerenderManager::PrerenderContentsData>::iterator |
| 1039 PrerenderManager::FindPrerenderContentsForChildRouteIdPair( |
| 1040 const std::pair<int, int>& child_route_id_pair) { |
| 1041 PrerenderContentsDataList::iterator it = prerender_list_.begin(); |
| 1042 for (; it != prerender_list_.end(); ++it) { |
| 1043 PrerenderContents* prerender_contents = it->contents_; |
| 1044 |
| 1045 int child_id; |
| 1046 int route_id; |
| 1047 bool has_child_id = prerender_contents->GetChildId(&child_id); |
| 1048 bool has_route_id = has_child_id && |
| 1049 prerender_contents->GetRouteId(&route_id); |
| 1050 |
| 1051 if (has_child_id && has_route_id && |
| 1052 child_id == child_route_id_pair.first && |
| 1053 route_id == child_route_id_pair.second) { |
| 1054 break; |
| 1055 } |
| 1056 } |
| 1057 return it; |
| 1058 } |
| 1059 |
| 1060 bool PrerenderManager::DoesRateLimitAllowPrerender() const { |
| 1061 DCHECK(CalledOnValidThread()); |
| 1062 base::TimeDelta elapsed_time = |
| 1063 GetCurrentTimeTicks() - last_prerender_start_time_; |
| 1064 histograms_->RecordTimeBetweenPrerenderRequests(elapsed_time); |
| 1065 if (!config_.rate_limit_enabled) |
| 1066 return true; |
| 1067 return elapsed_time > |
| 1068 base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs); |
| 1069 } |
| 1070 |
| 1071 void PrerenderManager::DeleteOldTabContents() { |
| 1072 while (!old_tab_contents_list_.empty()) { |
| 1073 TabContentsWrapper* tab_contents = old_tab_contents_list_.front(); |
| 1074 old_tab_contents_list_.pop_front(); |
| 1075 // TODO(dominich): should we use Instant Unload Handler here? |
| 1076 delete tab_contents; |
| 1077 } |
| 1078 } |
| 1079 |
| 1080 void PrerenderManager::CleanUpOldNavigations() { |
| 1081 DCHECK(CalledOnValidThread()); |
| 1082 |
| 1083 // Cutoff. Navigations before this cutoff can be discarded. |
| 1084 base::TimeTicks cutoff = GetCurrentTimeTicks() - |
| 1085 base::TimeDelta::FromMilliseconds(kNavigationRecordWindowMs); |
| 1086 while (!navigations_.empty()) { |
| 1087 if (navigations_.front().time_ > cutoff) |
| 1088 break; |
| 1089 navigations_.pop_front(); |
| 1090 } |
| 1091 } |
| 1092 |
| 1093 void PrerenderManager::ScheduleDeleteOldTabContents( |
| 1094 TabContentsWrapper* tab, |
| 1095 OnCloseTabContentsDeleter* deleter) { |
| 1096 old_tab_contents_list_.push_back(tab); |
| 1097 PostCleanupTask(); |
| 1098 |
| 1099 if (deleter) { |
| 1100 ScopedVector<OnCloseTabContentsDeleter>::iterator i = std::find( |
| 1101 on_close_tab_contents_deleters_.begin(), |
| 1102 on_close_tab_contents_deleters_.end(), |
| 1103 deleter); |
| 1104 DCHECK(i != on_close_tab_contents_deleters_.end()); |
| 1105 on_close_tab_contents_deleters_.erase(i); |
| 1106 } |
| 1107 } |
| 1108 |
| 1109 void PrerenderManager::AddToHistory(PrerenderContents* contents) { |
| 1110 PrerenderHistory::Entry entry(contents->prerender_url(), |
| 1111 contents->final_status(), |
| 1112 contents->origin(), |
| 1113 base::Time::Now()); |
| 1114 prerender_history_->AddEntry(entry); |
| 1115 } |
| 1116 |
| 1117 void PrerenderManager::RecordNavigation(const GURL& url) { |
| 1118 DCHECK(CalledOnValidThread()); |
| 1119 |
| 1120 navigations_.push_back(NavigationRecord(url, GetCurrentTimeTicks())); |
| 1121 CleanUpOldNavigations(); |
| 1122 } |
| 1123 |
| 1124 Value* PrerenderManager::GetActivePrerendersAsValue() const { |
| 1125 ListValue* list_value = new ListValue(); |
| 1126 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); |
| 1127 it != prerender_list_.end(); |
| 1128 ++it) { |
| 1129 Value* prerender_value = it->contents_->GetAsValue(); |
| 1130 if (!prerender_value) |
| 1131 continue; |
| 1132 list_value->Append(prerender_value); |
| 1133 } |
| 1134 return list_value; |
| 1135 } |
| 1136 |
| 1137 void PrerenderManager::DestroyAllContents(FinalStatus final_status) { |
| 1138 DeleteOldTabContents(); |
| 1139 while (!prerender_list_.empty()) { |
| 1140 PrerenderContentsData data = prerender_list_.front(); |
| 1141 prerender_list_.pop_front(); |
| 1142 data.contents_->Destroy(final_status); |
| 1143 } |
| 1144 DeletePendingDeleteEntries(); |
| 1145 } |
| 1146 |
| 1147 void PrerenderManager::DestroyAndMarkMatchCompleteAsUsed( |
| 1148 PrerenderContents* prerender_contents, |
| 1149 FinalStatus final_status) { |
| 1150 prerender_contents->set_match_complete_status( |
| 1151 PrerenderContents::MATCH_COMPLETE_REPLACED); |
| 1152 histograms_->RecordFinalStatus(prerender_contents->origin(), |
| 1153 prerender_contents->experiment_id(), |
| 1154 PrerenderContents::MATCH_COMPLETE_REPLACEMENT, |
| 1155 FINAL_STATUS_WOULD_HAVE_BEEN_USED); |
| 1156 prerender_contents->Destroy(final_status); |
| 1157 } |
| 1158 |
| 1146 void PrerenderManager::RecordFinalStatus(Origin origin, | 1159 void PrerenderManager::RecordFinalStatus(Origin origin, |
| 1147 uint8 experiment_id, | 1160 uint8 experiment_id, |
| 1148 FinalStatus final_status) const { | 1161 FinalStatus final_status) const { |
| 1149 RecordFinalStatusWithMatchCompleteStatus( | 1162 RecordFinalStatusWithMatchCompleteStatus( |
| 1150 origin, experiment_id, | 1163 origin, experiment_id, |
| 1151 PrerenderContents::MATCH_COMPLETE_DEFAULT, | 1164 PrerenderContents::MATCH_COMPLETE_DEFAULT, |
| 1152 final_status); | 1165 final_status); |
| 1153 } | 1166 } |
| 1154 | 1167 |
| 1155 | |
| 1156 PrerenderManager* FindPrerenderManagerUsingRenderProcessId( | 1168 PrerenderManager* FindPrerenderManagerUsingRenderProcessId( |
| 1157 int render_process_id) { | 1169 int render_process_id) { |
| 1158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 1170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 1159 content::RenderProcessHost* render_process_host = | 1171 content::RenderProcessHost* render_process_host = |
| 1160 content::RenderProcessHost::FromID(render_process_id); | 1172 content::RenderProcessHost::FromID(render_process_id); |
| 1161 // Each render process is guaranteed to only hold RenderViews owned by the | 1173 // Each render process is guaranteed to only hold RenderViews owned by the |
| 1162 // same BrowserContext. This is enforced by | 1174 // same BrowserContext. This is enforced by |
| 1163 // RenderProcessHost::GetExistingProcessHost. | 1175 // RenderProcessHost::GetExistingProcessHost. |
| 1164 if (!render_process_host || !render_process_host->GetBrowserContext()) | 1176 if (!render_process_host || !render_process_host->GetBrowserContext()) |
| 1165 return NULL; | 1177 return NULL; |
| 1166 Profile* profile = Profile::FromBrowserContext( | 1178 Profile* profile = Profile::FromBrowserContext( |
| 1167 render_process_host->GetBrowserContext()); | 1179 render_process_host->GetBrowserContext()); |
| 1168 if (!profile) | 1180 if (!profile) |
| 1169 return NULL; | 1181 return NULL; |
| 1170 return PrerenderManagerFactory::GetInstance()->GetForProfile(profile); | 1182 return PrerenderManagerFactory::GetInstance()->GetForProfile(profile); |
| 1171 } | 1183 } |
| 1172 | 1184 |
| 1173 } // namespace prerender | 1185 } // namespace prerender |
| OLD | NEW |