Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(396)

Unified Diff: components/password_manager/core/browser/affiliation_backend.cc

Issue 868253005: AffiliationBackend: Implement the better part of on-demand fetching. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Missing override. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/password_manager/core/browser/affiliation_backend.cc
diff --git a/components/password_manager/core/browser/affiliation_backend.cc b/components/password_manager/core/browser/affiliation_backend.cc
index e1668444f854199aad3bc333fdf54753008f4539..2555260bb779524e7eb11d856abc4ea8fd15575d 100644
--- a/components/password_manager/core/browser/affiliation_backend.cc
+++ b/components/password_manager/core/browser/affiliation_backend.cc
@@ -4,26 +4,208 @@
#include "components/password_manager/core/browser/affiliation_backend.h"
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/location.h"
#include "base/task_runner.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/affiliation_database.h"
+#include "components/password_manager/core/browser/affiliation_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
namespace password_manager {
+namespace {
+
+// The duration after which cached affiliation data is considered stale and will
+// not be used to serve requests any longer.
+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
+
+// RequestInfo ----------------------------------------------------------------
+
+// Encapsulates the details of a pending GetAffiliations() request.
+struct RequestInfo {
+ RequestInfo();
+ ~RequestInfo();
+
+ AffiliationService::ResultCallback callback;
+ scoped_refptr<base::TaskRunner> callback_task_runner;
+};
+
+RequestInfo::RequestInfo() {
+}
+
+RequestInfo::~RequestInfo() {
+}
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.
+
+} // namespace
+
+// AffiliationBackend::FacetManager -------------------------------------------
+
+// Manages and performs ongoing tasks concerning a single facet.
+class AffiliationBackend::FacetManager {
+ public:
+ // The |backend| must outlive this object.
+ FacetManager(AffiliationBackend* backend, const FacetURI& facet_uri);
+ ~FacetManager();
+
+ // Called when |affiliation| information regarding this facet has just been
+ // fetched from the Affiliation API.
+ void OnFetchSucceeded(const AffiliatedFacetsWithUpdateTime& affiliation);
+
+ // Returns whether this instance has becomes redundant, that is, it has no
+ // more meaningful state than a newly created instance would have.
+ bool CanBeDiscarded() const;
+
+ // Returns whether or not affiliation information relating to this facet needs
+ // to be fetched right now.
+ bool DoesRequireFetch() const;
+
+ // Facet-specific implementations for methods in AffiliationService of the
+ // same name. See documentation in affiliation_service.h for details:
+ void GetAffiliations(bool cached_only, const RequestInfo& request_info);
+
+ private:
+ // Returns the time when cached data for this facet will become stale.
+ base::Time GetCacheHardExpiryTime() const;
+
+ // Returns whether or not the cache has fresh data for this facet.
+ bool IsCachedDataFresh() const;
+
+ // Posts the callback of the request described by |request_info| with success.
+ static void ServeRequestWithSuccess(const RequestInfo& request_info,
+ const AffiliatedFacets& affiliation);
-AffiliationBackend::AffiliationBackend() {
+ // Posts the callback of the request described by |request_info| with failure.
+ static void ServeRequestWithFailure(const RequestInfo& request_info);
+
+ AffiliationBackend* backend_;
+ FacetURI facet_uri_;
+
+ // The last time affiliation information was fetched for this facet, i.e. the
+ // freshness of the data in the database. If there is no corresponding data in
+ // in the database, this will contain something sufficiently far away in the
+ // past to be considered stale for sure. Otherwise, the last_update_time in
+ // the database should match this value; it is only cached to reduce disk I/O.
+ base::Time last_update_time_;
+
+ // Contains information about the GetAffiliations() requests that are waiting
+ // for the result of looking up this facet.
+ std::vector<RequestInfo> pending_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(FacetManager);
+};
+
+AffiliationBackend::FacetManager::FacetManager(AffiliationBackend* backend,
+ const FacetURI& facet_uri)
+ : backend_(backend),
+ facet_uri_(facet_uri),
+ last_update_time_(backend_->ReadLastUpdateTimeFromDatabase(facet_uri)) {
+}
+
+AffiliationBackend::FacetManager::~FacetManager() {
+ // The manager will only be destroyed while there are pending requests if the
+ // entire backend is going. Call failure on pending requests in this case.
+ for (const auto& request_info : pending_requests_)
+ ServeRequestWithFailure(request_info);
+}
+
+void AffiliationBackend::FacetManager::GetAffiliations(
+ bool cached_only,
+ const RequestInfo& request_info) {
+ if (IsCachedDataFresh()) {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ if (!backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliation))
+ NOTIMPLEMENTED();
+ DCHECK_EQ(affiliation.last_update_time, last_update_time_) << facet_uri_;
+ ServeRequestWithSuccess(request_info, affiliation.facets);
+ } else if (cached_only) {
+ ServeRequestWithFailure(request_info);
+ } else {
+ pending_requests_.push_back(request_info);
+ backend_->SignalNeedNetworkRequest();
+ }
+}
+
+void AffiliationBackend::FacetManager::OnFetchSucceeded(
+ const AffiliatedFacetsWithUpdateTime& affiliation) {
+ last_update_time_ = affiliation.last_update_time;
+ DCHECK(IsCachedDataFresh()) << facet_uri_;
+ for (const auto& request_info : pending_requests_)
+ ServeRequestWithSuccess(request_info, affiliation.facets);
+ pending_requests_.clear();
+}
+
+bool AffiliationBackend::FacetManager::CanBeDiscarded() const {
+ return pending_requests_.empty();
+}
+
+bool AffiliationBackend::FacetManager::DoesRequireFetch() const {
+ return !pending_requests_.empty() && !IsCachedDataFresh();
+}
+
+base::Time AffiliationBackend::FacetManager::GetCacheHardExpiryTime() const {
+ return last_update_time_ +
+ base::TimeDelta::FromSeconds(kCacheHardExpiryDurationInSeconds);
+}
+
+bool AffiliationBackend::FacetManager::IsCachedDataFresh() const {
+ return backend_->GetCurrentTime() < GetCacheHardExpiryTime();
+}
+
+// static
+void AffiliationBackend::FacetManager::ServeRequestWithSuccess(
+ const RequestInfo& request_info,
+ const AffiliatedFacets& affiliation) {
+ request_info.callback_task_runner->PostTask(
+ FROM_HERE, base::Bind(request_info.callback, affiliation, true));
+}
+
+// static
+void AffiliationBackend::FacetManager::ServeRequestWithFailure(
+ const RequestInfo& request_info) {
+ request_info.callback_task_runner->PostTask(
+ FROM_HERE, base::Bind(request_info.callback, AffiliatedFacets(), false));
+}
+
+// AffiliationBackend ---------------------------------------------------------
+
+AffiliationBackend::AffiliationBackend(
+ const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
+ scoped_ptr<base::Clock> time_source)
+ : request_context_getter_(request_context_getter),
+ clock_(time_source.Pass()),
+ weak_ptr_factory_(this) {
}
AffiliationBackend::~AffiliationBackend() {
}
-void AffiliationBackend::Initialize() {
- NOTIMPLEMENTED();
+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
+ cache_.reset(new AffiliationDatabase());
+ if (!cache_->Init(db_path))
+ NOTIMPLEMENTED();
}
void AffiliationBackend::GetAffiliations(
const FacetURI& facet_uri,
bool cached_only,
const AffiliationService::ResultCallback& callback,
- scoped_refptr<base::TaskRunner> callback_task_runner) {
- NOTIMPLEMENTED();
+ const scoped_refptr<base::TaskRunner>& callback_task_runner) {
+ if (!facet_managers_.contains(facet_uri)) {
+ scoped_ptr<FacetManager> new_manager(new FacetManager(this, facet_uri));
+ facet_managers_.add(facet_uri, new_manager.Pass());
+ }
+
+ 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
+ RequestInfo request_info;
+ request_info.callback = callback;
+ request_info.callback_task_runner = callback_task_runner;
+ facet_manager->GetAffiliations(cached_only, request_info);
+
+ if (facet_manager->CanBeDiscarded())
+ facet_managers_.erase(facet_uri);
}
void AffiliationBackend::Prefetch(const FacetURI& facet_uri,
@@ -40,4 +222,88 @@ void AffiliationBackend::TrimCache() {
NOTIMPLEMENTED();
}
+void AffiliationBackend::SendNetworkRequest() {
+ 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.
+
+ std::vector<FacetURI> requested_facet_uris;
+ for (const auto& facet_manager_pair : facet_managers_) {
+ if (facet_manager_pair.second->DoesRequireFetch())
+ requested_facet_uris.push_back(facet_manager_pair.first);
+ }
+ DCHECK(!requested_facet_uris.empty());
+ fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(),
+ requested_facet_uris, this));
+ fetcher_->StartRequest();
+}
+
+base::Time AffiliationBackend::GetCurrentTime() {
+ return clock_->Now();
+}
+
+base::Time AffiliationBackend::ReadLastUpdateTimeFromDatabase(
+ const FacetURI& facet_uri) {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ 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.
+ return affiliation.last_update_time;
+ else
+ return clock_->Now() -
+ 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.
+}
+
+bool AffiliationBackend::ReadAffiliationsFromDatabase(
+ const FacetURI& facet_uri,
+ AffiliatedFacetsWithUpdateTime* affiliations) {
+ return cache_->GetAffiliationsForFacet(facet_uri, affiliations);
+}
+
+void AffiliationBackend::SignalNeedNetworkRequest() {
+ // TODO(engedy): Integrate more sophisticated throttling logic here.
+ if (fetcher_)
+ return;
+ SendNetworkRequest();
+}
+
+void AffiliationBackend::OnFetchSucceeded(
+ scoped_ptr<AffiliationFetcherDelegate::Result> result) {
+ fetcher_.reset();
+
+ for (const AffiliatedFacets& affiliated_facets : *result) {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ affiliation.facets = affiliated_facets;
+ affiliation.last_update_time = clock_->Now();
+
+ std::vector<AffiliatedFacetsWithUpdateTime> obsoleted_affiliations;
+ cache_->StoreAndRemoveConflicting(affiliation, &obsoleted_affiliations);
+
+ if (!obsoleted_affiliations.empty())
+ 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.
+
+ for (const auto& facet_uri : affiliated_facets) {
+ 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
+ continue;
+ FacetManager* facet_manager = facet_managers_.get(facet_uri);
+ facet_manager->OnFetchSucceeded(affiliation);
+ if (facet_manager->CanBeDiscarded())
+ facet_managers_.erase(facet_uri);
+ }
+ }
+
+ // 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.
+ // in while the current fetch was in flight.
+ for (const auto& facet_manager_pair : facet_managers_) {
+ if (facet_manager_pair.second->DoesRequireFetch()) {
+ SendNetworkRequest();
+ return;
+ }
+ }
+}
+
+void AffiliationBackend::OnFetchFailed() {
+ 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
+}
+
+void AffiliationBackend::OnMalformedResponse() {
+ NOTIMPLEMENTED();
+}
+
} // namespace password_manager

Powered by Google App Engine
This is Rietveld 408576698