OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/prerender/prerender_local_predictor.h" | |
6 | |
7 #include <ctype.h> | |
8 | |
9 #include <algorithm> | |
10 #include <map> | |
11 #include <set> | |
12 #include <string> | |
13 #include <utility> | |
14 | |
15 #include "base/json/json_reader.h" | |
16 #include "base/json/json_writer.h" | |
17 #include "base/metrics/field_trial.h" | |
18 #include "base/metrics/histogram.h" | |
19 #include "base/stl_util.h" | |
20 #include "base/timer/timer.h" | |
21 #include "chrome/browser/browser_process.h" | |
22 #include "chrome/browser/history/history_service_factory.h" | |
23 #include "chrome/browser/prerender/prerender_field_trial.h" | |
24 #include "chrome/browser/prerender/prerender_handle.h" | |
25 #include "chrome/browser/prerender/prerender_histograms.h" | |
26 #include "chrome/browser/prerender/prerender_manager.h" | |
27 #include "chrome/browser/prerender/prerender_util.h" | |
28 #include "chrome/browser/profiles/profile.h" | |
29 #include "chrome/browser/safe_browsing/database_manager.h" | |
30 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | |
31 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | |
32 #include "chrome/common/prefetch_messages.h" | |
33 #include "components/history/core/browser/history_database.h" | |
34 #include "components/history/core/browser/history_db_task.h" | |
35 #include "components/history/core/browser/history_service.h" | |
36 #include "content/public/browser/browser_thread.h" | |
37 #include "content/public/browser/navigation_controller.h" | |
38 #include "content/public/browser/navigation_entry.h" | |
39 #include "content/public/browser/render_frame_host.h" | |
40 #include "content/public/browser/render_process_host.h" | |
41 #include "content/public/browser/web_contents.h" | |
42 #include "crypto/secure_hash.h" | |
43 #include "grit/browser_resources.h" | |
44 #include "net/base/escape.h" | |
45 #include "net/base/load_flags.h" | |
46 #include "net/url_request/url_fetcher.h" | |
47 #include "ui/base/page_transition_types.h" | |
48 #include "ui/base/resource/resource_bundle.h" | |
49 #include "url/url_canon.h" | |
50 | |
51 using base::DictionaryValue; | |
52 using base::ListValue; | |
53 using base::Value; | |
54 using content::BrowserThread; | |
55 using ui::PageTransition; | |
56 using content::RenderFrameHost; | |
57 using content::SessionStorageNamespace; | |
58 using content::WebContents; | |
59 using history::URLID; | |
60 using net::URLFetcher; | |
61 using predictors::LoggedInPredictorTable; | |
62 using std::string; | |
63 using std::vector; | |
64 | |
65 namespace prerender { | |
66 | |
67 namespace { | |
68 | |
69 static const size_t kURLHashSize = 5; | |
70 static const int kNumPrerenderCandidates = 5; | |
71 static const int kInvalidProcessId = -1; | |
72 static const int kInvalidFrameId = -1; | |
73 static const int kMaxPrefetchItems = 100; | |
74 | |
75 } // namespace | |
76 | |
77 // When considering a candidate URL to be prerendered, we need to collect the | |
78 // data in this struct to make the determination whether we should issue the | |
79 // prerender or not. | |
80 struct PrerenderLocalPredictor::LocalPredictorURLInfo { | |
81 URLID id; | |
82 GURL url; | |
83 bool url_lookup_success; | |
84 bool logged_in; | |
85 bool logged_in_lookup_ok; | |
86 bool local_history_based; | |
87 bool service_whitelist; | |
88 bool service_whitelist_lookup_ok; | |
89 bool service_whitelist_reported; | |
90 double priority; | |
91 }; | |
92 | |
93 // A struct consisting of everything needed for launching a potential prerender | |
94 // on a navigation: The navigation URL (source) triggering potential prerenders, | |
95 // and a set of candidate URLs. | |
96 struct PrerenderLocalPredictor::CandidatePrerenderInfo { | |
97 LocalPredictorURLInfo source_url_; | |
98 vector<LocalPredictorURLInfo> candidate_urls_; | |
99 scoped_refptr<SessionStorageNamespace> session_storage_namespace_; | |
100 // Render Process ID and Route ID of the page causing the prerender to be | |
101 // issued. Needed so that we can cause its renderer to issue prefetches within | |
102 // its context. | |
103 int render_process_id_; | |
104 int render_frame_id_; | |
105 scoped_ptr<gfx::Size> size_; | |
106 base::Time start_time_; // used for various time measurements | |
107 explicit CandidatePrerenderInfo(URLID source_id) | |
108 : render_process_id_(kInvalidProcessId), | |
109 render_frame_id_(kInvalidFrameId) { | |
110 source_url_.id = source_id; | |
111 } | |
112 void MaybeAddCandidateURLFromLocalData(URLID id, double priority) { | |
113 LocalPredictorURLInfo info; | |
114 info.id = id; | |
115 info.local_history_based = true; | |
116 info.service_whitelist = false; | |
117 info.service_whitelist_lookup_ok = false; | |
118 info.service_whitelist_reported = false; | |
119 info.priority = priority; | |
120 MaybeAddCandidateURLInternal(info); | |
121 } | |
122 void MaybeAddCandidateURLFromService(GURL url, double priority, | |
123 bool whitelist, | |
124 bool whitelist_lookup_ok) { | |
125 LocalPredictorURLInfo info; | |
126 info.id = kint64max; | |
127 info.url = url; | |
128 info.url_lookup_success = true; | |
129 info.local_history_based = false; | |
130 info.service_whitelist = whitelist; | |
131 info.service_whitelist_lookup_ok = whitelist_lookup_ok; | |
132 info.service_whitelist_reported = true; | |
133 info.priority = priority; | |
134 MaybeAddCandidateURLInternal(info); | |
135 } | |
136 void MaybeAddCandidateURLInternal(const LocalPredictorURLInfo& info) { | |
137 // TODO(tburkard): clean up this code, potentially using a list or a heap | |
138 int max_candidates = kNumPrerenderCandidates; | |
139 // We first insert local candidates, then service candidates. | |
140 // Since we want to keep kNumPrerenderCandidates for both local & service | |
141 // candidates, we need to double the maximum number of candidates once | |
142 // we start seeing service candidates. | |
143 if (!info.local_history_based) | |
144 max_candidates *= 2; | |
145 int insert_pos = candidate_urls_.size(); | |
146 if (insert_pos < max_candidates) | |
147 candidate_urls_.push_back(info); | |
148 while (insert_pos > 0 && | |
149 candidate_urls_[insert_pos - 1].priority < info.priority) { | |
150 if (insert_pos < max_candidates) | |
151 candidate_urls_[insert_pos] = candidate_urls_[insert_pos - 1]; | |
152 insert_pos--; | |
153 } | |
154 if (insert_pos < max_candidates) | |
155 candidate_urls_[insert_pos] = info; | |
156 } | |
157 }; | |
158 | |
159 namespace { | |
160 | |
161 #define TIMING_HISTOGRAM(name, value) \ | |
162 UMA_HISTOGRAM_CUSTOM_TIMES(name, value, \ | |
163 base::TimeDelta::FromMilliseconds(10), \ | |
164 base::TimeDelta::FromSeconds(10), \ | |
165 50); | |
166 | |
167 // Task to lookup the URL for a given URLID. | |
168 class GetURLForURLIDTask : public history::HistoryDBTask { | |
169 public: | |
170 GetURLForURLIDTask( | |
171 PrerenderLocalPredictor::CandidatePrerenderInfo* request, | |
172 const base::Closure& callback) | |
173 : request_(request), | |
174 callback_(callback), | |
175 start_time_(base::Time::Now()) { | |
176 } | |
177 | |
178 bool RunOnDBThread(history::HistoryBackend* backend, | |
179 history::HistoryDatabase* db) override { | |
180 DoURLLookup(db, &request_->source_url_); | |
181 for (int i = 0; i < static_cast<int>(request_->candidate_urls_.size()); i++) | |
182 DoURLLookup(db, &request_->candidate_urls_[i]); | |
183 return true; | |
184 } | |
185 | |
186 void DoneRunOnMainThread() override { | |
187 callback_.Run(); | |
188 TIMING_HISTOGRAM("Prerender.LocalPredictorURLLookupTime", | |
189 base::Time::Now() - start_time_); | |
mmenke
2015/04/10 16:42:04
Obsolete UMA
davidben
2015/04/13 15:51:14
Done. (Also seems worth having tooling, though I s
| |
190 } | |
191 | |
192 private: | |
193 ~GetURLForURLIDTask() override {} | |
194 | |
195 void DoURLLookup(history::HistoryDatabase* db, | |
196 PrerenderLocalPredictor::LocalPredictorURLInfo* request) { | |
197 history::URLRow url_row; | |
198 request->url_lookup_success = db->GetURLRow(request->id, &url_row); | |
199 if (request->url_lookup_success) | |
200 request->url = url_row.url(); | |
201 } | |
202 | |
203 PrerenderLocalPredictor::CandidatePrerenderInfo* request_; | |
204 base::Closure callback_; | |
205 base::Time start_time_; | |
206 DISALLOW_COPY_AND_ASSIGN(GetURLForURLIDTask); | |
207 }; | |
208 | |
209 // Task to load history from the visit database on startup. | |
210 class GetVisitHistoryTask : public history::HistoryDBTask { | |
211 public: | |
212 GetVisitHistoryTask(PrerenderLocalPredictor* local_predictor, | |
213 int max_visits) | |
214 : local_predictor_(local_predictor), | |
215 max_visits_(max_visits), | |
216 visit_history_(new vector<history::BriefVisitInfo>) { | |
217 } | |
218 | |
219 bool RunOnDBThread(history::HistoryBackend* backend, | |
220 history::HistoryDatabase* db) override { | |
221 db->GetBriefVisitInfoOfMostRecentVisits(max_visits_, visit_history_.get()); | |
mmenke
2015/04/10 16:42:04
In fact, I think this is the only reason BriefVisi
mmenke
2015/04/10 16:42:04
This class is the only consumer of this method. R
davidben
2015/04/13 15:51:14
Good catch. I'll do that in a follow-up.
davidben
2015/04/13 16:40:01
(For posterity, here's that follow-up. https://cod
| |
222 return true; | |
223 } | |
224 | |
225 void DoneRunOnMainThread() override { | |
226 local_predictor_->OnGetInitialVisitHistory(visit_history_.Pass()); | |
227 } | |
228 | |
229 private: | |
230 ~GetVisitHistoryTask() override {} | |
231 | |
232 PrerenderLocalPredictor* local_predictor_; | |
233 int max_visits_; | |
234 scoped_ptr<vector<history::BriefVisitInfo> > visit_history_; | |
235 DISALLOW_COPY_AND_ASSIGN(GetVisitHistoryTask); | |
236 }; | |
237 | |
238 // Maximum visit history to retrieve from the visit database. | |
239 const int kMaxVisitHistory = 100 * 1000; | |
240 | |
241 // Visit history size at which to trigger pruning, and number of items to prune. | |
242 const int kVisitHistoryPruneThreshold = 120 * 1000; | |
243 const int kVisitHistoryPruneAmount = 20 * 1000; | |
244 | |
245 const int kMinLocalPredictionTimeMs = 500; | |
246 | |
247 int GetMaxLocalPredictionTimeMs() { | |
248 return GetLocalPredictorTTLSeconds() * 1000; | |
249 } | |
250 | |
251 bool IsBackForward(PageTransition transition) { | |
252 return (transition & ui::PAGE_TRANSITION_FORWARD_BACK) != 0; | |
253 } | |
254 | |
255 bool IsHomePage(PageTransition transition) { | |
256 return (transition & ui::PAGE_TRANSITION_HOME_PAGE) != 0; | |
257 } | |
258 | |
259 bool IsIntermediateRedirect(PageTransition transition) { | |
260 return (transition & ui::PAGE_TRANSITION_CHAIN_END) == 0; | |
261 } | |
262 | |
263 bool IsFormSubmit(PageTransition transition) { | |
264 return ui::PageTransitionCoreTypeIs(transition, | |
265 ui::PAGE_TRANSITION_FORM_SUBMIT); | |
266 } | |
267 | |
268 bool ShouldExcludeTransitionForPrediction(PageTransition transition) { | |
269 return IsBackForward(transition) || IsHomePage(transition) || | |
270 IsIntermediateRedirect(transition); | |
271 } | |
272 | |
273 base::Time GetCurrentTime() { | |
274 return base::Time::Now(); | |
275 } | |
276 | |
277 bool StringContainsIgnoringCase(string haystack, string needle) { | |
278 std::transform(haystack.begin(), haystack.end(), haystack.begin(), ::tolower); | |
279 std::transform(needle.begin(), needle.end(), needle.begin(), ::tolower); | |
280 return haystack.find(needle) != string::npos; | |
281 } | |
282 | |
283 bool IsExtendedRootURL(const GURL& url) { | |
284 const string& path = url.path(); | |
285 return path == "/index.html" || path == "/home.html" || | |
286 path == "/main.html" || | |
287 path == "/index.htm" || path == "/home.htm" || path == "/main.htm" || | |
288 path == "/index.php" || path == "/home.php" || path == "/main.php" || | |
289 path == "/index.asp" || path == "/home.asp" || path == "/main.asp" || | |
290 path == "/index.py" || path == "/home.py" || path == "/main.py" || | |
291 path == "/index.pl" || path == "/home.pl" || path == "/main.pl"; | |
292 } | |
293 | |
294 bool IsRootPageURL(const GURL& url) { | |
295 return (url.path() == "/" || url.path().empty() || IsExtendedRootURL(url)) && | |
296 (!url.has_query()) && (!url.has_ref()); | |
297 } | |
298 | |
299 bool IsLogInURL(const GURL& url) { | |
300 return StringContainsIgnoringCase(url.spec().c_str(), "login") || | |
301 StringContainsIgnoringCase(url.spec().c_str(), "signin"); | |
302 } | |
303 | |
304 bool IsLogOutURL(const GURL& url) { | |
305 return StringContainsIgnoringCase(url.spec().c_str(), "logout") || | |
306 StringContainsIgnoringCase(url.spec().c_str(), "signout"); | |
307 } | |
308 | |
309 int64 URLHashToInt64(const unsigned char* data) { | |
310 static_assert(kURLHashSize < sizeof(int64), "url hash must fit in int64"); | |
311 int64 value = 0; | |
312 memcpy(&value, data, kURLHashSize); | |
313 return value; | |
314 } | |
315 | |
316 int64 GetInt64URLHashForURL(const GURL& url) { | |
317 static_assert(kURLHashSize < sizeof(int64), "url hash must fit in int64"); | |
318 scoped_ptr<crypto::SecureHash> hash( | |
319 crypto::SecureHash::Create(crypto::SecureHash::SHA256)); | |
320 int64 hash_value = 0; | |
321 const char* url_string = url.spec().c_str(); | |
322 hash->Update(url_string, strlen(url_string)); | |
323 hash->Finish(&hash_value, kURLHashSize); | |
324 return hash_value; | |
325 } | |
326 | |
327 bool URLsIdenticalIgnoringFragments(const GURL& url1, const GURL& url2) { | |
328 url::Replacements<char> replacement; | |
329 replacement.ClearRef(); | |
330 GURL u1 = url1.ReplaceComponents(replacement); | |
331 GURL u2 = url2.ReplaceComponents(replacement); | |
332 return (u1 == u2); | |
333 } | |
334 | |
335 void LookupLoggedInStatesOnDBThread( | |
336 scoped_refptr<LoggedInPredictorTable> logged_in_predictor_table, | |
337 PrerenderLocalPredictor::CandidatePrerenderInfo* request) { | |
338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
339 for (int i = 0; i < static_cast<int>(request->candidate_urls_.size()); i++) { | |
340 PrerenderLocalPredictor::LocalPredictorURLInfo* info = | |
341 &request->candidate_urls_[i]; | |
342 if (info->url_lookup_success) { | |
343 logged_in_predictor_table->HasUserLoggedIn( | |
344 info->url, &info->logged_in, &info->logged_in_lookup_ok); | |
345 } else { | |
346 info->logged_in_lookup_ok = false; | |
347 } | |
348 } | |
349 } | |
350 | |
351 } // namespace | |
352 | |
353 struct PrerenderLocalPredictor::PrerenderProperties { | |
354 PrerenderProperties(URLID url_id, const GURL& url, double priority, | |
355 base::Time start_time) | |
356 : url_id(url_id), | |
357 url(url), | |
358 priority(priority), | |
359 start_time(start_time), | |
360 would_have_matched(false) { | |
361 } | |
362 | |
363 // Default constructor for dummy element | |
364 PrerenderProperties() | |
365 : priority(0.0), would_have_matched(false) { | |
366 } | |
367 | |
368 double GetCurrentDecayedPriority() { | |
369 // If we are no longer prerendering, the priority is 0. | |
370 if (!prerender_handle || !prerender_handle->IsPrerendering()) | |
371 return 0.0; | |
372 int half_life_time_seconds = | |
373 GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds(); | |
374 if (half_life_time_seconds < 1) | |
375 return priority; | |
376 double multiple_elapsed = | |
377 (GetCurrentTime() - actual_start_time).InMillisecondsF() / | |
378 base::TimeDelta::FromSeconds(half_life_time_seconds).InMillisecondsF(); | |
379 // Decay factor: 2 ^ (-multiple_elapsed) | |
380 double decay_factor = exp(- multiple_elapsed * log(2.0)); | |
381 return priority * decay_factor; | |
382 } | |
383 | |
384 URLID url_id; | |
385 GURL url; | |
386 double priority; | |
387 // For expiration purposes, this is a synthetic start time consisting either | |
388 // of the actual start time, or of the last time the page was re-requested | |
389 // for prerendering - 10 seconds (unless the original request came after | |
390 // that). This is to emulate the effect of re-prerendering a page that is | |
391 // about to expire, because it was re-requested for prerendering a second | |
392 // time after the actual prerender being kept around. | |
393 base::Time start_time; | |
394 // The actual time this page was last requested for prerendering. | |
395 base::Time actual_start_time; | |
396 scoped_ptr<PrerenderHandle> prerender_handle; | |
397 // Indicates whether this prerender would have matched a URL navigated to, | |
398 // but was not swapped in for some reason. | |
399 bool would_have_matched; | |
400 }; | |
401 | |
402 // A class simulating a set of URLs prefetched, for statistical purposes. | |
403 class PrerenderLocalPredictor::PrefetchList { | |
404 public: | |
405 enum SeenType { | |
406 SEEN_TABCONTENTS_OBSERVER, | |
407 SEEN_HISTORY, | |
408 SEEN_MAX_VALUE | |
409 }; | |
410 | |
411 PrefetchList() {} | |
412 ~PrefetchList() { | |
413 STLDeleteValues(&entries_); | |
414 } | |
415 | |
416 // Adds a new URL being prefetched. If the URL is already in the list, | |
417 // nothing will happen. Returns whether a new prefetch was added. | |
418 bool AddURL(const GURL& url) { | |
419 ExpireOldItems(); | |
420 string url_string = url.spec().c_str(); | |
421 base::hash_map<string, ListEntry*>::iterator it = entries_.find(url_string); | |
422 if (it != entries_.end()) { | |
423 // If a prefetch previously existed, and has not been seen yet in either | |
424 // a tab contents or a history, we will not re-issue it. Otherwise, if it | |
425 // may have been consumed by either tab contents or history, we will | |
426 // permit re-issuing another one. | |
427 if (!it->second->seen_history_ && | |
428 !it->second->seen_tabcontents_) { | |
429 return false; | |
430 } | |
431 } | |
432 ListEntry* entry = new ListEntry(url_string); | |
433 entries_[entry->url_] = entry; | |
434 entry_list_.push_back(entry); | |
435 ExpireOldItems(); | |
436 return true; | |
437 } | |
438 | |
439 // Marks the URL provided as seen in the context specified. Returns true | |
440 // iff the item is currently in the list and had not been seen before in | |
441 // the context specified, i.e. the marking was successful. | |
442 bool MarkURLSeen(const GURL& url, SeenType type) { | |
443 ExpireOldItems(); | |
444 bool return_value = false; | |
445 base::hash_map<string, ListEntry*>::iterator it = | |
446 entries_.find(url.spec().c_str()); | |
447 if (it == entries_.end()) | |
448 return return_value; | |
449 if (type == SEEN_TABCONTENTS_OBSERVER && !it->second->seen_tabcontents_) { | |
450 it->second->seen_tabcontents_ = true; | |
451 return_value = true; | |
452 } | |
453 if (type == SEEN_HISTORY && !it->second->seen_history_) { | |
454 it->second->seen_history_ = true; | |
455 return_value = true; | |
456 } | |
457 // If the item has been seen in both the history and in tab contents, | |
458 // and the page load time has been recorded, erase it from the map to | |
459 // make room for new prefetches. | |
460 if (it->second->seen_tabcontents_ && it->second->seen_history_ && | |
461 it->second->seen_plt_) { | |
462 entries_.erase(url.spec().c_str()); | |
463 } | |
464 return return_value; | |
465 } | |
466 | |
467 // Marks the PLT for the provided UR as seen. Returns true | |
468 // iff the item is currently in the list and the PLT had not been seen | |
469 // before, i.e. the sighting was successful. | |
470 bool MarkPLTSeen(const GURL& url, base::TimeDelta plt) { | |
471 ExpireOldItems(); | |
472 base::hash_map<string, ListEntry*>::iterator it = | |
473 entries_.find(url.spec().c_str()); | |
474 if (it == entries_.end() || it->second->seen_plt_ || | |
475 it->second->add_time_ > GetCurrentTime() - plt) { | |
476 return false; | |
477 } | |
478 it->second->seen_plt_ = true; | |
479 // If the item has been seen in both the history and in tab contents, | |
480 // and the page load time has been recorded, erase it from the map to | |
481 // make room for new prefetches. | |
482 if (it->second->seen_tabcontents_ && it->second->seen_history_ && | |
483 it->second->seen_plt_) { | |
484 entries_.erase(url.spec().c_str()); | |
485 } | |
486 return true; | |
487 } | |
488 | |
489 private: | |
490 struct ListEntry { | |
491 explicit ListEntry(const string& url) | |
492 : url_(url), | |
493 add_time_(GetCurrentTime()), | |
494 seen_tabcontents_(false), | |
495 seen_history_(false), | |
496 seen_plt_(false) { | |
497 } | |
498 std::string url_; | |
499 base::Time add_time_; | |
500 bool seen_tabcontents_; | |
501 bool seen_history_; | |
502 bool seen_plt_; | |
503 }; | |
504 | |
505 void ExpireOldItems() { | |
506 base::Time expiry_cutoff = GetCurrentTime() - | |
507 base::TimeDelta::FromSeconds(GetPrerenderPrefetchListTimeoutSeconds()); | |
508 while (!entry_list_.empty() && | |
509 (entry_list_.front()->add_time_ < expiry_cutoff || | |
510 entries_.size() > kMaxPrefetchItems)) { | |
511 ListEntry* entry = entry_list_.front(); | |
512 entry_list_.pop_front(); | |
513 // If the entry to be deleted is still the one active in entries_, | |
514 // we must erase it from entries_. | |
515 base::hash_map<string, ListEntry*>::iterator it = | |
516 entries_.find(entry->url_); | |
517 if (it != entries_.end() && it->second == entry) | |
518 entries_.erase(entry->url_); | |
519 delete entry; | |
520 } | |
521 } | |
522 | |
523 base::hash_map<string, ListEntry*> entries_; | |
524 std::list<ListEntry*> entry_list_; | |
525 DISALLOW_COPY_AND_ASSIGN(PrefetchList); | |
526 }; | |
527 | |
528 PrerenderLocalPredictor::PrerenderLocalPredictor( | |
529 PrerenderManager* prerender_manager) | |
530 : prerender_manager_(prerender_manager), | |
531 prefetch_list_(new PrefetchList()), | |
532 history_service_observer_(this), | |
533 weak_factory_(this) { | |
534 RecordEvent(EVENT_CONSTRUCTED); | |
535 if (base::MessageLoop::current()) { | |
536 timer_.Start(FROM_HERE, | |
537 base::TimeDelta::FromMilliseconds(kInitDelayMs), | |
538 this, | |
539 &PrerenderLocalPredictor::Init); | |
540 RecordEvent(EVENT_INIT_SCHEDULED); | |
541 } | |
542 | |
543 static const size_t kChecksumHashSize = 32; | |
544 base::RefCountedStaticMemory* url_whitelist_data = | |
545 ResourceBundle::GetSharedInstance().LoadDataResourceBytes( | |
546 IDR_PRERENDER_URL_WHITELIST); | |
mmenke
2015/04/10 16:42:04
Also remove this resource (File and GRD entry)? L
davidben
2015/04/13 15:51:14
Done.
| |
547 size_t size = url_whitelist_data->size(); | |
548 const unsigned char* front = url_whitelist_data->front(); | |
549 if (size < kChecksumHashSize || | |
550 (size - kChecksumHashSize) % kURLHashSize != 0) { | |
551 RecordEvent(EVENT_URL_WHITELIST_ERROR); | |
552 return; | |
553 } | |
554 scoped_ptr<crypto::SecureHash> hash( | |
555 crypto::SecureHash::Create(crypto::SecureHash::SHA256)); | |
556 hash->Update(front + kChecksumHashSize, size - kChecksumHashSize); | |
557 char hash_value[kChecksumHashSize]; | |
558 hash->Finish(hash_value, kChecksumHashSize); | |
559 if (memcmp(hash_value, front, kChecksumHashSize)) { | |
560 RecordEvent(EVENT_URL_WHITELIST_ERROR); | |
561 return; | |
562 } | |
563 for (const unsigned char* p = front + kChecksumHashSize; | |
564 p < front + size; | |
565 p += kURLHashSize) { | |
566 url_whitelist_.insert(URLHashToInt64(p)); | |
567 } | |
568 RecordEvent(EVENT_URL_WHITELIST_OK); | |
569 } | |
570 | |
571 PrerenderLocalPredictor::~PrerenderLocalPredictor() { | |
572 Shutdown(); | |
573 for (PrerenderProperties* p : issued_prerenders_) { | |
574 DCHECK(p != NULL); | |
575 if (p->prerender_handle) | |
576 p->prerender_handle->OnCancel(); | |
577 } | |
578 STLDeleteContainerPairPointers( | |
579 outstanding_prerender_service_requests_.begin(), | |
580 outstanding_prerender_service_requests_.end()); | |
581 } | |
582 | |
583 void PrerenderLocalPredictor::Shutdown() { | |
584 timer_.Stop(); | |
585 history_service_observer_.RemoveAll(); | |
586 } | |
587 | |
588 void PrerenderLocalPredictor::OnAddVisit( | |
589 history::HistoryService* history_service, | |
590 const history::BriefVisitInfo& info) { | |
591 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
592 RecordEvent(EVENT_ADD_VISIT); | |
593 if (!visit_history_.get()) | |
594 return; | |
595 visit_history_->push_back(info); | |
596 if (static_cast<int>(visit_history_->size()) > kVisitHistoryPruneThreshold) { | |
597 visit_history_->erase(visit_history_->begin(), | |
598 visit_history_->begin() + kVisitHistoryPruneAmount); | |
599 } | |
600 RecordEvent(EVENT_ADD_VISIT_INITIALIZED); | |
601 if (current_prerender_.get() && | |
602 current_prerender_->url_id == info.url_id && | |
603 IsPrerenderStillValid(current_prerender_.get())) { | |
604 UMA_HISTOGRAM_CUSTOM_TIMES( | |
605 "Prerender.LocalPredictorTimeUntilUsed", | |
mmenke
2015/04/10 16:42:04
Obsolete UMA
davidben
2015/04/13 15:51:14
Done.
| |
606 GetCurrentTime() - current_prerender_->actual_start_time, | |
607 base::TimeDelta::FromMilliseconds(10), | |
608 base::TimeDelta::FromMilliseconds(GetMaxLocalPredictionTimeMs()), | |
609 50); | |
610 last_swapped_in_prerender_.reset(current_prerender_.release()); | |
611 RecordEvent(EVENT_ADD_VISIT_PRERENDER_IDENTIFIED); | |
612 } | |
613 if (ShouldExcludeTransitionForPrediction(info.transition)) | |
614 return; | |
615 Profile* profile = prerender_manager_->profile(); | |
616 if (!profile || | |
617 ShouldDisableLocalPredictorDueToPreferencesAndNetwork(profile)) { | |
618 return; | |
619 } | |
620 RecordEvent(EVENT_ADD_VISIT_RELEVANT_TRANSITION); | |
621 base::TimeDelta max_age = | |
622 base::TimeDelta::FromMilliseconds(GetMaxLocalPredictionTimeMs()); | |
623 base::TimeDelta min_age = | |
624 base::TimeDelta::FromMilliseconds(kMinLocalPredictionTimeMs); | |
625 std::set<URLID> next_urls_currently_found; | |
626 std::map<URLID, int> next_urls_num_found; | |
627 int num_occurrences_of_current_visit = 0; | |
628 base::Time last_visited; | |
629 scoped_ptr<CandidatePrerenderInfo> lookup_info( | |
630 new CandidatePrerenderInfo(info.url_id)); | |
631 const vector<history::BriefVisitInfo>& visits = *(visit_history_.get()); | |
632 for (int i = 0; i < static_cast<int>(visits.size()); i++) { | |
633 if (!ShouldExcludeTransitionForPrediction(visits[i].transition)) { | |
634 if (visits[i].url_id == info.url_id) { | |
635 last_visited = visits[i].time; | |
636 num_occurrences_of_current_visit++; | |
637 next_urls_currently_found.clear(); | |
638 continue; | |
639 } | |
640 if (!last_visited.is_null() && | |
641 last_visited > visits[i].time - max_age && | |
642 last_visited < visits[i].time - min_age) { | |
643 if (!IsFormSubmit(visits[i].transition)) | |
644 next_urls_currently_found.insert(visits[i].url_id); | |
645 } | |
646 } | |
647 if (i == static_cast<int>(visits.size()) - 1 || | |
648 visits[i+1].url_id == info.url_id) { | |
649 for (std::set<URLID>::iterator it = next_urls_currently_found.begin(); | |
650 it != next_urls_currently_found.end(); | |
651 ++it) { | |
652 std::pair<std::map<URLID, int>::iterator, bool> insert_ret = | |
653 next_urls_num_found.insert(std::pair<URLID, int>(*it, 0)); | |
654 std::map<URLID, int>::iterator num_found_it = insert_ret.first; | |
655 num_found_it->second++; | |
656 } | |
657 } | |
658 } | |
659 | |
660 if (num_occurrences_of_current_visit > 1) { | |
661 RecordEvent(EVENT_ADD_VISIT_RELEVANT_TRANSITION_REPEAT_URL); | |
662 } else { | |
663 RecordEvent(EVENT_ADD_VISIT_RELEVANT_TRANSITION_NEW_URL); | |
664 } | |
665 | |
666 for (std::map<URLID, int>::const_iterator it = next_urls_num_found.begin(); | |
667 it != next_urls_num_found.end(); | |
668 ++it) { | |
669 // Only consider a candidate next page for prerendering if it was viewed | |
670 // at least twice, and at least 10% of the time. | |
671 if (num_occurrences_of_current_visit > 0 && | |
672 it->second > 1 && | |
673 it->second * 10 >= num_occurrences_of_current_visit) { | |
674 RecordEvent(EVENT_ADD_VISIT_IDENTIFIED_PRERENDER_CANDIDATE); | |
675 double priority = static_cast<double>(it->second) / | |
676 static_cast<double>(num_occurrences_of_current_visit); | |
677 lookup_info->MaybeAddCandidateURLFromLocalData(it->first, priority); | |
678 } | |
679 } | |
680 | |
681 RecordEvent(EVENT_START_URL_LOOKUP); | |
682 history::HistoryService* history = GetHistoryIfExists(); | |
683 if (history) { | |
684 RecordEvent(EVENT_GOT_HISTORY_ISSUING_LOOKUP); | |
685 CandidatePrerenderInfo* lookup_info_ptr = lookup_info.get(); | |
686 history->ScheduleDBTask( | |
687 scoped_ptr<history::HistoryDBTask>( | |
688 new GetURLForURLIDTask( | |
689 lookup_info_ptr, | |
690 base::Bind(&PrerenderLocalPredictor::OnLookupURL, | |
691 base::Unretained(this), | |
692 base::Passed(&lookup_info)))), | |
693 &history_db_tracker_); | |
694 } | |
695 } | |
696 | |
697 void PrerenderLocalPredictor::OnLookupURL( | |
698 scoped_ptr<CandidatePrerenderInfo> info) { | |
699 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
700 | |
701 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT); | |
702 | |
703 if (!info->source_url_.url_lookup_success) { | |
704 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_FAILED); | |
705 return; | |
706 } | |
707 | |
708 if (prefetch_list_->MarkURLSeen(info->source_url_.url, | |
709 PrefetchList::SEEN_HISTORY)) { | |
710 RecordEvent(EVENT_PREFETCH_LIST_SEEN_HISTORY); | |
711 } | |
712 | |
713 if (info->candidate_urls_.size() > 0 && | |
714 info->candidate_urls_[0].url_lookup_success) { | |
715 LogCandidateURLStats(info->candidate_urls_[0].url); | |
716 } | |
717 | |
718 WebContents* source_web_contents = NULL; | |
719 bool multiple_source_web_contents_candidates = false; | |
720 | |
721 #if !defined(OS_ANDROID) | |
722 // We need to figure out what tab launched the prerender. We do this by | |
723 // comparing URLs. This may not always work: the URL may occur in two | |
724 // tabs, and we pick the wrong one, or the tab we should have picked | |
725 // may have navigated elsewhere. Hopefully, this doesn't happen too often, | |
726 // so we ignore these cases for now. | |
727 // TODO(tburkard): Reconsider this, potentially measure it, and fix this | |
728 // in the future. | |
729 for (TabContentsIterator it; !it.done(); it.Next()) { | |
730 if (it->GetURL() == info->source_url_.url) { | |
731 if (!source_web_contents) | |
732 source_web_contents = *it; | |
733 else | |
734 multiple_source_web_contents_candidates = true; | |
735 } | |
736 } | |
737 #endif | |
738 | |
739 if (!source_web_contents) { | |
740 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_NO_SOURCE_WEBCONTENTS_FOUND); | |
741 return; | |
742 } | |
743 | |
744 if (multiple_source_web_contents_candidates) | |
745 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_MULTIPLE_SOURCE_WEBCONTENTS_FOUND); | |
746 | |
747 info->session_storage_namespace_ = | |
748 source_web_contents->GetController().GetDefaultSessionStorageNamespace(); | |
749 RenderFrameHost* rfh = source_web_contents->GetMainFrame(); | |
750 info->render_process_id_ = rfh->GetProcess()->GetID(); | |
751 info->render_frame_id_ = rfh->GetRoutingID(); | |
752 | |
753 gfx::Rect container_bounds = source_web_contents->GetContainerBounds(); | |
754 info->size_.reset(new gfx::Size(container_bounds.size())); | |
755 | |
756 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_SUCCESS); | |
757 | |
758 DoPrerenderServiceCheck(info.Pass()); | |
759 } | |
760 | |
761 void PrerenderLocalPredictor::DoPrerenderServiceCheck( | |
762 scoped_ptr<CandidatePrerenderInfo> info) { | |
763 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
764 if (!ShouldQueryPrerenderService(prerender_manager_->profile())) { | |
765 RecordEvent(EVENT_PRERENDER_SERVICE_DISABLED); | |
766 DoLoggedInLookup(info.Pass()); | |
767 return; | |
768 } | |
769 /* | |
770 Create a JSON request. | |
771 Here is a sample request: | |
772 { "prerender_request": { | |
773 "version": 1, | |
774 "behavior_id": 6, | |
775 "hint_request": { | |
776 "browse_history": [ | |
777 { "url": "http://www.cnn.com/" | |
778 } | |
779 ] | |
780 }, | |
781 "candidate_check_request": { | |
782 "candidates": [ | |
783 { "url": "http://www.cnn.com/sports/" | |
784 }, | |
785 { "url": "http://www.cnn.com/politics/" | |
786 } | |
787 ] | |
788 } | |
789 } | |
790 } | |
791 */ | |
792 base::DictionaryValue json_data; | |
793 base::DictionaryValue* req = new base::DictionaryValue(); | |
794 req->SetInteger("version", 1); | |
795 req->SetInteger("behavior_id", GetPrerenderServiceBehaviorID()); | |
796 if (ShouldQueryPrerenderServiceForCurrentURL() && | |
797 info->source_url_.url_lookup_success) { | |
798 base::ListValue* browse_history = new base::ListValue(); | |
799 base::DictionaryValue* browse_item = new base::DictionaryValue(); | |
800 browse_item->SetString("url", info->source_url_.url.spec()); | |
801 browse_history->Append(browse_item); | |
802 base::DictionaryValue* hint_request = new base::DictionaryValue(); | |
803 hint_request->Set("browse_history", browse_history); | |
804 req->Set("hint_request", hint_request); | |
805 } | |
806 int num_candidate_urls = 0; | |
807 for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) { | |
808 if (info->candidate_urls_[i].url_lookup_success) | |
809 num_candidate_urls++; | |
810 } | |
811 if (ShouldQueryPrerenderServiceForCandidateURLs() && | |
812 num_candidate_urls > 0) { | |
813 base::ListValue* candidates = new base::ListValue(); | |
814 base::DictionaryValue* candidate; | |
815 for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) { | |
816 if (info->candidate_urls_[i].url_lookup_success) { | |
817 candidate = new base::DictionaryValue(); | |
818 candidate->SetString("url", info->candidate_urls_[i].url.spec()); | |
819 candidates->Append(candidate); | |
820 } | |
821 } | |
822 base::DictionaryValue* candidate_check_request = | |
823 new base::DictionaryValue(); | |
824 candidate_check_request->Set("candidates", candidates); | |
825 req->Set("candidate_check_request", candidate_check_request); | |
826 } | |
827 json_data.Set("prerender_request", req); | |
828 string request_string; | |
829 base::JSONWriter::Write(&json_data, &request_string); | |
830 GURL fetch_url(GetPrerenderServiceURLPrefix() + | |
831 net::EscapeQueryParamValue(request_string, false)); | |
832 net::URLFetcher* fetcher = net::URLFetcher::Create( | |
833 0, | |
834 fetch_url, | |
835 URLFetcher::GET, this); | |
836 fetcher->SetRequestContext( | |
837 prerender_manager_->profile()->GetRequestContext()); | |
838 fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | | |
839 net::LOAD_DO_NOT_SAVE_COOKIES | | |
840 net::LOAD_DO_NOT_SEND_COOKIES); | |
841 fetcher->AddExtraRequestHeader("Pragma: no-cache"); | |
842 info->start_time_ = base::Time::Now(); | |
843 outstanding_prerender_service_requests_.insert( | |
844 std::make_pair(fetcher, info.release())); | |
845 base::MessageLoop::current()->PostDelayedTask( | |
846 FROM_HERE, | |
847 base::Bind(&PrerenderLocalPredictor::MaybeCancelURLFetcher, | |
848 weak_factory_.GetWeakPtr(), fetcher), | |
849 base::TimeDelta::FromMilliseconds(GetPrerenderServiceFetchTimeoutMs())); | |
850 RecordEvent(EVENT_PRERENDER_SERVICE_ISSUED_LOOKUP); | |
851 fetcher->Start(); | |
852 } | |
853 | |
854 void PrerenderLocalPredictor::MaybeCancelURLFetcher(net::URLFetcher* fetcher) { | |
855 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
856 OutstandingFetchers::iterator it = | |
857 outstanding_prerender_service_requests_.find(fetcher); | |
858 if (it == outstanding_prerender_service_requests_.end()) | |
859 return; | |
860 delete it->first; | |
861 scoped_ptr<CandidatePrerenderInfo> info(it->second); | |
862 outstanding_prerender_service_requests_.erase(it); | |
863 RecordEvent(EVENT_PRERENDER_SERVICE_LOOKUP_TIMED_OUT); | |
864 DoLoggedInLookup(info.Pass()); | |
865 } | |
866 | |
867 bool PrerenderLocalPredictor::ApplyParsedPrerenderServiceResponse( | |
868 base::DictionaryValue* dict, | |
869 CandidatePrerenderInfo* info, | |
870 bool* hinting_timed_out, | |
871 bool* hinting_url_lookup_timed_out, | |
872 bool* candidate_url_lookup_timed_out) { | |
873 /* | |
874 Process the response to the request. | |
875 Here is a sample response to illustrate the format. | |
876 { | |
877 "prerender_response": { | |
878 "behavior_id": 6, | |
879 "hint_response": { | |
880 "hinting_timed_out": 0, | |
881 "candidates": [ | |
882 { "url": "http://www.cnn.com/story-1", | |
883 "in_index": 1, | |
884 "likelihood": 0.60, | |
885 "in_index_timed_out": 0 | |
886 }, | |
887 { "url": "http://www.cnn.com/story-2", | |
888 "in_index": 1, | |
889 "likelihood": 0.30, | |
890 "in_index_timed_out": 0 | |
891 } | |
892 ] | |
893 }, | |
894 "candidate_check_response": { | |
895 "candidates": [ | |
896 { "url": "http://www.cnn.com/sports/", | |
897 "in_index": 1, | |
898 "in_index_timed_out": 0 | |
899 }, | |
900 { "url": "http://www.cnn.com/politics/", | |
901 "in_index": 0, | |
902 "in_index_timed_out": "1" | |
903 } | |
904 ] | |
905 } | |
906 } | |
907 } | |
908 */ | |
909 base::ListValue* list = NULL; | |
910 int int_value; | |
911 if (!dict->GetInteger("prerender_response.behavior_id", &int_value) || | |
912 int_value != GetPrerenderServiceBehaviorID()) { | |
913 return false; | |
914 } | |
915 if (!dict->GetList("prerender_response.candidate_check_response.candidates", | |
916 &list)) { | |
917 if (ShouldQueryPrerenderServiceForCandidateURLs()) { | |
918 for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) { | |
919 if (info->candidate_urls_[i].url_lookup_success) | |
920 return false; | |
921 } | |
922 } | |
923 } else { | |
924 for (size_t i = 0; i < list->GetSize(); i++) { | |
925 base::DictionaryValue* d; | |
926 if (!list->GetDictionary(i, &d)) | |
927 return false; | |
928 string url_string; | |
929 if (!d->GetString("url", &url_string) || !GURL(url_string).is_valid()) | |
930 return false; | |
931 GURL url(url_string); | |
932 int in_index_timed_out = 0; | |
933 int in_index = 0; | |
934 if ((!d->GetInteger("in_index_timed_out", &in_index_timed_out) || | |
935 in_index_timed_out != 1) && | |
936 !d->GetInteger("in_index", &in_index)) { | |
937 return false; | |
938 } | |
939 if (in_index < 0 || in_index > 1 || | |
940 in_index_timed_out < 0 || in_index_timed_out > 1) { | |
941 return false; | |
942 } | |
943 if (in_index_timed_out == 1) | |
944 *candidate_url_lookup_timed_out = true; | |
945 for (size_t j = 0; j < info->candidate_urls_.size(); j++) { | |
946 if (info->candidate_urls_[j].url == url) { | |
947 info->candidate_urls_[j].service_whitelist_reported = true; | |
948 info->candidate_urls_[j].service_whitelist = (in_index == 1); | |
949 info->candidate_urls_[j].service_whitelist_lookup_ok = | |
950 ((1 - in_index_timed_out) == 1); | |
951 } | |
952 } | |
953 } | |
954 for (size_t i = 0; i < info->candidate_urls_.size(); i++) { | |
955 if (info->candidate_urls_[i].url_lookup_success && | |
956 !info->candidate_urls_[i].service_whitelist_reported) { | |
957 return false; | |
958 } | |
959 } | |
960 } | |
961 | |
962 if (ShouldQueryPrerenderServiceForCurrentURL() && | |
963 info->source_url_.url_lookup_success) { | |
964 list = NULL; | |
965 if (dict->GetInteger("prerender_response.hint_response.hinting_timed_out", | |
966 &int_value) && | |
967 int_value == 1) { | |
968 *hinting_timed_out = true; | |
969 } else if (!dict->GetList("prerender_response.hint_response.candidates", | |
970 &list)) { | |
971 return false; | |
972 } else { | |
973 for (int i = 0; i < static_cast<int>(list->GetSize()); i++) { | |
974 base::DictionaryValue* d; | |
975 if (!list->GetDictionary(i, &d)) | |
976 return false; | |
977 string url; | |
978 if (!d->GetString("url", &url) || !GURL(url).is_valid()) | |
979 return false; | |
980 double priority; | |
981 if (!d->GetDouble("likelihood", &priority) || priority < 0.0 || | |
982 priority > 1.0) { | |
983 return false; | |
984 } | |
985 int in_index_timed_out = 0; | |
986 int in_index = 0; | |
987 if ((!d->GetInteger("in_index_timed_out", &in_index_timed_out) || | |
988 in_index_timed_out != 1) && | |
989 !d->GetInteger("in_index", &in_index)) { | |
990 return false; | |
991 } | |
992 if (in_index < 0 || in_index > 1 || in_index_timed_out < 0 || | |
993 in_index_timed_out > 1) { | |
994 return false; | |
995 } | |
996 if (in_index_timed_out == 1) | |
997 *hinting_url_lookup_timed_out = true; | |
998 info->MaybeAddCandidateURLFromService(GURL(url), | |
999 priority, | |
1000 in_index == 1, | |
1001 !in_index_timed_out); | |
1002 } | |
1003 if (list->GetSize() > 0) | |
1004 RecordEvent(EVENT_PRERENDER_SERVICE_RETURNED_HINTING_CANDIDATES); | |
1005 } | |
1006 } | |
1007 | |
1008 return true; | |
1009 } | |
1010 | |
1011 void PrerenderLocalPredictor::OnURLFetchComplete( | |
1012 const net::URLFetcher* source) { | |
1013 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1014 RecordEvent(EVENT_PRERENDER_SERVICE_RECEIVED_RESULT); | |
1015 net::URLFetcher* fetcher = const_cast<net::URLFetcher*>(source); | |
1016 OutstandingFetchers::iterator it = | |
1017 outstanding_prerender_service_requests_.find(fetcher); | |
1018 if (it == outstanding_prerender_service_requests_.end()) { | |
1019 RecordEvent(EVENT_PRERENDER_SERVICE_NO_RECORD_FOR_RESULT); | |
1020 return; | |
1021 } | |
1022 scoped_ptr<CandidatePrerenderInfo> info(it->second); | |
1023 outstanding_prerender_service_requests_.erase(it); | |
1024 TIMING_HISTOGRAM("Prerender.LocalPredictorServiceLookupTime", | |
1025 base::Time::Now() - info->start_time_); | |
mmenke
2015/04/10 16:42:04
Obsolete UMA
davidben
2015/04/13 15:51:14
Done.
| |
1026 string result; | |
1027 fetcher->GetResponseAsString(&result); | |
1028 scoped_ptr<base::Value> root; | |
1029 root.reset(base::JSONReader::Read(result)); | |
1030 bool hinting_timed_out = false; | |
1031 bool hinting_url_lookup_timed_out = false; | |
1032 bool candidate_url_lookup_timed_out = false; | |
1033 if (!root.get() || !root->IsType(base::Value::TYPE_DICTIONARY)) { | |
1034 RecordEvent(EVENT_PRERENDER_SERVICE_PARSE_ERROR_INCORRECT_JSON); | |
1035 } else { | |
1036 if (ApplyParsedPrerenderServiceResponse( | |
1037 static_cast<base::DictionaryValue*>(root.get()), | |
1038 info.get(), | |
1039 &hinting_timed_out, | |
1040 &hinting_url_lookup_timed_out, | |
1041 &candidate_url_lookup_timed_out)) { | |
1042 // We finished parsing the result, and found no errors. | |
1043 RecordEvent(EVENT_PRERENDER_SERVICE_PARSED_CORRECTLY); | |
1044 if (hinting_timed_out) | |
1045 RecordEvent(EVENT_PRERENDER_SERVICE_HINTING_TIMED_OUT); | |
1046 if (hinting_url_lookup_timed_out) | |
1047 RecordEvent(EVENT_PRERENDER_SERVICE_HINTING_URL_LOOKUP_TIMED_OUT); | |
1048 if (candidate_url_lookup_timed_out) | |
1049 RecordEvent(EVENT_PRERENDER_SERVICE_CANDIDATE_URL_LOOKUP_TIMED_OUT); | |
1050 DoLoggedInLookup(info.Pass()); | |
1051 return; | |
1052 } | |
1053 } | |
1054 | |
1055 // If we did not return earlier, an error happened during parsing. | |
1056 // Record this, and proceed. | |
1057 RecordEvent(EVENT_PRERENDER_SERVICE_PARSE_ERROR); | |
1058 DoLoggedInLookup(info.Pass()); | |
1059 } | |
1060 | |
1061 void PrerenderLocalPredictor:: DoLoggedInLookup( | |
1062 scoped_ptr<CandidatePrerenderInfo> info) { | |
1063 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1064 scoped_refptr<LoggedInPredictorTable> logged_in_table = | |
1065 prerender_manager_->logged_in_predictor_table(); | |
1066 | |
1067 if (!logged_in_table.get()) { | |
1068 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_NO_LOGGED_IN_TABLE_FOUND); | |
1069 return; | |
1070 } | |
1071 | |
1072 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_ISSUING_LOGGED_IN_LOOKUP); | |
1073 | |
1074 info->start_time_ = base::Time::Now(); | |
1075 | |
1076 CandidatePrerenderInfo* info_ptr = info.get(); | |
1077 BrowserThread::PostTaskAndReply( | |
1078 BrowserThread::DB, FROM_HERE, | |
1079 base::Bind(&LookupLoggedInStatesOnDBThread, | |
1080 logged_in_table, | |
1081 info_ptr), | |
1082 base::Bind(&PrerenderLocalPredictor::ContinuePrerenderCheck, | |
1083 weak_factory_.GetWeakPtr(), | |
1084 base::Passed(&info))); | |
1085 } | |
1086 | |
1087 void PrerenderLocalPredictor::LogCandidateURLStats(const GURL& url) const { | |
1088 if (url_whitelist_.count(GetInt64URLHashForURL(url)) > 0) { | |
1089 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_ON_WHITELIST); | |
1090 if (IsRootPageURL(url)) | |
1091 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_ON_WHITELIST_ROOT_PAGE); | |
1092 } | |
1093 if (IsRootPageURL(url)) | |
1094 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_ROOT_PAGE); | |
1095 if (IsExtendedRootURL(url)) | |
1096 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_EXTENDED_ROOT_PAGE); | |
1097 if (IsRootPageURL(url) && url.SchemeIs("http")) | |
1098 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_ROOT_PAGE_HTTP); | |
1099 if (url.SchemeIs("http")) | |
1100 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_IS_HTTP); | |
1101 if (url.has_query()) | |
1102 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_HAS_QUERY_STRING); | |
1103 if (IsLogOutURL(url)) | |
1104 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_CONTAINS_LOGOUT); | |
1105 if (IsLogInURL(url)) | |
1106 RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_CONTAINS_LOGIN); | |
1107 } | |
1108 | |
1109 void PrerenderLocalPredictor::OnGetInitialVisitHistory( | |
1110 scoped_ptr<vector<history::BriefVisitInfo> > visit_history) { | |
1111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1112 DCHECK(!visit_history_.get()); | |
1113 RecordEvent(EVENT_INIT_SUCCEEDED); | |
1114 // Since the visit history has descending timestamps, we must reverse it. | |
1115 visit_history_.reset(new vector<history::BriefVisitInfo>( | |
1116 visit_history->rbegin(), visit_history->rend())); | |
1117 } | |
1118 | |
1119 history::HistoryService* PrerenderLocalPredictor::GetHistoryIfExists() const { | |
1120 Profile* profile = prerender_manager_->profile(); | |
1121 if (!profile) | |
1122 return NULL; | |
1123 return HistoryServiceFactory::GetForProfileWithoutCreating(profile); | |
1124 } | |
1125 | |
1126 void PrerenderLocalPredictor::Init() { | |
1127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1128 RecordEvent(EVENT_INIT_STARTED); | |
1129 Profile* profile = prerender_manager_->profile(); | |
1130 if (!profile || | |
1131 ShouldDisableLocalPredictorBasedOnSyncAndConfiguration(profile)) { | |
1132 RecordEvent(EVENT_INIT_FAILED_UNENCRYPTED_SYNC_NOT_ENABLED); | |
1133 return; | |
1134 } | |
1135 history::HistoryService* history = GetHistoryIfExists(); | |
1136 if (history) { | |
1137 CHECK(!history_service_observer_.IsObserving(history)); | |
1138 history->ScheduleDBTask( | |
1139 scoped_ptr<history::HistoryDBTask>( | |
1140 new GetVisitHistoryTask(this, kMaxVisitHistory)), | |
1141 &history_db_tracker_); | |
1142 history_service_observer_.Add(history); | |
1143 } else { | |
1144 RecordEvent(EVENT_INIT_FAILED_NO_HISTORY); | |
1145 } | |
1146 } | |
1147 | |
1148 void PrerenderLocalPredictor::OnPLTEventForURL(const GURL& url, | |
1149 base::TimeDelta page_load_time) { | |
1150 if (prefetch_list_->MarkPLTSeen(url, page_load_time)) { | |
1151 UMA_HISTOGRAM_CUSTOM_TIMES("Prerender.LocalPredictorPrefetchMatchPLT", | |
1152 page_load_time, | |
1153 base::TimeDelta::FromMilliseconds(10), | |
1154 base::TimeDelta::FromSeconds(60), | |
1155 100); | |
mmenke
2015/04/10 16:42:04
Obsolete UMA
davidben
2015/04/13 15:51:14
Done.
| |
1156 } | |
1157 | |
1158 scoped_ptr<PrerenderProperties> prerender; | |
1159 if (DoesPrerenderMatchPLTRecord(last_swapped_in_prerender_.get(), | |
1160 url, page_load_time)) { | |
1161 prerender.reset(last_swapped_in_prerender_.release()); | |
1162 } | |
1163 if (DoesPrerenderMatchPLTRecord(current_prerender_.get(), | |
1164 url, page_load_time)) { | |
1165 prerender.reset(current_prerender_.release()); | |
1166 } | |
1167 if (!prerender.get()) | |
1168 return; | |
1169 if (IsPrerenderStillValid(prerender.get())) { | |
1170 UMA_HISTOGRAM_CUSTOM_TIMES("Prerender.SimulatedLocalBrowsingBaselinePLT", | |
mmenke
2015/04/10 16:42:04
Obsolete UMA
davidben
2015/04/13 15:51:14
Done.
| |
1171 page_load_time, | |
1172 base::TimeDelta::FromMilliseconds(10), | |
1173 base::TimeDelta::FromSeconds(60), | |
1174 100); | |
1175 | |
1176 base::TimeDelta prerender_age = GetCurrentTime() - prerender->start_time; | |
1177 if (prerender_age > page_load_time) { | |
1178 base::TimeDelta new_plt; | |
1179 if (prerender_age < 2 * page_load_time) | |
1180 new_plt = 2 * page_load_time - prerender_age; | |
1181 UMA_HISTOGRAM_CUSTOM_TIMES("Prerender.SimulatedLocalBrowsingPLT", | |
mmenke
2015/04/10 16:42:04
Obsolete UMA
davidben
2015/04/13 15:51:14
Done.
| |
1182 new_plt, | |
1183 base::TimeDelta::FromMilliseconds(10), | |
1184 base::TimeDelta::FromSeconds(60), | |
1185 100); | |
1186 } | |
1187 } | |
1188 } | |
1189 | |
1190 bool PrerenderLocalPredictor::IsPrerenderStillValid( | |
1191 PrerenderLocalPredictor::PrerenderProperties* prerender) const { | |
1192 return (prerender && | |
1193 (prerender->start_time + | |
1194 base::TimeDelta::FromMilliseconds(GetMaxLocalPredictionTimeMs())) | |
1195 > GetCurrentTime()); | |
1196 } | |
1197 | |
1198 void PrerenderLocalPredictor::RecordEvent( | |
1199 PrerenderLocalPredictor::Event event) const { | |
1200 UMA_HISTOGRAM_ENUMERATION("Prerender.LocalPredictorEvent", | |
mmenke
2015/04/10 16:42:04
Obsolete UMA
davidben
2015/04/13 15:51:14
Done.
| |
1201 event, PrerenderLocalPredictor::EVENT_MAX_VALUE); | |
1202 } | |
1203 | |
1204 bool PrerenderLocalPredictor::DoesPrerenderMatchPLTRecord( | |
1205 PrerenderProperties* prerender, | |
1206 const GURL& url, | |
1207 base::TimeDelta plt) const { | |
1208 if (prerender && prerender->start_time < GetCurrentTime() - plt) { | |
1209 if (prerender->url.is_empty()) | |
1210 RecordEvent(EVENT_ERROR_NO_PRERENDER_URL_FOR_PLT); | |
1211 return (prerender->url == url); | |
1212 } else { | |
1213 return false; | |
1214 } | |
1215 } | |
1216 | |
1217 PrerenderLocalPredictor::PrerenderProperties* | |
1218 PrerenderLocalPredictor::GetIssuedPrerenderSlotForPriority(const GURL& url, | |
1219 double priority) { | |
1220 int num_prerenders = GetLocalPredictorMaxConcurrentPrerenders(); | |
1221 while (static_cast<int>(issued_prerenders_.size()) < num_prerenders) | |
1222 issued_prerenders_.push_back(new PrerenderProperties()); | |
1223 // First, check if we already have a prerender for the same URL issued. | |
1224 // If yes, we don't want to prerender this URL again, so we return NULL | |
1225 // (on matching slot found). | |
1226 for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) { | |
1227 PrerenderProperties* p = issued_prerenders_[i]; | |
1228 DCHECK(p != NULL); | |
1229 if (p->prerender_handle && p->prerender_handle->IsPrerendering() && | |
1230 p->prerender_handle->Matches(url, NULL)) { | |
1231 return NULL; | |
1232 } | |
1233 } | |
1234 // Otherwise, let's see if there are any empty slots. If yes, return the first | |
1235 // one we find. Otherwise, if the lowest priority prerender has a lower | |
1236 // priority than the page we want to prerender, use its slot. | |
1237 PrerenderProperties* lowest_priority_prerender = NULL; | |
1238 for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) { | |
1239 PrerenderProperties* p = issued_prerenders_[i]; | |
1240 DCHECK(p != NULL); | |
1241 if (!p->prerender_handle || !p->prerender_handle->IsPrerendering()) | |
1242 return p; | |
1243 double decayed_priority = p->GetCurrentDecayedPriority(); | |
1244 if (decayed_priority > priority) | |
1245 continue; | |
1246 if (lowest_priority_prerender == NULL || | |
1247 lowest_priority_prerender->GetCurrentDecayedPriority() > | |
1248 decayed_priority) { | |
1249 lowest_priority_prerender = p; | |
1250 } | |
1251 } | |
1252 return lowest_priority_prerender; | |
1253 } | |
1254 | |
1255 void PrerenderLocalPredictor::ContinuePrerenderCheck( | |
1256 scoped_ptr<CandidatePrerenderInfo> info) { | |
1257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1258 TIMING_HISTOGRAM("Prerender.LocalPredictorLoggedInLookupTime", | |
1259 base::Time::Now() - info->start_time_); | |
mmenke
2015/04/10 16:42:04
Obsolete UMA
davidben
2015/04/13 15:51:14
Done.
| |
1260 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_STARTED); | |
1261 if (info->candidate_urls_.size() == 0) { | |
1262 RecordEvent(EVENT_NO_PRERENDER_CANDIDATES); | |
1263 return; | |
1264 } | |
1265 scoped_ptr<LocalPredictorURLInfo> url_info; | |
1266 #if defined(FULL_SAFE_BROWSING) | |
1267 scoped_refptr<SafeBrowsingDatabaseManager> sb_db_manager = | |
1268 g_browser_process->safe_browsing_service()->database_manager(); | |
1269 #endif | |
1270 int num_issued = 0; | |
1271 for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) { | |
1272 if (num_issued >= GetLocalPredictorMaxLaunchPrerenders()) | |
1273 return; | |
1274 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL); | |
1275 url_info.reset(new LocalPredictorURLInfo(info->candidate_urls_[i])); | |
1276 if (url_info->local_history_based) { | |
1277 if (SkipLocalPredictorLocalCandidates()) { | |
1278 url_info.reset(NULL); | |
1279 continue; | |
1280 } | |
1281 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_LOCAL); | |
1282 } | |
1283 if (!url_info->local_history_based) { | |
1284 if (SkipLocalPredictorServiceCandidates()) { | |
1285 url_info.reset(NULL); | |
1286 continue; | |
1287 } | |
1288 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_SERVICE); | |
1289 } | |
1290 | |
1291 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_NOT_SKIPPED); | |
1292 | |
1293 // We need to check whether we can issue a prerender for this URL. | |
1294 // We test a set of conditions. Each condition can either rule out | |
1295 // a prerender (in which case we reset url_info, so that it will not | |
1296 // be prerendered, and we continue, which means try the next candidate | |
1297 // URL), or it can be sufficient to issue the prerender without any | |
1298 // further checks (in which case we just break). | |
1299 // The order of the checks is critical, because it prescribes the logic | |
1300 // we use here to decide what to prerender. | |
1301 if (!url_info->url_lookup_success) { | |
1302 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_NO_URL); | |
1303 url_info.reset(NULL); | |
1304 continue; | |
1305 } | |
1306 if (!SkipLocalPredictorFragment() && | |
1307 URLsIdenticalIgnoringFragments(info->source_url_.url, | |
1308 url_info->url)) { | |
1309 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_URLS_IDENTICAL_BUT_FRAGMENT); | |
1310 url_info.reset(NULL); | |
1311 continue; | |
1312 } | |
1313 if (!SkipLocalPredictorHTTPS() && url_info->url.SchemeIs("https")) { | |
1314 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_HTTPS); | |
1315 url_info.reset(NULL); | |
1316 continue; | |
1317 } | |
1318 if (IsRootPageURL(url_info->url)) { | |
1319 // For root pages, we assume that they are reasonably safe, and we | |
1320 // will just prerender them without any additional checks. | |
1321 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ROOT_PAGE); | |
1322 IssuePrerender(info.get(), url_info.get()); | |
1323 num_issued++; | |
1324 continue; | |
1325 } | |
1326 if (IsLogOutURL(url_info->url)) { | |
1327 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_LOGOUT_URL); | |
1328 url_info.reset(NULL); | |
1329 continue; | |
1330 } | |
1331 if (IsLogInURL(url_info->url)) { | |
1332 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_LOGIN_URL); | |
1333 url_info.reset(NULL); | |
1334 continue; | |
1335 } | |
1336 #if defined(FULL_SAFE_BROWSING) | |
1337 if (!SkipLocalPredictorWhitelist() && sb_db_manager.get() && | |
1338 sb_db_manager->CheckSideEffectFreeWhitelistUrl(url_info->url)) { | |
1339 // If a page is on the side-effect free whitelist, we will just prerender | |
1340 // it without any additional checks. | |
1341 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SIDE_EFFECT_FREE_WHITELIST); | |
1342 IssuePrerender(info.get(), url_info.get()); | |
1343 num_issued++; | |
1344 continue; | |
1345 } | |
1346 #endif | |
1347 if (!SkipLocalPredictorServiceWhitelist() && | |
1348 url_info->service_whitelist && url_info->service_whitelist_lookup_ok) { | |
1349 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SERVICE_WHITELIST); | |
1350 IssuePrerender(info.get(), url_info.get()); | |
1351 num_issued++; | |
1352 continue; | |
1353 } | |
1354 if (!SkipLocalPredictorLoggedIn() && | |
1355 !url_info->logged_in && url_info->logged_in_lookup_ok) { | |
1356 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_NOT_LOGGED_IN); | |
1357 IssuePrerender(info.get(), url_info.get()); | |
1358 num_issued++; | |
1359 continue; | |
1360 } | |
1361 if (!SkipLocalPredictorDefaultNoPrerender()) { | |
1362 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_NOT_PRERENDERING); | |
1363 url_info.reset(NULL); | |
1364 } else { | |
1365 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_PRERENDERING); | |
1366 IssuePrerender(info.get(), url_info.get()); | |
1367 num_issued++; | |
1368 continue; | |
1369 } | |
1370 } | |
1371 } | |
1372 | |
1373 void PrerenderLocalPredictor::IssuePrerender( | |
1374 CandidatePrerenderInfo* info, | |
1375 LocalPredictorURLInfo* url_info) { | |
1376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1377 RecordEvent(EVENT_ISSUE_PRERENDER_CALLED); | |
1378 if (prefetch_list_->AddURL(url_info->url)) { | |
1379 RecordEvent(EVENT_PREFETCH_LIST_ADDED); | |
1380 // If we are prefetching rather than prerendering, now is the time to launch | |
1381 // the prefetch. | |
1382 if (IsLocalPredictorPrerenderPrefetchEnabled()) { | |
1383 RecordEvent(EVENT_ISSUE_PRERENDER_PREFETCH_ENABLED); | |
1384 // Obtain the render frame host that caused this prefetch. | |
1385 RenderFrameHost* rfh = RenderFrameHost::FromID(info->render_process_id_, | |
1386 info->render_frame_id_); | |
1387 // If it is still alive, launch the prefresh. | |
1388 if (rfh) { | |
1389 rfh->Send(new PrefetchMsg_Prefetch(rfh->GetRoutingID(), url_info->url)); | |
1390 RecordEvent(EVENT_ISSUE_PRERENDER_PREFETCH_ISSUED); | |
1391 } | |
1392 } | |
1393 } | |
1394 PrerenderProperties* prerender_properties = | |
1395 GetIssuedPrerenderSlotForPriority(url_info->url, url_info->priority); | |
1396 if (!prerender_properties) { | |
1397 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_PRIORITY_TOO_LOW); | |
1398 return; | |
1399 } | |
1400 RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ISSUING_PRERENDER); | |
1401 DCHECK(prerender_properties != NULL); | |
1402 DCHECK(info != NULL); | |
1403 DCHECK(url_info != NULL); | |
1404 if (!IsLocalPredictorPrerenderLaunchEnabled()) | |
1405 return; | |
1406 URLID url_id = url_info->id; | |
1407 const GURL& url = url_info->url; | |
1408 double priority = url_info->priority; | |
1409 base::Time current_time = GetCurrentTime(); | |
1410 RecordEvent(EVENT_ISSUING_PRERENDER); | |
1411 | |
1412 // Issue the prerender and obtain a new handle. | |
1413 scoped_ptr<prerender::PrerenderHandle> new_prerender_handle( | |
1414 prerender_manager_->AddPrerenderFromLocalPredictor( | |
1415 url, info->session_storage_namespace_.get(), *(info->size_))); | |
1416 | |
1417 // Check if this is a duplicate of an existing prerender. If yes, clean up | |
1418 // the new handle. | |
1419 for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) { | |
1420 PrerenderProperties* p = issued_prerenders_[i]; | |
1421 DCHECK(p != NULL); | |
1422 if (new_prerender_handle && | |
1423 new_prerender_handle->RepresentingSamePrerenderAs( | |
1424 p->prerender_handle.get())) { | |
1425 new_prerender_handle->OnCancel(); | |
1426 new_prerender_handle.reset(NULL); | |
1427 RecordEvent(EVENT_ISSUE_PRERENDER_ALREADY_PRERENDERING); | |
1428 break; | |
1429 } | |
1430 } | |
1431 | |
1432 if (new_prerender_handle.get()) { | |
1433 RecordEvent(EVENT_ISSUE_PRERENDER_NEW_PRERENDER); | |
1434 // The new prerender does not match any existing prerenders. Update | |
1435 // prerender_properties so that it reflects the new entry. | |
1436 prerender_properties->url_id = url_id; | |
1437 prerender_properties->url = url; | |
1438 prerender_properties->priority = priority; | |
1439 prerender_properties->start_time = current_time; | |
1440 prerender_properties->actual_start_time = current_time; | |
1441 prerender_properties->would_have_matched = false; | |
1442 prerender_properties->prerender_handle.swap(new_prerender_handle); | |
1443 // new_prerender_handle now represents the old previou prerender that we | |
1444 // are replacing. So we need to cancel it. | |
1445 if (new_prerender_handle) { | |
1446 new_prerender_handle->OnCancel(); | |
1447 RecordEvent(EVENT_ISSUE_PRERENDER_CANCELLED_OLD_PRERENDER); | |
1448 } | |
1449 } | |
1450 | |
1451 RecordEvent(EVENT_ADD_VISIT_PRERENDERING); | |
1452 if (current_prerender_.get() && current_prerender_->url_id == url_id) { | |
1453 RecordEvent(EVENT_ADD_VISIT_PRERENDERING_EXTENDED); | |
1454 if (priority > current_prerender_->priority) | |
1455 current_prerender_->priority = priority; | |
1456 // If the prerender already existed, we want to extend it. However, | |
1457 // we do not want to set its start_time to the current time to | |
1458 // disadvantage PLT computations when the prerender is swapped in. | |
1459 // So we set the new start time to current_time - 10s (since the vast | |
1460 // majority of PLTs are < 10s), provided that is not before the actual | |
1461 // time the prerender was started (so as to not artificially advantage | |
1462 // the PLT computation). | |
1463 base::Time simulated_new_start_time = | |
1464 current_time - base::TimeDelta::FromSeconds(10); | |
1465 if (simulated_new_start_time > current_prerender_->start_time) | |
1466 current_prerender_->start_time = simulated_new_start_time; | |
1467 } else { | |
1468 current_prerender_.reset( | |
1469 new PrerenderProperties(url_id, url, priority, current_time)); | |
1470 } | |
1471 current_prerender_->actual_start_time = current_time; | |
1472 } | |
1473 | |
1474 void PrerenderLocalPredictor::OnTabHelperURLSeen( | |
1475 const GURL& url, WebContents* web_contents) { | |
1476 RecordEvent(EVENT_TAB_HELPER_URL_SEEN); | |
1477 | |
1478 if (prefetch_list_->MarkURLSeen(url, PrefetchList::SEEN_TABCONTENTS_OBSERVER)) | |
1479 RecordEvent(EVENT_PREFETCH_LIST_SEEN_TABCONTENTS); | |
1480 bool browser_navigate_initiated = false; | |
1481 const content::NavigationEntry* entry = | |
1482 web_contents->GetController().GetPendingEntry(); | |
1483 if (entry) { | |
1484 base::string16 result; | |
1485 browser_navigate_initiated = | |
1486 entry->GetExtraData(kChromeNavigateExtraDataKey, &result); | |
1487 } | |
1488 | |
1489 // If the namespace matches and the URL matches, we might be able to swap | |
1490 // in. However, the actual code initating the swapin is in the renderer | |
1491 // and is checking for other criteria (such as POSTs). There may | |
1492 // also be conditions when a swapin should happen but does not. By recording | |
1493 // the two previous events, we can keep an eye on the magnitude of the | |
1494 // discrepancy. | |
1495 | |
1496 PrerenderProperties* best_matched_prerender = NULL; | |
1497 bool session_storage_namespace_matches = false; | |
1498 SessionStorageNamespace* tab_session_storage_namespace = | |
1499 web_contents->GetController().GetDefaultSessionStorageNamespace(); | |
1500 for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) { | |
1501 PrerenderProperties* p = issued_prerenders_[i]; | |
1502 DCHECK(p != NULL); | |
1503 if (!p->prerender_handle.get() || | |
1504 !p->prerender_handle->Matches(url, NULL) || | |
1505 p->would_have_matched) { | |
1506 continue; | |
1507 } | |
1508 if (!best_matched_prerender || !session_storage_namespace_matches) { | |
1509 best_matched_prerender = p; | |
1510 session_storage_namespace_matches = | |
1511 p->prerender_handle->Matches(url, tab_session_storage_namespace); | |
1512 } | |
1513 } | |
1514 if (best_matched_prerender) { | |
1515 RecordEvent(EVENT_TAB_HELPER_URL_SEEN_MATCH); | |
1516 if (entry) | |
1517 RecordEvent(EVENT_TAB_HELPER_URL_SEEN_MATCH_ENTRY); | |
1518 if (browser_navigate_initiated) | |
1519 RecordEvent(EVENT_TAB_HELPER_URL_SEEN_MATCH_BROWSER_NAVIGATE); | |
1520 best_matched_prerender->would_have_matched = true; | |
1521 if (session_storage_namespace_matches) { | |
1522 RecordEvent(EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH); | |
1523 if (entry) | |
1524 RecordEvent(EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH_ENTRY); | |
1525 if (browser_navigate_initiated) | |
1526 RecordEvent(EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH_BROWSER_NAVIGATE); | |
1527 } | |
1528 } | |
1529 } | |
1530 | |
1531 } // namespace prerender | |
OLD | NEW |