Chromium Code Reviews| 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/threading/thread_checker.h" | |
| 13 #include "base/time/clock.h" | |
| 14 #include "base/time/time.h" | |
| 15 #include "components/password_manager/core/browser/affiliation_database.h" | |
| 16 #include "components/password_manager/core/browser/affiliation_fetcher.h" | |
| 17 #include "net/url_request/url_request_context_getter.h" | |
| 8 | 18 |
| 9 namespace password_manager { | 19 namespace password_manager { |
| 10 | 20 namespace { |
| 11 AffiliationBackend::AffiliationBackend() { | 21 |
| 22 // The duration after which cached affiliation data is considered stale and will | |
| 23 // not be used to serve requests any longer. | |
| 24 const int64_t kCacheHardExpiryDurationInHours = 24; | |
|
Garrett Casto
2015/02/03 08:20:37
Why is this int64_t? Seems like overkill, especial
engedy
2015/02/03 10:49:23
It used to be in seconds, and I did not notice Tim
| |
| 25 | |
| 26 // RequestInfo ---------------------------------------------------------------- | |
| 27 | |
| 28 // Encapsulates the details of a pending GetAffiliations() request. | |
| 29 struct RequestInfo { | |
| 30 RequestInfo(); | |
| 31 ~RequestInfo(); | |
| 32 | |
| 33 AffiliationService::ResultCallback callback; | |
| 34 scoped_refptr<base::TaskRunner> callback_task_runner; | |
| 35 }; | |
| 36 | |
| 37 RequestInfo::RequestInfo() { | |
| 38 } | |
| 39 | |
| 40 RequestInfo::~RequestInfo() { | |
| 41 } | |
| 42 | |
| 43 } // namespace | |
| 44 | |
| 45 // AffiliationBackend::FacetManager ------------------------------------------- | |
| 46 | |
| 47 // Manages and performs ongoing tasks concerning a single facet. | |
| 48 class AffiliationBackend::FacetManager { | |
| 49 public: | |
| 50 // The |backend| must outlive this object. | |
| 51 FacetManager(AffiliationBackend* backend, const FacetURI& facet_uri); | |
| 52 ~FacetManager(); | |
| 53 | |
| 54 // Called when |affiliation| information regarding this facet has just been | |
| 55 // fetched from the Affiliation API. | |
| 56 void OnFetchSucceeded(const AffiliatedFacetsWithUpdateTime& affiliation); | |
| 57 | |
| 58 // Returns whether this instance has becomes redundant, that is, it has no | |
| 59 // more meaningful state than a newly created instance would have. | |
| 60 bool CanBeDiscarded() const; | |
| 61 | |
| 62 // Returns whether or not affiliation information relating to this facet needs | |
| 63 // to be fetched right now. | |
| 64 bool DoesRequireFetch() const; | |
| 65 | |
| 66 // Facet-specific implementations for methods in AffiliationService of the | |
| 67 // same name. See documentation in affiliation_service.h for details: | |
| 68 void GetAffiliations(bool cached_only, const RequestInfo& request_info); | |
| 69 | |
| 70 private: | |
| 71 // Returns the time when cached data for this facet will become stale. | |
| 72 base::Time GetCacheHardExpiryTime() const; | |
|
Garrett Casto
2015/02/03 08:20:38
Why do you call this HardExpiry instead of just Ex
engedy
2015/02/03 10:49:23
Yeah, there will be such a distinction, introduced
| |
| 73 | |
| 74 // Returns whether or not the cache has fresh data for this facet. | |
| 75 bool IsCachedDataFresh() const; | |
| 76 | |
| 77 // Posts the callback of the request described by |request_info| with success. | |
| 78 static void ServeRequestWithSuccess(const RequestInfo& request_info, | |
| 79 const AffiliatedFacets& affiliation); | |
| 80 | |
| 81 // Posts the callback of the request described by |request_info| with failure. | |
| 82 static void ServeRequestWithFailure(const RequestInfo& request_info); | |
| 83 | |
| 84 AffiliationBackend* backend_; | |
| 85 FacetURI facet_uri_; | |
| 86 | |
| 87 // The last time affiliation information was fetched for this facet, i.e. the | |
| 88 // freshness of the data in the database. If there is no corresponding data in | |
| 89 // in the database, this will contain something sufficiently far away in the | |
| 90 // past to be considered stale for sure. Otherwise, the last_update_time in | |
| 91 // the database should match this value; it is only cached to reduce disk I/O. | |
| 92 base::Time last_update_time_; | |
| 93 | |
| 94 // Contains information about the GetAffiliations() requests that are waiting | |
| 95 // for the result of looking up this facet. | |
| 96 std::vector<RequestInfo> pending_requests_; | |
| 97 | |
| 98 DISALLOW_COPY_AND_ASSIGN(FacetManager); | |
| 99 }; | |
| 100 | |
| 101 AffiliationBackend::FacetManager::FacetManager(AffiliationBackend* backend, | |
| 102 const FacetURI& facet_uri) | |
| 103 : backend_(backend), | |
| 104 facet_uri_(facet_uri), | |
| 105 last_update_time_(backend_->ReadLastUpdateTimeFromDatabase(facet_uri)) { | |
| 106 } | |
| 107 | |
| 108 AffiliationBackend::FacetManager::~FacetManager() { | |
| 109 // The manager will only be destroyed while there are pending requests if the | |
| 110 // entire backend is going. Call failure on pending requests in this case. | |
| 111 for (const auto& request_info : pending_requests_) | |
| 112 ServeRequestWithFailure(request_info); | |
| 113 } | |
| 114 | |
| 115 void AffiliationBackend::FacetManager::GetAffiliations( | |
| 116 bool cached_only, | |
| 117 const RequestInfo& request_info) { | |
| 118 if (IsCachedDataFresh()) { | |
| 119 AffiliatedFacetsWithUpdateTime affiliation; | |
| 120 if (!backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliation)) { | |
| 121 // TODO(engedy): Implement this. crbug.com/437865. | |
| 122 NOTIMPLEMENTED(); | |
| 123 } | |
| 124 DCHECK_EQ(affiliation.last_update_time, last_update_time_) << facet_uri_; | |
| 125 ServeRequestWithSuccess(request_info, affiliation.facets); | |
| 126 } else if (cached_only) { | |
| 127 ServeRequestWithFailure(request_info); | |
| 128 } else { | |
| 129 pending_requests_.push_back(request_info); | |
| 130 backend_->SignalNeedNetworkRequest(); | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 void AffiliationBackend::FacetManager::OnFetchSucceeded( | |
| 135 const AffiliatedFacetsWithUpdateTime& affiliation) { | |
| 136 last_update_time_ = affiliation.last_update_time; | |
| 137 DCHECK(IsCachedDataFresh()) << facet_uri_; | |
| 138 for (const auto& request_info : pending_requests_) | |
| 139 ServeRequestWithSuccess(request_info, affiliation.facets); | |
| 140 pending_requests_.clear(); | |
| 141 } | |
| 142 | |
| 143 bool AffiliationBackend::FacetManager::CanBeDiscarded() const { | |
| 144 return pending_requests_.empty(); | |
| 145 } | |
| 146 | |
| 147 bool AffiliationBackend::FacetManager::DoesRequireFetch() const { | |
| 148 return !pending_requests_.empty() && !IsCachedDataFresh(); | |
| 149 } | |
| 150 | |
| 151 base::Time AffiliationBackend::FacetManager::GetCacheHardExpiryTime() const { | |
| 152 return last_update_time_ + | |
| 153 base::TimeDelta::FromHours(kCacheHardExpiryDurationInHours); | |
| 154 } | |
| 155 | |
| 156 bool AffiliationBackend::FacetManager::IsCachedDataFresh() const { | |
| 157 return backend_->GetCurrentTime() < GetCacheHardExpiryTime(); | |
| 158 } | |
| 159 | |
| 160 // static | |
| 161 void AffiliationBackend::FacetManager::ServeRequestWithSuccess( | |
| 162 const RequestInfo& request_info, | |
| 163 const AffiliatedFacets& affiliation) { | |
| 164 request_info.callback_task_runner->PostTask( | |
| 165 FROM_HERE, base::Bind(request_info.callback, affiliation, true)); | |
| 166 } | |
| 167 | |
| 168 // static | |
| 169 void AffiliationBackend::FacetManager::ServeRequestWithFailure( | |
| 170 const RequestInfo& request_info) { | |
| 171 request_info.callback_task_runner->PostTask( | |
| 172 FROM_HERE, base::Bind(request_info.callback, AffiliatedFacets(), false)); | |
| 173 } | |
| 174 | |
| 175 // AffiliationBackend --------------------------------------------------------- | |
| 176 | |
| 177 AffiliationBackend::AffiliationBackend( | |
| 178 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, | |
| 179 scoped_ptr<base::Clock> time_source) | |
| 180 : request_context_getter_(request_context_getter), | |
| 181 clock_(time_source.Pass()), | |
| 182 weak_ptr_factory_(this) { | |
| 12 } | 183 } |
| 13 | 184 |
| 14 AffiliationBackend::~AffiliationBackend() { | 185 AffiliationBackend::~AffiliationBackend() { |
| 15 } | 186 } |
| 16 | 187 |
| 17 void AffiliationBackend::Initialize() { | 188 void AffiliationBackend::Initialize(const base::FilePath& db_path) { |
| 18 NOTIMPLEMENTED(); | 189 thread_checker_.reset(new base::ThreadChecker); |
| 190 cache_.reset(new AffiliationDatabase()); | |
| 191 if (!cache_->Init(db_path)) { | |
| 192 // TODO(engedy): Implement this. crbug.com/437865. | |
| 193 NOTIMPLEMENTED(); | |
| 194 } | |
| 19 } | 195 } |
| 20 | 196 |
| 21 void AffiliationBackend::GetAffiliations( | 197 void AffiliationBackend::GetAffiliations( |
| 22 const FacetURI& facet_uri, | 198 const FacetURI& facet_uri, |
| 23 bool cached_only, | 199 bool cached_only, |
| 24 const AffiliationService::ResultCallback& callback, | 200 const AffiliationService::ResultCallback& callback, |
| 25 scoped_refptr<base::TaskRunner> callback_task_runner) { | 201 const scoped_refptr<base::TaskRunner>& callback_task_runner) { |
| 26 NOTIMPLEMENTED(); | 202 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); |
| 203 if (!facet_managers_.contains(facet_uri)) { | |
| 204 scoped_ptr<FacetManager> new_manager(new FacetManager(this, facet_uri)); | |
| 205 facet_managers_.add(facet_uri, new_manager.Pass()); | |
| 206 } | |
| 207 | |
| 208 FacetManager* facet_manager = facet_managers_.get(facet_uri); | |
| 209 DCHECK(facet_manager); | |
| 210 RequestInfo request_info; | |
| 211 request_info.callback = callback; | |
| 212 request_info.callback_task_runner = callback_task_runner; | |
| 213 facet_manager->GetAffiliations(cached_only, request_info); | |
| 214 | |
| 215 if (facet_manager->CanBeDiscarded()) | |
| 216 facet_managers_.erase(facet_uri); | |
| 27 } | 217 } |
| 28 | 218 |
| 29 void AffiliationBackend::Prefetch(const FacetURI& facet_uri, | 219 void AffiliationBackend::Prefetch(const FacetURI& facet_uri, |
| 30 const base::Time& keep_fresh_until) { | 220 const base::Time& keep_fresh_until) { |
| 221 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
| 222 | |
| 223 // TODO(engedy): Implement this. crbug.com/437865. | |
| 31 NOTIMPLEMENTED(); | 224 NOTIMPLEMENTED(); |
| 32 } | 225 } |
| 33 | 226 |
| 34 void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri, | 227 void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri, |
| 35 const base::Time& keep_fresh_until) { | 228 const base::Time& keep_fresh_until) { |
| 229 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
| 230 | |
| 231 // TODO(engedy): Implement this. crbug.com/437865. | |
| 36 NOTIMPLEMENTED(); | 232 NOTIMPLEMENTED(); |
| 37 } | 233 } |
| 38 | 234 |
| 39 void AffiliationBackend::TrimCache() { | 235 void AffiliationBackend::TrimCache() { |
| 40 NOTIMPLEMENTED(); | 236 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); |
| 41 } | 237 |
| 42 | 238 // TODO(engedy): Implement this. crbug.com/437865. |
| 239 NOTIMPLEMENTED(); | |
| 240 } | |
| 241 | |
| 242 void AffiliationBackend::SendNetworkRequest() { | |
| 243 DCHECK(!fetcher_); | |
| 244 | |
| 245 std::vector<FacetURI> requested_facet_uris; | |
| 246 for (const auto& facet_manager_pair : facet_managers_) { | |
| 247 if (facet_manager_pair.second->DoesRequireFetch()) | |
| 248 requested_facet_uris.push_back(facet_manager_pair.first); | |
| 249 } | |
| 250 DCHECK(!requested_facet_uris.empty()); | |
| 251 fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(), | |
| 252 requested_facet_uris, this)); | |
| 253 fetcher_->StartRequest(); | |
| 254 } | |
| 255 | |
| 256 base::Time AffiliationBackend::GetCurrentTime() { | |
| 257 return clock_->Now(); | |
| 258 } | |
| 259 | |
| 260 base::Time AffiliationBackend::ReadLastUpdateTimeFromDatabase( | |
| 261 const FacetURI& facet_uri) { | |
| 262 AffiliatedFacetsWithUpdateTime affiliation; | |
| 263 return ReadAffiliationsFromDatabase(facet_uri, &affiliation) | |
| 264 ? affiliation.last_update_time | |
| 265 : base::Time() - | |
| 266 base::TimeDelta::FromHours(kCacheHardExpiryDurationInHours); | |
|
Garrett Casto
2015/02/03 08:20:37
From reading the other comments it sounds like you
engedy
2015/02/03 10:49:23
Done.
| |
| 267 } | |
| 268 | |
| 269 bool AffiliationBackend::ReadAffiliationsFromDatabase( | |
| 270 const FacetURI& facet_uri, | |
| 271 AffiliatedFacetsWithUpdateTime* affiliations) { | |
| 272 return cache_->GetAffiliationsForFacet(facet_uri, affiliations); | |
| 273 } | |
| 274 | |
| 275 void AffiliationBackend::SignalNeedNetworkRequest() { | |
| 276 // TODO(engedy): Add more sophisticated throttling logic. crbug.com/437865. | |
| 277 if (fetcher_) | |
| 278 return; | |
| 279 SendNetworkRequest(); | |
| 280 } | |
| 281 | |
| 282 void AffiliationBackend::OnFetchSucceeded( | |
| 283 scoped_ptr<AffiliationFetcherDelegate::Result> result) { | |
| 284 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
| 285 fetcher_.reset(); | |
| 286 | |
| 287 for (const AffiliatedFacets& affiliated_facets : *result) { | |
| 288 AffiliatedFacetsWithUpdateTime affiliation; | |
| 289 affiliation.facets = affiliated_facets; | |
| 290 affiliation.last_update_time = clock_->Now(); | |
| 291 | |
| 292 std::vector<AffiliatedFacetsWithUpdateTime> obsoleted_affiliations; | |
| 293 cache_->StoreAndRemoveConflicting(affiliation, &obsoleted_affiliations); | |
| 294 | |
| 295 // Cached data in contradiction with newly stored data automatically gets | |
| 296 // removed from the DB, and will be stored into |obsoleted_affiliations|. | |
| 297 // Let facet managers know if data is removed from under them. | |
| 298 // TODO(engedy): Implement this. crbug.com/437865. | |
| 299 if (!obsoleted_affiliations.empty()) | |
| 300 NOTIMPLEMENTED(); | |
| 301 | |
| 302 for (const auto& facet_uri : affiliated_facets) { | |
| 303 if (!facet_managers_.contains(facet_uri)) | |
| 304 continue; | |
| 305 FacetManager* facet_manager = facet_managers_.get(facet_uri); | |
| 306 facet_manager->OnFetchSucceeded(affiliation); | |
| 307 if (facet_manager->CanBeDiscarded()) | |
| 308 facet_managers_.erase(facet_uri); | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 // A subsequent fetch may be needed if any additional GetAffiliations() | |
| 313 // requests came in while the current fetch was in flight. | |
| 314 for (const auto& facet_manager_pair : facet_managers_) { | |
| 315 if (facet_manager_pair.second->DoesRequireFetch()) { | |
| 316 SendNetworkRequest(); | |
| 317 return; | |
| 318 } | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 void AffiliationBackend::OnFetchFailed() { | |
| 323 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
| 324 | |
| 325 // TODO(engedy): Implement this. crbug.com/437865. | |
| 326 NOTIMPLEMENTED(); | |
| 327 } | |
| 328 | |
| 329 void AffiliationBackend::OnMalformedResponse() { | |
| 330 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
| 331 | |
| 332 // TODO(engedy): Implement this. crbug.com/437865. | |
| 333 NOTIMPLEMENTED(); | |
| 334 } | |
| 335 | |
| 43 } // namespace password_manager | 336 } // namespace password_manager |
| OLD | NEW |