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/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 |