| 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();
|
| }
|
|
|
|
|