Commit c6fe6160 authored by sebsg's avatar sebsg Committed by Commit Bot

[AF USS] Implement AutofillWalletSyncBridge::ApplySyncChanges

Bug: 853688
Change-Id: Ifa83c61a4bf2865379e27945bd1dc7eba1e74fc8
Reviewed-on: https://chromium-review.googlesource.com/1164162
Commit-Queue: Sebastien Seguin-Gagnon <sebsg@chromium.org>
Reviewed-by: default avatarJan Krcal <jkrcal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581713}
parent 681af3f1
...@@ -465,6 +465,7 @@ source_set("unit_tests") { ...@@ -465,6 +465,7 @@ source_set("unit_tests") {
"webdata/autofill_profile_sync_bridge_unittest.cc", "webdata/autofill_profile_sync_bridge_unittest.cc",
"webdata/autofill_profile_sync_difference_tracker_unittest.cc", "webdata/autofill_profile_sync_difference_tracker_unittest.cc",
"webdata/autofill_profile_syncable_service_unittest.cc", "webdata/autofill_profile_syncable_service_unittest.cc",
"webdata/autofill_sync_bridge_util_unittest.cc",
"webdata/autofill_table_unittest.cc", "webdata/autofill_table_unittest.cc",
"webdata/autofill_wallet_metadata_sync_bridge_unittest.cc", "webdata/autofill_wallet_metadata_sync_bridge_unittest.cc",
"webdata/autofill_wallet_metadata_syncable_service_unittest.cc", "webdata/autofill_wallet_metadata_syncable_service_unittest.cc",
......
...@@ -36,6 +36,8 @@ namespace test { ...@@ -36,6 +36,8 @@ namespace test {
namespace { namespace {
const int kValidityStateBitfield = 1984;
std::string GetRandomCardNumber() { std::string GetRandomCardNumber() {
const size_t length = 16; const size_t length = 16;
std::string value; std::string value;
...@@ -332,6 +334,50 @@ AutofillProfile GetVerifiedProfile2() { ...@@ -332,6 +334,50 @@ AutofillProfile GetVerifiedProfile2() {
return profile; return profile;
} }
AutofillProfile GetServerProfile() {
AutofillProfile profile(AutofillProfile::SERVER_PROFILE, "id1");
// Note: server profiles don't have email addresses and only have full names.
SetProfileInfo(&profile, "", "", "", "", "Google, Inc.", "123 Fake St.",
"Apt. 42", "Mountain View", "California", "94043", "US",
"1.800.555.1234");
profile.SetInfo(NAME_FULL, ASCIIToUTF16("John K. Doe"), "en");
profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE, ASCIIToUTF16("CEDEX"));
profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
ASCIIToUTF16("Santa Clara"));
profile.set_language_code("en");
profile.SetValidityFromBitfieldValue(kValidityStateBitfield);
profile.set_use_count(7);
profile.set_use_date(base::Time::FromTimeT(54321));
profile.GenerateServerProfileIdentifier();
return profile;
}
AutofillProfile GetServerProfile2() {
AutofillProfile profile(AutofillProfile::SERVER_PROFILE, "id2");
// Note: server profiles don't have email addresses.
SetProfileInfo(&profile, "", "", "", "", "Main, Inc.", "4323 Wrong St.",
"Apt. 1032", "Sunnyvale", "California", "10011", "US",
"+1 514-123-1234");
profile.SetInfo(NAME_FULL, ASCIIToUTF16("Jim S. Bristow"), "en");
profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE, ASCIIToUTF16("XEDEC"));
profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
ASCIIToUTF16("Santa Monica"));
profile.set_language_code("en");
profile.SetValidityFromBitfieldValue(kValidityStateBitfield);
profile.set_use_count(14);
profile.set_use_date(base::Time::FromTimeT(98765));
profile.GenerateServerProfileIdentifier();
return profile;
}
CreditCard GetCreditCard() { CreditCard GetCreditCard() {
CreditCard credit_card(base::GenerateGUID(), kEmptyOrigin); CreditCard credit_card(base::GenerateGUID(), kEmptyOrigin);
SetCreditCardInfo(&credit_card, "Test User", "4111111111111111" /* Visa */, SetCreditCardInfo(&credit_card, "Test User", "4111111111111111" /* Visa */,
...@@ -363,6 +409,7 @@ CreditCard GetMaskedServerCard() { ...@@ -363,6 +409,7 @@ CreditCard GetMaskedServerCard() {
test::SetCreditCardInfo(&credit_card, "Bonnie Parker", test::SetCreditCardInfo(&credit_card, "Bonnie Parker",
"2109" /* Mastercard */, "12", "2020", "1"); "2109" /* Mastercard */, "12", "2020", "1");
credit_card.SetNetworkForMaskedCard(kMasterCard); credit_card.SetNetworkForMaskedCard(kMasterCard);
credit_card.set_card_type(CreditCard::CARD_TYPE_CREDIT);
return credit_card; return credit_card;
} }
...@@ -371,6 +418,7 @@ CreditCard GetMaskedServerCardAmex() { ...@@ -371,6 +418,7 @@ CreditCard GetMaskedServerCardAmex() {
test::SetCreditCardInfo(&credit_card, "Justin Thyme", "8431" /* Amex */, "9", test::SetCreditCardInfo(&credit_card, "Justin Thyme", "8431" /* Amex */, "9",
"2020", "1"); "2020", "1");
credit_card.SetNetworkForMaskedCard(kAmericanExpressCard); credit_card.SetNetworkForMaskedCard(kAmericanExpressCard);
credit_card.set_card_type(CreditCard::CARD_TYPE_PREPAID);
return credit_card; return credit_card;
} }
......
...@@ -107,6 +107,12 @@ AutofillProfile GetVerifiedProfile(); ...@@ -107,6 +107,12 @@ AutofillProfile GetVerifiedProfile();
// Returns a verified profile full of dummy info, different to the above. // Returns a verified profile full of dummy info, different to the above.
AutofillProfile GetVerifiedProfile2(); AutofillProfile GetVerifiedProfile2();
// Returns a server profile full of dummy info.
AutofillProfile GetServerProfile();
// Returns a server profile full of dummy info, different to the above.
AutofillProfile GetServerProfile2();
// Returns a credit card full of dummy info. // Returns a credit card full of dummy info.
CreditCard GetCreditCard(); CreditCard GetCreditCard();
......
...@@ -16,4 +16,32 @@ CreditCard CreateServerCreditCard(const std::string& server_id) { ...@@ -16,4 +16,32 @@ CreditCard CreateServerCreditCard(const std::string& server_id) {
return CreditCard(CreditCard::MASKED_SERVER_CARD, server_id); return CreditCard(CreditCard::MASKED_SERVER_CARD, server_id);
} }
sync_pb::AutofillWalletSpecifics CreateAutofillWalletSpecificsForCard(
const std::string& specifics_id,
const std::string& billing_address_id) {
sync_pb::AutofillWalletSpecifics wallet_specifics;
wallet_specifics.set_type(
sync_pb::AutofillWalletSpecifics_WalletInfoType::
AutofillWalletSpecifics_WalletInfoType_MASKED_CREDIT_CARD);
sync_pb::WalletMaskedCreditCard* card_specifics =
wallet_specifics.mutable_masked_card();
card_specifics->set_id(specifics_id);
card_specifics->set_billing_address_id(billing_address_id);
return wallet_specifics;
}
sync_pb::AutofillWalletSpecifics CreateAutofillWalletSpecificsForAddress(
const std::string& specifics_id) {
sync_pb::AutofillWalletSpecifics wallet_specifics;
wallet_specifics.set_type(
sync_pb::AutofillWalletSpecifics_WalletInfoType::
AutofillWalletSpecifics_WalletInfoType_POSTAL_ADDRESS);
sync_pb::WalletPostalAddress* profile_specifics =
wallet_specifics.mutable_address();
profile_specifics->set_id(specifics_id);
return wallet_specifics;
}
} // namespace autofill } // namespace autofill
\ No newline at end of file
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/credit_card.h"
#include "components/sync/protocol/sync.pb.h"
namespace autofill { namespace autofill {
...@@ -16,6 +17,13 @@ AutofillProfile CreateServerProfile(const std::string& server_id); ...@@ -16,6 +17,13 @@ AutofillProfile CreateServerProfile(const std::string& server_id);
CreditCard CreateServerCreditCard(const std::string& server_id); CreditCard CreateServerCreditCard(const std::string& server_id);
sync_pb::AutofillWalletSpecifics CreateAutofillWalletSpecificsForAddress(
const std::string& specifics_id);
sync_pb::AutofillWalletSpecifics CreateAutofillWalletSpecificsForCard(
const std::string& specifics_id,
const std::string& billing_address_id = "");
} // namespace autofill } // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_SYNC_BRIDGE_TEST_UTIL_H_ #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_SYNC_BRIDGE_TEST_UTIL_H_
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/sync/model/entity_data.h" #include "components/sync/model/entity_data.h"
using sync_pb::AutofillWalletSpecifics; using sync_pb::AutofillWalletSpecifics;
...@@ -30,11 +31,20 @@ sync_pb::WalletMaskedCreditCard::WalletCardStatus LocalToServerStatus( ...@@ -30,11 +31,20 @@ sync_pb::WalletMaskedCreditCard::WalletCardStatus LocalToServerStatus(
case CreditCard::OK: case CreditCard::OK:
return sync_pb::WalletMaskedCreditCard::VALID; return sync_pb::WalletMaskedCreditCard::VALID;
case CreditCard::EXPIRED: case CreditCard::EXPIRED:
default:
return sync_pb::WalletMaskedCreditCard::EXPIRED; return sync_pb::WalletMaskedCreditCard::EXPIRED;
} }
} }
CreditCard::ServerStatus ServerToLocalStatus(
sync_pb::WalletMaskedCreditCard::WalletCardStatus status) {
switch (status) {
case sync_pb::WalletMaskedCreditCard::VALID:
return CreditCard::OK;
case sync_pb::WalletMaskedCreditCard::EXPIRED:
return CreditCard::EXPIRED;
}
}
sync_pb::WalletMaskedCreditCard::WalletCardType WalletCardTypeFromCardNetwork( sync_pb::WalletMaskedCreditCard::WalletCardType WalletCardTypeFromCardNetwork(
const std::string& network) { const std::string& network) {
if (network == kAmericanExpressCard) if (network == kAmericanExpressCard)
...@@ -54,6 +64,31 @@ sync_pb::WalletMaskedCreditCard::WalletCardType WalletCardTypeFromCardNetwork( ...@@ -54,6 +64,31 @@ sync_pb::WalletMaskedCreditCard::WalletCardType WalletCardTypeFromCardNetwork(
return sync_pb::WalletMaskedCreditCard::UNKNOWN; return sync_pb::WalletMaskedCreditCard::UNKNOWN;
} }
const char* CardNetworkFromWalletCardType(
sync_pb::WalletMaskedCreditCard::WalletCardType type) {
switch (type) {
case sync_pb::WalletMaskedCreditCard::AMEX:
return kAmericanExpressCard;
case sync_pb::WalletMaskedCreditCard::DISCOVER:
return kDiscoverCard;
case sync_pb::WalletMaskedCreditCard::JCB:
return kJCBCard;
case sync_pb::WalletMaskedCreditCard::MASTER_CARD:
return kMasterCard;
case sync_pb::WalletMaskedCreditCard::UNIONPAY:
return kUnionPay;
case sync_pb::WalletMaskedCreditCard::VISA:
return kVisaCard;
// These aren't supported by the client, so just declare a generic card.
case sync_pb::WalletMaskedCreditCard::MAESTRO:
case sync_pb::WalletMaskedCreditCard::SOLO:
case sync_pb::WalletMaskedCreditCard::SWITCH:
case sync_pb::WalletMaskedCreditCard::UNKNOWN:
return kGenericCard;
}
}
sync_pb::WalletMaskedCreditCard::WalletCardClass WalletCardClassFromCardType( sync_pb::WalletMaskedCreditCard::WalletCardClass WalletCardClassFromCardType(
CreditCard::CardType card_type) { CreditCard::CardType card_type) {
switch (card_type) { switch (card_type) {
...@@ -63,11 +98,25 @@ sync_pb::WalletMaskedCreditCard::WalletCardClass WalletCardClassFromCardType( ...@@ -63,11 +98,25 @@ sync_pb::WalletMaskedCreditCard::WalletCardClass WalletCardClassFromCardType(
return sync_pb::WalletMaskedCreditCard::DEBIT; return sync_pb::WalletMaskedCreditCard::DEBIT;
case CreditCard::CARD_TYPE_PREPAID: case CreditCard::CARD_TYPE_PREPAID:
return sync_pb::WalletMaskedCreditCard::PREPAID; return sync_pb::WalletMaskedCreditCard::PREPAID;
default: case CreditCard::CARD_TYPE_UNKNOWN:
return sync_pb::WalletMaskedCreditCard::UNKNOWN_CARD_CLASS; return sync_pb::WalletMaskedCreditCard::UNKNOWN_CARD_CLASS;
} }
} }
CreditCard::CardType CardTypeFromWalletCardClass(
sync_pb::WalletMaskedCreditCard::WalletCardClass card_class) {
switch (card_class) {
case sync_pb::WalletMaskedCreditCard::CREDIT:
return CreditCard::CARD_TYPE_CREDIT;
case sync_pb::WalletMaskedCreditCard::DEBIT:
return CreditCard::CARD_TYPE_DEBIT;
case sync_pb::WalletMaskedCreditCard::PREPAID:
return CreditCard::CARD_TYPE_PREPAID;
case sync_pb::WalletMaskedCreditCard::UNKNOWN_CARD_CLASS:
return CreditCard::CARD_TYPE_UNKNOWN;
}
}
} // namespace } // namespace
std::string GetSpecificsIdForEntryServerId(const std::string& server_id) { std::string GetSpecificsIdForEntryServerId(const std::string& server_id) {
...@@ -172,6 +221,44 @@ std::unique_ptr<EntityData> CreateEntityDataFromAutofillServerProfile( ...@@ -172,6 +221,44 @@ std::unique_ptr<EntityData> CreateEntityDataFromAutofillServerProfile(
return entity_data; return entity_data;
} }
AutofillProfile ProfileFromSpecifics(
const sync_pb::WalletPostalAddress& address) {
AutofillProfile profile(AutofillProfile::SERVER_PROFILE, std::string());
// AutofillProfile stores multi-line addresses with newline separators.
std::vector<base::StringPiece> street_address(
address.street_address().begin(), address.street_address().end());
profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS,
base::UTF8ToUTF16(base::JoinString(street_address, "\n")));
profile.SetRawInfo(COMPANY_NAME, base::UTF8ToUTF16(address.company_name()));
profile.SetRawInfo(ADDRESS_HOME_STATE,
base::UTF8ToUTF16(address.address_1()));
profile.SetRawInfo(ADDRESS_HOME_CITY, base::UTF8ToUTF16(address.address_2()));
profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
base::UTF8ToUTF16(address.address_3()));
// AutofillProfile doesn't support address_4 ("sub dependent locality").
profile.SetRawInfo(ADDRESS_HOME_ZIP,
base::UTF8ToUTF16(address.postal_code()));
profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE,
base::UTF8ToUTF16(address.sorting_code()));
profile.SetRawInfo(ADDRESS_HOME_COUNTRY,
base::UTF8ToUTF16(address.country_code()));
profile.set_language_code(address.language_code());
// SetInfo instead of SetRawInfo so the constituent pieces will be parsed
// for these data types.
profile.SetInfo(NAME_FULL, base::UTF8ToUTF16(address.recipient_name()),
profile.language_code());
profile.SetInfo(PHONE_HOME_WHOLE_NUMBER,
base::UTF8ToUTF16(address.phone_number()),
profile.language_code());
profile.GenerateServerProfileIdentifier();
return profile;
}
void SetAutofillWalletSpecificsFromServerCard( void SetAutofillWalletSpecificsFromServerCard(
const CreditCard& card, const CreditCard& card,
AutofillWalletSpecifics* wallet_specifics) { AutofillWalletSpecifics* wallet_specifics) {
...@@ -209,4 +296,87 @@ std::unique_ptr<EntityData> CreateEntityDataFromCard(const CreditCard& card) { ...@@ -209,4 +296,87 @@ std::unique_ptr<EntityData> CreateEntityDataFromCard(const CreditCard& card) {
return entity_data; return entity_data;
} }
CreditCard CardFromSpecifics(const sync_pb::WalletMaskedCreditCard& card) {
CreditCard result(CreditCard::MASKED_SERVER_CARD, card.id());
result.SetNumber(base::UTF8ToUTF16(card.last_four()));
result.SetServerStatus(ServerToLocalStatus(card.status()));
result.SetNetworkForMaskedCard(CardNetworkFromWalletCardType(card.type()));
result.set_card_type(CardTypeFromWalletCardClass(card.card_class()));
result.SetRawInfo(CREDIT_CARD_NAME_FULL,
base::UTF8ToUTF16(card.name_on_card()));
result.SetExpirationMonth(card.exp_month());
result.SetExpirationYear(card.exp_year());
result.set_billing_address_id(card.billing_address_id());
result.set_bank_name(card.bank_name());
return result;
}
void CopyRelevantWalletMetadataFromDisk(
const AutofillTable& table,
std::vector<CreditCard>* cards_from_server) {
std::vector<std::unique_ptr<CreditCard>> cards_on_disk;
table.GetServerCreditCards(&cards_on_disk);
// Since the number of cards is fairly small, the brute-force search is good
// enough.
for (const auto& saved_card : cards_on_disk) {
for (CreditCard& server_card : *cards_from_server) {
if (saved_card->server_id() == server_card.server_id()) {
// The wallet data doesn't have the use stats. Use the ones present on
// disk to not overwrite them with bad data.
server_card.set_use_count(saved_card->use_count());
server_card.set_use_date(saved_card->use_date());
// Keep the billing address id of the saved cards only if it points to
// a local address.
if (saved_card->billing_address_id().length() == kLocalGuidSize) {
server_card.set_billing_address_id(saved_card->billing_address_id());
break;
}
}
}
}
}
void PopulateWalletCardsAndAddresses(
const syncer::EntityChangeList& entity_data,
std::vector<CreditCard>* wallet_cards,
std::vector<AutofillProfile>* wallet_addresses) {
std::map<std::string, std::string> ids;
for (const syncer::EntityChange& change : entity_data) {
DCHECK(change.data().specifics.has_autofill_wallet());
const sync_pb::AutofillWalletSpecifics& autofill_specifics =
change.data().specifics.autofill_wallet();
switch (autofill_specifics.type()) {
case sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD:
wallet_cards->push_back(
CardFromSpecifics(autofill_specifics.masked_card()));
break;
case sync_pb::AutofillWalletSpecifics::POSTAL_ADDRESS:
wallet_addresses->push_back(
ProfileFromSpecifics(autofill_specifics.address()));
// Map the sync billing address id to the profile's id.
ids[autofill_specifics.address().id()] =
wallet_addresses->back().server_id();
break;
case sync_pb::AutofillWalletSpecifics::CUSTOMER_DATA:
case sync_pb::AutofillWalletSpecifics::UNKNOWN:
// Just ignore new entry types that the client doesn't know about.
break;
}
}
// Set the billing address of the wallet cards to the id of the appropriate
// profile.
for (CreditCard& card : *wallet_cards) {
auto it = ids.find(card.billing_address_id());
if (it != ids.end())
card.set_billing_address_id(it->second);
}
}
} // namespace autofill } // namespace autofill
\ No newline at end of file
...@@ -8,11 +8,13 @@ ...@@ -8,11 +8,13 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "components/sync/model/entity_change.h"
#include "components/sync/model/entity_data.h" #include "components/sync/model/entity_data.h"
namespace autofill { namespace autofill {
class AutofillProfile; class AutofillProfile;
class AutofillTable;
class CreditCard; class CreditCard;
// Returns the wallet specifics id for the specified |server_id|. // Returns the wallet specifics id for the specified |server_id|.
...@@ -40,6 +42,10 @@ void SetAutofillWalletSpecificsFromServerProfile( ...@@ -40,6 +42,10 @@ void SetAutofillWalletSpecificsFromServerProfile(
std::unique_ptr<syncer::EntityData> CreateEntityDataFromAutofillServerProfile( std::unique_ptr<syncer::EntityData> CreateEntityDataFromAutofillServerProfile(
const AutofillProfile& address); const AutofillProfile& address);
// Creates an AutofillProfile from the specified |address| specifics.
AutofillProfile ProfileFromSpecifics(
const sync_pb::WalletPostalAddress& address);
// Sets the fields of the |wallet_specifics| based on the the specified |card|. // Sets the fields of the |wallet_specifics| based on the the specified |card|.
void SetAutofillWalletSpecificsFromServerCard( void SetAutofillWalletSpecificsFromServerCard(
const CreditCard& card, const CreditCard& card,
...@@ -49,6 +55,26 @@ void SetAutofillWalletSpecificsFromServerCard( ...@@ -49,6 +55,26 @@ void SetAutofillWalletSpecificsFromServerCard(
std::unique_ptr<syncer::EntityData> CreateEntityDataFromCard( std::unique_ptr<syncer::EntityData> CreateEntityDataFromCard(
const CreditCard& card); const CreditCard& card);
// Creates an AutofillProfile from the specified |card| specifics.
CreditCard CardFromSpecifics(const sync_pb::WalletMaskedCreditCard& card);
// TODO(sebsg): This should probably copy the converted state for the address
// too.
// Copies the metadata from the local cards (if present) to the corresponding
// server cards so that they don't get overwritten. This is because the wallet
// data does not include those. They are handled by the
// AutofillWalletMetadataSyncBridge.
void CopyRelevantWalletMetadataFromDisk(
const AutofillTable& table,
std::vector<CreditCard>* cards_from_server);
// Populates the wallet cards and addresses from the sync data and uses the
// sync data to link the card to its billing address.
void PopulateWalletCardsAndAddresses(
const ::syncer::EntityChangeList& entity_data,
std::vector<CreditCard>* wallet_cards,
std::vector<AutofillProfile>* wallet_addresses);
} // namespace autofill } // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_SYNC_BRIDGE_UTIL_H_ #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_SYNC_BRIDGE_UTIL_H_
// Copyright 2018 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_sync_bridge_util.h"
#include <vector>
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/sync/base/hash_util.h"
#include "components/sync/model/entity_data.h"
#include "components/sync/protocol/sync.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace autofill {
namespace {
using syncer::EntityChange;
using syncer::EntityData;
class TestAutofillTable : public AutofillTable {
public:
explicit TestAutofillTable(std::vector<CreditCard> cards_on_disk)
: cards_on_disk_(cards_on_disk) {}
~TestAutofillTable() override {}
bool GetServerCreditCards(
std::vector<std::unique_ptr<CreditCard>>* cards) const override {
for (const auto& card_on_disk : cards_on_disk_)
cards->push_back(std::make_unique<CreditCard>(card_on_disk));
return true;
}
private:
std::vector<CreditCard> cards_on_disk_;
DISALLOW_COPY_AND_ASSIGN(TestAutofillTable);
};
EntityData SpecificsToEntity(const sync_pb::AutofillWalletSpecifics& specifics,
const std::string& client_tag) {
EntityData data;
*data.specifics.mutable_autofill_wallet() = specifics;
data.client_tag_hash =
syncer::GenerateSyncableHash(syncer::AUTOFILL_WALLET_DATA, client_tag);
return data;
}
class AutofillSyncBridgeUtilTest : public testing::Test {
public:
AutofillSyncBridgeUtilTest() {}
~AutofillSyncBridgeUtilTest() override {}
private:
DISALLOW_COPY_AND_ASSIGN(AutofillSyncBridgeUtilTest);
};
// Tests that the link between a card and its billing address from sync is
// present in the generated Autofill objects.
TEST_F(AutofillSyncBridgeUtilTest,
PopulateWalletCardsAndAddresses_BillingAddressIdTransfer) {
// Add an address and a card that has its billing address id set to the
// address' id.
syncer::EntityChangeList entity_data;
std::string address_id("address1");
entity_data.push_back(EntityChange::CreateAdd(
address_id,
SpecificsToEntity(CreateAutofillWalletSpecificsForAddress(address_id),
/*client_tag=*/"address-address1")
.PassToPtr()));
entity_data.push_back(EntityChange::CreateAdd(
"card1",
SpecificsToEntity(CreateAutofillWalletSpecificsForCard(
/*id=*/"card1", /*billing_address_id=*/address_id),
/*client_tag=*/"card-card1")
.PassToPtr()));
std::vector<CreditCard> wallet_cards;
std::vector<AutofillProfile> wallet_addresses;
PopulateWalletCardsAndAddresses(entity_data, &wallet_cards,
&wallet_addresses);
ASSERT_EQ(1U, wallet_cards.size());
ASSERT_EQ(1U, wallet_addresses.size());
// Make sure the card's billing address id is equal to the address' server id.
EXPECT_EQ(wallet_addresses.back().server_id(),
wallet_cards.back().billing_address_id());
}
// Verify that the billing address id from the card saved on disk is kept if it
// is a local profile guid.
TEST_F(AutofillSyncBridgeUtilTest,
CopyRelevantWalletMetadataFromDisk_KeepLocalAddresses) {
std::vector<CreditCard> cards_on_disk;
std::vector<CreditCard> wallet_cards;
// Create a local profile to be used as a billing address.
AutofillProfile billing_address;
// Create a card on disk that refers to that local profile as its billing
// address.
cards_on_disk.push_back(CreditCard());
cards_on_disk.back().set_billing_address_id(billing_address.guid());
// Create a card pulled from wallet with the same id, but a different billing
// address id.
wallet_cards.push_back(CreditCard(cards_on_disk.back()));
wallet_cards.back().set_billing_address_id("1234");
// Setup the TestAutofillTable with the cards_on_disk.
TestAutofillTable table(cards_on_disk);
CopyRelevantWalletMetadataFromDisk(table, &wallet_cards);
ASSERT_EQ(1U, wallet_cards.size());
// Make sure the wallet card replace its billing address id for the one that
// was saved on disk.
EXPECT_EQ(cards_on_disk.back().billing_address_id(),
wallet_cards.back().billing_address_id());
}
// Verify that the billing address id from the card saved on disk is overwritten
// if it does not refer to a local profile.
TEST_F(AutofillSyncBridgeUtilTest,
CopyRelevantWalletMetadataFromDisk_OverwriteOtherAddresses) {
std::string old_billing_id = "1234";
std::string new_billing_id = "9876";
std::vector<CreditCard> cards_on_disk;
std::vector<CreditCard> wallet_cards;
// Create a card on disk that does not refer to a local profile (which have 36
// chars ids).
cards_on_disk.push_back(CreditCard());
cards_on_disk.back().set_billing_address_id(old_billing_id);
// Create a card pulled from wallet with the same id, but a different billing
// address id.
wallet_cards.push_back(CreditCard(cards_on_disk.back()));
wallet_cards.back().set_billing_address_id(new_billing_id);
// Setup the TestAutofillTable with the cards_on_disk.
TestAutofillTable table(cards_on_disk);
CopyRelevantWalletMetadataFromDisk(table, &wallet_cards);
ASSERT_EQ(1U, wallet_cards.size());
// Make sure the local address billing id that was saved on disk did not
// replace the new one.
EXPECT_EQ(new_billing_id, wallet_cards.back().billing_address_id());
}
// Verify that the use stats on disk are kept when server cards are synced.
TEST_F(AutofillSyncBridgeUtilTest,
CopyRelevantWalletMetadataFromDisk_KeepUseStats) {
TestAutofillClock test_clock;
base::Time arbitrary_time = base::Time::FromDoubleT(25);
base::Time disk_time = base::Time::FromDoubleT(10);
test_clock.SetNow(arbitrary_time);
std::vector<CreditCard> cards_on_disk;
std::vector<CreditCard> wallet_cards;
// Create a card on disk with specific use stats.
cards_on_disk.push_back(CreditCard());
cards_on_disk.back().set_use_count(3U);
cards_on_disk.back().set_use_date(disk_time);
// Create a card pulled from wallet with the same id, but a different billing
// address id.
wallet_cards.push_back(CreditCard());
wallet_cards.back().set_use_count(10U);
// Setup the TestAutofillTable with the cards_on_disk.
TestAutofillTable table(cards_on_disk);
CopyRelevantWalletMetadataFromDisk(table, &wallet_cards);
ASSERT_EQ(1U, wallet_cards.size());
// Make sure the use stats from disk were kept
EXPECT_EQ(3U, wallet_cards.back().use_count());
EXPECT_EQ(disk_time, wallet_cards.back().use_date());
}
} // namespace
} // namespace autofill
\ No newline at end of file
...@@ -2394,6 +2394,7 @@ bool AutofillTable::SupportsMetadataForModelType( ...@@ -2394,6 +2394,7 @@ bool AutofillTable::SupportsMetadataForModelType(
syncer::ModelType model_type) const { syncer::ModelType model_type) const {
return (model_type == syncer::AUTOFILL || return (model_type == syncer::AUTOFILL ||
model_type == syncer::AUTOFILL_PROFILE || model_type == syncer::AUTOFILL_PROFILE ||
model_type == syncer::AUTOFILL_WALLET_DATA ||
model_type == syncer::AUTOFILL_WALLET_METADATA); model_type == syncer::AUTOFILL_WALLET_METADATA);
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/base64.h" #include "base/base64.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_profile.h"
...@@ -17,8 +18,10 @@ ...@@ -17,8 +18,10 @@
#include "components/autofill/core/browser/webdata/autofill_table.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_backend.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/sync/model/entity_data.h" #include "components/sync/model/entity_data.h"
#include "components/sync/model/mutable_data_batch.h" #include "components/sync/model/mutable_data_batch.h"
#include "components/sync/model/sync_merge_result.h"
#include "components/sync/model_impl/client_tag_based_model_type_processor.h" #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
#include "components/sync/model_impl/sync_metadata_store_change_list.h" #include "components/sync/model_impl/sync_metadata_store_change_list.h"
...@@ -75,7 +78,11 @@ AutofillWalletSyncBridge::AutofillWalletSyncBridge( ...@@ -75,7 +78,11 @@ AutofillWalletSyncBridge::AutofillWalletSyncBridge(
std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor, std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
AutofillWebDataBackend* web_data_backend) AutofillWebDataBackend* web_data_backend)
: ModelTypeSyncBridge(std::move(change_processor)), : ModelTypeSyncBridge(std::move(change_processor)),
web_data_backend_(web_data_backend) {} web_data_backend_(web_data_backend) {
DCHECK(web_data_backend_);
LoadMetadata();
}
AutofillWalletSyncBridge::~AutofillWalletSyncBridge() { AutofillWalletSyncBridge::~AutofillWalletSyncBridge() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
...@@ -91,14 +98,14 @@ AutofillWalletSyncBridge::CreateMetadataChangeList() { ...@@ -91,14 +98,14 @@ AutofillWalletSyncBridge::CreateMetadataChangeList() {
base::Optional<syncer::ModelError> AutofillWalletSyncBridge::MergeSyncData( base::Optional<syncer::ModelError> AutofillWalletSyncBridge::MergeSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_data) { syncer::EntityChangeList entity_data) {
return ApplySyncChanges(std::move(metadata_change_list), SetSyncData(entity_data, /*is_initial_data=*/true);
std::move(entity_data)); return base::nullopt;
} }
base::Optional<syncer::ModelError> AutofillWalletSyncBridge::ApplySyncChanges( base::Optional<syncer::ModelError> AutofillWalletSyncBridge::ApplySyncChanges(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_data) { syncer::EntityChangeList entity_data) {
NOTIMPLEMENTED(); SetSyncData(entity_data, /*is_initial_data=*/false);
return base::nullopt; return base::nullopt;
} }
...@@ -149,8 +156,132 @@ std::string AutofillWalletSyncBridge::GetStorageKey( ...@@ -149,8 +156,132 @@ std::string AutofillWalletSyncBridge::GetStorageKey(
entity_data.specifics.autofill_wallet()); entity_data.specifics.autofill_wallet());
} }
void AutofillWalletSyncBridge::SetSyncData(
const syncer::EntityChangeList& entity_data,
bool is_initial_data) {
std::vector<CreditCard> wallet_cards;
std::vector<AutofillProfile> wallet_addresses;
PopulateWalletCardsAndAddresses(entity_data, &wallet_cards,
&wallet_addresses);
// Users can set billing address of the server credit card locally, but that
// information does not propagate to either Chrome Sync or Google Payments
// server. To preserve user's preferred billing address and most recent use
// stats, copy them from disk into |wallet_cards|.
AutofillTable* table = GetAutofillTable();
CopyRelevantWalletMetadataFromDisk(*table, &wallet_cards);
// In the common case, the database won't have changed. Committing an update
// to the database will require at least one DB page write and will schedule
// a fsync. To avoid this I/O, it should be more efficient to do a read and
// only do the writes if something changed.
std::vector<std::unique_ptr<CreditCard>> existing_cards;
table->GetServerCreditCards(&existing_cards);
AutofillWalletDiff cards_diff =
ComputeAutofillWalletDiff(existing_cards, wallet_cards);
if (!cards_diff.IsEmpty())
table->SetServerCreditCards(wallet_cards);
std::vector<std::unique_ptr<AutofillProfile>> existing_addresses;
table->GetServerProfiles(&existing_addresses);
AutofillWalletDiff addresses_diff =
ComputeAutofillWalletDiff(existing_addresses, wallet_addresses);
if (!addresses_diff.IsEmpty())
table->SetServerProfiles(wallet_addresses);
if (!is_initial_data) {
UMA_HISTOGRAM_COUNTS_100("Autofill.WalletCardsAdded",
cards_diff.items_added);
UMA_HISTOGRAM_COUNTS_100("Autofill.WalletCardsRemoved",
cards_diff.items_removed);
UMA_HISTOGRAM_COUNTS_100("Autofill.WalletCardsAddedOrRemoved",
cards_diff.items_added + cards_diff.items_removed);
UMA_HISTOGRAM_COUNTS_100("Autofill.WalletAddressesAdded",
addresses_diff.items_added);
UMA_HISTOGRAM_COUNTS_100("Autofill.WalletAddressesRemoved",
addresses_diff.items_removed);
UMA_HISTOGRAM_COUNTS_100(
"Autofill.WalletAddressesAddedOrRemoved",
addresses_diff.items_added + addresses_diff.items_removed);
}
if (web_data_backend_ && (!cards_diff.IsEmpty() || !addresses_diff.IsEmpty()))
web_data_backend_->NotifyOfMultipleAutofillChanges();
}
// static
template <class Item>
AutofillWalletSyncBridge::AutofillWalletDiff
AutofillWalletSyncBridge::ComputeAutofillWalletDiff(
const std::vector<std::unique_ptr<Item>>& old_data,
const std::vector<Item>& new_data) {
// Build vectors of pointers, so that we can mutate (sort) them.
std::vector<const Item*> old_ptrs;
old_ptrs.reserve(old_data.size());
for (const std::unique_ptr<Item>& old_item : old_data)
old_ptrs.push_back(old_item.get());
std::vector<const Item*> new_ptrs;
new_ptrs.reserve(new_data.size());
for (const Item& new_item : new_data)
new_ptrs.push_back(&new_item);
// Sort our vectors.
auto compare = [](const Item* lhs, const Item* rhs) {
return lhs->Compare(*rhs) < 0;
};
std::sort(old_ptrs.begin(), old_ptrs.end(), compare);
std::sort(new_ptrs.begin(), new_ptrs.end(), compare);
// Walk over both of them and count added/removed elements.
AutofillWalletDiff result;
auto old_it = old_ptrs.begin();
auto new_it = new_ptrs.begin();
while (old_it != old_ptrs.end()) {
if (new_it == new_ptrs.end()) {
result.items_removed += std::distance(old_it, old_ptrs.end());
break;
}
int cmp = (*old_it)->Compare(**new_it);
if (cmp < 0) {
++result.items_removed;
++old_it;
} else if (cmp == 0) {
++old_it;
++new_it;
} else {
++result.items_added;
++new_it;
}
}
result.items_added += std::distance(new_it, new_ptrs.end());
DCHECK_EQ(old_data.size() + result.items_added - result.items_removed,
new_data.size());
return result;
}
AutofillTable* AutofillWalletSyncBridge::GetAutofillTable() { AutofillTable* AutofillWalletSyncBridge::GetAutofillTable() {
return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase()); return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
} }
void AutofillWalletSyncBridge::LoadMetadata() {
if (!web_data_backend_ || !web_data_backend_->GetDatabase() ||
!GetAutofillTable()) {
change_processor()->ReportError(
{FROM_HERE, "Failed to load AutofillWebDatabase."});
return;
}
auto batch = std::make_unique<syncer::MetadataBatch>();
if (!GetAutofillTable()->GetAllSyncMetadata(syncer::AUTOFILL_PROFILE,
batch.get())) {
change_processor()->ReportError(
{FROM_HERE, "Failed reading autofill metadata from WebDatabase."});
return;
}
change_processor()->ModelReadyToSync(std::move(batch));
}
} // namespace autofill } // namespace autofill
...@@ -19,9 +19,11 @@ ...@@ -19,9 +19,11 @@
namespace autofill { namespace autofill {
class AutofillProfile;
class AutofillTable; class AutofillTable;
class AutofillWebDataBackend; class AutofillWebDataBackend;
class AutofillWebDataService; class AutofillWebDataService;
class CreditCard;
// Sync bridge responsible for propagating local changes to the processor and // Sync bridge responsible for propagating local changes to the processor and
// applying remote changes to the local database. // applying remote changes to the local database.
...@@ -59,6 +61,30 @@ class AutofillWalletSyncBridge : public base::SupportsUserData::Data, ...@@ -59,6 +61,30 @@ class AutofillWalletSyncBridge : public base::SupportsUserData::Data,
std::string GetStorageKey(const syncer::EntityData& entity_data) override; std::string GetStorageKey(const syncer::EntityData& entity_data) override;
private: private:
struct AutofillWalletDiff {
int items_added = 0;
int items_removed = 0;
bool IsEmpty() const { return items_added == 0 && items_removed == 0; }
};
// Sets the wallet data from |entity_data| to this client. Records metrics
// about added/deleted data if not the |is_initial_data|.
void SetSyncData(const syncer::EntityChangeList& entity_data,
bool is_initial_data);
// Computes a "diff" (items added, items removed) of two vectors of items,
// which should be either CreditCard or AutofillProfile. This is used for two
// purposes:
// 1) Detecting if anything has changed, so that we don't write to disk in the
// common case where nothing has changed.
// 2) Recording metrics on the number of added/removed items.
// This is exposed as a static method so that it can be tested.
template <class Item>
static AutofillWalletDiff ComputeAutofillWalletDiff(
const std::vector<std::unique_ptr<Item>>& old_data,
const std::vector<Item>& new_data);
// Returns the table associated with the |web_data_backend_|. // Returns the table associated with the |web_data_backend_|.
AutofillTable* GetAutofillTable(); AutofillTable* GetAutofillTable();
...@@ -66,6 +92,10 @@ class AutofillWalletSyncBridge : public base::SupportsUserData::Data, ...@@ -66,6 +92,10 @@ class AutofillWalletSyncBridge : public base::SupportsUserData::Data,
// SupportsUserData, so it's guaranteed to outlive |this|. // SupportsUserData, so it's guaranteed to outlive |this|.
AutofillWebDataBackend* const web_data_backend_; AutofillWebDataBackend* const web_data_backend_;
// Synchronously load sync metadata from the autofill table and pass it to the
// processor so that it can start tracking changes.
void LoadMetadata();
// The bridge should be used on the same sequence where it is constructed. // The bridge should be used on the same sequence where it is constructed.
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment