| Index: components/password_manager/core/browser/affiliated_match_helper.cc
|
| diff --git a/components/password_manager/core/browser/affiliated_match_helper.cc b/components/password_manager/core/browser/affiliated_match_helper.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b88033ad5b9aa00dafd4033f106d56692622eb24
|
| --- /dev/null
|
| +++ b/components/password_manager/core/browser/affiliated_match_helper.cc
|
| @@ -0,0 +1,223 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "components/password_manager/core/browser/affiliated_match_helper.h"
|
| +
|
| +#include <utility>
|
| +
|
| +#include "base/barrier_closure.h"
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/single_thread_task_runner.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "components/autofill/core/common/password_form.h"
|
| +#include "components/password_manager/core/browser/affiliation_service.h"
|
| +
|
| +namespace password_manager {
|
| +
|
| +namespace {
|
| +
|
| +// Returns whether or not |form| represents a credential for an Android
|
| +// application, and if so, returns the |facet_uri| of that application.
|
| +bool IsAndroidApplicationCredential(const autofill::PasswordForm& form,
|
| + FacetURI* facet_uri) {
|
| + DCHECK(facet_uri);
|
| + if (form.scheme != autofill::PasswordForm::SCHEME_HTML)
|
| + return false;
|
| +
|
| + *facet_uri = FacetURI::FromPotentiallyInvalidSpec(form.signon_realm);
|
| + return facet_uri->IsValidAndroidFacetURI();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +constexpr base::TimeDelta AffiliatedMatchHelper::kInitializationDelayOnStartup;
|
| +
|
| +AffiliatedMatchHelper::AffiliatedMatchHelper(
|
| + PasswordStore* password_store,
|
| + std::unique_ptr<AffiliationService> affiliation_service)
|
| + : password_store_(password_store),
|
| + affiliation_service_(std::move(affiliation_service)),
|
| + weak_ptr_factory_(this) {}
|
| +
|
| +AffiliatedMatchHelper::~AffiliatedMatchHelper() {
|
| + if (password_store_)
|
| + password_store_->RemoveObserver(this);
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::Initialize() {
|
| + DCHECK(password_store_);
|
| + DCHECK(affiliation_service_);
|
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&AffiliatedMatchHelper::DoDeferredInitialization,
|
| + weak_ptr_factory_.GetWeakPtr()),
|
| + kInitializationDelayOnStartup);
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::GetAffiliatedAndroidRealms(
|
| + const PasswordStore::FormDigest& observed_form,
|
| + const AffiliatedRealmsCallback& result_callback) {
|
| + if (IsValidWebCredential(observed_form)) {
|
| + FacetURI facet_uri(
|
| + FacetURI::FromPotentiallyInvalidSpec(observed_form.signon_realm));
|
| + affiliation_service_->GetAffiliations(
|
| + facet_uri, AffiliationService::StrategyOnCacheMiss::FAIL,
|
| + base::Bind(&AffiliatedMatchHelper::CompleteGetAffiliatedAndroidRealms,
|
| + weak_ptr_factory_.GetWeakPtr(), facet_uri, result_callback));
|
| + } else {
|
| + result_callback.Run(std::vector<std::string>());
|
| + }
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::GetAffiliatedWebRealms(
|
| + const PasswordStore::FormDigest& android_form,
|
| + const AffiliatedRealmsCallback& result_callback) {
|
| + if (IsValidAndroidCredential(android_form)) {
|
| + affiliation_service_->GetAffiliations(
|
| + FacetURI::FromPotentiallyInvalidSpec(android_form.signon_realm),
|
| + AffiliationService::StrategyOnCacheMiss::FETCH_OVER_NETWORK,
|
| + base::Bind(&AffiliatedMatchHelper::CompleteGetAffiliatedWebRealms,
|
| + weak_ptr_factory_.GetWeakPtr(), result_callback));
|
| + } else {
|
| + result_callback.Run(std::vector<std::string>());
|
| + }
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::InjectAffiliatedWebRealms(
|
| + std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
|
| + const PasswordFormsCallback& result_callback) {
|
| + std::vector<autofill::PasswordForm*> android_credentials;
|
| + for (const auto& form : forms) {
|
| + if (IsValidAndroidCredential(PasswordStore::FormDigest(*form)))
|
| + android_credentials.push_back(form.get());
|
| + }
|
| + base::Closure on_get_all_realms(
|
| + base::Bind(result_callback, base::Passed(&forms)));
|
| + base::Closure barrier_closure =
|
| + base::BarrierClosure(android_credentials.size(), on_get_all_realms);
|
| + for (auto* form : android_credentials) {
|
| + affiliation_service_->GetAffiliations(
|
| + FacetURI::FromPotentiallyInvalidSpec(form->signon_realm),
|
| + AffiliationService::StrategyOnCacheMiss::FAIL,
|
| + base::Bind(&AffiliatedMatchHelper::CompleteInjectAffiliatedWebRealm,
|
| + weak_ptr_factory_.GetWeakPtr(), base::Unretained(form),
|
| + barrier_closure));
|
| + }
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::CompleteInjectAffiliatedWebRealm(
|
| + autofill::PasswordForm* form,
|
| + base::Closure barrier_closure,
|
| + const AffiliatedFacets& results,
|
| + bool success) {
|
| + // If there is a number of realms, choose the first in the list.
|
| + if (success) {
|
| + for (const FacetURI& affiliated_facet : results) {
|
| + if (affiliated_facet.IsValidWebFacetURI()) {
|
| + form->affiliated_web_realm = affiliated_facet.canonical_spec() + "/";
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + barrier_closure.Run();
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::TrimAffiliationCache() {
|
| + affiliation_service_->TrimCache();
|
| +}
|
| +
|
| +// static
|
| +bool AffiliatedMatchHelper::IsValidAndroidCredential(
|
| + const PasswordStore::FormDigest& form) {
|
| + return form.scheme == autofill::PasswordForm::SCHEME_HTML &&
|
| + IsValidAndroidFacetURI(form.signon_realm);
|
| +}
|
| +
|
| +// static
|
| +bool AffiliatedMatchHelper::IsValidWebCredential(
|
| + const PasswordStore::FormDigest& form) {
|
| + FacetURI facet_uri(FacetURI::FromPotentiallyInvalidSpec(form.signon_realm));
|
| + return form.scheme == autofill::PasswordForm::SCHEME_HTML &&
|
| + facet_uri.IsValidWebFacetURI();
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::DoDeferredInitialization() {
|
| + // Must start observing for changes at the same time as when the snapshot is
|
| + // taken to avoid inconsistencies due to any changes taking place in-between.
|
| + password_store_->AddObserver(this);
|
| + password_store_->GetAutofillableLogins(this);
|
| + password_store_->GetBlacklistLogins(this);
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::CompleteGetAffiliatedAndroidRealms(
|
| + const FacetURI& original_facet_uri,
|
| + const AffiliatedRealmsCallback& result_callback,
|
| + const AffiliatedFacets& results,
|
| + bool success) {
|
| + std::vector<std::string> affiliated_realms;
|
| + if (success) {
|
| + for (const FacetURI& affiliated_facet : results) {
|
| + if (affiliated_facet != original_facet_uri &&
|
| + affiliated_facet.IsValidAndroidFacetURI())
|
| + // Facet URIs have no trailing slash, whereas realms do.
|
| + affiliated_realms.push_back(affiliated_facet.canonical_spec() + "/");
|
| + }
|
| + }
|
| + result_callback.Run(affiliated_realms);
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::CompleteGetAffiliatedWebRealms(
|
| + const AffiliatedRealmsCallback& result_callback,
|
| + const AffiliatedFacets& results,
|
| + bool success) {
|
| + std::vector<std::string> affiliated_realms;
|
| + if (success) {
|
| + for (const FacetURI& affiliated_facet : results) {
|
| + if (affiliated_facet.IsValidWebFacetURI())
|
| + // Facet URIs have no trailing slash, whereas realms do.
|
| + affiliated_realms.push_back(affiliated_facet.canonical_spec() + "/");
|
| + }
|
| + }
|
| + result_callback.Run(affiliated_realms);
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::OnLoginsChanged(
|
| + const PasswordStoreChangeList& changes) {
|
| + std::vector<FacetURI> facet_uris_to_trim;
|
| + for (const PasswordStoreChange& change : changes) {
|
| + FacetURI facet_uri;
|
| + if (!IsAndroidApplicationCredential(change.form(), &facet_uri))
|
| + continue;
|
| +
|
| + if (change.type() == PasswordStoreChange::ADD) {
|
| + affiliation_service_->Prefetch(facet_uri, base::Time::Max());
|
| + } else if (change.type() == PasswordStoreChange::REMOVE) {
|
| + // Stop keeping affiliation information fresh for deleted Android logins,
|
| + // and make a note to potentially remove any unneeded cached data later.
|
| + facet_uris_to_trim.push_back(facet_uri);
|
| + affiliation_service_->CancelPrefetch(facet_uri, base::Time::Max());
|
| + }
|
| + }
|
| +
|
| + // When the primary key for a login is updated, |changes| will contain both a
|
| + // REMOVE and ADD change for that login. Cached affiliation data should not be
|
| + // deleted in this case. A simple solution is to call TrimCacheForFacet()
|
| + // always after Prefetch() calls -- the trimming logic will detect that there
|
| + // is an active prefetch and not delete the corresponding data.
|
| + for (const FacetURI& facet_uri : facet_uris_to_trim)
|
| + affiliation_service_->TrimCacheForFacet(facet_uri);
|
| +}
|
| +
|
| +void AffiliatedMatchHelper::OnGetPasswordStoreResults(
|
| + std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
|
| + for (const auto& form : results) {
|
| + FacetURI facet_uri;
|
| + if (IsAndroidApplicationCredential(*form, &facet_uri))
|
| + affiliation_service_->Prefetch(facet_uri, base::Time::Max());
|
| + }
|
| +}
|
| +
|
| +} // namespace password_manager
|
|
|