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

Unified 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: Reorg prerender_manager.cc to match header. Add tests for cancellation. Created 8 years, 11 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/prerender/prerender_manager.cc
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 59bb8609fd6a56cf8b05fb11f920b8a2b014b779..e52557c3721f09c43afdf9c3228e19f1a88cd2d2 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -153,52 +153,6 @@ int PrerenderManager::prerenders_per_session_count_ = 0;
PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ =
PRERENDER_MODE_ENABLED;
-// static
-PrerenderManager::PrerenderManagerMode PrerenderManager::GetMode() {
- return mode_;
-}
-
-// static
-void PrerenderManager::SetMode(PrerenderManagerMode mode) {
- mode_ = mode;
-}
-
-// static
-bool PrerenderManager::IsPrerenderingPossible() {
- return GetMode() == PRERENDER_MODE_ENABLED ||
- GetMode() == PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP ||
- GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP ||
- GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP;
-}
-
-// static
-bool PrerenderManager::ActuallyPrerendering() {
- return IsPrerenderingPossible() && !IsControlGroup();
-}
-
-// static
-bool PrerenderManager::IsControlGroup() {
- return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP;
-}
-
-// static
-bool PrerenderManager::IsNoUseGroup() {
- return GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP;
-}
-
-// static
-bool PrerenderManager::IsValidHttpMethod(const std::string& method) {
- // method has been canonicalized to upper case at this point so we can just
- // compare them.
- DCHECK_EQ(method, StringToUpperASCII(method));
- for (size_t i = 0; i < arraysize(kValidHttpMethods); ++i) {
- if (method.compare(kValidHttpMethods[i]) == 0)
- return true;
- }
-
- return false;
-}
-
struct PrerenderManager::PrerenderContentsData {
PrerenderContents* contents_;
base::Time start_time_;
@@ -272,24 +226,6 @@ class PrerenderManager::MostVisitedSites
std::set<GURL> urls_;
};
-bool PrerenderManager::IsTopSite(const GURL& url) {
- if (!most_visited_.get())
- most_visited_.reset(new MostVisitedSites(profile_));
- return most_visited_->IsTopSite(url);
-}
-
-bool PrerenderManager::IsPendingEntry(const GURL& url) const {
- DCHECK(CalledOnValidThread());
- for (std::list<PrerenderContentsData>::const_iterator it =
- prerender_list_.begin();
- it != prerender_list_.end();
- ++it) {
- if (it->contents_->IsPendingEntry(url))
- return true;
- }
- return false;
-}
-
PrerenderManager::PrerenderManager(Profile* profile,
PrerenderTracker* prerender_tracker)
: enabled_(true),
@@ -314,12 +250,6 @@ void PrerenderManager::Shutdown() {
DoShutdown();
}
-void PrerenderManager::SetPrerenderContentsFactory(
- PrerenderContents::Factory* prerender_contents_factory) {
- DCHECK(CalledOnValidThread());
- prerender_contents_factory_.reset(prerender_contents_factory);
-}
-
bool PrerenderManager::AddPrerenderFromLinkRelPrerender(
int process_id,
int route_id,
@@ -335,146 +265,12 @@ bool PrerenderManager::AddPrerenderFromLinkRelPrerender(
bool PrerenderManager::AddPrerenderFromOmnibox(
const GURL& url,
SessionStorageNamespace* session_storage_namespace) {
- DCHECK(session_storage_namespace);
cbentzel 2012/01/25 12:14:01 Why was this removed?
if (!IsOmniboxEnabled(profile_))
return false;
return AddPrerender(ORIGIN_OMNIBOX, std::make_pair(-1, -1), url,
content::Referrer(), session_storage_namespace);
}
-bool PrerenderManager::AddPrerender(
- Origin origin,
- const std::pair<int, int>& child_route_id_pair,
- const GURL& url_arg,
- const content::Referrer& referrer,
- SessionStorageNamespace* session_storage_namespace) {
- DCHECK(CalledOnValidThread());
-
- if (origin == ORIGIN_LINK_REL_PRERENDER &&
- IsGoogleSearchResultURL(referrer.url)) {
- origin = ORIGIN_GWS_PRERENDER;
- }
-
- // If the referring page is prerendering, defer the prerender.
- std::list<PrerenderContentsData>::iterator source_prerender =
- FindPrerenderContentsForChildRouteIdPair(child_route_id_pair);
- if (source_prerender != prerender_list_.end()) {
- source_prerender->contents_->AddPendingPrerender(
- origin, url_arg, referrer);
- return true;
- }
-
- DeleteOldEntries();
- DeletePendingDeleteEntries();
-
- GURL url = url_arg;
- GURL alias_url;
- if (IsControlGroup() && MaybeGetQueryStringBasedAliasURL(url, &alias_url))
- url = alias_url;
-
- // From here on, we will record a FinalStatus so we need to register with the
- // histogram tracking.
- histograms_->RecordPrerender(origin, url_arg);
-
- uint8 experiment = GetQueryStringBasedExperiment(url_arg);
-
- if (FindEntry(url)) {
- RecordFinalStatus(origin, experiment, FINAL_STATUS_DUPLICATE);
- return false;
- }
-
- // Do not prerender if there are too many render processes, and we would
- // have to use an existing one. We do not want prerendering to happen in
- // a shared process, so that we can always reliably lower the CPU
- // priority for prerendering.
- // In single-process mode, ShouldTryToUseExistingProcessHost() always returns
- // true, so that case needs to be explicitly checked for.
- // TODO(tburkard): Figure out how to cancel prerendering in the opposite
- // case, when a new tab is added to a process used for prerendering.
- if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost() &&
- !content::RenderProcessHost::run_renderer_in_process()) {
- RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES);
- return false;
- }
-
- // Check if enough time has passed since the last prerender.
- if (!DoesRateLimitAllowPrerender()) {
- // Cancel the prerender. We could add it to the pending prerender list but
- // this doesn't make sense as the next prerender request will be triggered
- // by a navigation and is unlikely to be the same site.
- RecordFinalStatus(origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED);
- return false;
- }
-
- RenderViewHost* source_render_view_host = NULL;
- if (child_route_id_pair.first != -1) {
- source_render_view_host =
- RenderViewHost::FromID(child_route_id_pair.first,
- child_route_id_pair.second);
- // Don't prerender page if parent RenderViewHost no longer exists, or it has
- // no view. The latter should only happen when the RenderView has closed.
- if (!source_render_view_host || !source_render_view_host->view()) {
- RecordFinalStatus(origin, experiment,
- FINAL_STATUS_SOURCE_RENDER_VIEW_CLOSED);
- return false;
- }
- }
-
- if (!session_storage_namespace && source_render_view_host) {
- session_storage_namespace =
- source_render_view_host->session_storage_namespace();
- }
-
- PrerenderContents* prerender_contents = CreatePrerenderContents(
- url, referrer, origin, experiment);
- if (!prerender_contents || !prerender_contents->Init())
- return false;
-
- histograms_->RecordPrerenderStarted(origin);
-
- // TODO(cbentzel): Move invalid checks here instead of PrerenderContents?
- PrerenderContentsData data(prerender_contents, GetCurrentTime());
-
- prerender_list_.push_back(data);
-
- if (IsControlGroup()) {
- data.contents_->set_final_status(FINAL_STATUS_CONTROL_GROUP);
- } else {
- last_prerender_start_time_ = GetCurrentTimeTicks();
- data.contents_->StartPrerendering(source_render_view_host,
- session_storage_namespace);
- }
- while (prerender_list_.size() > config_.max_elements) {
- data = prerender_list_.front();
- prerender_list_.pop_front();
- data.contents_->Destroy(FINAL_STATUS_EVICTED);
- }
- StartSchedulingPeriodicCleanups();
- return true;
-}
-
-std::list<PrerenderManager::PrerenderContentsData>::iterator
- PrerenderManager::FindPrerenderContentsForChildRouteIdPair(
- const std::pair<int, int>& child_route_id_pair) {
- std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
- for (; it != prerender_list_.end(); ++it) {
- PrerenderContents* prerender_contents = it->contents_;
-
- int child_id;
- int route_id;
- bool has_child_id = prerender_contents->GetChildId(&child_id);
- bool has_route_id = has_child_id &&
- prerender_contents->GetRouteId(&route_id);
-
- if (has_child_id && has_route_id &&
- child_id == child_route_id_pair.first &&
- route_id == child_route_id_pair.second) {
- break;
- }
- }
- return it;
-}
-
void PrerenderManager::DestroyPrerenderForRenderView(
int process_id, int view_id, FinalStatus final_status) {
DCHECK(CalledOnValidThread());
@@ -496,42 +292,14 @@ void PrerenderManager::CancelAllPrerenders() {
}
}
-void PrerenderManager::DeleteOldEntries() {
- DCHECK(CalledOnValidThread());
- while (!prerender_list_.empty()) {
- PrerenderContentsData data = prerender_list_.front();
- if (IsPrerenderElementFresh(data.start_time_))
- return;
- data.contents_->Destroy(FINAL_STATUS_TIMED_OUT);
- }
- MaybeStopSchedulingPeriodicCleanups();
-}
-
-PrerenderContents* PrerenderManager::GetEntryButNotSpecifiedWC(
- const GURL& url,
- WebContents* wc) {
+void PrerenderManager::CancelOmniboxPrerenders() {
DCHECK(CalledOnValidThread());
- DeleteOldEntries();
- DeletePendingDeleteEntries();
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
- it != prerender_list_.end();
- ++it) {
- PrerenderContents* prerender_contents = it->contents_;
- if (prerender_contents->MatchesURL(url, NULL)) {
- if (!prerender_contents->prerender_contents() ||
- !wc ||
- prerender_contents->prerender_contents()->web_contents() != wc) {
- prerender_list_.erase(it);
- return prerender_contents;
- }
- }
+ it != prerender_list_.end(); ) {
+ std::list<PrerenderContentsData>::iterator cur = it++;
+ if (cur->contents_->origin() == ORIGIN_OMNIBOX)
+ cur->contents_->Destroy(FINAL_STATUS_CANCELLED);
}
- // Entry not found.
- return NULL;
-}
-
-PrerenderContents* PrerenderManager::GetEntry(const GURL& url) {
- return GetEntryButNotSpecifiedWC(url, NULL);
}
bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents,
@@ -741,52 +509,6 @@ void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry,
PostCleanupTask();
}
-base::Time PrerenderManager::GetCurrentTime() const {
- return base::Time::Now();
-}
-
-base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const {
- return base::TimeTicks::Now();
-}
-
-bool PrerenderManager::IsPrerenderElementFresh(const base::Time start) const {
- DCHECK(CalledOnValidThread());
- base::Time now = GetCurrentTime();
- return (now - start < config_.max_age);
-}
-
-PrerenderContents* PrerenderManager::CreatePrerenderContents(
- const GURL& url,
- const content::Referrer& referrer,
- Origin origin,
- uint8 experiment_id) {
- DCHECK(CalledOnValidThread());
- return prerender_contents_factory_->CreatePrerenderContents(
- this, prerender_tracker_, profile_, url,
- referrer, origin, experiment_id);
-}
-
-bool PrerenderManager::IsPendingDelete(PrerenderContents* entry) const {
- DCHECK(CalledOnValidThread());
- for (std::list<PrerenderContents*>::const_iterator it =
- pending_delete_list_.begin();
- it != pending_delete_list_.end();
- ++it) {
- if (*it == entry)
- return true;
- }
-
- return false;
-}
-
-void PrerenderManager::DeletePendingDeleteEntries() {
- while (!pending_delete_list_.empty()) {
- PrerenderContents* contents = pending_delete_list_.front();
- pending_delete_list_.pop_front();
- delete contents;
- }
-}
-
// static
void PrerenderManager::RecordPerceivedPageLoadTime(
base::TimeDelta perceived_page_load_time,
@@ -828,108 +550,37 @@ void PrerenderManager::set_enabled(bool enabled) {
enabled_ = enabled;
}
-void PrerenderManager::AddCondition(const PrerenderCondition* condition) {
- prerender_conditions_.push_back(condition);
-}
-
-PrerenderContents* PrerenderManager::FindEntry(const GURL& url) {
- DCHECK(CalledOnValidThread());
- for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
- it != prerender_list_.end();
- ++it) {
- if (it->contents_->MatchesURL(url, NULL))
- return it->contents_;
- }
- // Entry not found.
- return NULL;
-}
-
-void PrerenderManager::DoShutdown() {
- DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN);
- STLDeleteElements(&prerender_conditions_);
- profile_ = NULL;
-}
-
-bool PrerenderManager::DoesRateLimitAllowPrerender() const {
- DCHECK(CalledOnValidThread());
- base::TimeDelta elapsed_time =
- GetCurrentTimeTicks() - last_prerender_start_time_;
- histograms_->RecordTimeBetweenPrerenderRequests(elapsed_time);
- if (!config_.rate_limit_enabled)
- return true;
- return elapsed_time >
- base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs);
-}
-
-void PrerenderManager::StartSchedulingPeriodicCleanups() {
- DCHECK(CalledOnValidThread());
- if (repeating_timer_.IsRunning())
- return;
- repeating_timer_.Start(FROM_HERE,
- base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs),
- this,
- &PrerenderManager::PeriodicCleanup);
+// static
+PrerenderManager::PrerenderManagerMode PrerenderManager::GetMode() {
+ return mode_;
}
-void PrerenderManager::MaybeStopSchedulingPeriodicCleanups() {
- if (!prerender_list_.empty())
- return;
-
- DCHECK(CalledOnValidThread());
- repeating_timer_.Stop();
+// static
+void PrerenderManager::SetMode(PrerenderManagerMode mode) {
+ mode_ = mode;
}
-void PrerenderManager::DeleteOldTabContents() {
- while (!old_tab_contents_list_.empty()) {
- TabContentsWrapper* tab_contents = old_tab_contents_list_.front();
- old_tab_contents_list_.pop_front();
- // TODO(dominich): should we use Instant Unload Handler here?
- delete tab_contents;
- }
+// static
+bool PrerenderManager::IsPrerenderingPossible() {
+ return GetMode() == PRERENDER_MODE_ENABLED ||
+ GetMode() == PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP ||
+ GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP ||
+ GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP;
}
-bool PrerenderManager::IsOldRenderViewHost(
- const RenderViewHost* render_view_host) const {
- for (std::list<TabContentsWrapper*>::const_iterator it =
- old_tab_contents_list_.begin();
- it != old_tab_contents_list_.end();
- ++it) {
- if ((*it)->web_contents()->GetRenderViewHost() == render_view_host)
- return true;
- }
- return false;
+// static
+bool PrerenderManager::ActuallyPrerendering() {
+ return IsPrerenderingPossible() && !IsControlGroup();
}
-void PrerenderManager::PeriodicCleanup() {
- DCHECK(CalledOnValidThread());
- DeleteOldTabContents();
- DeleteOldEntries();
-
- // Grab a copy of the current PrerenderContents pointers, so that we
- // will not interfere with potential deletions of the list.
- std::vector<PrerenderContents*> prerender_contents;
- for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
- it != prerender_list_.end();
- ++it) {
- DCHECK(it->contents_);
- prerender_contents.push_back(it->contents_);
- }
- for (std::vector<PrerenderContents*>::iterator it =
- prerender_contents.begin();
- it != prerender_contents.end();
- ++it) {
- (*it)->DestroyWhenUsingTooManyResources();
- }
-
- DeletePendingDeleteEntries();
+// static
+bool PrerenderManager::IsControlGroup() {
+ return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP;
}
-void PrerenderManager::PostCleanupTask() {
- DCHECK(CalledOnValidThread());
- MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&PrerenderManager::PeriodicCleanup,
- weak_factory_.GetWeakPtr()));
+// static
+bool PrerenderManager::IsNoUseGroup() {
+ return GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP;
}
bool PrerenderManager::IsWebContentsPrerendering(
@@ -992,6 +643,18 @@ bool PrerenderManager::WouldWebContentsBePrerendered(
return would_be_prerendered_tab_contents_set_.count(web_contents) > 0;
}
+bool PrerenderManager::IsOldRenderViewHost(
+ const RenderViewHost* render_view_host) const {
+ for (std::list<TabContentsWrapper*>::const_iterator it =
+ old_tab_contents_list_.begin();
+ it != old_tab_contents_list_.end();
+ ++it) {
+ if ((*it)->web_contents()->GetRenderViewHost() == render_view_host)
+ return true;
+ }
+ return false;
+}
+
bool PrerenderManager::HasRecentlyBeenNavigatedTo(const GURL& url) {
DCHECK(CalledOnValidThread());
@@ -1006,33 +669,17 @@ bool PrerenderManager::HasRecentlyBeenNavigatedTo(const GURL& url) {
return false;
}
-void PrerenderManager::CleanUpOldNavigations() {
- DCHECK(CalledOnValidThread());
-
- // Cutoff. Navigations before this cutoff can be discarded.
- base::TimeTicks cutoff = GetCurrentTimeTicks() -
- base::TimeDelta::FromMilliseconds(kNavigationRecordWindowMs);
- while (!navigations_.empty()) {
- if (navigations_.front().time_ > cutoff)
- break;
- navigations_.pop_front();
+// static
+bool PrerenderManager::IsValidHttpMethod(const std::string& method) {
+ // method has been canonicalized to upper case at this point so we can just
+ // compare them.
+ DCHECK_EQ(method, StringToUpperASCII(method));
+ for (size_t i = 0; i < arraysize(kValidHttpMethods); ++i) {
+ if (method.compare(kValidHttpMethods[i]) == 0)
+ return true;
}
-}
-
-void PrerenderManager::ScheduleDeleteOldTabContents(
- TabContentsWrapper* tab,
- OnCloseTabContentsDeleter* deleter) {
- old_tab_contents_list_.push_back(tab);
- PostCleanupTask();
- if (deleter) {
- ScopedVector<OnCloseTabContentsDeleter>::iterator i = std::find(
- on_close_tab_contents_deleters_.begin(),
- on_close_tab_contents_deleters_.end(),
- deleter);
- DCHECK(i != on_close_tab_contents_deleters_.end());
- on_close_tab_contents_deleters_.erase(i);
- }
+ return false;
}
DictionaryValue* PrerenderManager::GetAsValue() const {
@@ -1060,49 +707,418 @@ void PrerenderManager::ClearData(int clear_flags) {
prerender_history_->Clear();
}
-void PrerenderManager::AddToHistory(PrerenderContents* contents) {
- PrerenderHistory::Entry entry(contents->prerender_url(),
- contents->final_status(),
- contents->origin(),
- base::Time::Now());
- prerender_history_->AddEntry(entry);
+void PrerenderManager::RecordFinalStatus(Origin origin,
+ uint8 experiment_id,
+ FinalStatus final_status) const {
+ histograms_->RecordFinalStatus(origin, experiment_id, final_status);
}
-void PrerenderManager::RecordNavigation(const GURL& url) {
- DCHECK(CalledOnValidThread());
+void PrerenderManager::AddCondition(const PrerenderCondition* condition) {
+ prerender_conditions_.push_back(condition);
+}
- navigations_.push_back(NavigationRecord(url, GetCurrentTimeTicks()));
- CleanUpOldNavigations();
+bool PrerenderManager::IsTopSite(const GURL& url) {
+ if (!most_visited_.get())
+ most_visited_.reset(new MostVisitedSites(profile_));
+ return most_visited_->IsTopSite(url);
}
-Value* PrerenderManager::GetActivePrerendersAsValue() const {
- ListValue* list_value = new ListValue();
+bool PrerenderManager::IsPendingEntry(const GURL& url) const {
+ DCHECK(CalledOnValidThread());
for (std::list<PrerenderContentsData>::const_iterator it =
prerender_list_.begin();
it != prerender_list_.end();
++it) {
- Value* prerender_value = it->contents_->GetAsValue();
- if (!prerender_value)
- continue;
- list_value->Append(prerender_value);
+ if (it->contents_->IsPendingEntry(url))
+ return true;
}
- return list_value;
+ return false;
}
-void PrerenderManager::DestroyAllContents(FinalStatus final_status) {
- DeleteOldTabContents();
- while (!prerender_list_.empty()) {
- PrerenderContentsData data = prerender_list_.front();
- prerender_list_.pop_front();
- data.contents_->Destroy(final_status);
- }
- DeletePendingDeleteEntries();
+bool PrerenderManager::IsPrerendering(const GURL& url) {
+ DCHECK(CalledOnValidThread());
+ return (FindEntry(url) != NULL);
}
-void PrerenderManager::RecordFinalStatus(Origin origin,
- uint8 experiment_id,
- FinalStatus final_status) const {
- histograms_->RecordFinalStatus(origin, experiment_id, final_status);
+// protected
+void PrerenderManager::SetPrerenderContentsFactory(
+ PrerenderContents::Factory* prerender_contents_factory) {
+ DCHECK(CalledOnValidThread());
+ prerender_contents_factory_.reset(prerender_contents_factory);
+}
+
+void PrerenderManager::DoShutdown() {
+ DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN);
+ STLDeleteElements(&prerender_conditions_);
+ profile_ = NULL;
+}
+
+// private
cbentzel 2012/01/25 12:14:01 Nit: I haven't really seen the // private comment
+bool PrerenderManager::AddPrerender(
+ Origin origin,
+ const std::pair<int, int>& child_route_id_pair,
+ const GURL& url_arg,
+ const content::Referrer& referrer,
+ SessionStorageNamespace* session_storage_namespace) {
+ DCHECK(CalledOnValidThread());
+
+ if (origin == ORIGIN_LINK_REL_PRERENDER &&
+ IsGoogleSearchResultURL(referrer.url)) {
+ origin = ORIGIN_GWS_PRERENDER;
+ }
+
+ // If the referring page is prerendering, defer the prerender.
+ std::list<PrerenderContentsData>::iterator source_prerender =
+ FindPrerenderContentsForChildRouteIdPair(child_route_id_pair);
+ if (source_prerender != prerender_list_.end()) {
+ source_prerender->contents_->AddPendingPrerender(
+ origin, url_arg, referrer);
+ return true;
+ }
+
+ DeleteOldEntries();
+ DeletePendingDeleteEntries();
+
+ GURL url = url_arg;
+ GURL alias_url;
+ if (IsControlGroup() && MaybeGetQueryStringBasedAliasURL(url, &alias_url))
+ url = alias_url;
+
+ // From here on, we will record a FinalStatus so we need to register with the
+ // histogram tracking.
+ histograms_->RecordPrerender(origin, url_arg);
+
+ uint8 experiment = GetQueryStringBasedExperiment(url_arg);
+
+ if (FindEntry(url)) {
+ RecordFinalStatus(origin, experiment, FINAL_STATUS_DUPLICATE);
+ return false;
+ }
+
+ // Do not prerender if there are too many render processes, and we would
+ // have to use an existing one. We do not want prerendering to happen in
+ // a shared process, so that we can always reliably lower the CPU
+ // priority for prerendering.
+ // In single-process mode, ShouldTryToUseExistingProcessHost() always returns
+ // true, so that case needs to be explicitly checked for.
+ // TODO(tburkard): Figure out how to cancel prerendering in the opposite
+ // case, when a new tab is added to a process used for prerendering.
+ if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost() &&
+ !content::RenderProcessHost::run_renderer_in_process()) {
+ RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES);
+ return false;
+ }
+
+ // Check if enough time has passed since the last prerender.
+ if (!DoesRateLimitAllowPrerender()) {
+ // Cancel the prerender. We could add it to the pending prerender list but
+ // this doesn't make sense as the next prerender request will be triggered
+ // by a navigation and is unlikely to be the same site.
+ RecordFinalStatus(origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED);
+ return false;
+ }
+
+ RenderViewHost* source_render_view_host = NULL;
+ if (child_route_id_pair.first != -1) {
+ source_render_view_host =
+ RenderViewHost::FromID(child_route_id_pair.first,
+ child_route_id_pair.second);
+ // Don't prerender page if parent RenderViewHost no longer exists, or it has
+ // no view. The latter should only happen when the RenderView has closed.
+ if (!source_render_view_host || !source_render_view_host->view()) {
+ RecordFinalStatus(origin, experiment,
+ FINAL_STATUS_SOURCE_RENDER_VIEW_CLOSED);
+ return false;
+ }
+ }
+
+ if (!session_storage_namespace && source_render_view_host) {
+ session_storage_namespace =
+ source_render_view_host->session_storage_namespace();
+ }
+
+ PrerenderContents* prerender_contents = CreatePrerenderContents(
+ url, referrer, origin, experiment);
+ if (!prerender_contents || !prerender_contents->Init())
+ return false;
+
+ histograms_->RecordPrerenderStarted(origin);
+
+ // TODO(cbentzel): Move invalid checks here instead of PrerenderContents?
+ PrerenderContentsData data(prerender_contents, GetCurrentTime());
+
+ prerender_list_.push_back(data);
+
+ if (IsControlGroup()) {
+ data.contents_->set_final_status(FINAL_STATUS_CONTROL_GROUP);
+ } else {
+ last_prerender_start_time_ = GetCurrentTimeTicks();
+ data.contents_->StartPrerendering(source_render_view_host,
+ session_storage_namespace);
+ }
+ while (prerender_list_.size() > config_.max_elements) {
+ data = prerender_list_.front();
+ prerender_list_.pop_front();
+ data.contents_->Destroy(FINAL_STATUS_EVICTED);
+ }
+ StartSchedulingPeriodicCleanups();
+ return true;
+}
+
+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
+ return GetEntryButNotSpecifiedWC(url, NULL);
+}
+
+PrerenderContents* PrerenderManager::GetEntryButNotSpecifiedWC(
+ const GURL& url,
+ WebContents* wc) {
+ DCHECK(CalledOnValidThread());
+ DeleteOldEntries();
+ DeletePendingDeleteEntries();
+ for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
+ it != prerender_list_.end();
+ ++it) {
+ PrerenderContents* prerender_contents = it->contents_;
+ if (prerender_contents->MatchesURL(url, NULL)) {
+ if (!prerender_contents->prerender_contents() ||
+ !wc ||
+ prerender_contents->prerender_contents()->web_contents() != wc) {
+ prerender_list_.erase(it);
+ return prerender_contents;
+ }
+ }
+ }
+ // Entry not found.
+ return NULL;
+}
+
+void PrerenderManager::StartSchedulingPeriodicCleanups() {
+ DCHECK(CalledOnValidThread());
+ if (repeating_timer_.IsRunning())
+ return;
+ repeating_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs),
+ this,
+ &PrerenderManager::PeriodicCleanup);
+}
+
+void PrerenderManager::MaybeStopSchedulingPeriodicCleanups() {
+ if (!prerender_list_.empty())
+ return;
+
+ DCHECK(CalledOnValidThread());
+ repeating_timer_.Stop();
+}
+
+void PrerenderManager::PeriodicCleanup() {
+ DCHECK(CalledOnValidThread());
+ DeleteOldTabContents();
+ DeleteOldEntries();
+
+ // Grab a copy of the current PrerenderContents pointers, so that we
+ // will not interfere with potential deletions of the list.
+ std::vector<PrerenderContents*> prerender_contents;
+ for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
+ it != prerender_list_.end();
+ ++it) {
+ DCHECK(it->contents_);
+ prerender_contents.push_back(it->contents_);
+ }
+ for (std::vector<PrerenderContents*>::iterator it =
+ prerender_contents.begin();
+ it != prerender_contents.end();
+ ++it) {
+ (*it)->DestroyWhenUsingTooManyResources();
+ }
+
+ DeletePendingDeleteEntries();
+}
+
+void PrerenderManager::PostCleanupTask() {
+ DCHECK(CalledOnValidThread());
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&PrerenderManager::PeriodicCleanup,
+ weak_factory_.GetWeakPtr()));
+}
+
+bool PrerenderManager::IsPrerenderElementFresh(const base::Time start) const {
+ DCHECK(CalledOnValidThread());
+ base::Time now = GetCurrentTime();
+ return (now - start < config_.max_age);
+}
+
+void PrerenderManager::DeleteOldEntries() {
+ DCHECK(CalledOnValidThread());
+ while (!prerender_list_.empty()) {
+ PrerenderContentsData data = prerender_list_.front();
+ if (IsPrerenderElementFresh(data.start_time_))
+ return;
+ data.contents_->Destroy(FINAL_STATUS_TIMED_OUT);
+ }
+ MaybeStopSchedulingPeriodicCleanups();
+}
+
+base::Time PrerenderManager::GetCurrentTime() const {
+ return base::Time::Now();
+}
+
+base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const {
+ return base::TimeTicks::Now();
+}
+
+PrerenderContents* PrerenderManager::CreatePrerenderContents(
+ const GURL& url,
+ const content::Referrer& referrer,
+ Origin origin,
+ uint8 experiment_id) {
+ DCHECK(CalledOnValidThread());
+ return prerender_contents_factory_->CreatePrerenderContents(
+ this, prerender_tracker_, profile_, url,
+ referrer, origin, experiment_id);
+}
+
+bool PrerenderManager::IsPendingDelete(PrerenderContents* entry) const {
+ DCHECK(CalledOnValidThread());
+ for (std::list<PrerenderContents*>::const_iterator it =
+ pending_delete_list_.begin();
+ it != pending_delete_list_.end();
+ ++it) {
+ if (*it == entry)
+ return true;
+ }
+
+ return false;
+}
+
+void PrerenderManager::DeletePendingDeleteEntries() {
+ while (!pending_delete_list_.empty()) {
+ PrerenderContents* contents = pending_delete_list_.front();
+ pending_delete_list_.pop_front();
+ delete contents;
+ }
+}
+
+PrerenderContents* PrerenderManager::FindEntry(const GURL& url) {
+ DCHECK(CalledOnValidThread());
+ for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
+ it != prerender_list_.end();
+ ++it) {
+ if (it->contents_->MatchesURL(url, NULL))
+ return it->contents_;
+ }
+ // Entry not found.
+ return NULL;
+}
+
+std::list<PrerenderManager::PrerenderContentsData>::iterator
+ PrerenderManager::FindPrerenderContentsForChildRouteIdPair(
+ const std::pair<int, int>& child_route_id_pair) {
+ std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
+ for (; it != prerender_list_.end(); ++it) {
+ PrerenderContents* prerender_contents = it->contents_;
+
+ int child_id;
+ int route_id;
+ bool has_child_id = prerender_contents->GetChildId(&child_id);
+ bool has_route_id = has_child_id &&
+ prerender_contents->GetRouteId(&route_id);
+
+ if (has_child_id && has_route_id &&
+ child_id == child_route_id_pair.first &&
+ route_id == child_route_id_pair.second) {
+ break;
+ }
+ }
+ return it;
+}
+
+bool PrerenderManager::DoesRateLimitAllowPrerender() const {
+ DCHECK(CalledOnValidThread());
+ base::TimeDelta elapsed_time =
+ GetCurrentTimeTicks() - last_prerender_start_time_;
+ histograms_->RecordTimeBetweenPrerenderRequests(elapsed_time);
+ if (!config_.rate_limit_enabled)
+ return true;
+ return elapsed_time >
+ base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs);
+}
+
+void PrerenderManager::DeleteOldTabContents() {
+ while (!old_tab_contents_list_.empty()) {
+ TabContentsWrapper* tab_contents = old_tab_contents_list_.front();
+ old_tab_contents_list_.pop_front();
+ // TODO(dominich): should we use Instant Unload Handler here?
+ delete tab_contents;
+ }
+}
+
+void PrerenderManager::CleanUpOldNavigations() {
+ DCHECK(CalledOnValidThread());
+
+ // Cutoff. Navigations before this cutoff can be discarded.
+ base::TimeTicks cutoff = GetCurrentTimeTicks() -
+ base::TimeDelta::FromMilliseconds(kNavigationRecordWindowMs);
+ while (!navigations_.empty()) {
+ if (navigations_.front().time_ > cutoff)
+ break;
+ navigations_.pop_front();
+ }
+}
+
+void PrerenderManager::ScheduleDeleteOldTabContents(
+ TabContentsWrapper* tab,
+ OnCloseTabContentsDeleter* deleter) {
+ old_tab_contents_list_.push_back(tab);
+ PostCleanupTask();
+
+ if (deleter) {
+ ScopedVector<OnCloseTabContentsDeleter>::iterator i = std::find(
+ on_close_tab_contents_deleters_.begin(),
+ on_close_tab_contents_deleters_.end(),
+ deleter);
+ DCHECK(i != on_close_tab_contents_deleters_.end());
+ on_close_tab_contents_deleters_.erase(i);
+ }
+}
+
+void PrerenderManager::AddToHistory(PrerenderContents* contents) {
+ PrerenderHistory::Entry entry(contents->prerender_url(),
+ contents->final_status(),
+ contents->origin(),
+ base::Time::Now());
+ prerender_history_->AddEntry(entry);
+}
+
+void PrerenderManager::RecordNavigation(const GURL& url) {
+ DCHECK(CalledOnValidThread());
+
+ navigations_.push_back(NavigationRecord(url, GetCurrentTimeTicks()));
+ CleanUpOldNavigations();
+}
+
+Value* PrerenderManager::GetActivePrerendersAsValue() const {
+ ListValue* list_value = new ListValue();
+ for (std::list<PrerenderContentsData>::const_iterator it =
+ prerender_list_.begin();
+ it != prerender_list_.end();
+ ++it) {
+ Value* prerender_value = it->contents_->GetAsValue();
+ if (!prerender_value)
+ continue;
+ list_value->Append(prerender_value);
+ }
+ return list_value;
+}
+
+void PrerenderManager::DestroyAllContents(FinalStatus final_status) {
+ DeleteOldTabContents();
+ while (!prerender_list_.empty()) {
+ PrerenderContentsData data = prerender_list_.front();
+ prerender_list_.pop_front();
+ data.contents_->Destroy(final_status);
+ }
+ DeletePendingDeleteEntries();
}
PrerenderManager* FindPrerenderManagerUsingRenderProcessId(

Powered by Google App Engine
This is Rietveld 408576698