Index: chrome/browser/prerender/prerender_local_predictor.cc |
diff --git a/chrome/browser/prerender/prerender_local_predictor.cc b/chrome/browser/prerender/prerender_local_predictor.cc |
index 0b686095936e49d53fd1195ee7920f574668e114..b473e7ff834f39c9e010393766823d66f1047c07 100644 |
--- a/chrome/browser/prerender/prerender_local_predictor.cc |
+++ b/chrome/browser/prerender/prerender_local_predictor.cc |
@@ -70,6 +70,7 @@ static const size_t kURLHashSize = 5; |
static const int kNumPrerenderCandidates = 5; |
static const int kInvalidProcessId = -1; |
static const int kInvalidFrameId = -1; |
+static const int kMaxPrefetchItems = 100; |
} // namespace |
@@ -398,11 +399,106 @@ struct PrerenderLocalPredictor::PrerenderProperties { |
bool would_have_matched; |
}; |
+// A class simulating a set of URLs prefetched, for statistical purposes. |
+class PrerenderLocalPredictor::PrefetchList { |
+ public: |
+ enum SeenType { |
+ SEEN_TABCONTENTS_OBSERVER, |
+ SEEN_HISTORY, |
+ SEEN_MAX_VALUE |
+ }; |
davidben
2014/08/05 18:51:12
Nit: newline here
tburkard
2014/08/05 20:05:54
Done.
|
+ PrefetchList() {} |
+ ~PrefetchList() { |
+ STLDeleteValues(&entries_); |
+ } |
davidben
2014/08/05 18:51:12
Nit: newline here
tburkard
2014/08/05 20:05:54
Done.
|
+ // Adds a new URL being prefetched. If the URL is already in the list, |
+ // nothing will happen. Returns whether a new prefetch was added. |
+ bool AddURL(const GURL& url) { |
+ ExpireOldItems(); |
+ string url_string = url.spec().c_str(); |
+ base::hash_map<string, ListEntry*>::iterator it = entries_.find(url_string); |
+ if (it != entries_.end()) { |
+ // If a prefetch previously existed, and has not been seen yet in either |
+ // a tab contents or a history, we will not re-issue it. Otherwise, if it |
+ // may have been consumed by either tab contents or history, we will |
+ // permit re-issuing another one. |
+ if (!it->second->seen_history_ && |
+ !it->second->seen_tabcontents_) { |
+ return false; |
+ } |
+ } |
+ ListEntry* entry = new ListEntry(url_string); |
+ entries_[entry->url_] = entry; |
+ entry_list_.push_back(entry); |
+ ExpireOldItems(); |
+ return true; |
+ } |
davidben
2014/08/05 18:51:12
Nit: newline here
tburkard
2014/08/05 20:05:54
Done.
|
+ // Marks the URL provided as seen in the context specified. Returns true |
+ // iff the item is currently in the list and had not been seen before in |
+ // the context specified, i.e. the marking was successful. |
+ bool MarkURLSeen(const GURL& url, SeenType type) { |
+ ExpireOldItems(); |
+ bool return_value = false; |
+ base::hash_map<string, ListEntry*>::iterator it = |
+ entries_.find(url.spec().c_str()); |
+ if (it == entries_.end()) |
+ return return_value; |
+ if (type == SEEN_TABCONTENTS_OBSERVER && !it->second->seen_tabcontents_) { |
+ it->second->seen_tabcontents_ = true; |
+ return_value = true; |
+ } |
+ if (type == SEEN_HISTORY && !it->second->seen_history_) { |
+ it->second->seen_history_ = true; |
+ return_value = true; |
+ } |
+ // If the item has been seen in both the history and in tab contents, |
+ // erase it from the map to make room for new prefetches. |
+ if (it->second->seen_tabcontents_ && it->second->seen_history_) |
+ entries_.erase(url.spec().c_str()); |
+ return return_value; |
+ } |
+ |
+ private: |
+ struct ListEntry { |
+ std::string url_; |
+ base::Time add_time_; |
+ bool seen_tabcontents_; |
+ bool seen_history_; |
+ explicit ListEntry(string url) |
davidben
2014/08/05 18:51:12
Nit: I think we usually put methods before members
Alexei Svitkine (slow)
2014/08/05 19:20:22
Pass the param by const ref.
tburkard
2014/08/05 20:05:54
Done.
tburkard
2014/08/05 20:05:54
Done.
|
+ : url_(url), |
+ add_time_(GetCurrentTime()), |
+ seen_tabcontents_(false), |
+ seen_history_(false) { |
+ } |
+ }; |
davidben
2014/08/05 18:51:12
Nit: newline here
tburkard
2014/08/05 20:05:54
Done.
|
+ void ExpireOldItems() { |
+ base::Time expiry_cutoff = GetCurrentTime() - |
+ base::TimeDelta::FromSeconds(GetPrerenderPrefetchListTimeoutSeconds()); |
+ while (!entry_list_.empty() && |
+ (entry_list_.front()->add_time_ < expiry_cutoff || |
+ entries_.size() > kMaxPrefetchItems)) { |
+ ListEntry* entry = entry_list_.front(); |
+ entry_list_.pop_front(); |
+ // If the entry to be deleted is still the one active in entries_, |
+ // we must erase it from entries_. |
+ base::hash_map<string, ListEntry*>::iterator it = |
+ entries_.find(entry->url_); |
+ if (it != entries_.end() && it->second == entry) |
+ entries_.erase(entry->url_); |
+ delete entry; |
Alexei Svitkine (slow)
2014/08/05 19:20:22
Can you use a scoped_ptr instead?
tburkard
2014/08/05 20:05:54
Not obvious to me how this would work well here.
C
Alexei Svitkine (slow)
2014/08/05 20:16:22
Sorry, I meant in the body of the loop, not in the
|
+ } |
+ } |
davidben
2014/08/05 18:51:12
Nit: newline here
tburkard
2014/08/05 20:05:54
Done.
|
+ base::hash_map<string, ListEntry*> entries_; |
+ std::list<ListEntry*> entry_list_; |
+ DISALLOW_COPY_AND_ASSIGN(PrefetchList); |
+}; |
+ |
PrerenderLocalPredictor::PrerenderLocalPredictor( |
PrerenderManager* prerender_manager) |
: prerender_manager_(prerender_manager), |
is_visit_database_observer_(false), |
- weak_factory_(this) { |
+ weak_factory_(this), |
+ prefetch_list_(new PrefetchList()) { |
RecordEvent(EVENT_CONSTRUCTED); |
if (base::MessageLoop::current()) { |
timer_.Start(FROM_HERE, |
@@ -576,6 +672,11 @@ void PrerenderLocalPredictor::OnLookupURL( |
return; |
} |
+ if (prefetch_list_->MarkURLSeen(info->source_url_.url, |
+ PrefetchList::SEEN_HISTORY)) { |
+ RecordEvent(EVENT_PREFETCH_LIST_SEEN_HISTORY); |
+ } |
+ |
if (info->candidate_urls_.size() > 0 && |
info->candidate_urls_[0].url_lookup_success) { |
LogCandidateURLStats(info->candidate_urls_[0].url); |
@@ -1125,10 +1226,9 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( |
scoped_refptr<SafeBrowsingDatabaseManager> sb_db_manager = |
g_browser_process->safe_browsing_service()->database_manager(); |
#endif |
- PrerenderProperties* prerender_properties = NULL; |
int num_issued = 0; |
for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) { |
- if (num_issued > GetLocalPredictorMaxLaunchPrerenders()) |
+ if (num_issued >= GetLocalPredictorMaxLaunchPrerenders()) |
return; |
RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL); |
url_info.reset(new LocalPredictorURLInfo(info->candidate_urls_[i])); |
@@ -1162,13 +1262,6 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( |
url_info.reset(NULL); |
continue; |
} |
- prerender_properties = |
- GetIssuedPrerenderSlotForPriority(url_info->url, url_info->priority); |
- if (!prerender_properties) { |
- RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_PRIORITY_TOO_LOW); |
- url_info.reset(NULL); |
- continue; |
- } |
davidben
2014/08/05 18:51:12
This will count candidate URLs toward num_issued e
tburkard
2014/08/05 20:05:54
Yes, that's fine, because they are ordered in desc
|
if (!SkipLocalPredictorFragment() && |
URLsIdenticalIgnoringFragments(info->source_url_.url, |
url_info->url)) { |
@@ -1185,7 +1278,7 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( |
// For root pages, we assume that they are reasonably safe, and we |
// will just prerender them without any additional checks. |
RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ROOT_PAGE); |
- IssuePrerender(info.get(), url_info.get(), prerender_properties); |
+ IssuePrerender(info.get(), url_info.get()); |
num_issued++; |
continue; |
} |
@@ -1205,7 +1298,7 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( |
// If a page is on the side-effect free whitelist, we will just prerender |
// it without any additional checks. |
RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SIDE_EFFECT_FREE_WHITELIST); |
- IssuePrerender(info.get(), url_info.get(), prerender_properties); |
+ IssuePrerender(info.get(), url_info.get()); |
num_issued++; |
continue; |
} |
@@ -1213,14 +1306,14 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( |
if (!SkipLocalPredictorServiceWhitelist() && |
url_info->service_whitelist && url_info->service_whitelist_lookup_ok) { |
RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SERVICE_WHITELIST); |
- IssuePrerender(info.get(), url_info.get(), prerender_properties); |
+ IssuePrerender(info.get(), url_info.get()); |
num_issued++; |
continue; |
} |
if (!SkipLocalPredictorLoggedIn() && |
!url_info->logged_in && url_info->logged_in_lookup_ok) { |
RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_NOT_LOGGED_IN); |
- IssuePrerender(info.get(), url_info.get(), prerender_properties); |
+ IssuePrerender(info.get(), url_info.get()); |
num_issued++; |
continue; |
} |
@@ -1229,7 +1322,7 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( |
url_info.reset(NULL); |
} else { |
RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_PRERENDERING); |
- IssuePrerender(info.get(), url_info.get(), prerender_properties); |
+ IssuePrerender(info.get(), url_info.get()); |
num_issued++; |
continue; |
} |
@@ -1238,9 +1331,16 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( |
void PrerenderLocalPredictor::IssuePrerender( |
CandidatePrerenderInfo* info, |
- LocalPredictorURLInfo* url_info, |
- PrerenderProperties* prerender_properties) { |
+ LocalPredictorURLInfo* url_info) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ if (prefetch_list_->AddURL(url_info->url)) |
+ RecordEvent(EVENT_PREFETCH_LIST_ADDED); |
+ PrerenderProperties* prerender_properties = |
+ GetIssuedPrerenderSlotForPriority(url_info->url, url_info->priority); |
+ if (!prerender_properties) { |
+ RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_PRIORITY_TOO_LOW); |
+ return; |
+ } |
RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ISSUING_PRERENDER); |
DCHECK(prerender_properties != NULL); |
DCHECK(info != NULL); |
@@ -1329,6 +1429,8 @@ void PrerenderLocalPredictor::OnTabHelperURLSeen( |
const GURL& url, WebContents* web_contents) { |
RecordEvent(EVENT_TAB_HELPER_URL_SEEN); |
+ if (prefetch_list_->MarkURLSeen(url, PrefetchList::SEEN_TABCONTENTS_OBSERVER)) |
+ RecordEvent(EVENT_PREFETCH_LIST_SEEN_TABCONTENTS); |
bool browser_navigate_initiated = false; |
const content::NavigationEntry* entry = |
web_contents->GetController().GetPendingEntry(); |