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"
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/time/time.h"
18 #include "components/history/core/browser/history_service.h"
19 #include "components/precache/core/precache_database.h"
20 #include "components/precache/core/precache_switches.h"
21 #include "components/sync_driver/sync_service.h"
22 #include "components/variations/variations_associated_data.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "net/base/network_change_notifier.h"
27 using content::BrowserThread
;
31 const char kPrecacheFieldTrialName
[] = "Precache";
32 const char kPrecacheFieldTrialEnabledGroup
[] = "Enabled";
33 const char kManifestURLPrefixParam
[] = "manifest_url_prefix";
34 const int kNumTopHosts
= 100;
44 PrecacheManager::PrecacheManager(
45 content::BrowserContext
* browser_context
,
46 const sync_driver::SyncService
* const sync_service
,
47 const history::HistoryService
* const history_service
)
48 : browser_context_(browser_context
),
49 sync_service_(sync_service
),
50 history_service_(history_service
),
51 precache_database_(new PrecacheDatabase()),
52 is_precaching_(false) {
53 base::FilePath
db_path(browser_context_
->GetPath().Append(
54 base::FilePath(FILE_PATH_LITERAL("PrecacheDatabase"))));
56 BrowserThread::PostTask(
57 BrowserThread::DB
, FROM_HERE
,
58 base::Bind(base::IgnoreResult(&PrecacheDatabase::Init
),
59 precache_database_
, db_path
));
62 PrecacheManager::~PrecacheManager() {}
64 bool PrecacheManager::ShouldRun() const {
65 // Verify PrecachingAllowed() before calling IsPrecachingEnabled(). This is
66 // because field trials are only assigned when requested. This allows us to
67 // create Control and Experiment groups that are limited to users for whom
68 // PrecachingAllowed() is true, thus accentuating the impact of precaching.
69 return PrecachingAllowed() == AllowedType::ALLOWED
&& IsPrecachingEnabled();
72 bool PrecacheManager::WouldRun() const {
73 return PrecachingAllowed() == AllowedType::ALLOWED
;
77 bool PrecacheManager::IsPrecachingEnabled() {
78 return base::FieldTrialList::FindFullName(kPrecacheFieldTrialName
) ==
79 kPrecacheFieldTrialEnabledGroup
||
80 base::CommandLine::ForCurrentProcess()->HasSwitch(
81 switches::kEnablePrecache
);
84 PrecacheManager::AllowedType
PrecacheManager::PrecachingAllowed() const {
85 if (!(sync_service_
&& sync_service_
->backend_initialized()))
86 return AllowedType::PENDING
;
88 // SyncService delegates to SyncPrefs, which must be called on the UI thread.
89 if (sync_service_
->GetActiveDataTypes().Has(syncer::SESSIONS
) &&
90 !sync_service_
->GetEncryptedDataTypes().Has(syncer::SESSIONS
))
91 return AllowedType::ALLOWED
;
93 return AllowedType::DISALLOWED
;
96 void PrecacheManager::StartPrecaching(
97 const PrecacheCompletionCallback
& precache_completion_callback
) {
98 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
100 if (is_precaching_
) {
101 DLOG(WARNING
) << "Cannot start precaching because precaching is already "
105 precache_completion_callback_
= precache_completion_callback
;
107 if (history_service_
&& ShouldRun()) {
108 is_precaching_
= true;
110 BrowserThread::PostTask(
111 BrowserThread::DB
, FROM_HERE
,
112 base::Bind(&PrecacheDatabase::DeleteExpiredPrecacheHistory
,
113 precache_database_
, base::Time::Now()));
115 // Request NumTopHosts() top hosts. Note that PrecacheFetcher is further
116 // bound by the value of PrecacheConfigurationSettings.top_sites_count, as
117 // retrieved from the server.
118 history_service_
->TopHosts(
120 base::Bind(&PrecacheManager::OnHostsReceived
, AsWeakPtr()));
122 if (PrecachingAllowed() != AllowedType::PENDING
) {
123 // We are not waiting on the sync backend to be initialized. The user
124 // either is not in the field trial, or does not have sync enabled.
125 // Pretend that precaching started, so that the PrecacheServiceLauncher
126 // doesn't try to start it again.
127 is_precaching_
= true;
134 void PrecacheManager::CancelPrecaching() {
135 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
137 if (!is_precaching_
) {
138 // Do nothing if precaching is not in progress.
141 is_precaching_
= false;
143 // Destroying the |precache_fetcher_| will cancel any fetch in progress.
144 precache_fetcher_
.reset();
146 // Uninitialize the callback so that any scoped_refptrs in it are released.
147 precache_completion_callback_
.Reset();
150 bool PrecacheManager::IsPrecaching() const {
151 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
152 return is_precaching_
;
155 void PrecacheManager::RecordStatsForFetch(const GURL
& url
,
156 const base::TimeDelta
& latency
,
157 const base::Time
& fetch_time
,
160 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
162 if (size
== 0 || url
.is_empty() || !url
.SchemeIsHTTPOrHTTPS()) {
163 // Ignore empty responses, empty URLs, or URLs that aren't HTTP or HTTPS.
167 if (is_precaching_
) {
168 // Assume that precache is responsible for all requests made while
169 // precaching is currently in progress.
170 // TODO(sclittle): Make PrecacheFetcher explicitly mark precache-motivated
171 // fetches, and use that to determine whether or not a fetch was motivated
173 BrowserThread::PostTask(
174 BrowserThread::DB
, FROM_HERE
,
175 base::Bind(&PrecacheDatabase::RecordURLPrefetch
, precache_database_
,
176 url
, latency
, fetch_time
, size
, was_cached
));
178 bool is_connection_cellular
=
179 net::NetworkChangeNotifier::IsConnectionCellular(
180 net::NetworkChangeNotifier::GetConnectionType());
182 BrowserThread::PostTask(
183 BrowserThread::DB
, FROM_HERE
,
184 base::Bind(&PrecacheDatabase::RecordURLNonPrefetch
, precache_database_
,
185 url
, latency
, fetch_time
, size
, was_cached
,
186 is_connection_cellular
));
190 void PrecacheManager::ClearHistory() {
191 // PrecacheDatabase::ClearHistory must run after PrecacheDatabase::Init has
192 // finished. Using PostNonNestableTask guarantees this, by definition. See
193 // base::SequencedTaskRunner for details.
194 BrowserThread::PostNonNestableTask(
195 BrowserThread::DB
, FROM_HERE
,
196 base::Bind(&PrecacheDatabase::ClearHistory
, precache_database_
));
199 void PrecacheManager::Shutdown() {
203 void PrecacheManager::OnDone() {
204 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
206 precache_fetcher_
.reset();
208 precache_completion_callback_
.Run(!is_precaching_
);
209 // Uninitialize the callback so that any scoped_refptrs in it are released.
210 precache_completion_callback_
.Reset();
212 is_precaching_
= false;
215 void PrecacheManager::OnHostsReceived(
216 const history::TopHostsList
& host_counts
) {
217 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
219 if (!is_precaching_
) {
220 // Don't start precaching if it was canceled while waiting for the list of
225 std::vector
<std::string
> hosts
;
226 for (const auto& host_count
: host_counts
)
227 hosts
.push_back(host_count
.first
);
230 precache_fetcher_
.reset(
231 new PrecacheFetcher(hosts
, browser_context_
->GetRequestContext(),
232 variations::GetVariationParamValue(
233 kPrecacheFieldTrialName
, kManifestURLPrefixParam
),
235 precache_fetcher_
->Start();
238 } // namespace precache