[autofill] Sync server card and address metadata.
BUG=481595 Review URL: https://codereview.chromium.org/1110833002 Cr-Commit-Position: refs/heads/master@{#333202}
This commit is contained in:
@ -45,6 +45,7 @@
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "components/autofill/core/browser/webdata/autocomplete_syncable_service.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
|
||||
#include "components/autofill/core/common/autofill_pref_names.h"
|
||||
@ -519,7 +520,8 @@ base::WeakPtr<syncer::SyncableService> ProfileSyncComponentsFactoryImpl::
|
||||
return autofill::AutofillProfileSyncableService::FromWebDataService(
|
||||
web_data_service_.get())->AsWeakPtr();
|
||||
} else if (type == syncer::AUTOFILL_WALLET_METADATA) {
|
||||
return base::WeakPtr<syncer::SyncableService>();
|
||||
return autofill::AutofillWalletMetadataSyncableService::
|
||||
FromWebDataService(web_data_service_.get())->AsWeakPtr();
|
||||
}
|
||||
return autofill::AutofillWalletSyncableService::FromWebDataService(
|
||||
web_data_service_.get())->AsWeakPtr();
|
||||
|
@ -202,6 +202,8 @@
|
||||
'autofill/core/browser/webdata/autofill_profile_syncable_service.h',
|
||||
'autofill/core/browser/webdata/autofill_table.cc',
|
||||
'autofill/core/browser/webdata/autofill_table.h',
|
||||
'autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc',
|
||||
'autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h',
|
||||
'autofill/core/browser/webdata/autofill_wallet_syncable_service.cc',
|
||||
'autofill/core/browser/webdata/autofill_wallet_syncable_service.h',
|
||||
'autofill/core/browser/webdata/autofill_webdata.h',
|
||||
|
@ -123,6 +123,8 @@ static_library("browser") {
|
||||
"webdata/autofill_profile_syncable_service.h",
|
||||
"webdata/autofill_table.cc",
|
||||
"webdata/autofill_table.h",
|
||||
"webdata/autofill_wallet_metadata_syncable_service.cc",
|
||||
"webdata/autofill_wallet_metadata_syncable_service.h",
|
||||
"webdata/autofill_wallet_syncable_service.cc",
|
||||
"webdata/autofill_wallet_syncable_service.h",
|
||||
"webdata/autofill_webdata.h",
|
||||
@ -241,6 +243,7 @@ source_set("unit_tests") {
|
||||
"validation_unittest.cc",
|
||||
"webdata/autofill_profile_syncable_service_unittest.cc",
|
||||
"webdata/autofill_table_unittest.cc",
|
||||
"webdata/autofill_wallet_metadata_syncable_service_unittest.cc",
|
||||
"webdata/web_data_service_unittest.cc",
|
||||
]
|
||||
|
||||
|
@ -17,13 +17,11 @@ AutofillChange::AutofillChange(Type type, const AutofillKey& key)
|
||||
AutofillChange::~AutofillChange() {
|
||||
}
|
||||
|
||||
AutofillProfileChange::AutofillProfileChange(
|
||||
Type type, const std::string& key, const AutofillProfile* profile)
|
||||
: GenericAutofillChange<std::string>(type, key),
|
||||
profile_(profile) {
|
||||
DCHECK(type == ADD ? (profile && profile->guid() == key) : true);
|
||||
DCHECK(type == UPDATE ? (profile && profile->guid() == key) : true);
|
||||
DCHECK(type == REMOVE ? !profile : true);
|
||||
AutofillProfileChange::AutofillProfileChange(Type type,
|
||||
const std::string& key,
|
||||
const AutofillProfile* profile)
|
||||
: GenericAutofillChange<std::string>(type, key), profile_(profile) {
|
||||
DCHECK(type == REMOVE ? !profile : profile && profile->guid() == key);
|
||||
}
|
||||
|
||||
AutofillProfileChange::~AutofillProfileChange() {
|
||||
@ -31,9 +29,23 @@ AutofillProfileChange::~AutofillProfileChange() {
|
||||
|
||||
bool AutofillProfileChange::operator==(
|
||||
const AutofillProfileChange& change) const {
|
||||
return type() == change.type() &&
|
||||
key() == change.key() &&
|
||||
(type() != REMOVE) ? *profile() == *change.profile() : true;
|
||||
return type() == change.type() && key() == change.key() &&
|
||||
(type() == REMOVE || *profile() == *change.profile());
|
||||
}
|
||||
|
||||
CreditCardChange::CreditCardChange(Type type,
|
||||
const std::string& key,
|
||||
const CreditCard* card)
|
||||
: GenericAutofillChange<std::string>(type, key), card_(card) {
|
||||
DCHECK(type == REMOVE ? !card : card && card->guid() == key);
|
||||
}
|
||||
|
||||
CreditCardChange::~CreditCardChange() {
|
||||
}
|
||||
|
||||
bool CreditCardChange::operator==(const CreditCardChange& change) const {
|
||||
return type() == change.type() && key() == change.key() &&
|
||||
(type() == REMOVE || *card() == *change.card());
|
||||
}
|
||||
|
||||
} // namespace autofill
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
|
||||
#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "components/autofill/core/browser/webdata/autofill_entry.h"
|
||||
@ -12,9 +13,10 @@
|
||||
namespace autofill {
|
||||
|
||||
class AutofillProfile;
|
||||
class CreditCard;
|
||||
|
||||
// For classic Autofill form fields, the KeyType is AutofillKey.
|
||||
// Autofill++ types such as AutofillProfile and CreditCard simply use an int.
|
||||
// Autofill++ types such as AutofillProfile and CreditCard simply use a string.
|
||||
template <typename KeyType>
|
||||
class GenericAutofillChange {
|
||||
public:
|
||||
@ -70,6 +72,25 @@ class AutofillProfileChange : public GenericAutofillChange<std::string> {
|
||||
const AutofillProfile* profile_;
|
||||
};
|
||||
|
||||
// Change notification details for credit card changes.
|
||||
class CreditCardChange : public GenericAutofillChange<std::string> {
|
||||
public:
|
||||
// The |type| input specifies the change type. The |key| input is the key,
|
||||
// which is expected to be the GUID identifying the |card|.
|
||||
// When |type| == ADD, |card| should be non-NULL.
|
||||
// When |type| == UPDATE, |card| should be non-NULL.
|
||||
// When |type| == REMOVE, |card| should be NULL.
|
||||
CreditCardChange(Type type, const std::string& key, const CreditCard* card);
|
||||
~CreditCardChange() override;
|
||||
|
||||
const CreditCard* card() const { return card_; }
|
||||
bool operator==(const CreditCardChange& change) const;
|
||||
|
||||
private:
|
||||
// Weak reference, can be NULL.
|
||||
const CreditCard* card_;
|
||||
};
|
||||
|
||||
} // namespace autofill
|
||||
|
||||
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
|
||||
|
460
components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
Normal file
460
components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
Normal file
@ -0,0 +1,460 @@
|
||||
// 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/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/location.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/time/time.h"
|
||||
#include "components/autofill/core/browser/autofill_data_model.h"
|
||||
#include "components/autofill/core/browser/autofill_profile.h"
|
||||
#include "components/autofill/core/browser/credit_card.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_change.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_table.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
|
||||
#include "sync/api/sync_change.h"
|
||||
#include "sync/api/sync_error_factory.h"
|
||||
#include "sync/protocol/autofill_specifics.pb.h"
|
||||
#include "sync/protocol/sync.pb.h"
|
||||
|
||||
namespace autofill {
|
||||
|
||||
namespace {
|
||||
|
||||
void* UserDataKey() {
|
||||
// Use the address of a static so that COMDAT folding won't ever fold
|
||||
// with something else.
|
||||
static int user_data_key = 0;
|
||||
return reinterpret_cast<void*>(&user_data_key);
|
||||
}
|
||||
|
||||
// Returns syncable metadata for the |local| profile or credit card.
|
||||
syncer::SyncData BuildSyncData(sync_pb::WalletMetadataSpecifics::Type type,
|
||||
const std::string& server_id,
|
||||
const AutofillDataModel& local) {
|
||||
sync_pb::EntitySpecifics entity;
|
||||
sync_pb::WalletMetadataSpecifics* metadata = entity.mutable_wallet_metadata();
|
||||
metadata->set_type(type);
|
||||
metadata->set_id(server_id);
|
||||
metadata->set_use_count(local.use_count());
|
||||
metadata->set_use_date(local.use_date().ToInternalValue());
|
||||
|
||||
std::string sync_tag;
|
||||
switch (type) {
|
||||
case sync_pb::WalletMetadataSpecifics::ADDRESS:
|
||||
sync_tag = "address-" + server_id;
|
||||
break;
|
||||
case sync_pb::WalletMetadataSpecifics::CARD:
|
||||
sync_tag = "card-" + server_id;
|
||||
break;
|
||||
case sync_pb::WalletMetadataSpecifics::UNKNOWN:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
|
||||
return syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AutofillWalletMetadataSyncableService::
|
||||
~AutofillWalletMetadataSyncableService() {
|
||||
}
|
||||
|
||||
syncer::SyncMergeResult
|
||||
AutofillWalletMetadataSyncableService::MergeDataAndStartSyncing(
|
||||
syncer::ModelType type,
|
||||
const syncer::SyncDataList& initial_sync_data,
|
||||
scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
|
||||
scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
DCHECK(!sync_processor_);
|
||||
DCHECK(!sync_error_factory_);
|
||||
DCHECK_EQ(syncer::AUTOFILL_WALLET_METADATA, type);
|
||||
|
||||
sync_processor_ = sync_processor.Pass();
|
||||
sync_error_factory_ = sync_error_factory.Pass();
|
||||
|
||||
return MergeData(initial_sync_data);
|
||||
}
|
||||
|
||||
void AutofillWalletMetadataSyncableService::StopSyncing(
|
||||
syncer::ModelType type) {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
DCHECK_EQ(syncer::AUTOFILL_WALLET_METADATA, type);
|
||||
|
||||
sync_processor_.reset();
|
||||
sync_error_factory_.reset();
|
||||
cache_.clear();
|
||||
}
|
||||
|
||||
syncer::SyncDataList AutofillWalletMetadataSyncableService::GetAllSyncData(
|
||||
syncer::ModelType type) const {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
DCHECK_EQ(syncer::AUTOFILL_WALLET_METADATA, type);
|
||||
|
||||
syncer::SyncDataList data_list;
|
||||
std::map<std::string, AutofillProfile*> profiles;
|
||||
std::map<std::string, CreditCard*> cards;
|
||||
if (GetLocalData(&profiles, &cards)) {
|
||||
for (const auto& it : profiles) {
|
||||
data_list.push_back(BuildSyncData(
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, it.first, *it.second));
|
||||
}
|
||||
|
||||
for (const auto& it : cards) {
|
||||
data_list.push_back(BuildSyncData(sync_pb::WalletMetadataSpecifics::CARD,
|
||||
it.first, *it.second));
|
||||
}
|
||||
}
|
||||
|
||||
return data_list;
|
||||
}
|
||||
|
||||
syncer::SyncError AutofillWalletMetadataSyncableService::ProcessSyncChanges(
|
||||
const tracked_objects::Location& from_here,
|
||||
const syncer::SyncChangeList& changes_from_sync) {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
|
||||
cache_.clear();
|
||||
|
||||
std::map<std::string, AutofillProfile*> profiles;
|
||||
std::map<std::string, CreditCard*> cards;
|
||||
GetLocalData(&profiles, &cards);
|
||||
|
||||
base::Callback<bool(const AutofillProfile&)> address_updater =
|
||||
base::Bind(&AutofillWalletMetadataSyncableService::UpdateAddressStats,
|
||||
base::Unretained(this));
|
||||
base::Callback<bool(const CreditCard&)> card_updater =
|
||||
base::Bind(&AutofillWalletMetadataSyncableService::UpdateCardStats,
|
||||
base::Unretained(this));
|
||||
|
||||
syncer::SyncChangeList changes_to_sync;
|
||||
for (const syncer::SyncChange& change : changes_from_sync) {
|
||||
const sync_pb::WalletMetadataSpecifics& remote_metadata =
|
||||
change.sync_data().GetSpecifics().wallet_metadata();
|
||||
switch (change.change_type()) {
|
||||
// Do not immediately delete data.
|
||||
case syncer::SyncChange::ACTION_ADD:
|
||||
// Intentional fall through.
|
||||
case syncer::SyncChange::ACTION_UPDATE:
|
||||
switch (remote_metadata.type()) {
|
||||
case sync_pb::WalletMetadataSpecifics::ADDRESS:
|
||||
MergeRemote(change.sync_data(), address_updater, &profiles,
|
||||
&changes_to_sync);
|
||||
break;
|
||||
|
||||
case sync_pb::WalletMetadataSpecifics::CARD:
|
||||
MergeRemote(change.sync_data(), card_updater, &cards,
|
||||
&changes_to_sync);
|
||||
break;
|
||||
|
||||
case sync_pb::WalletMetadataSpecifics::UNKNOWN:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// Undelete data immediately.
|
||||
case syncer::SyncChange::ACTION_DELETE:
|
||||
switch (remote_metadata.type()) {
|
||||
case sync_pb::WalletMetadataSpecifics::ADDRESS: {
|
||||
const auto& it = profiles.find(remote_metadata.id());
|
||||
if (it != profiles.end()) {
|
||||
cache_.push_back(
|
||||
BuildSyncData(sync_pb::WalletMetadataSpecifics::ADDRESS,
|
||||
it->first, *it->second));
|
||||
changes_to_sync.push_back(syncer::SyncChange(
|
||||
FROM_HERE, syncer::SyncChange::ACTION_ADD, cache_.back()));
|
||||
profiles.erase(it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case sync_pb::WalletMetadataSpecifics::CARD: {
|
||||
const auto& it = cards.find(remote_metadata.id());
|
||||
if (it != cards.end()) {
|
||||
cache_.push_back(
|
||||
BuildSyncData(sync_pb::WalletMetadataSpecifics::CARD,
|
||||
it->first, *it->second));
|
||||
changes_to_sync.push_back(syncer::SyncChange(
|
||||
FROM_HERE, syncer::SyncChange::ACTION_ADD, cache_.back()));
|
||||
cards.erase(it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case sync_pb::WalletMetadataSpecifics::UNKNOWN:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case syncer::SyncChange::ACTION_INVALID:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The remainder of |profiles| were not listed in |changes_from_sync|.
|
||||
for (const auto& it : profiles) {
|
||||
cache_.push_back(BuildSyncData(sync_pb::WalletMetadataSpecifics::ADDRESS,
|
||||
it.first, *it.second));
|
||||
}
|
||||
|
||||
// The remainder of |cards| were not listed in |changes_from_sync|.
|
||||
for (const auto& it : cards) {
|
||||
cache_.push_back(BuildSyncData(sync_pb::WalletMetadataSpecifics::CARD,
|
||||
it.first, *it.second));
|
||||
}
|
||||
|
||||
syncer::SyncError status;
|
||||
if (!changes_to_sync.empty())
|
||||
status = SendChangesToSyncServer(changes_to_sync);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void AutofillWalletMetadataSyncableService::AutofillProfileChanged(
|
||||
const AutofillProfileChange& change) {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
|
||||
if (sync_processor_ && change.profile() &&
|
||||
change.profile()->record_type() == AutofillProfile::SERVER_PROFILE) {
|
||||
AutofillDataModelChanged(change.profile()->server_id(), *change.profile());
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillWalletMetadataSyncableService::CreditCardChanged(
|
||||
const CreditCardChange& change) {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
|
||||
if (sync_processor_ && change.card() &&
|
||||
change.card()->record_type() != CreditCard::LOCAL_CARD) {
|
||||
AutofillDataModelChanged(change.card()->server_id(), *change.card());
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillWalletMetadataSyncableService::AutofillMultipleChanged() {
|
||||
// Merging data will clear the cache, so make a copy to avoid merging with
|
||||
// empty remote data. Copying the cache is expensive, but still cheaper than
|
||||
// GetAllSyncData().
|
||||
if (sync_processor_)
|
||||
MergeData(syncer::SyncDataList(cache_));
|
||||
}
|
||||
|
||||
// static
|
||||
void AutofillWalletMetadataSyncableService::CreateForWebDataServiceAndBackend(
|
||||
AutofillWebDataService* web_data_service,
|
||||
AutofillWebDataBackend* webdata_backend,
|
||||
const std::string& app_locale) {
|
||||
web_data_service->GetDBUserData()->SetUserData(
|
||||
UserDataKey(),
|
||||
new AutofillWalletMetadataSyncableService(webdata_backend, app_locale));
|
||||
}
|
||||
|
||||
// static
|
||||
AutofillWalletMetadataSyncableService*
|
||||
AutofillWalletMetadataSyncableService::FromWebDataService(
|
||||
AutofillWebDataService* web_data_service) {
|
||||
return static_cast<AutofillWalletMetadataSyncableService*>(
|
||||
web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
|
||||
}
|
||||
|
||||
AutofillWalletMetadataSyncableService::AutofillWalletMetadataSyncableService(
|
||||
AutofillWebDataBackend* webdata_backend,
|
||||
const std::string& app_locale)
|
||||
: webdata_backend_(webdata_backend), scoped_observer_(this) {
|
||||
// No webdata in tests.
|
||||
if (webdata_backend_)
|
||||
scoped_observer_.Add(webdata_backend_);
|
||||
}
|
||||
|
||||
bool AutofillWalletMetadataSyncableService::GetLocalData(
|
||||
std::map<std::string, AutofillProfile*>* profiles,
|
||||
std::map<std::string, CreditCard*>* cards) const {
|
||||
std::vector<AutofillProfile*> profile_list;
|
||||
bool success = AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase())
|
||||
->GetServerProfiles(&profile_list);
|
||||
for (AutofillProfile* profile : profile_list)
|
||||
profiles->insert(std::make_pair(profile->server_id(), profile));
|
||||
|
||||
std::vector<CreditCard*> card_list;
|
||||
success &= AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase())
|
||||
->GetServerCreditCards(&card_list);
|
||||
for (CreditCard* card : card_list)
|
||||
cards->insert(std::make_pair(card->server_id(), card));
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AutofillWalletMetadataSyncableService::UpdateAddressStats(
|
||||
const AutofillProfile& profile) {
|
||||
return AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase())
|
||||
->UpdateServerAddressUsageStats(profile);
|
||||
}
|
||||
|
||||
bool AutofillWalletMetadataSyncableService::UpdateCardStats(
|
||||
const CreditCard& credit_card) {
|
||||
return AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase())
|
||||
->UpdateServerCardUsageStats(credit_card);
|
||||
}
|
||||
|
||||
syncer::SyncError
|
||||
AutofillWalletMetadataSyncableService::SendChangesToSyncServer(
|
||||
const syncer::SyncChangeList& changes_to_sync) {
|
||||
DCHECK(sync_processor_);
|
||||
return sync_processor_->ProcessSyncChanges(FROM_HERE, changes_to_sync);
|
||||
}
|
||||
|
||||
syncer::SyncMergeResult AutofillWalletMetadataSyncableService::MergeData(
|
||||
const syncer::SyncDataList& sync_data) {
|
||||
cache_.clear();
|
||||
|
||||
std::map<std::string, AutofillProfile*> profiles;
|
||||
std::map<std::string, CreditCard*> cards;
|
||||
GetLocalData(&profiles, &cards);
|
||||
|
||||
syncer::SyncMergeResult result(syncer::AUTOFILL_WALLET_METADATA);
|
||||
result.set_num_items_before_association(profiles.size() + cards.size());
|
||||
|
||||
base::Callback<bool(const AutofillProfile&)> address_updater =
|
||||
base::Bind(&AutofillWalletMetadataSyncableService::UpdateAddressStats,
|
||||
base::Unretained(this));
|
||||
base::Callback<bool(const CreditCard&)> card_updater =
|
||||
base::Bind(&AutofillWalletMetadataSyncableService::UpdateCardStats,
|
||||
base::Unretained(this));
|
||||
|
||||
syncer::SyncChangeList changes_to_sync;
|
||||
for (const syncer::SyncData& remote : sync_data) {
|
||||
DCHECK(remote.IsValid());
|
||||
DCHECK_EQ(syncer::AUTOFILL_WALLET_METADATA, remote.GetDataType());
|
||||
switch (remote.GetSpecifics().wallet_metadata().type()) {
|
||||
case sync_pb::WalletMetadataSpecifics::ADDRESS:
|
||||
if (!MergeRemote(remote, address_updater, &profiles,
|
||||
&changes_to_sync)) {
|
||||
changes_to_sync.push_back(syncer::SyncChange(
|
||||
FROM_HERE, syncer::SyncChange::ACTION_DELETE, remote));
|
||||
}
|
||||
break;
|
||||
|
||||
case sync_pb::WalletMetadataSpecifics::CARD:
|
||||
if (!MergeRemote(remote, card_updater, &cards, &changes_to_sync)) {
|
||||
changes_to_sync.push_back(syncer::SyncChange(
|
||||
FROM_HERE, syncer::SyncChange::ACTION_DELETE, remote));
|
||||
}
|
||||
break;
|
||||
|
||||
case sync_pb::WalletMetadataSpecifics::UNKNOWN:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The remainder of |profiles| were not listed in |sync_data|.
|
||||
for (const auto& it : profiles) {
|
||||
cache_.push_back(BuildSyncData(sync_pb::WalletMetadataSpecifics::ADDRESS,
|
||||
it.first, *it.second));
|
||||
changes_to_sync.push_back(syncer::SyncChange(
|
||||
FROM_HERE, syncer::SyncChange::ACTION_ADD, cache_.back()));
|
||||
}
|
||||
|
||||
// The remainder of |cards| were not listed in |sync_data|.
|
||||
for (const auto& it : cards) {
|
||||
cache_.push_back(BuildSyncData(sync_pb::WalletMetadataSpecifics::CARD,
|
||||
it.first, *it.second));
|
||||
changes_to_sync.push_back(syncer::SyncChange(
|
||||
FROM_HERE, syncer::SyncChange::ACTION_ADD, cache_.back()));
|
||||
}
|
||||
|
||||
// Metadata is not added or deleted locally to maintain a 1:1 relationship
|
||||
// with Wallet data.
|
||||
result.set_num_items_after_association(result.num_items_before_association());
|
||||
result.set_num_items_added(0);
|
||||
result.set_num_items_deleted(0);
|
||||
|
||||
if (!changes_to_sync.empty())
|
||||
result.set_error(SendChangesToSyncServer(changes_to_sync));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class DataType>
|
||||
bool AutofillWalletMetadataSyncableService::MergeRemote(
|
||||
const syncer::SyncData& remote,
|
||||
const base::Callback<bool(const DataType&)>& updater,
|
||||
std::map<std::string, DataType*>* locals,
|
||||
syncer::SyncChangeList* changes_to_sync) {
|
||||
DCHECK(locals);
|
||||
DCHECK(changes_to_sync);
|
||||
|
||||
const sync_pb::WalletMetadataSpecifics& remote_metadata =
|
||||
remote.GetSpecifics().wallet_metadata();
|
||||
auto it = locals->find(remote_metadata.id());
|
||||
if (it == locals->end())
|
||||
return false;
|
||||
|
||||
DataType* local_metadata = it->second;
|
||||
locals->erase(it);
|
||||
|
||||
size_t remote_use_count = static_cast<size_t>(remote_metadata.use_count());
|
||||
bool is_local_modified = false;
|
||||
bool is_remote_outdated = false;
|
||||
if (local_metadata->use_count() < remote_use_count) {
|
||||
local_metadata->set_use_count(remote_use_count);
|
||||
is_local_modified = true;
|
||||
} else if (local_metadata->use_count() > remote_use_count) {
|
||||
is_remote_outdated = true;
|
||||
}
|
||||
|
||||
base::Time remote_use_date =
|
||||
base::Time::FromInternalValue(remote_metadata.use_date());
|
||||
if (local_metadata->use_date() < remote_use_date) {
|
||||
local_metadata->set_use_date(remote_use_date);
|
||||
is_local_modified = true;
|
||||
} else if (local_metadata->use_date() > remote_use_date) {
|
||||
is_remote_outdated = true;
|
||||
}
|
||||
|
||||
if (is_remote_outdated) {
|
||||
cache_.push_back(BuildSyncData(remote_metadata.type(), remote_metadata.id(),
|
||||
*local_metadata));
|
||||
changes_to_sync->push_back(syncer::SyncChange(
|
||||
FROM_HERE, syncer::SyncChange::ACTION_UPDATE, cache_.back()));
|
||||
} else {
|
||||
cache_.push_back(remote);
|
||||
}
|
||||
|
||||
if (is_local_modified)
|
||||
updater.Run(*local_metadata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutofillWalletMetadataSyncableService::AutofillDataModelChanged(
|
||||
const std::string& server_id,
|
||||
const AutofillDataModel& local) {
|
||||
for (auto it = cache_.begin(); it != cache_.end(); ++it) {
|
||||
const sync_pb::WalletMetadataSpecifics& remote =
|
||||
it->GetSpecifics().wallet_metadata();
|
||||
if (remote.id() == server_id) {
|
||||
if (static_cast<size_t>(remote.use_count()) < local.use_count() &&
|
||||
base::Time::FromInternalValue(remote.use_date()) < local.use_date()) {
|
||||
*it = BuildSyncData(remote.type(), server_id, local);
|
||||
SendChangesToSyncServer(syncer::SyncChangeList(
|
||||
1, syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
|
||||
*it)));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace autofill
|
150
components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h
Normal file
150
components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h
Normal file
@ -0,0 +1,150 @@
|
||||
// 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.
|
||||
|
||||
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WALLET_METADATA_SYNCABLE_SERVICE_H_
|
||||
#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WALLET_METADATA_SYNCABLE_SERVICE_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/scoped_observer.h"
|
||||
#include "base/supports_user_data.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
|
||||
#include "sync/api/sync_change_processor.h"
|
||||
#include "sync/api/sync_data.h"
|
||||
#include "sync/api/sync_error.h"
|
||||
#include "sync/api/sync_merge_result.h"
|
||||
#include "sync/api/syncable_service.h"
|
||||
|
||||
namespace syncer {
|
||||
class SyncErrorFactory;
|
||||
}
|
||||
|
||||
namespace tracked_objects {
|
||||
class Location;
|
||||
}
|
||||
|
||||
namespace autofill {
|
||||
|
||||
class AutofillDataModel;
|
||||
class AutofillProfile;
|
||||
class AutofillProfileChange;
|
||||
class AutofillWebDataBackend;
|
||||
class AutofillWebDataService;
|
||||
class CreditCard;
|
||||
class CreditCardChange;
|
||||
|
||||
// Syncs usage counts and last use dates (metadata) for Wallet cards and
|
||||
// addresses (data). Creation and deletion of metadata on the sync server
|
||||
// follows the creation and deletion of the local metadata. (Local metadata has
|
||||
// 1:1 relationship with Wallet data.)
|
||||
class AutofillWalletMetadataSyncableService
|
||||
: public base::SupportsUserData::Data,
|
||||
public syncer::SyncableService,
|
||||
public AutofillWebDataServiceObserverOnDBThread {
|
||||
public:
|
||||
~AutofillWalletMetadataSyncableService() override;
|
||||
|
||||
// syncer::SyncableService implementation.
|
||||
syncer::SyncMergeResult MergeDataAndStartSyncing(
|
||||
syncer::ModelType type,
|
||||
const syncer::SyncDataList& initial_sync_data,
|
||||
scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
|
||||
scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) override;
|
||||
void StopSyncing(syncer::ModelType type) override;
|
||||
syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const override;
|
||||
syncer::SyncError ProcessSyncChanges(
|
||||
const tracked_objects::Location& from_here,
|
||||
const syncer::SyncChangeList& changes_from_sync) override;
|
||||
|
||||
// AutofillWebDataServiceObserverOnDBThread implementation.
|
||||
void AutofillProfileChanged(const AutofillProfileChange& change) override;
|
||||
void CreditCardChanged(const CreditCardChange& change) override;
|
||||
void AutofillMultipleChanged() override;
|
||||
|
||||
// Creates a new AutofillWalletMetadataSyncableService and hangs it off of
|
||||
// |web_data_service|, which takes ownership. This method should only be
|
||||
// called on |web_data_service|'s DB thread.
|
||||
static void CreateForWebDataServiceAndBackend(
|
||||
AutofillWebDataService* web_data_service,
|
||||
AutofillWebDataBackend* webdata_backend,
|
||||
const std::string& app_locale);
|
||||
|
||||
// Retrieves the AutofillWalletMetadataSyncableService stored on
|
||||
// |web_data_service|.
|
||||
static AutofillWalletMetadataSyncableService* FromWebDataService(
|
||||
AutofillWebDataService* web_data_service);
|
||||
|
||||
protected:
|
||||
AutofillWalletMetadataSyncableService(AutofillWebDataBackend* webdata_backend,
|
||||
const std::string& app_locale);
|
||||
|
||||
// Populates the provided |profiles| and |cards| with mappings from server ID
|
||||
// to unowned server profiles and server cards as read from disk. This data
|
||||
// contains the usage stats. Returns true on success.
|
||||
virtual bool GetLocalData(std::map<std::string, AutofillProfile*>* profiles,
|
||||
std::map<std::string, CreditCard*>* cards) const;
|
||||
|
||||
// Updates the stats for |profile| stored on disk. Does not trigger
|
||||
// notifications that this profile was updated.
|
||||
virtual bool UpdateAddressStats(const AutofillProfile& profile);
|
||||
|
||||
// Updates the stats for |credit_card| stored on disk. Does not trigger
|
||||
// notifications that this credit card was updated.
|
||||
virtual bool UpdateCardStats(const CreditCard& credit_card);
|
||||
|
||||
// Sends the |changes_to_sync| to the sync server.
|
||||
virtual syncer::SyncError SendChangesToSyncServer(
|
||||
const syncer::SyncChangeList& changes_to_sync);
|
||||
|
||||
private:
|
||||
// Merges local metadata with |sync_data|.
|
||||
//
|
||||
// Sends an "update" to the sync server if |sync_data| contains metadata that
|
||||
// is present locally, but local metadata has higher use count and later use
|
||||
// date.
|
||||
//
|
||||
// Sends a "create" to the sync server if |sync_data| does not have some local
|
||||
// metadata.
|
||||
//
|
||||
// Sends a "delete" to the sync server if |sync_data| contains metadata that
|
||||
// is not present locally.
|
||||
syncer::SyncMergeResult MergeData(const syncer::SyncDataList& sync_data);
|
||||
|
||||
// Merges |remote| metadata into a collection of metadata |locals|. Returns
|
||||
// true if the corresponding local metadata was found.
|
||||
//
|
||||
// Stores an "update" in |changes_to_sync| if |remote| corresponds to an item
|
||||
// in |locals| that has higher use count and later use date.
|
||||
template <class DataType>
|
||||
bool MergeRemote(const syncer::SyncData& remote,
|
||||
const base::Callback<bool(const DataType&)>& updater,
|
||||
std::map<std::string, DataType*>* locals,
|
||||
syncer::SyncChangeList* changes_to_sync);
|
||||
|
||||
// Sends updates to the sync server.
|
||||
void AutofillDataModelChanged(const std::string& server_id,
|
||||
const AutofillDataModel& local);
|
||||
|
||||
base::ThreadChecker thread_checker_;
|
||||
AutofillWebDataBackend* webdata_backend_; // Weak ref.
|
||||
ScopedObserver<AutofillWebDataBackend, AutofillWalletMetadataSyncableService>
|
||||
scoped_observer_;
|
||||
scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
|
||||
scoped_ptr<syncer::SyncErrorFactory> sync_error_factory_;
|
||||
|
||||
// Cache of sync server data maintained to avoid expensive calls to
|
||||
// GetAllSyncData().
|
||||
syncer::SyncDataList cache_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AutofillWalletMetadataSyncableService);
|
||||
};
|
||||
|
||||
} // namespace autofill
|
||||
|
||||
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WALLET_METADATA_SYNCABLE_SERVICE_H_
|
771
components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc
Normal file
771
components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc
Normal file
@ -0,0 +1,771 @@
|
||||
// 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/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "components/autofill/core/browser/autofill_profile.h"
|
||||
#include "components/autofill/core/browser/credit_card.h"
|
||||
#include "sync/api/sync_change.h"
|
||||
#include "sync/api/sync_change_processor_wrapper_for_test.h"
|
||||
#include "sync/api/sync_error_factory_mock.h"
|
||||
#include "sync/protocol/autofill_specifics.pb.h"
|
||||
#include "sync/protocol/sync.pb.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace autofill {
|
||||
namespace {
|
||||
|
||||
using testing::DoAll;
|
||||
using testing::ElementsAre;
|
||||
using testing::Invoke;
|
||||
using testing::NiceMock;
|
||||
using testing::Return;
|
||||
using testing::UnorderedElementsAre;
|
||||
using testing::Value;
|
||||
using testing::_;
|
||||
|
||||
ACTION_P2(GetPointersTo, profiles, cards) {
|
||||
for (auto& profile : *profiles)
|
||||
arg0->insert(std::make_pair(profile.server_id(), &profile));
|
||||
|
||||
for (auto& card : *cards)
|
||||
arg1->insert(std::make_pair(card.server_id(), &card));
|
||||
}
|
||||
|
||||
ACTION_P(SaveDataIn, list) {
|
||||
for (auto it = list->begin(); it != list->end(); ++it) {
|
||||
if (it->server_id() == arg0.server_id()) {
|
||||
*it = arg0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
list->push_back(arg0);
|
||||
}
|
||||
|
||||
// A syncable service for Wallet metadata that mocks out disk IO.
|
||||
class MockService : public AutofillWalletMetadataSyncableService {
|
||||
public:
|
||||
MockService()
|
||||
: AutofillWalletMetadataSyncableService(nullptr, std::string()) {
|
||||
ON_CALL(*this, GetLocalData(_, _))
|
||||
.WillByDefault(DoAll(GetPointersTo(&server_profiles_, &server_cards_),
|
||||
Return(true)));
|
||||
|
||||
ON_CALL(*this, UpdateAddressStats(_))
|
||||
.WillByDefault(DoAll(SaveDataIn(&server_profiles_), Return(true)));
|
||||
|
||||
ON_CALL(*this, UpdateCardStats(_))
|
||||
.WillByDefault(DoAll(SaveDataIn(&server_cards_), Return(true)));
|
||||
|
||||
ON_CALL(*this, SendChangesToSyncServer(_))
|
||||
.WillByDefault(
|
||||
Invoke(this, &MockService::SendChangesToSyncServerConcrete));
|
||||
}
|
||||
|
||||
~MockService() override {}
|
||||
|
||||
MOCK_METHOD1(UpdateAddressStats, bool(const AutofillProfile&));
|
||||
MOCK_METHOD1(UpdateCardStats, bool(const CreditCard&));
|
||||
MOCK_METHOD1(SendChangesToSyncServer,
|
||||
syncer::SyncError(const syncer::SyncChangeList&));
|
||||
|
||||
void ClearServerData() {
|
||||
server_profiles_.clear();
|
||||
server_cards_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
MOCK_CONST_METHOD2(GetLocalData,
|
||||
bool(std::map<std::string, AutofillProfile*>*,
|
||||
std::map<std::string, CreditCard*>*));
|
||||
|
||||
syncer::SyncError SendChangesToSyncServerConcrete(
|
||||
const syncer::SyncChangeList& changes) {
|
||||
return AutofillWalletMetadataSyncableService::SendChangesToSyncServer(
|
||||
changes);
|
||||
}
|
||||
|
||||
syncer::SyncDataList GetAllSyncDataConcrete(syncer::ModelType type) const {
|
||||
return AutofillWalletMetadataSyncableService::GetAllSyncData(type);
|
||||
}
|
||||
|
||||
std::vector<AutofillProfile> server_profiles_;
|
||||
std::vector<CreditCard> server_cards_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MockService);
|
||||
};
|
||||
|
||||
// Verify that nothing is sent to the sync server when there's no metadata on
|
||||
// disk.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, NoMetadataToReturn) {
|
||||
EXPECT_TRUE(NiceMock<MockService>()
|
||||
.GetAllSyncData(syncer::AUTOFILL_WALLET_METADATA)
|
||||
.empty());
|
||||
}
|
||||
|
||||
AutofillProfile BuildAddress(const std::string& server_id,
|
||||
int64 use_count,
|
||||
int64 use_date) {
|
||||
AutofillProfile profile(AutofillProfile::SERVER_PROFILE, server_id);
|
||||
profile.set_use_count(use_count);
|
||||
profile.set_use_date(base::Time::FromInternalValue(use_date));
|
||||
return profile;
|
||||
}
|
||||
|
||||
CreditCard BuildCard(const std::string& server_id,
|
||||
int64 use_count,
|
||||
int64 use_date) {
|
||||
CreditCard card(CreditCard::MASKED_SERVER_CARD, server_id);
|
||||
card.set_use_count(use_count);
|
||||
card.set_use_date(base::Time::FromInternalValue(use_date));
|
||||
return card;
|
||||
}
|
||||
|
||||
MATCHER_P5(SyncDataMatches,
|
||||
sync_tag,
|
||||
metadata_type,
|
||||
server_id,
|
||||
use_count,
|
||||
use_date,
|
||||
"") {
|
||||
return arg.IsValid() &&
|
||||
syncer::AUTOFILL_WALLET_METADATA == arg.GetDataType() &&
|
||||
sync_tag == syncer::SyncDataLocal(arg).GetTag() &&
|
||||
metadata_type == arg.GetSpecifics().wallet_metadata().type() &&
|
||||
server_id == arg.GetSpecifics().wallet_metadata().id() &&
|
||||
use_count == arg.GetSpecifics().wallet_metadata().use_count() &&
|
||||
use_date == arg.GetSpecifics().wallet_metadata().use_date();
|
||||
}
|
||||
|
||||
// Verify that all metadata from disk is sent to the sync server.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, ReturnAllMetadata) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
|
||||
EXPECT_THAT(
|
||||
local.GetAllSyncData(syncer::AUTOFILL_WALLET_METADATA),
|
||||
UnorderedElementsAre(
|
||||
SyncDataMatches("address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 1,
|
||||
2),
|
||||
SyncDataMatches("card-card", sync_pb::WalletMetadataSpecifics::CARD,
|
||||
"card", 3, 4)));
|
||||
}
|
||||
|
||||
void MergeMetadata(MockService* local, MockService* remote) {
|
||||
// The wrapper for |remote| gives it a null change processor, so sending
|
||||
// changes is not possible.
|
||||
ON_CALL(*remote, SendChangesToSyncServer(_))
|
||||
.WillByDefault(Return(syncer::SyncError()));
|
||||
|
||||
scoped_ptr<syncer::SyncErrorFactoryMock> errors(
|
||||
new syncer::SyncErrorFactoryMock);
|
||||
EXPECT_CALL(*errors, CreateAndUploadError(_, _)).Times(0);
|
||||
EXPECT_FALSE(
|
||||
local->MergeDataAndStartSyncing(
|
||||
syncer::AUTOFILL_WALLET_METADATA,
|
||||
remote->GetAllSyncData(syncer::AUTOFILL_WALLET_METADATA),
|
||||
scoped_ptr<syncer::SyncChangeProcessor>(
|
||||
new syncer::SyncChangeProcessorWrapperForTest(remote)),
|
||||
errors.Pass())
|
||||
.error()
|
||||
.IsSet());
|
||||
}
|
||||
|
||||
// Verify that nothing is written to disk or sent to the sync server when two
|
||||
// empty clients are syncing.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, TwoEmptyClients) {
|
||||
NiceMock<MockService> local;
|
||||
NiceMock<MockService> remote;
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
MergeMetadata(&local, &remote);
|
||||
}
|
||||
|
||||
MATCHER_P2(SyncChangeMatches, change_type, sync_tag, "") {
|
||||
return arg.IsValid() && change_type == arg.change_type() &&
|
||||
sync_tag == syncer::SyncDataLocal(arg.sync_data()).GetTag() &&
|
||||
syncer::AUTOFILL_WALLET_METADATA == arg.sync_data().GetDataType();
|
||||
}
|
||||
|
||||
// Verify that remote data without local counterpart is deleted during the
|
||||
// initial merge.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, DeleteFromServerOnMerge) {
|
||||
NiceMock<MockService> local;
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(
|
||||
local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeMatches(syncer::SyncChange::ACTION_DELETE, "address-addr"),
|
||||
SyncChangeMatches(syncer::SyncChange::ACTION_DELETE, "card-card"))));
|
||||
|
||||
MergeMetadata(&local, &remote);
|
||||
}
|
||||
|
||||
MATCHER_P6(SyncChangeAndDataMatch,
|
||||
change_type,
|
||||
sync_tag,
|
||||
metadata_type,
|
||||
server_id,
|
||||
use_count,
|
||||
use_date,
|
||||
"") {
|
||||
return Value(arg, SyncChangeMatches(change_type, sync_tag)) &&
|
||||
Value(arg.sync_data(),
|
||||
SyncDataMatches(sync_tag, metadata_type, server_id, use_count,
|
||||
use_date));
|
||||
}
|
||||
|
||||
// Verify that local data is sent to the sync server during the initial merge,
|
||||
// if the server does not have the data already.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMerge) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_ADD, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 1, 2),
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_ADD, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card", 3, 4))));
|
||||
|
||||
MergeMetadata(&local, &remote);
|
||||
}
|
||||
|
||||
// Verify that no data is written to disk or sent to the sync server if the
|
||||
// local and remote data are identical during the initial merge.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, IgnoreIdenticalValuesOnMerge) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
MergeMetadata(&local, &remote);
|
||||
}
|
||||
|
||||
MATCHER_P3(AutofillMetadataMatches, server_id, use_count, use_date, "") {
|
||||
return arg.server_id() == server_id &&
|
||||
arg.use_count() == static_cast<size_t>(use_count) &&
|
||||
arg.use_date() == base::Time::FromInternalValue(use_date);
|
||||
}
|
||||
|
||||
// Verify that remote data with higher values of use count and last use date is
|
||||
// saved to disk during the initial merge.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
SaveHigherValuesLocallyOnMerge) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 10, 20));
|
||||
remote.UpdateCardStats(BuildCard("card", 30, 40));
|
||||
|
||||
EXPECT_CALL(local,
|
||||
UpdateAddressStats(AutofillMetadataMatches("addr", 10, 20)));
|
||||
EXPECT_CALL(local, UpdateCardStats(AutofillMetadataMatches("card", 30, 40)));
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
MergeMetadata(&local, &remote);
|
||||
}
|
||||
|
||||
// Verify that local data with higher values of use count and last use date is
|
||||
// sent to the sync server during the initial merge.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
SendHigherValuesToServerOnMerge) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 10, 20));
|
||||
local.UpdateCardStats(BuildCard("card", 30, 40));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(
|
||||
local, SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 10, 20),
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card", 30, 40))));
|
||||
|
||||
MergeMetadata(&local, &remote);
|
||||
}
|
||||
|
||||
// Verify that lower values of metadata are not sent to the sync server when
|
||||
// local metadata is updated.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
DontSendLowerValueToServerOnSingleChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
AutofillProfile address = BuildAddress("addr", 0, 0);
|
||||
CreditCard card = BuildCard("card", 0, 0);
|
||||
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
local.AutofillProfileChanged(AutofillProfileChange(
|
||||
AutofillProfileChange::UPDATE, address.guid(), &address));
|
||||
local.CreditCardChanged(
|
||||
CreditCardChange(CreditCardChange::UPDATE, card.guid(), &card));
|
||||
}
|
||||
|
||||
// Verify that higher values of metadata are sent to the sync server when local
|
||||
// metadata is updated.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
SendHigherValuesToServerOnLocalSingleChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
AutofillProfile address = BuildAddress("addr", 10, 20);
|
||||
CreditCard card = BuildCard("card", 30, 40);
|
||||
|
||||
EXPECT_CALL(local,
|
||||
SendChangesToSyncServer(ElementsAre(SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 10, 20))));
|
||||
EXPECT_CALL(local,
|
||||
SendChangesToSyncServer(ElementsAre(SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card", 30, 40))));
|
||||
|
||||
local.AutofillProfileChanged(AutofillProfileChange(
|
||||
AutofillProfileChange::UPDATE, address.guid(), &address));
|
||||
local.CreditCardChanged(
|
||||
CreditCardChange(CreditCardChange::UPDATE, card.guid(), &card));
|
||||
}
|
||||
|
||||
// Verify that one-off addition of metadata is not sent to the sync
|
||||
// server. Metadata add and delete trigger multiple changes notification
|
||||
// instead.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, DontAddToServerOnSingleChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
AutofillProfile address = BuildAddress("new-addr", 5, 6);
|
||||
CreditCard card = BuildCard("new-card", 7, 8);
|
||||
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
local.AutofillProfileChanged(AutofillProfileChange(
|
||||
AutofillProfileChange::UPDATE, address.guid(), &address));
|
||||
local.CreditCardChanged(
|
||||
CreditCardChange(CreditCardChange::UPDATE, card.guid(), &card));
|
||||
}
|
||||
|
||||
// Verify that new metadata is sent to the sync server when multiple metadata
|
||||
// values change at once.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMultiChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
// These methods do not trigger notifications or sync:
|
||||
local.UpdateAddressStats(BuildAddress("new-addr", 5, 6));
|
||||
local.UpdateCardStats(BuildCard("new-card", 7, 8));
|
||||
|
||||
EXPECT_CALL(
|
||||
local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_ADD, "address-new-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "new-addr", 5, 6),
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_ADD, "card-new-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "new-card", 7, 8))));
|
||||
|
||||
local.AutofillMultipleChanged();
|
||||
}
|
||||
|
||||
// Verify that higher values of existing metadata are sent to the sync server
|
||||
// when multiple metadata values change at once.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
UpdateToHigherValueOnServerOnMultiChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
// These methods do not trigger notifications or sync:
|
||||
local.UpdateAddressStats(BuildAddress("addr", 5, 6));
|
||||
local.UpdateCardStats(BuildCard("card", 7, 8));
|
||||
|
||||
EXPECT_CALL(local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 5, 6),
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card", 7, 8))));
|
||||
|
||||
local.AutofillMultipleChanged();
|
||||
}
|
||||
|
||||
// Verify that lower values of existing metadata are not sent to the sync server
|
||||
// when multiple metadata values change at once.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
DontUpdateToLowerValueOnServerOnMultiChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
// These methods do not trigger notifications or sync:
|
||||
local.UpdateAddressStats(BuildAddress("addr", 0, 0));
|
||||
local.UpdateCardStats(BuildCard("card", 0, 0));
|
||||
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
local.AutofillMultipleChanged();
|
||||
}
|
||||
|
||||
// Verify that erased local metadata is also erased from the sync server when
|
||||
// multiple metadata values change at once.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, DeleteFromServerOnMultiChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
// This method dooes not trigger notifications or sync:
|
||||
local.ClearServerData();
|
||||
|
||||
EXPECT_CALL(
|
||||
local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeMatches(syncer::SyncChange::ACTION_DELETE, "address-addr"),
|
||||
SyncChangeMatches(syncer::SyncChange::ACTION_DELETE, "card-card"))));
|
||||
|
||||
local.AutofillMultipleChanged();
|
||||
}
|
||||
|
||||
// Verify that empty sync change from the sync server does not trigger writing
|
||||
// to disk or sending any data to the sync server.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest, EmptySyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
local.ProcessSyncChanges(FROM_HERE, syncer::SyncChangeList());
|
||||
}
|
||||
|
||||
syncer::SyncChange BuildChange(
|
||||
syncer::SyncChange::SyncChangeType change_type,
|
||||
const std::string& sync_tag,
|
||||
sync_pb::WalletMetadataSpecifics::Type metadata_type,
|
||||
const std::string& server_id,
|
||||
int64 use_count,
|
||||
int64 use_date) {
|
||||
sync_pb::EntitySpecifics entity;
|
||||
entity.mutable_wallet_metadata()->set_type(metadata_type);
|
||||
entity.mutable_wallet_metadata()->set_id(server_id);
|
||||
entity.mutable_wallet_metadata()->set_use_count(use_count);
|
||||
entity.mutable_wallet_metadata()->set_use_date(use_date);
|
||||
return syncer::SyncChange(
|
||||
FROM_HERE, change_type,
|
||||
syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity));
|
||||
}
|
||||
|
||||
// Verify that new metadata from the sync server is ignored when processing
|
||||
// on-going sync changes. There should be no disk writes or messages to the sync
|
||||
// server.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
IgnoreNewMetadataFromServerOnSyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
syncer::SyncChangeList changes;
|
||||
changes.push_back(
|
||||
BuildChange(syncer::SyncChange::ACTION_ADD, "address-new-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "new-addr", 5, 6));
|
||||
changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, "card-new-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD,
|
||||
"new-card", 7, 8));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
local.ProcessSyncChanges(FROM_HERE, changes);
|
||||
}
|
||||
|
||||
// Verify that higher values of metadata from the sync server are saved to
|
||||
// disk when processing on-going sync changes.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
SaveHigherValuesFromServerOnSyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
syncer::SyncChangeList changes;
|
||||
changes.push_back(
|
||||
BuildChange(syncer::SyncChange::ACTION_UPDATE, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 10, 20));
|
||||
changes.push_back(BuildChange(syncer::SyncChange::ACTION_UPDATE, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card",
|
||||
30, 40));
|
||||
|
||||
EXPECT_CALL(local,
|
||||
UpdateAddressStats(AutofillMetadataMatches("addr", 10, 20)));
|
||||
EXPECT_CALL(local, UpdateCardStats(AutofillMetadataMatches("card", 30, 40)));
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
local.ProcessSyncChanges(FROM_HERE, changes);
|
||||
}
|
||||
|
||||
// Verify that higher local values of metadata are sent to the sync server when
|
||||
// processing on-going sync changes.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
SendHigherValuesToServerOnSyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
syncer::SyncChangeList changes;
|
||||
changes.push_back(
|
||||
BuildChange(syncer::SyncChange::ACTION_UPDATE, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 0, 0));
|
||||
changes.push_back(BuildChange(syncer::SyncChange::ACTION_UPDATE, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card",
|
||||
0, 0));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 1, 2),
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card", 3, 4))));
|
||||
|
||||
local.ProcessSyncChanges(FROM_HERE, changes);
|
||||
}
|
||||
|
||||
// Verify that addition of known metadata is treated the same as an update.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
TreatAdditionOfKnownMetadataAsUpdateOnSyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
syncer::SyncChangeList changes;
|
||||
changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS,
|
||||
"addr", 0, 0));
|
||||
changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card",
|
||||
0, 0));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 1, 2),
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_UPDATE, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card", 3, 4))));
|
||||
|
||||
local.ProcessSyncChanges(FROM_HERE, changes);
|
||||
}
|
||||
|
||||
// Verify that an update of locally unknown metadata is ignored. There should be
|
||||
// no disk writes and no messages sent to the server.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
IgnoreUpdateOfUnknownMetadataOnSyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
syncer::SyncChangeList changes;
|
||||
changes.push_back(BuildChange(
|
||||
syncer::SyncChange::ACTION_UPDATE, "address-unknown-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "unknown-addr", 0, 0));
|
||||
changes.push_back(BuildChange(
|
||||
syncer::SyncChange::ACTION_UPDATE, "card-unknown-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "unknown-card", 0, 0));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
local.ProcessSyncChanges(FROM_HERE, changes);
|
||||
}
|
||||
|
||||
// Verify that deletion from the sync server of locally unknown metadata is
|
||||
// ignored. There should be no disk writes and no messages sent to the server.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
IgnoreDeleteOfUnknownMetadataOnSyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
syncer::SyncChangeList changes;
|
||||
changes.push_back(BuildChange(
|
||||
syncer::SyncChange::ACTION_DELETE, "address-unknown-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "unknown-addr", 0, 0));
|
||||
changes.push_back(BuildChange(
|
||||
syncer::SyncChange::ACTION_DELETE, "card-unknown-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "unknown-card", 0, 0));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local, SendChangesToSyncServer(_)).Times(0);
|
||||
|
||||
local.ProcessSyncChanges(FROM_HERE, changes);
|
||||
}
|
||||
|
||||
// Verify that deletion from the sync server of locally existing metadata will
|
||||
// trigger an undelete message sent to the server.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
UndeleteExistingMetadataOnSyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
local.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr", 1, 2));
|
||||
remote.UpdateCardStats(BuildCard("card", 3, 4));
|
||||
MergeMetadata(&local, &remote);
|
||||
syncer::SyncChangeList changes;
|
||||
changes.push_back(
|
||||
BuildChange(syncer::SyncChange::ACTION_DELETE, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 0, 0));
|
||||
changes.push_back(BuildChange(syncer::SyncChange::ACTION_DELETE, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card",
|
||||
0, 0));
|
||||
|
||||
EXPECT_CALL(local, UpdateAddressStats(_)).Times(0);
|
||||
EXPECT_CALL(local, UpdateCardStats(_)).Times(0);
|
||||
EXPECT_CALL(local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_ADD, "address-addr",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr", 1, 2),
|
||||
SyncChangeAndDataMatch(
|
||||
syncer::SyncChange::ACTION_ADD, "card-card",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card", 3, 4))));
|
||||
|
||||
local.ProcessSyncChanges(FROM_HERE, changes);
|
||||
}
|
||||
|
||||
// Verify that processing sync changes maintains the local cache of sync server
|
||||
// data, which is used to avoid calling the expensive GetAllSyncData() function.
|
||||
TEST(AutofillWalletMetadataSyncableServiceTest,
|
||||
CacheIsUpToDateAfterSyncChange) {
|
||||
NiceMock<MockService> local;
|
||||
local.UpdateAddressStats(BuildAddress("addr1", 1, 2));
|
||||
local.UpdateAddressStats(BuildAddress("addr2", 3, 4));
|
||||
local.UpdateCardStats(BuildCard("card1", 5, 6));
|
||||
local.UpdateCardStats(BuildCard("card2", 7, 8));
|
||||
NiceMock<MockService> remote;
|
||||
remote.UpdateAddressStats(BuildAddress("addr1", 1, 2));
|
||||
remote.UpdateAddressStats(BuildAddress("addr2", 3, 4));
|
||||
remote.UpdateCardStats(BuildCard("card1", 5, 6));
|
||||
remote.UpdateCardStats(BuildCard("card2", 7, 8));
|
||||
MergeMetadata(&local, &remote);
|
||||
syncer::SyncChangeList changes;
|
||||
changes.push_back(
|
||||
BuildChange(syncer::SyncChange::ACTION_UPDATE, "address-addr1",
|
||||
sync_pb::WalletMetadataSpecifics::ADDRESS, "addr1", 10, 20));
|
||||
changes.push_back(BuildChange(syncer::SyncChange::ACTION_UPDATE, "card-card1",
|
||||
sync_pb::WalletMetadataSpecifics::CARD, "card1",
|
||||
50, 60));
|
||||
local.ProcessSyncChanges(FROM_HERE, changes);
|
||||
// This method dooes not trigger notifications or sync:
|
||||
local.ClearServerData();
|
||||
|
||||
EXPECT_CALL(
|
||||
local,
|
||||
SendChangesToSyncServer(UnorderedElementsAre(
|
||||
SyncChangeMatches(syncer::SyncChange::ACTION_DELETE, "address-addr1"),
|
||||
SyncChangeMatches(syncer::SyncChange::ACTION_DELETE, "address-addr2"),
|
||||
SyncChangeMatches(syncer::SyncChange::ACTION_DELETE, "card-card1"),
|
||||
SyncChangeMatches(syncer::SyncChange::ACTION_DELETE, "card-card2"))));
|
||||
|
||||
local.AutofillMultipleChanged();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace autofill
|
@ -31,10 +31,10 @@ class AutofillWebDataBackend {
|
||||
// Remove expired elements from the database and commit if needed.
|
||||
virtual void RemoveExpiredFormElements() = 0;
|
||||
|
||||
// Notifies listeners on the UI thread that multiple changes have been made to
|
||||
// to Autofill records of the database.
|
||||
// NOTE: This method is intended to be called from the DB thread. It
|
||||
// asynchronously notifies listeners on the UI thread.
|
||||
// Notifies listeners on both DB and UI threads that multiple changes have
|
||||
// been made to to Autofill records of the database.
|
||||
// NOTE: This method is intended to be called from the DB thread. The UI
|
||||
// thread notifications are asynchronous.
|
||||
virtual void NotifyOfMultipleAutofillChanges() = 0;
|
||||
};
|
||||
|
||||
|
@ -67,6 +67,12 @@ void AutofillWebDataBackendImpl::RemoveExpiredFormElements() {
|
||||
|
||||
void AutofillWebDataBackendImpl::NotifyOfMultipleAutofillChanges() {
|
||||
DCHECK(db_thread_->BelongsToCurrentThread());
|
||||
|
||||
// DB thread notification.
|
||||
FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread, db_observer_list_,
|
||||
AutofillMultipleChanged());
|
||||
|
||||
// UI thread notification.
|
||||
ui_thread_->PostTask(FROM_HERE, on_changed_callback_);
|
||||
}
|
||||
|
||||
@ -278,6 +284,10 @@ WebDatabase::State AutofillWebDataBackendImpl::AddCreditCard(
|
||||
return WebDatabase::COMMIT_NOT_NEEDED;
|
||||
}
|
||||
|
||||
FOR_EACH_OBSERVER(
|
||||
AutofillWebDataServiceObserverOnDBThread, db_observer_list_,
|
||||
CreditCardChanged(CreditCardChange(CreditCardChange::ADD,
|
||||
credit_card.guid(), &credit_card)));
|
||||
return WebDatabase::COMMIT_NEEDED;
|
||||
}
|
||||
|
||||
@ -297,6 +307,11 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateCreditCard(
|
||||
NOTREACHED();
|
||||
return WebDatabase::COMMIT_NOT_NEEDED;
|
||||
}
|
||||
|
||||
FOR_EACH_OBSERVER(
|
||||
AutofillWebDataServiceObserverOnDBThread, db_observer_list_,
|
||||
CreditCardChanged(CreditCardChange(CreditCardChange::UPDATE,
|
||||
credit_card.guid(), &credit_card)));
|
||||
return WebDatabase::COMMIT_NEEDED;
|
||||
}
|
||||
|
||||
@ -307,6 +322,10 @@ WebDatabase::State AutofillWebDataBackendImpl::RemoveCreditCard(
|
||||
NOTREACHED();
|
||||
return WebDatabase::COMMIT_NOT_NEEDED;
|
||||
}
|
||||
|
||||
FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread, db_observer_list_,
|
||||
CreditCardChanged(CreditCardChange(CreditCardChange::REMOVE,
|
||||
guid, nullptr)));
|
||||
return WebDatabase::COMMIT_NEEDED;
|
||||
}
|
||||
|
||||
@ -361,20 +380,31 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardUsageStats(
|
||||
const CreditCard& card,
|
||||
WebDatabase* db) {
|
||||
DCHECK(db_thread_->BelongsToCurrentThread());
|
||||
if (AutofillTable::FromWebDatabase(db)->UpdateServerCardUsageStats(card))
|
||||
return WebDatabase::COMMIT_NEEDED;
|
||||
return WebDatabase::COMMIT_NOT_NEEDED;
|
||||
if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardUsageStats(card))
|
||||
return WebDatabase::COMMIT_NOT_NEEDED;
|
||||
|
||||
FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread, db_observer_list_,
|
||||
CreditCardChanged(CreditCardChange(CreditCardChange::UPDATE,
|
||||
card.guid(), &card)));
|
||||
|
||||
return WebDatabase::COMMIT_NEEDED;
|
||||
}
|
||||
|
||||
WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressUsageStats(
|
||||
const AutofillProfile& profile,
|
||||
WebDatabase* db) {
|
||||
DCHECK(db_thread_->BelongsToCurrentThread());
|
||||
if (AutofillTable::FromWebDatabase(db)->UpdateServerAddressUsageStats(
|
||||
if (!AutofillTable::FromWebDatabase(db)->UpdateServerAddressUsageStats(
|
||||
profile)) {
|
||||
return WebDatabase::COMMIT_NEEDED;
|
||||
return WebDatabase::COMMIT_NOT_NEEDED;
|
||||
}
|
||||
return WebDatabase::COMMIT_NOT_NEEDED;
|
||||
|
||||
FOR_EACH_OBSERVER(
|
||||
AutofillWebDataServiceObserverOnDBThread, db_observer_list_,
|
||||
AutofillProfileChanged(AutofillProfileChange(
|
||||
AutofillProfileChange::UPDATE, profile.guid(), &profile)));
|
||||
|
||||
return WebDatabase::COMMIT_NEEDED;
|
||||
}
|
||||
|
||||
WebDatabase::State AutofillWebDataBackendImpl::ClearAllServerData(
|
||||
@ -400,12 +430,17 @@ WebDatabase::State
|
||||
delete_end,
|
||||
&profile_guids,
|
||||
&credit_card_guids)) {
|
||||
for (std::vector<std::string>::iterator iter = profile_guids.begin();
|
||||
iter != profile_guids.end(); ++iter) {
|
||||
AutofillProfileChange change(AutofillProfileChange::REMOVE, *iter, NULL);
|
||||
for (const std::string& guid : profile_guids) {
|
||||
FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
|
||||
db_observer_list_,
|
||||
AutofillProfileChanged(change));
|
||||
AutofillProfileChanged(AutofillProfileChange(
|
||||
AutofillProfileChange::REMOVE, guid, nullptr)));
|
||||
}
|
||||
for (const std::string& guid : credit_card_guids) {
|
||||
FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
|
||||
db_observer_list_,
|
||||
CreditCardChanged(CreditCardChange(
|
||||
CreditCardChange::REMOVE, guid, nullptr)));
|
||||
}
|
||||
// Note: It is the caller's responsibility to post notifications for any
|
||||
// changes, e.g. by calling the Refresh() method of PersonalDataManager.
|
||||
|
@ -18,6 +18,14 @@ class AutofillWebDataServiceObserverOnDBThread {
|
||||
// in the WebDatabase.
|
||||
virtual void AutofillProfileChanged(const AutofillProfileChange& change) {}
|
||||
|
||||
// Called on DB thread when a CreditCard has been added/removed/updated in the
|
||||
// WebDatabase.
|
||||
virtual void CreditCardChanged(const CreditCardChange& change) {}
|
||||
|
||||
// Called on DB thread when multiple Autofill entries have been modified by
|
||||
// Sync.
|
||||
virtual void AutofillMultipleChanged() {}
|
||||
|
||||
protected:
|
||||
virtual ~AutofillWebDataServiceObserverOnDBThread() {}
|
||||
};
|
||||
|
@ -58,6 +58,7 @@
|
||||
'autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc',
|
||||
'autofill/core/browser/validation_unittest.cc',
|
||||
'autofill/core/browser/webdata/autofill_profile_syncable_service_unittest.cc',
|
||||
'autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc',
|
||||
'autofill/core/browser/webdata/autofill_table_unittest.cc',
|
||||
'autofill/core/browser/webdata/web_data_service_unittest.cc',
|
||||
'autofill/core/common/autofill_regexes_unittest.cc',
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "components/autofill/core/browser/webdata/autocomplete_syncable_service.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_table.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h"
|
||||
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
|
||||
#include "components/password_manager/core/browser/webdata/logins_table.h"
|
||||
@ -47,6 +48,9 @@ void InitSyncableServicesOnDBThread(
|
||||
autofill_web_data.get(), autofill_backend, app_locale);
|
||||
autofill::AutofillWalletSyncableService::CreateForWebDataServiceAndBackend(
|
||||
autofill_web_data.get(), autofill_backend, app_locale);
|
||||
autofill::AutofillWalletMetadataSyncableService::
|
||||
CreateForWebDataServiceAndBackend(autofill_web_data.get(),
|
||||
autofill_backend, app_locale);
|
||||
|
||||
autofill::AutofillProfileSyncableService::FromWebDataService(
|
||||
autofill_web_data.get())->InjectStartSyncFlare(sync_flare);
|
||||
|
@ -193,7 +193,7 @@ class SYNC_EXPORT SyncDataRemote : public SyncData {
|
||||
};
|
||||
|
||||
// gmock printer helper.
|
||||
void PrintTo(const SyncData& sync_data, std::ostream* os);
|
||||
void SYNC_EXPORT PrintTo(const SyncData& sync_data, std::ostream* os);
|
||||
|
||||
typedef std::vector<SyncData> SyncDataList;
|
||||
|
||||
|
Reference in New Issue
Block a user