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

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: Tidy up NULL time related issues. 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..2b8dcc12b0b526311470ecc4c6c921597db81ebb 100644
--- a/components/password_manager/core/browser/affiliation_backend.cc
+++ b/components/password_manager/core/browser/affiliation_backend.cc
@@ -4,39 +4,324 @@
#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/threading/thread_checker.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 int kCacheLifetimeInHours = 24;
+
+// RequestInfo ----------------------------------------------------------------
+
+// Encapsulates the details of a pending GetAffiliations() request.
+struct RequestInfo {
+ AffiliationService::ResultCallback callback;
+ scoped_refptr<base::TaskRunner> callback_task_runner;
+};
+
+} // 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 expire. The data is
+ // already considered expired at the returned microsecond.
+ base::Time GetCacheExpirationTime() 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);
+
+ // 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 cache. If there is no corresponding data in
+ // the database, this will contain the NULL time. Otherwise, the update time
+ // in the database should match this value; it is stored 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)) {
+ // TODO(engedy): Implement this. crbug.com/437865.
+ 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::GetCacheExpirationTime() const {
+ if (last_update_time_.is_null())
+ return base::Time();
+ return last_update_time_ + base::TimeDelta::FromHours(kCacheLifetimeInHours);
+}
+
+bool AffiliationBackend::FacetManager::IsCachedDataFresh() const {
+ return backend_->GetCurrentTime() < GetCacheExpirationTime();
+}
+
+// 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));
+}
-AffiliationBackend::AffiliationBackend() {
+// 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) {
+ DCHECK_LT(base::Time(), clock_->Now());
}
AffiliationBackend::~AffiliationBackend() {
}
-void AffiliationBackend::Initialize() {
- NOTIMPLEMENTED();
+void AffiliationBackend::Initialize(const base::FilePath& db_path) {
+ thread_checker_.reset(new base::ThreadChecker);
+ cache_.reset(new AffiliationDatabase());
+ if (!cache_->Init(db_path)) {
+ // TODO(engedy): Implement this. crbug.com/437865.
+ 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) {
+ DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
+ 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);
+ DCHECK(facet_manager);
+ 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,
const base::Time& keep_fresh_until) {
+ DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
+
+ // TODO(engedy): Implement this. crbug.com/437865.
NOTIMPLEMENTED();
}
void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri,
const base::Time& keep_fresh_until) {
+ DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
+
+ // TODO(engedy): Implement this. crbug.com/437865.
NOTIMPLEMENTED();
}
void AffiliationBackend::TrimCache() {
+ DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
+
+ // TODO(engedy): Implement this. crbug.com/437865.
+ NOTIMPLEMENTED();
+}
+
+void AffiliationBackend::SendNetworkRequest() {
+ DCHECK(!fetcher_);
+
+ 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;
+ return ReadAffiliationsFromDatabase(facet_uri, &affiliation)
+ ? affiliation.last_update_time
+ : base::Time();
+}
+
+bool AffiliationBackend::ReadAffiliationsFromDatabase(
+ const FacetURI& facet_uri,
+ AffiliatedFacetsWithUpdateTime* affiliations) {
+ return cache_->GetAffiliationsForFacet(facet_uri, affiliations);
+}
+
+void AffiliationBackend::SignalNeedNetworkRequest() {
+ // TODO(engedy): Add more sophisticated throttling logic. crbug.com/437865.
+ if (fetcher_)
+ return;
+ SendNetworkRequest();
+}
+
+void AffiliationBackend::OnFetchSucceeded(
+ scoped_ptr<AffiliationFetcherDelegate::Result> result) {
+ DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
+ 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);
+
+ // Cached data in contradiction with newly stored data automatically gets
+ // removed from the DB, and will be stored into |obsoleted_affiliations|.
+ // Let facet managers know if data is removed from under them.
+ // TODO(engedy): Implement this. crbug.com/437865.
+ if (!obsoleted_affiliations.empty())
+ NOTIMPLEMENTED();
+
+ for (const auto& facet_uri : affiliated_facets) {
+ if (!facet_managers_.contains(facet_uri))
+ 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 may be needed if any additional GetAffiliations()
+ // requests came 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() {
+ DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
+
+ // TODO(engedy): Implement this. crbug.com/437865.
+ NOTIMPLEMENTED();
+}
+
+void AffiliationBackend::OnMalformedResponse() {
+ DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
+
+ // TODO(engedy): Implement this. crbug.com/437865.
NOTIMPLEMENTED();
}

Powered by Google App Engine
This is Rietveld 408576698