OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/password_manager/core/browser/affiliation_backend.h" | 5 #include "components/password_manager/core/browser/affiliation_backend.h" |
6 | 6 |
7 #include <stdint.h> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/location.h" | |
7 #include "base/task_runner.h" | 11 #include "base/task_runner.h" |
12 #include "base/time/clock.h" | |
13 #include "base/time/time.h" | |
14 #include "components/password_manager/core/browser/affiliation_database.h" | |
15 #include "components/password_manager/core/browser/affiliation_fetcher.h" | |
16 #include "net/url_request/url_request_context_getter.h" | |
8 | 17 |
9 namespace password_manager { | 18 namespace password_manager { |
10 | 19 namespace { |
11 AffiliationBackend::AffiliationBackend() { | 20 |
21 // The duration after which cached affiliation data is considered stale and will | |
22 // not be used to serve requests any longer. | |
23 const int64_t kCacheHardExpiryDurationInSeconds = 24 * 3600; | |
Mike West
2015/02/02 15:19:44
Nit: "24 * 60 * 60" seems slightly more common in
engedy
2015/02/02 17:00:12
Done. I have replaced this with kCacheHardExpiryDu
| |
24 | |
25 // RequestInfo ---------------------------------------------------------------- | |
26 | |
27 // Encapsulates the details of a pending GetAffiliations() request. | |
28 struct RequestInfo { | |
29 RequestInfo(); | |
30 ~RequestInfo(); | |
31 | |
32 AffiliationService::ResultCallback callback; | |
33 scoped_refptr<base::TaskRunner> callback_task_runner; | |
34 }; | |
35 | |
36 RequestInfo::RequestInfo() { | |
37 } | |
38 | |
39 RequestInfo::~RequestInfo() { | |
40 } | |
Mike West
2015/02/02 15:19:44
Nit: Any reason not to inline these? FacetManager
engedy
2015/02/02 15:33:15
Vaclav prefers we do not inline in production code
vabr (Chromium)
2015/02/02 15:37:37
http://dev.chromium.org/developers/coding-style/cp
Garrett Casto
2015/02/03 08:20:37
Further down on that page it talks about how in th
engedy
2015/02/03 10:49:22
Done.
| |
41 | |
42 } // namespace | |
43 | |
44 // AffiliationBackend::FacetManager ------------------------------------------- | |
45 | |
46 // Manages and performs ongoing tasks concerning a single facet. | |
47 class AffiliationBackend::FacetManager { | |
48 public: | |
49 // The |backend| must outlive this object. | |
50 FacetManager(AffiliationBackend* backend, const FacetURI& facet_uri); | |
51 ~FacetManager(); | |
52 | |
53 // Called when |affiliation| information regarding this facet has just been | |
54 // fetched from the Affiliation API. | |
55 void OnFetchSucceeded(const AffiliatedFacetsWithUpdateTime& affiliation); | |
56 | |
57 // Returns whether this instance has becomes redundant, that is, it has no | |
58 // more meaningful state than a newly created instance would have. | |
59 bool CanBeDiscarded() const; | |
60 | |
61 // Returns whether or not affiliation information relating to this facet needs | |
62 // to be fetched right now. | |
63 bool DoesRequireFetch() const; | |
64 | |
65 // Facet-specific implementations for methods in AffiliationService of the | |
66 // same name. See documentation in affiliation_service.h for details: | |
67 void GetAffiliations(bool cached_only, const RequestInfo& request_info); | |
68 | |
69 private: | |
70 // Returns the time when cached data for this facet will become stale. | |
71 base::Time GetCacheHardExpiryTime() const; | |
72 | |
73 // Returns whether or not the cache has fresh data for this facet. | |
74 bool IsCachedDataFresh() const; | |
75 | |
76 // Posts the callback of the request described by |request_info| with success. | |
77 static void ServeRequestWithSuccess(const RequestInfo& request_info, | |
78 const AffiliatedFacets& affiliation); | |
79 | |
80 // Posts the callback of the request described by |request_info| with failure. | |
81 static void ServeRequestWithFailure(const RequestInfo& request_info); | |
82 | |
83 AffiliationBackend* backend_; | |
84 FacetURI facet_uri_; | |
85 | |
86 // The last time affiliation information was fetched for this facet, i.e. the | |
87 // freshness of the data in the database. If there is no corresponding data in | |
88 // in the database, this will contain something sufficiently far away in the | |
89 // past to be considered stale for sure. Otherwise, the last_update_time in | |
90 // the database should match this value; it is only cached to reduce disk I/O. | |
91 base::Time last_update_time_; | |
92 | |
93 // Contains information about the GetAffiliations() requests that are waiting | |
94 // for the result of looking up this facet. | |
95 std::vector<RequestInfo> pending_requests_; | |
96 | |
97 DISALLOW_COPY_AND_ASSIGN(FacetManager); | |
98 }; | |
99 | |
100 AffiliationBackend::FacetManager::FacetManager(AffiliationBackend* backend, | |
101 const FacetURI& facet_uri) | |
102 : backend_(backend), | |
103 facet_uri_(facet_uri), | |
104 last_update_time_(backend_->ReadLastUpdateTimeFromDatabase(facet_uri)) { | |
105 } | |
106 | |
107 AffiliationBackend::FacetManager::~FacetManager() { | |
108 // The manager will only be destroyed while there are pending requests if the | |
109 // entire backend is going. Call failure on pending requests in this case. | |
110 for (const auto& request_info : pending_requests_) | |
111 ServeRequestWithFailure(request_info); | |
112 } | |
113 | |
114 void AffiliationBackend::FacetManager::GetAffiliations( | |
115 bool cached_only, | |
116 const RequestInfo& request_info) { | |
117 if (IsCachedDataFresh()) { | |
118 AffiliatedFacetsWithUpdateTime affiliation; | |
119 if (!backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliation)) | |
120 NOTIMPLEMENTED(); | |
121 DCHECK_EQ(affiliation.last_update_time, last_update_time_) << facet_uri_; | |
122 ServeRequestWithSuccess(request_info, affiliation.facets); | |
123 } else if (cached_only) { | |
124 ServeRequestWithFailure(request_info); | |
125 } else { | |
126 pending_requests_.push_back(request_info); | |
127 backend_->SignalNeedNetworkRequest(); | |
128 } | |
129 } | |
130 | |
131 void AffiliationBackend::FacetManager::OnFetchSucceeded( | |
132 const AffiliatedFacetsWithUpdateTime& affiliation) { | |
133 last_update_time_ = affiliation.last_update_time; | |
134 DCHECK(IsCachedDataFresh()) << facet_uri_; | |
135 for (const auto& request_info : pending_requests_) | |
136 ServeRequestWithSuccess(request_info, affiliation.facets); | |
137 pending_requests_.clear(); | |
138 } | |
139 | |
140 bool AffiliationBackend::FacetManager::CanBeDiscarded() const { | |
141 return pending_requests_.empty(); | |
142 } | |
143 | |
144 bool AffiliationBackend::FacetManager::DoesRequireFetch() const { | |
145 return !pending_requests_.empty() && !IsCachedDataFresh(); | |
146 } | |
147 | |
148 base::Time AffiliationBackend::FacetManager::GetCacheHardExpiryTime() const { | |
149 return last_update_time_ + | |
150 base::TimeDelta::FromSeconds(kCacheHardExpiryDurationInSeconds); | |
151 } | |
152 | |
153 bool AffiliationBackend::FacetManager::IsCachedDataFresh() const { | |
154 return backend_->GetCurrentTime() < GetCacheHardExpiryTime(); | |
155 } | |
156 | |
157 // static | |
158 void AffiliationBackend::FacetManager::ServeRequestWithSuccess( | |
159 const RequestInfo& request_info, | |
160 const AffiliatedFacets& affiliation) { | |
161 request_info.callback_task_runner->PostTask( | |
162 FROM_HERE, base::Bind(request_info.callback, affiliation, true)); | |
163 } | |
164 | |
165 // static | |
166 void AffiliationBackend::FacetManager::ServeRequestWithFailure( | |
167 const RequestInfo& request_info) { | |
168 request_info.callback_task_runner->PostTask( | |
169 FROM_HERE, base::Bind(request_info.callback, AffiliatedFacets(), false)); | |
170 } | |
171 | |
172 // AffiliationBackend --------------------------------------------------------- | |
173 | |
174 AffiliationBackend::AffiliationBackend( | |
175 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, | |
176 scoped_ptr<base::Clock> time_source) | |
177 : request_context_getter_(request_context_getter), | |
178 clock_(time_source.Pass()), | |
179 weak_ptr_factory_(this) { | |
12 } | 180 } |
13 | 181 |
14 AffiliationBackend::~AffiliationBackend() { | 182 AffiliationBackend::~AffiliationBackend() { |
15 } | 183 } |
16 | 184 |
17 void AffiliationBackend::Initialize() { | 185 void AffiliationBackend::Initialize(const base::FilePath& db_path) { |
Mike West
2015/02/02 15:19:44
Should initialize be called on the same thread as
engedy
2015/02/02 17:00:12
As discussed, I have added a thread checker in a s
| |
18 NOTIMPLEMENTED(); | 186 cache_.reset(new AffiliationDatabase()); |
187 if (!cache_->Init(db_path)) | |
188 NOTIMPLEMENTED(); | |
19 } | 189 } |
20 | 190 |
21 void AffiliationBackend::GetAffiliations( | 191 void AffiliationBackend::GetAffiliations( |
22 const FacetURI& facet_uri, | 192 const FacetURI& facet_uri, |
23 bool cached_only, | 193 bool cached_only, |
24 const AffiliationService::ResultCallback& callback, | 194 const AffiliationService::ResultCallback& callback, |
25 scoped_refptr<base::TaskRunner> callback_task_runner) { | 195 const scoped_refptr<base::TaskRunner>& callback_task_runner) { |
26 NOTIMPLEMENTED(); | 196 if (!facet_managers_.contains(facet_uri)) { |
197 scoped_ptr<FacetManager> new_manager(new FacetManager(this, facet_uri)); | |
198 facet_managers_.add(facet_uri, new_manager.Pass()); | |
199 } | |
200 | |
201 FacetManager* facet_manager = facet_managers_.get(facet_uri); | |
Mike West
2015/02/02 15:19:45
`.add()` can fail, can't it? OOM, etc. Perhaps `DC
engedy
2015/02/02 17:00:12
Do I understand correctly that you prefer this bec
Mike West
2015/02/03 09:59:02
Right. The value of a DCHECK is that you crash pre
| |
202 RequestInfo request_info; | |
203 request_info.callback = callback; | |
204 request_info.callback_task_runner = callback_task_runner; | |
205 facet_manager->GetAffiliations(cached_only, request_info); | |
206 | |
207 if (facet_manager->CanBeDiscarded()) | |
208 facet_managers_.erase(facet_uri); | |
27 } | 209 } |
28 | 210 |
29 void AffiliationBackend::Prefetch(const FacetURI& facet_uri, | 211 void AffiliationBackend::Prefetch(const FacetURI& facet_uri, |
30 const base::Time& keep_fresh_until) { | 212 const base::Time& keep_fresh_until) { |
31 NOTIMPLEMENTED(); | 213 NOTIMPLEMENTED(); |
32 } | 214 } |
33 | 215 |
34 void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri, | 216 void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri, |
35 const base::Time& keep_fresh_until) { | 217 const base::Time& keep_fresh_until) { |
36 NOTIMPLEMENTED(); | 218 NOTIMPLEMENTED(); |
37 } | 219 } |
38 | 220 |
39 void AffiliationBackend::TrimCache() { | 221 void AffiliationBackend::TrimCache() { |
40 NOTIMPLEMENTED(); | 222 NOTIMPLEMENTED(); |
41 } | 223 } |
42 | 224 |
225 void AffiliationBackend::SendNetworkRequest() { | |
226 DCHECK(!fetcher_); | |
Mike West
2015/02/02 15:19:44
DCHECK that you have more than 0 managers? I guess
engedy
2015/02/02 17:00:11
Yes, I'd prefer not checking that separately.
| |
227 | |
228 std::vector<FacetURI> requested_facet_uris; | |
229 for (const auto& facet_manager_pair : facet_managers_) { | |
230 if (facet_manager_pair.second->DoesRequireFetch()) | |
231 requested_facet_uris.push_back(facet_manager_pair.first); | |
232 } | |
233 DCHECK(!requested_facet_uris.empty()); | |
234 fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(), | |
235 requested_facet_uris, this)); | |
236 fetcher_->StartRequest(); | |
237 } | |
238 | |
239 base::Time AffiliationBackend::GetCurrentTime() { | |
240 return clock_->Now(); | |
241 } | |
242 | |
243 base::Time AffiliationBackend::ReadLastUpdateTimeFromDatabase( | |
244 const FacetURI& facet_uri) { | |
245 AffiliatedFacetsWithUpdateTime affiliation; | |
246 if (cache_->GetAffiliationsForFacet(facet_uri, &affiliation)) | |
Mike West
2015/02/02 15:19:44
You could spell this `ReadAffiliationsFromDatabase
engedy
2015/02/02 17:00:11
Done.
| |
247 return affiliation.last_update_time; | |
248 else | |
249 return clock_->Now() - | |
250 base::TimeDelta::FromSeconds(kCacheHardExpiryDurationInSeconds); | |
Mike West
2015/02/02 15:19:45
Nit: I think a ternary if would be prettier here.
engedy
2015/02/02 17:00:11
Done.
| |
251 } | |
252 | |
253 bool AffiliationBackend::ReadAffiliationsFromDatabase( | |
254 const FacetURI& facet_uri, | |
255 AffiliatedFacetsWithUpdateTime* affiliations) { | |
256 return cache_->GetAffiliationsForFacet(facet_uri, affiliations); | |
257 } | |
258 | |
259 void AffiliationBackend::SignalNeedNetworkRequest() { | |
260 // TODO(engedy): Integrate more sophisticated throttling logic here. | |
261 if (fetcher_) | |
262 return; | |
263 SendNetworkRequest(); | |
264 } | |
265 | |
266 void AffiliationBackend::OnFetchSucceeded( | |
267 scoped_ptr<AffiliationFetcherDelegate::Result> result) { | |
268 fetcher_.reset(); | |
269 | |
270 for (const AffiliatedFacets& affiliated_facets : *result) { | |
271 AffiliatedFacetsWithUpdateTime affiliation; | |
272 affiliation.facets = affiliated_facets; | |
273 affiliation.last_update_time = clock_->Now(); | |
274 | |
275 std::vector<AffiliatedFacetsWithUpdateTime> obsoleted_affiliations; | |
276 cache_->StoreAndRemoveConflicting(affiliation, &obsoleted_affiliations); | |
277 | |
278 if (!obsoleted_affiliations.empty()) | |
279 NOTIMPLEMENTED(); | |
Mike West
2015/02/02 15:19:45
Can you add a comment here? It's not clear to me w
engedy
2015/02/02 17:00:12
Done.
| |
280 | |
281 for (const auto& facet_uri : affiliated_facets) { | |
282 if (!facet_managers_.contains(facet_uri)) | |
Mike West
2015/02/02 15:19:44
It feels like there can be a race here if requests
engedy
2015/02/02 17:00:11
Well, if the response happens to contain the data
| |
283 continue; | |
284 FacetManager* facet_manager = facet_managers_.get(facet_uri); | |
285 facet_manager->OnFetchSucceeded(affiliation); | |
286 if (facet_manager->CanBeDiscarded()) | |
287 facet_managers_.erase(facet_uri); | |
288 } | |
289 } | |
290 | |
291 // A subsequent fetch is needed if additional GetAffiliations() request came | |
Mike West
2015/02/02 15:19:45
Nit: s/request came/requests come/ or s/if additio
engedy
2015/02/02 17:00:11
Done.
| |
292 // in while the current fetch was in flight. | |
293 for (const auto& facet_manager_pair : facet_managers_) { | |
294 if (facet_manager_pair.second->DoesRequireFetch()) { | |
295 SendNetworkRequest(); | |
296 return; | |
297 } | |
298 } | |
299 } | |
300 | |
301 void AffiliationBackend::OnFetchFailed() { | |
302 NOTIMPLEMENTED(); | |
Mike West
2015/02/02 15:19:44
Please add TODO comments pointing to a bug for thi
engedy
2015/02/02 17:00:11
Done. I have mentioned the very same bug that this
| |
303 } | |
304 | |
305 void AffiliationBackend::OnMalformedResponse() { | |
306 NOTIMPLEMENTED(); | |
307 } | |
308 | |
43 } // namespace password_manager | 309 } // namespace password_manager |
OLD | NEW |