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#ifndef CHROME_BROWSER_PRERENDER_PRERENDER_LOCAL_PREDICTOR_H_
6#define CHROME_BROWSER_PRERENDER_PRERENDER_LOCAL_PREDICTOR_H_
7
8#include <map>
9#include <vector>
10
11#include "base/containers/hash_tables.h"
12#include "base/memory/scoped_vector.h"
13#include "base/memory/weak_ptr.h"
14#include "base/task/cancelable_task_tracker.h"
15#include "base/timer/timer.h"
16#include "chrome/browser/history/visit_database.h"
17#include "content/public/browser/session_storage_namespace.h"
18#include "net/url_request/url_fetcher_delegate.h"
19#include "url/gurl.h"
20
21class HistoryService;
22
23namespace base {
24class DictionaryValue;
25}
26
27namespace content {
28class WebContents;
29}
30
31namespace gfx {
32class Size;
33}
34
35namespace prerender {
36
37class PrerenderHandle;
38class PrerenderManager;
39
40// PrerenderLocalPredictor maintains local browsing history to make prerender
41// predictions.
42// At this point, the class is not actually creating prerenders, but just
43// recording timing stats about the effect prerendering would have.
44class PrerenderLocalPredictor : public history::VisitDatabaseObserver,
45                                public net::URLFetcherDelegate {
46 public:
47  struct LocalPredictorURLInfo;
48  struct CandidatePrerenderInfo;
49  // A class simulating a set of URLs prefetched, for statistical purposes.
50  class PrefetchList;
51  enum Event {
52    EVENT_CONSTRUCTED = 0,
53    EVENT_INIT_SCHEDULED = 1,
54    EVENT_INIT_STARTED = 2,
55    EVENT_INIT_FAILED_NO_HISTORY = 3,
56    EVENT_INIT_SUCCEEDED = 4,
57    EVENT_ADD_VISIT = 5,
58    EVENT_ADD_VISIT_INITIALIZED = 6,
59    EVENT_ADD_VISIT_PRERENDER_IDENTIFIED = 7,
60    EVENT_ADD_VISIT_RELEVANT_TRANSITION = 8,
61    EVENT_ADD_VISIT_IDENTIFIED_PRERENDER_CANDIDATE = 9,
62    EVENT_ADD_VISIT_PRERENDERING = 10,
63    EVENT_GOT_PRERENDER_URL = 11,
64    EVENT_ERROR_NO_PRERENDER_URL_FOR_PLT = 12,
65    EVENT_ADD_VISIT_PRERENDERING_EXTENDED = 13,
66    EVENT_PRERENDER_URL_LOOKUP_RESULT = 14,
67    EVENT_PRERENDER_URL_LOOKUP_RESULT_ROOT_PAGE = 15,
68    EVENT_PRERENDER_URL_LOOKUP_RESULT_IS_HTTP = 16,
69    EVENT_PRERENDER_URL_LOOKUP_RESULT_HAS_QUERY_STRING = 17,
70    EVENT_PRERENDER_URL_LOOKUP_RESULT_CONTAINS_LOGOUT = 18,
71    EVENT_PRERENDER_URL_LOOKUP_RESULT_CONTAINS_LOGIN = 19,
72    EVENT_START_URL_LOOKUP = 20,
73    EVENT_ADD_VISIT_NOT_ROOTPAGE = 21,
74    EVENT_URL_WHITELIST_ERROR = 22,
75    EVENT_URL_WHITELIST_OK = 23,
76    EVENT_PRERENDER_URL_LOOKUP_RESULT_ON_WHITELIST = 24,
77    EVENT_PRERENDER_URL_LOOKUP_RESULT_ON_WHITELIST_ROOT_PAGE = 25,
78    EVENT_PRERENDER_URL_LOOKUP_RESULT_EXTENDED_ROOT_PAGE = 26,
79    EVENT_PRERENDER_URL_LOOKUP_RESULT_ROOT_PAGE_HTTP = 27,
80    EVENT_PRERENDER_URL_LOOKUP_FAILED = 28,
81    EVENT_PRERENDER_URL_LOOKUP_NO_SOURCE_WEBCONTENTS_FOUND = 29,
82    EVENT_PRERENDER_URL_LOOKUP_NO_LOGGED_IN_TABLE_FOUND = 30,
83    EVENT_PRERENDER_URL_LOOKUP_ISSUING_LOGGED_IN_LOOKUP = 31,
84    EVENT_CONTINUE_PRERENDER_CHECK_STARTED = 32,
85    EVENT_CONTINUE_PRERENDER_CHECK_NO_URL = 33,
86    EVENT_CONTINUE_PRERENDER_CHECK_PRIORITY_TOO_LOW = 34,
87    EVENT_CONTINUE_PRERENDER_CHECK_URLS_IDENTICAL_BUT_FRAGMENT = 35,
88    EVENT_CONTINUE_PRERENDER_CHECK_HTTPS = 36,
89    EVENT_CONTINUE_PRERENDER_CHECK_ROOT_PAGE = 37,
90    EVENT_CONTINUE_PRERENDER_CHECK_LOGOUT_URL = 38,
91    EVENT_CONTINUE_PRERENDER_CHECK_LOGIN_URL = 39,
92    EVENT_CONTINUE_PRERENDER_CHECK_NOT_LOGGED_IN = 40,
93    EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_NOT_PRERENDERING = 41,
94    EVENT_CONTINUE_PRERENDER_CHECK_ISSUING_PRERENDER = 42,
95    EVENT_ISSUING_PRERENDER = 43,
96    EVENT_NO_PRERENDER_CANDIDATES = 44,
97    EVENT_GOT_HISTORY_ISSUING_LOOKUP = 45,
98    EVENT_TAB_HELPER_URL_SEEN = 46,
99    EVENT_TAB_HELPER_URL_SEEN_MATCH = 47,
100    EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH = 48,
101    EVENT_PRERENDER_URL_LOOKUP_MULTIPLE_SOURCE_WEBCONTENTS_FOUND = 49,
102    EVENT_CONTINUE_PRERENDER_CHECK_ON_SIDE_EFFECT_FREE_WHITELIST = 50,
103    EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL = 51,
104    EVENT_ISSUE_PRERENDER_ALREADY_PRERENDERING = 52,
105    EVENT_ISSUE_PRERENDER_NEW_PRERENDER = 53,
106    EVENT_ISSUE_PRERENDER_CANCELLED_OLD_PRERENDER = 54,
107    EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_PRERENDERING = 55,
108    EVENT_PRERENDER_URL_LOOKUP_SUCCESS = 56,
109    EVENT_PRERENDER_SERVICE_DISABLED = 57,
110    EVENT_PRERENDER_SERVICE_ISSUED_LOOKUP = 58,
111    EVENT_PRERENDER_SERVICE_LOOKUP_TIMED_OUT = 59,
112    EVENT_PRERENDER_SERVICE_RECEIVED_RESULT = 60,
113    EVENT_PRERENDER_SERVICE_NO_RECORD_FOR_RESULT = 61,
114    EVENT_PRERENDER_SERVICE_PARSED_CORRECTLY = 62,
115    EVENT_PRERENDER_SERVICE_PARSE_ERROR = 63,
116    EVENT_PRERENDER_SERVICE_PARSE_ERROR_INCORRECT_JSON = 64,
117    EVENT_PRERENDER_SERVICE_HINTING_TIMED_OUT = 65,
118    EVENT_PRERENDER_SERVICE_HINTING_URL_LOOKUP_TIMED_OUT = 66,
119    EVENT_PRERENDER_SERVICE_CANDIDATE_URL_LOOKUP_TIMED_OUT = 67,
120    EVENT_CONTINUE_PRERENDER_CHECK_ON_SERVICE_WHITELIST = 68,
121    EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_LOCAL = 69,
122    EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_SERVICE = 70,
123    EVENT_ADD_VISIT_RELEVANT_TRANSITION_REPEAT_URL = 71,
124    EVENT_ADD_VISIT_RELEVANT_TRANSITION_NEW_URL = 72,
125    EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MISMATCH_NO_NAMESPACE = 73,
126    EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MISMATCH_MERGE_ISSUED = 74,
127    EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_RECEIVED = 75,
128    EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_NAMESPACE_NOT_FOUND = 76,
129    EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_NOT_LOGGING = 77,
130    EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_NO_TRANSACTIONS = 78,
131    EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_TOO_MANY_TRANSACTIONS = 79,
132    EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_NOT_MERGEABLE = 80,
133    EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_MERGEABLE = 81,
134    EVENT_INIT_FAILED_UNENCRYPTED_SYNC_NOT_ENABLED = 82,
135    EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_NOT_SKIPPED = 83,
136    EVENT_PRERENDER_SERVICE_RETURNED_HINTING_CANDIDATES = 84,
137    EVENT_NAMESPACE_MISMATCH_MERGE_RESULT_NAMESPACE_NOT_ALIAS = 85,
138    EVENT_TAB_HELPER_URL_SEEN_MATCH_ENTRY = 86,
139    EVENT_TAB_HELPER_URL_SEEN_MATCH_BROWSER_NAVIGATE = 87,
140    EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH_ENTRY = 88,
141    EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH_BROWSER_NAVIGATE = 89,
142    EVENT_PREFETCH_LIST_ADDED = 90,
143    EVENT_PREFETCH_LIST_SEEN_TABCONTENTS = 91,
144    EVENT_PREFETCH_LIST_SEEN_HISTORY = 92,
145    EVENT_ISSUE_PRERENDER_CALLED = 93,
146    EVENT_ISSUE_PRERENDER_PREFETCH_ENABLED = 94,
147    EVENT_ISSUE_PRERENDER_PREFETCH_ISSUED = 95,
148    EVENT_MAX_VALUE
149  };
150
151  // A PrerenderLocalPredictor is owned by the PrerenderManager specified
152  // in the constructor.  It will be destoryed at the time its owning
153  // PrerenderManager is destroyed.
154  explicit PrerenderLocalPredictor(PrerenderManager* prerender_manager);
155  virtual ~PrerenderLocalPredictor();
156
157  void Shutdown();
158
159  // history::VisitDatabaseObserver implementation
160  virtual void OnAddVisit(const history::BriefVisitInfo& info) OVERRIDE;
161
162  void OnGetInitialVisitHistory(
163      scoped_ptr<std::vector<history::BriefVisitInfo> > visit_history);
164
165  void OnPLTEventForURL(const GURL& url, base::TimeDelta page_load_time);
166
167  void OnTabHelperURLSeen(const GURL& url, content::WebContents* web_contents);
168
169  // net::URLFetcherDelegate implementation:
170  void virtual OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
171
172 private:
173  struct PrerenderProperties;
174  HistoryService* GetHistoryIfExists() const;
175  void Init();
176  bool IsPrerenderStillValid(PrerenderProperties* prerender) const;
177  bool DoesPrerenderMatchPLTRecord(PrerenderProperties* prerender,
178                                   const GURL& url,
179                                   base::TimeDelta plt) const;
180  void RecordEvent(Event event) const;
181
182  void OnLookupURL(scoped_ptr<CandidatePrerenderInfo> info);
183
184  // Lookup the prerender candidate in the Prerender Service (if applicable).
185  void DoPrerenderServiceCheck(scoped_ptr<CandidatePrerenderInfo> info);
186
187  // Lookup the prerender candidate in the LoggedIn Predictor.
188  void DoLoggedInLookup(scoped_ptr<CandidatePrerenderInfo> info);
189
190  // Returns an element of issued_prerenders_, which should be replaced
191  // by a new prerender of the priority indicated, or NULL, if the priority
192  // is too low (or if the URL requested is already prerendering).
193  PrerenderProperties* GetIssuedPrerenderSlotForPriority(const GURL& url,
194                                                         double priority);
195
196  void ContinuePrerenderCheck(scoped_ptr<CandidatePrerenderInfo> info);
197  void LogCandidateURLStats(const GURL& url) const;
198  void IssuePrerender(CandidatePrerenderInfo* info,
199                      LocalPredictorURLInfo* url_info);
200  void MaybeCancelURLFetcher(net::URLFetcher* fetcher);
201  // Returns true if the parsed response is semantically correct and could
202  // be fully applied.
203  bool ApplyParsedPrerenderServiceResponse(
204      base::DictionaryValue* dict,
205      CandidatePrerenderInfo* info,
206      bool* hinting_timed_out,
207      bool* hinting_url_lookup_timed_out,
208      bool* candidate_url_lookup_timed_out);
209  void ProcessNamespaceMergeResult(
210      content::SessionStorageNamespace::MergeResult result);
211  typedef std::map<net::URLFetcher*, CandidatePrerenderInfo*>
212      OutstandingFetchers;
213  OutstandingFetchers outstanding_prerender_service_requests_;
214  PrerenderManager* prerender_manager_;
215  base::OneShotTimer<PrerenderLocalPredictor> timer_;
216
217  // Delay after which to initialize, to avoid putting to much load on the
218  // database thread early on when Chrome is starting up.
219  static const int kInitDelayMs = 5 * 1000;
220
221  // Whether we're registered with the history service as a
222  // history::VisitDatabaseObserver.
223  bool is_visit_database_observer_;
224
225  base::CancelableTaskTracker history_db_tracker_;
226
227  scoped_ptr<std::vector<history::BriefVisitInfo> > visit_history_;
228
229  scoped_ptr<PrerenderProperties> current_prerender_;
230  scoped_ptr<PrerenderProperties> last_swapped_in_prerender_;
231
232  ScopedVector<PrerenderProperties> issued_prerenders_;
233
234  base::hash_set<int64> url_whitelist_;
235
236  base::WeakPtrFactory<PrerenderLocalPredictor> weak_factory_;
237
238  scoped_ptr<PrefetchList> prefetch_list_;
239
240  DISALLOW_COPY_AND_ASSIGN(PrerenderLocalPredictor);
241};
242
243}  // namespace prerender
244
245#endif  // CHROME_BROWSER_PRERENDER_PRERENDER_LOCAL_PREDICTOR_H_
246