Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / precache / content / precache_manager.cc
blob3e91b9204364906c6d9c241514d0b58d190a258e
1 // Copyright 2013 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.
5 #include "components/precache/content/precache_manager.h"
7 #include <string>
8 #include <utility>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/strings/string_util.h"
18 #include "base/time/time.h"
19 #include "components/history/core/browser/history_service.h"
20 #include "components/precache/core/precache_database.h"
21 #include "components/precache/core/precache_switches.h"
22 #include "components/sync_driver/sync_service.h"
23 #include "components/variations/variations_associated_data.h"
24 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "net/base/network_change_notifier.h"
28 using content::BrowserThread;
30 namespace {
32 const char kPrecacheFieldTrialName[] = "Precache";
33 const char kPrecacheFieldTrialEnabledGroup[] = "Enabled";
34 const char kPrecacheFieldTrialControlGroup[] = "Control";
35 const char kConfigURLParam[] = "config_url";
36 const char kManifestURLPrefixParam[] = "manifest_url_prefix";
37 const int kNumTopHosts = 100;
39 } // namespace
41 namespace precache {
43 int NumTopHosts() {
44 return kNumTopHosts;
47 PrecacheManager::PrecacheManager(
48 content::BrowserContext* browser_context,
49 const sync_driver::SyncService* const sync_service,
50 const history::HistoryService* const history_service)
51 : browser_context_(browser_context),
52 sync_service_(sync_service),
53 history_service_(history_service),
54 precache_database_(new PrecacheDatabase()),
55 is_precaching_(false) {
56 base::FilePath db_path(browser_context_->GetPath().Append(
57 base::FilePath(FILE_PATH_LITERAL("PrecacheDatabase"))));
59 BrowserThread::PostTask(
60 BrowserThread::DB, FROM_HERE,
61 base::Bind(base::IgnoreResult(&PrecacheDatabase::Init),
62 precache_database_, db_path));
65 PrecacheManager::~PrecacheManager() {}
67 bool PrecacheManager::IsInExperimentGroup() const {
68 // Verify IsPrecachingAllowed() before calling FieldTrialList::FindFullName().
69 // This is because field trials are only assigned when requested. This allows
70 // us to create Control and Experiment groups that are limited to users for
71 // whom PrecachingAllowed() is true, thus accentuating the impact of
72 // precaching.
73 return IsPrecachingAllowed() &&
74 (base::StartsWith(
75 base::FieldTrialList::FindFullName(kPrecacheFieldTrialName),
76 kPrecacheFieldTrialEnabledGroup, base::CompareCase::SENSITIVE) ||
77 base::CommandLine::ForCurrentProcess()->HasSwitch(
78 switches::kEnablePrecache));
81 bool PrecacheManager::IsInControlGroup() const {
82 // Verify IsPrecachingAllowed() before calling FindFullName(). See
83 // PrecacheManager::IsInExperimentGroup() for an explanation of why.
84 return IsPrecachingAllowed() &&
85 base::StartsWith(
86 base::FieldTrialList::FindFullName(kPrecacheFieldTrialName),
87 kPrecacheFieldTrialControlGroup, base::CompareCase::SENSITIVE);
90 bool PrecacheManager::IsPrecachingAllowed() const {
91 return PrecachingAllowed() == AllowedType::ALLOWED;
94 PrecacheManager::AllowedType PrecacheManager::PrecachingAllowed() const {
95 if (!(sync_service_ && sync_service_->IsBackendInitialized()))
96 return AllowedType::PENDING;
98 // SyncService delegates to SyncPrefs, which must be called on the UI thread.
99 if (history_service_ &&
100 sync_service_->GetActiveDataTypes().Has(syncer::SESSIONS) &&
101 !sync_service_->GetEncryptedDataTypes().Has(syncer::SESSIONS))
102 return AllowedType::ALLOWED;
104 return AllowedType::DISALLOWED;
107 void PrecacheManager::StartPrecaching(
108 const PrecacheCompletionCallback& precache_completion_callback) {
109 DCHECK_CURRENTLY_ON(BrowserThread::UI);
111 if (is_precaching_) {
112 DLOG(WARNING) << "Cannot start precaching because precaching is already "
113 "in progress.";
114 return;
116 precache_completion_callback_ = precache_completion_callback;
118 if (IsInExperimentGroup()) {
119 is_precaching_ = true;
121 BrowserThread::PostTask(
122 BrowserThread::DB, FROM_HERE,
123 base::Bind(&PrecacheDatabase::DeleteExpiredPrecacheHistory,
124 precache_database_, base::Time::Now()));
126 // Request NumTopHosts() top hosts. Note that PrecacheFetcher is further
127 // bound by the value of PrecacheConfigurationSettings.top_sites_count, as
128 // retrieved from the server.
129 history_service_->TopHosts(
130 NumTopHosts(),
131 base::Bind(&PrecacheManager::OnHostsReceived, AsWeakPtr()));
132 } else if (IsInControlGroup()) {
133 // Set is_precaching_ so that the longer delay is placed between calls to
134 // TopHosts.
135 is_precaching_ = true;
137 // Calculate TopHosts solely for metrics purposes.
138 history_service_->TopHosts(
139 NumTopHosts(),
140 base::Bind(&PrecacheManager::OnHostsReceivedThenDone, AsWeakPtr()));
141 } else {
142 if (PrecachingAllowed() != AllowedType::PENDING) {
143 // We are not waiting on the sync backend to be initialized. The user
144 // either is not in the field trial, or does not have sync enabled.
145 // Pretend that precaching started, so that the PrecacheServiceLauncher
146 // doesn't try to start it again.
147 is_precaching_ = true;
150 OnDone();
154 void PrecacheManager::CancelPrecaching() {
155 DCHECK_CURRENTLY_ON(BrowserThread::UI);
157 if (!is_precaching_) {
158 // Do nothing if precaching is not in progress.
159 return;
161 is_precaching_ = false;
163 // Destroying the |precache_fetcher_| will cancel any fetch in progress.
164 precache_fetcher_.reset();
166 // Uninitialize the callback so that any scoped_refptrs in it are released.
167 precache_completion_callback_.Reset();
170 bool PrecacheManager::IsPrecaching() const {
171 DCHECK_CURRENTLY_ON(BrowserThread::UI);
172 return is_precaching_;
175 void PrecacheManager::RecordStatsForFetch(const GURL& url,
176 const GURL& referrer,
177 const base::TimeDelta& latency,
178 const base::Time& fetch_time,
179 int64 size,
180 bool was_cached) {
181 DCHECK_CURRENTLY_ON(BrowserThread::UI);
183 if (size == 0 || url.is_empty() || !url.SchemeIsHTTPOrHTTPS()) {
184 // Ignore empty responses, empty URLs, or URLs that aren't HTTP or HTTPS.
185 return;
188 if (!history_service_)
189 return;
191 history_service_->HostRankIfAvailable(
192 referrer,
193 base::Bind(&PrecacheManager::RecordStatsForFetchInternal, AsWeakPtr(),
194 url, latency, fetch_time, size, was_cached));
197 void PrecacheManager::RecordStatsForFetchInternal(
198 const GURL& url,
199 const base::TimeDelta& latency,
200 const base::Time& fetch_time,
201 int64 size,
202 bool was_cached,
203 int host_rank) {
204 if (is_precaching_) {
205 // Assume that precache is responsible for all requests made while
206 // precaching is currently in progress.
207 // TODO(sclittle): Make PrecacheFetcher explicitly mark precache-motivated
208 // fetches, and use that to determine whether or not a fetch was motivated
209 // by precaching.
210 BrowserThread::PostTask(
211 BrowserThread::DB, FROM_HERE,
212 base::Bind(&PrecacheDatabase::RecordURLPrefetch, precache_database_,
213 url, latency, fetch_time, size, was_cached));
214 } else {
215 bool is_connection_cellular =
216 net::NetworkChangeNotifier::IsConnectionCellular(
217 net::NetworkChangeNotifier::GetConnectionType());
219 BrowserThread::PostTask(
220 BrowserThread::DB, FROM_HERE,
221 base::Bind(&PrecacheDatabase::RecordURLNonPrefetch, precache_database_,
222 url, latency, fetch_time, size, was_cached, host_rank,
223 is_connection_cellular));
227 void PrecacheManager::ClearHistory() {
228 // PrecacheDatabase::ClearHistory must run after PrecacheDatabase::Init has
229 // finished. Using PostNonNestableTask guarantees this, by definition. See
230 // base::SequencedTaskRunner for details.
231 BrowserThread::PostNonNestableTask(
232 BrowserThread::DB, FROM_HERE,
233 base::Bind(&PrecacheDatabase::ClearHistory, precache_database_));
236 void PrecacheManager::Shutdown() {
237 CancelPrecaching();
240 void PrecacheManager::OnDone() {
241 DCHECK_CURRENTLY_ON(BrowserThread::UI);
243 precache_fetcher_.reset();
245 precache_completion_callback_.Run(!is_precaching_);
246 // Uninitialize the callback so that any scoped_refptrs in it are released.
247 precache_completion_callback_.Reset();
249 is_precaching_ = false;
252 void PrecacheManager::OnHostsReceived(
253 const history::TopHostsList& host_counts) {
254 DCHECK_CURRENTLY_ON(BrowserThread::UI);
256 if (!is_precaching_) {
257 // Don't start precaching if it was canceled while waiting for the list of
258 // hosts.
259 return;
262 std::vector<std::string> hosts;
263 for (const auto& host_count : host_counts)
264 hosts.push_back(host_count.first);
266 // Start precaching.
267 precache_fetcher_.reset(
268 new PrecacheFetcher(hosts, browser_context_->GetRequestContext(),
269 GURL(variations::GetVariationParamValue(
270 kPrecacheFieldTrialName, kConfigURLParam)),
271 variations::GetVariationParamValue(
272 kPrecacheFieldTrialName, kManifestURLPrefixParam),
273 this));
274 precache_fetcher_->Start();
277 void PrecacheManager::OnHostsReceivedThenDone(
278 const history::TopHostsList& host_counts) {
279 OnDone();
282 } // namespace precache