Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(771)

Side by Side Diff: chrome/browser/prerender/prerender_manager.cc

Issue 9226037: Cancel prerenders from Omnibox if we navigate to a different URL than predicted (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix leak Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « chrome/browser/prerender/prerender_manager.h ('k') | chrome/browser/prerender/prerender_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698