Infobar material design refresh: bg color
[chromium-blink-merge.git] / components / precache / content / precache_manager.cc
blob218bfebd87de59948898bfd4f58617683e76c0b8
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_->backend_initialized()))
96 return AllowedType::PENDING;
98 // SyncService delegates to SyncPrefs, which must be called on the UI thread.
99 if (sync_service_->GetActiveDataTypes().Has(syncer::SESSIONS) &&
100 !sync_service_->GetEncryptedDataTypes().Has(syncer::SESSIONS))
101 return AllowedType::ALLOWED;
103 return AllowedType::DISALLOWED;
106 void PrecacheManager::StartPrecaching(
107 const PrecacheCompletionCallback& precache_completion_callback) {
108 DCHECK_CURRENTLY_ON(BrowserThread::UI);
110 if (is_precaching_) {
111 DLOG(WARNING) << "Cannot start precaching because precaching is already "
112 "in progress.";
113 return;
115 precache_completion_callback_ = precache_completion_callback;
117 if (history_service_ && IsInExperimentGroup()) {
118 is_precaching_ = true;
120 BrowserThread::PostTask(
121 BrowserThread::DB, FROM_HERE,
122 base::Bind(&PrecacheDatabase::DeleteExpiredPrecacheHistory,
123 precache_database_, base::Time::Now()));
125 // Request NumTopHosts() top hosts. Note that PrecacheFetcher is further
126 // bound by the value of PrecacheConfigurationSettings.top_sites_count, as
127 // retrieved from the server.
128 history_service_->TopHosts(
129 NumTopHosts(),
130 base::Bind(&PrecacheManager::OnHostsReceived, AsWeakPtr()));
131 } else if (history_service_ && IsInControlGroup()) {
132 // Set is_precaching_ so that the longer delay is placed between calls to
133 // TopHosts.
134 is_precaching_ = true;
136 // Calculate TopHosts solely for metrics purposes.
137 history_service_->TopHosts(
138 NumTopHosts(),
139 base::Bind(&PrecacheManager::OnHostsReceivedThenDone, AsWeakPtr()));
140 } else {
141 if (PrecachingAllowed() != AllowedType::PENDING) {
142 // We are not waiting on the sync backend to be initialized. The user
143 // either is not in the field trial, or does not have sync enabled.
144 // Pretend that precaching started, so that the PrecacheServiceLauncher
145 // doesn't try to start it again.
146 is_precaching_ = true;
149 OnDone();
153 void PrecacheManager::CancelPrecaching() {
154 DCHECK_CURRENTLY_ON(BrowserThread::UI);
156 if (!is_precaching_) {
157 // Do nothing if precaching is not in progress.
158 return;
160 is_precaching_ = false;
162 // Destroying the |precache_fetcher_| will cancel any fetch in progress.
163 precache_fetcher_.reset();
165 // Uninitialize the callback so that any scoped_refptrs in it are released.
166 precache_completion_callback_.Reset();
169 bool PrecacheManager::IsPrecaching() const {
170 DCHECK_CURRENTLY_ON(BrowserThread::UI);
171 return is_precaching_;
174 void PrecacheManager::RecordStatsForFetch(const GURL& url,
175 const GURL& referrer,
176 const base::TimeDelta& latency,
177 const base::Time& fetch_time,
178 int64 size,
179 bool was_cached) {
180 DCHECK_CURRENTLY_ON(BrowserThread::UI);
182 if (size == 0 || url.is_empty() || !url.SchemeIsHTTPOrHTTPS()) {
183 // Ignore empty responses, empty URLs, or URLs that aren't HTTP or HTTPS.
184 return;
187 if (!history_service_)
188 return;
190 history_service_->HostRankIfAvailable(
191 referrer,
192 base::Bind(&PrecacheManager::RecordStatsForFetchInternal, AsWeakPtr(),
193 url, latency, fetch_time, size, was_cached));
196 void PrecacheManager::RecordStatsForFetchInternal(
197 const GURL& url,
198 const base::TimeDelta& latency,
199 const base::Time& fetch_time,
200 int64 size,
201 bool was_cached,
202 int host_rank) {
203 if (is_precaching_) {
204 // Assume that precache is responsible for all requests made while
205 // precaching is currently in progress.
206 // TODO(sclittle): Make PrecacheFetcher explicitly mark precache-motivated
207 // fetches, and use that to determine whether or not a fetch was motivated
208 // by precaching.
209 BrowserThread::PostTask(
210 BrowserThread::DB, FROM_HERE,
211 base::Bind(&PrecacheDatabase::RecordURLPrefetch, precache_database_,
212 url, latency, fetch_time, size, was_cached));
213 } else {
214 bool is_connection_cellular =
215 net::NetworkChangeNotifier::IsConnectionCellular(
216 net::NetworkChangeNotifier::GetConnectionType());
218 BrowserThread::PostTask(
219 BrowserThread::DB, FROM_HERE,
220 base::Bind(&PrecacheDatabase::RecordURLNonPrefetch, precache_database_,
221 url, latency, fetch_time, size, was_cached, host_rank,
222 is_connection_cellular));
226 void PrecacheManager::ClearHistory() {
227 // PrecacheDatabase::ClearHistory must run after PrecacheDatabase::Init has
228 // finished. Using PostNonNestableTask guarantees this, by definition. See
229 // base::SequencedTaskRunner for details.
230 BrowserThread::PostNonNestableTask(
231 BrowserThread::DB, FROM_HERE,
232 base::Bind(&PrecacheDatabase::ClearHistory, precache_database_));
235 void PrecacheManager::Shutdown() {
236 CancelPrecaching();
239 void PrecacheManager::OnDone() {
240 DCHECK_CURRENTLY_ON(BrowserThread::UI);
242 precache_fetcher_.reset();
244 precache_completion_callback_.Run(!is_precaching_);
245 // Uninitialize the callback so that any scoped_refptrs in it are released.
246 precache_completion_callback_.Reset();
248 is_precaching_ = false;
251 void PrecacheManager::OnHostsReceived(
252 const history::TopHostsList& host_counts) {
253 DCHECK_CURRENTLY_ON(BrowserThread::UI);
255 if (!is_precaching_) {
256 // Don't start precaching if it was canceled while waiting for the list of
257 // hosts.
258 return;
261 std::vector<std::string> hosts;
262 for (const auto& host_count : host_counts)
263 hosts.push_back(host_count.first);
265 // Start precaching.
266 precache_fetcher_.reset(
267 new PrecacheFetcher(hosts, browser_context_->GetRequestContext(),
268 GURL(variations::GetVariationParamValue(
269 kPrecacheFieldTrialName, kConfigURLParam)),
270 variations::GetVariationParamValue(
271 kPrecacheFieldTrialName, kManifestURLPrefixParam),
272 this));
273 precache_fetcher_->Start();
276 void PrecacheManager::OnHostsReceivedThenDone(
277 const history::TopHostsList& host_counts) {
278 OnDone();
281 } // namespace precache