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