1// Copyright (c) 2011 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_MANAGER_H_
6#define CHROME_BROWSER_PRERENDER_PRERENDER_MANAGER_H_
7#pragma once
8
9#include <list>
10#include <map>
11#include <vector>
12
13#include "base/hash_tables.h"
14#include "base/memory/ref_counted.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/time.h"
17#include "base/timer.h"
18#include "chrome/browser/prerender/prerender_contents.h"
19#include "googleurl/src/gurl.h"
20
21class Profile;
22class TabContents;
23
24#if defined(COMPILER_GCC)
25
26namespace __gnu_cxx {
27template <>
28struct hash<TabContents*> {
29  std::size_t operator()(TabContents* value) const {
30    return reinterpret_cast<std::size_t>(value);
31  }
32};
33}
34
35#endif
36
37namespace prerender {
38
39// PrerenderManager is responsible for initiating and keeping prerendered
40// views of webpages.
41class PrerenderManager : public base::RefCountedThreadSafe<PrerenderManager> {
42 public:
43  // PrerenderManagerMode is used in a UMA_HISTOGRAM, so please do not
44  // add in the middle.
45  enum PrerenderManagerMode {
46    PRERENDER_MODE_DISABLED,
47    PRERENDER_MODE_ENABLED,
48    PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP,
49    PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP,
50    PRERENDER_MODE_MAX
51  };
52
53  // Owned by a Profile object for the lifetime of the profile.
54  explicit PrerenderManager(Profile* profile);
55
56  // Preloads the URL supplied.  alias_urls indicates URLs that redirect
57  // to the same URL to be preloaded. Returns true if the URL was added,
58  // false if it was not.
59  bool AddPreload(const GURL& url, const std::vector<GURL>& alias_urls,
60                  const GURL& referrer);
61
62  void AddPendingPreload(const std::pair<int, int>& child_route_id_pair,
63                         const GURL& url,
64                         const std::vector<GURL>& alias_urls,
65                         const GURL& referrer);
66
67  // For a given TabContents that wants to navigate to the URL supplied,
68  // determines whether a preloaded version of the URL can be used,
69  // and substitutes the prerendered RVH into the TabContents.  Returns
70  // whether or not a prerendered RVH could be used or not.
71  bool MaybeUsePreloadedPage(TabContents* tc, const GURL& url);
72
73  // Allows PrerenderContents to remove itself when prerendering should
74  // be cancelled.
75  void RemoveEntry(PrerenderContents* entry);
76
77  // Retrieves the PrerenderContents object for the specified URL, if it
78  // has been prerendered.  The caller will then have ownership of the
79  // PrerenderContents object and is responsible for freeing it.
80  // Returns NULL if the specified URL has not been prerendered.
81  PrerenderContents* GetEntry(const GURL& url);
82
83  // Records the perceived page load time for a page - effectively the time from
84  // when the user navigates to a page to when it finishes loading. The actual
85  // load may have started prior to navigation due to prerender hints.
86  // This must be called on the UI thread.
87  static void RecordPerceivedPageLoadTime(
88      base::TimeDelta perceived_page_load_time,
89      TabContents* tab_contents);
90
91  // Records the time from when a page starts prerendering to when the user
92  // navigates to it. This must be called on the UI thread.
93  void RecordTimeUntilUsed(base::TimeDelta time_until_used);
94
95  base::TimeDelta max_prerender_age() const { return max_prerender_age_; }
96  void set_max_prerender_age(base::TimeDelta td) { max_prerender_age_ = td; }
97  unsigned int max_elements() const { return max_elements_; }
98  void set_max_elements(unsigned int num) { max_elements_ = num; }
99
100  // Returns whether prerendering is currently enabled for this manager.
101  // Must be called on the UI thread.
102  bool is_enabled() const;
103
104  // Set whether prerendering is currently enabled for this manager.
105  // Must be called on the UI thread.
106  // If |enabled| is false, existing prerendered pages will still persist until
107  // they time out, but new ones will not be generated.
108  void set_enabled(bool enabled);
109
110  static PrerenderManagerMode GetMode();
111  static void SetMode(PrerenderManagerMode mode);
112  static bool IsPrerenderingPossible();
113  static bool IsControlGroup();
114
115  // The following static method can be called from any thread, but will result
116  // in posting a task to the UI thread if we are not in the UI thread.
117  static void RecordPrefetchTagObserved();
118
119  // Maintaining and querying the set of TabContents belonging to this
120  // PrerenderManager that are currently showing prerendered pages.
121  void MarkTabContentsAsPrerendered(TabContents* tc);
122  void MarkTabContentsAsWouldBePrerendered(TabContents* tc);
123  void MarkTabContentsAsNotPrerendered(TabContents* tc);
124  bool IsTabContentsPrerendered(TabContents* tc) const;
125  bool WouldTabContentsBePrerendered(TabContents* tc) const;
126
127  // Extracts a urlencoded URL stored in a url= query parameter from a URL
128  // supplied, if available, and stores it in alias_url.  Returns whether or not
129  // the operation succeeded (i.e. a valid URL was found).
130  static bool MaybeGetQueryStringBasedAliasURL(const GURL& url,
131                                               GURL* alias_url);
132
133 protected:
134  struct PendingContentsData;
135
136  virtual ~PrerenderManager();
137
138  void SetPrerenderContentsFactory(
139      PrerenderContents::Factory* prerender_contents_factory);
140  bool rate_limit_enabled_;
141
142  PendingContentsData* FindPendingEntry(const GURL& url);
143
144 private:
145  // Test that needs needs access to internal functions.
146  friend class PrerenderBrowserTest;
147
148  friend class base::RefCountedThreadSafe<PrerenderManager>;
149
150  struct PrerenderContentsData;
151
152  // Starts and stops scheduling periodic cleanups, respectively.
153  void StartSchedulingPeriodicCleanups();
154  void StopSchedulingPeriodicCleanups();
155
156  // Deletes stale prerendered PrerenderContents.
157  // Also identifies and kills PrerenderContents that use too much
158  // resources.
159  void PeriodicCleanup();
160
161  bool IsPrerenderElementFresh(const base::Time start) const;
162  void DeleteOldEntries();
163  virtual base::Time GetCurrentTime() const;
164  virtual base::TimeTicks GetCurrentTimeTicks() const;
165  virtual PrerenderContents* CreatePrerenderContents(
166      const GURL& url,
167      const std::vector<GURL>& alias_urls,
168      const GURL& referrer);
169
170  // Finds the specified PrerenderContents and returns it, if it exists.
171  // Returns NULL otherwise.  Unlike GetEntry, the PrerenderManager maintains
172  // ownership of the PrerenderContents.
173  PrerenderContents* FindEntry(const GURL& url);
174
175  static bool WithinWindow();
176
177  static void RecordPrefetchTagObservedOnUIThread();
178
179  // Called when removing a preload to ensure we clean up any pending preloads
180  // that might remain in the map.
181  void RemovePendingPreload(PrerenderContents* entry);
182
183  bool DoesRateLimitAllowPrerender() const;
184
185  // Specifies whether prerendering is currently enabled for this
186  // manager. The value can change dynamically during the lifetime
187  // of the PrerenderManager.
188  bool enabled_;
189
190  Profile* profile_;
191
192  base::TimeDelta max_prerender_age_;
193  unsigned int max_elements_;
194
195  // List of prerendered elements.
196  std::list<PrerenderContentsData> prerender_list_;
197
198  // Set of TabContents which are currently displaying a prerendered page.
199  base::hash_set<TabContents*> prerendered_tc_set_;
200
201  // Set of TabContents which would be displaying a prerendered page
202  // (for the control group).
203  base::hash_set<TabContents*> would_be_prerendered_tc_set_;
204
205  // Map of child/route id pairs to pending prerender data.
206  typedef std::map<std::pair<int, int>, std::vector<PendingContentsData> >
207      PendingPrerenderList;
208  PendingPrerenderList pending_prerender_list_;
209
210  // Default maximum permitted elements to prerender.
211  static const unsigned int kDefaultMaxPrerenderElements = 1;
212
213  // Default maximum age a prerendered element may have, in seconds.
214  static const int kDefaultMaxPrerenderAgeSeconds = 20;
215
216  // Time window for which we will record windowed PLT's from the last
217  // observed link rel=prefetch tag.
218  static const int kWindowDurationSeconds = 30;
219
220  // Time interval at which periodic cleanups are performed.
221  static const int kPeriodicCleanupIntervalMs = 1000;
222
223  // Time interval before a new prerender is allowed.
224  static const int kMinTimeBetweenPrerendersMs = 500;
225
226  scoped_ptr<PrerenderContents::Factory> prerender_contents_factory_;
227
228  static PrerenderManagerMode mode_;
229
230  // The time when we last saw a prefetch request coming from a renderer.
231  // This is used to record perceived PLT's for a certain amount of time
232  // from the point that we last saw a <link rel=prefetch> tag.
233  // This static variable should only be modified on the UI thread.
234  static base::TimeTicks last_prefetch_seen_time_;
235
236  // A count of how many prerenders we do per session. Initialized to 0 then
237  // incremented and emitted to a histogram on each successful prerender.
238  static int prerenders_per_session_count_;
239
240  // RepeatingTimer to perform periodic cleanups of pending prerendered
241  // pages.
242  base::RepeatingTimer<PrerenderManager> repeating_timer_;
243
244  // Track time of last prerender to limit prerender spam.
245  base::TimeTicks last_prerender_start_time_;
246
247  DISALLOW_COPY_AND_ASSIGN(PrerenderManager);
248};
249
250}  // prerender
251
252#endif  // CHROME_BROWSER_PRERENDER_PRERENDER_MANAGER_H_
253