Index: components/password_manager/core/browser/password_syncable_service.cc |
diff --git a/components/password_manager/core/browser/password_syncable_service.cc b/components/password_manager/core/browser/password_syncable_service.cc |
index 75d20b2aeb5c91aacc9f1317575af85918021162..956cc07365a077a220c06d0d0b60b4bfd2d0e8b6 100644 |
--- a/components/password_manager/core/browser/password_syncable_service.cc |
+++ b/components/password_manager/core/browser/password_syncable_service.cc |
@@ -6,14 +6,19 @@ |
#include <algorithm> |
#include <iterator> |
+#include <numeric> |
#include <utility> |
#include "base/auto_reset.h" |
#include "base/location.h" |
#include "base/memory/ptr_util.h" |
#include "base/metrics/histogram_macros.h" |
+#include "base/optional.h" |
+#include "base/strings/string_util.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "base/time/default_clock.h" |
#include "components/autofill/core/common/password_form.h" |
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h" |
#include "components/password_manager/core/browser/password_manager_metrics_util.h" |
#include "components/password_manager/core/browser/password_store_sync.h" |
#include "components/sync/model/sync_error_factory.h" |
@@ -36,20 +41,12 @@ std::string MakePasswordSyncTag(const autofill::PasswordForm& password); |
namespace { |
// Returns true iff |password_specifics| and |password_specifics| are equal |
-// memberwise. |
-bool AreLocalAndSyncPasswordsEqual( |
+// in the fields which don't comprise the sync tag. |
+bool AreLocalAndSyncPasswordsNonSyncTagEqual( |
const sync_pb::PasswordSpecificsData& password_specifics, |
const autofill::PasswordForm& password_form) { |
return (password_form.scheme == password_specifics.scheme() && |
- password_form.signon_realm == password_specifics.signon_realm() && |
- password_form.origin.spec() == password_specifics.origin() && |
password_form.action.spec() == password_specifics.action() && |
- base::UTF16ToUTF8(password_form.username_element) == |
- password_specifics.username_element() && |
- base::UTF16ToUTF8(password_form.password_element) == |
- password_specifics.password_element() && |
- base::UTF16ToUTF8(password_form.username_value) == |
- password_specifics.username_value() && |
base::UTF16ToUTF8(password_form.password_value) == |
password_specifics.password_value() && |
password_form.preferred == password_specifics.preferred() && |
@@ -66,6 +63,38 @@ bool AreLocalAndSyncPasswordsEqual( |
password_form.federation_origin.Serialize()); |
} |
+// Returns true iff |password_specifics| and |password_specifics| are equal |
+// memberwise. |
+bool AreLocalAndSyncPasswordsEqual( |
+ const sync_pb::PasswordSpecificsData& password_specifics, |
+ const autofill::PasswordForm& password_form) { |
+ return (password_form.signon_realm == password_specifics.signon_realm() && |
+ password_form.origin.spec() == password_specifics.origin() && |
+ base::UTF16ToUTF8(password_form.username_element) == |
+ password_specifics.username_element() && |
+ base::UTF16ToUTF8(password_form.password_element) == |
+ password_specifics.password_element() && |
+ base::UTF16ToUTF8(password_form.username_value) == |
+ password_specifics.username_value() && |
+ AreLocalAndSyncPasswordsNonSyncTagEqual(password_specifics, |
+ password_form)); |
+} |
+ |
+// Compares the fields which are not part of the sync tag. |
+bool AreNonSyncTagFieldsEqual(const sync_pb::PasswordSpecificsData& left, |
+ const sync_pb::PasswordSpecificsData& right) { |
+ return (left.scheme() == right.scheme() && left.action() == right.action() && |
+ left.password_value() == right.password_value() && |
+ left.preferred() == right.preferred() && |
+ left.date_created() == right.date_created() && |
+ left.blacklisted() == right.blacklisted() && |
+ left.type() == right.type() && |
+ left.times_used() == right.times_used() && |
+ left.display_name() == right.display_name() && |
+ left.avatar_url() == right.avatar_url() && |
+ left.federation_url() == right.federation_url()); |
+} |
+ |
syncer::SyncChange::SyncChangeType GetSyncChangeType( |
PasswordStoreChange::Type type) { |
switch (type) { |
@@ -91,6 +120,188 @@ void AppendPasswordFromSpecifics( |
entries->back()->date_synced = sync_time; |
} |
+// Android autofill saves credential in a different format without trailing '/'. |
+std::string GetAndroidAutofillSignonRealm(std::string android_autofill_realm) { |
engedy
2017/07/20 18:34:14
For extra readability in the code below, how about
vasilii
2017/07/20 19:50:18
Done.
|
+ if (base::EndsWith(android_autofill_realm, "/", base::CompareCase::SENSITIVE)) |
+ android_autofill_realm.erase(android_autofill_realm.size() - 1); |
+ return android_autofill_realm; |
+} |
+ |
+// The correct Android signon_realm should have a trailing '/'. |
+std::string GetCorrectAndroidSignonRealm(std::string android_realm) { |
engedy
2017/07/20 18:34:14
Do we have complete confidence in [1] that we will
vasilii
2017/07/20 19:50:17
I didn't get the question. This particular functio
engedy
2017/07/20 20:12:14
Correct, no crashes, I was just wondering if havin
|
+ if (!base::EndsWith(android_realm, "/", base::CompareCase::SENSITIVE)) |
+ android_realm += '/'; |
+ return android_realm; |
+} |
+ |
+// Android autofill saves credential in a different format without trailing '/'. |
engedy
2017/07/20 18:34:14
typo: credentials
vasilii
2017/07/20 19:50:18
Done.
|
+// Return a sync tag for the style used by Android Autofill in the GMS Core v12. |
engedy
2017/07/20 18:34:14
typo: s/in the GMS/in GMS/
vasilii
2017/07/20 19:50:18
Done.
|
+std::string AndroidAutofillSyncTag( |
+ const sync_pb::PasswordSpecificsData& password) { |
+ // realm has the same value as the origin. |
+ std::string origin = GetAndroidAutofillSignonRealm(password.origin()); |
engedy
2017/07/20 18:34:14
Just to triple check:
-- Do you have some concre
vasilii
2017/07/20 19:50:17
- Yes, if you open the email thread then you can f
|
+ std::string signon_realm = |
+ GetAndroidAutofillSignonRealm(password.signon_realm()); |
+ return (net::EscapePath(origin) + "|" + |
+ net::EscapePath(password.username_element()) + "|" + |
+ net::EscapePath(password.username_value()) + "|" + |
+ net::EscapePath(password.password_element()) + "|" + |
+ net::EscapePath(signon_realm)); |
+} |
+ |
+// Return a sync tag for the correct format used by Android. |
+std::string AndroidCorrectSyncTag( |
+ const sync_pb::PasswordSpecificsData& password) { |
+ // realm has the same value as the origin. |
+ std::string origin = GetCorrectAndroidSignonRealm(password.origin()); |
+ std::string signon_realm = |
+ GetCorrectAndroidSignonRealm(password.signon_realm()); |
+ return (net::EscapePath(origin) + "|" + |
+ net::EscapePath(password.username_element()) + "|" + |
+ net::EscapePath(password.username_value()) + "|" + |
+ net::EscapePath(password.password_element()) + "|" + |
+ net::EscapePath(signon_realm)); |
+} |
+ |
+void PasswordSpecificsFromPassword( |
+ const autofill::PasswordForm& password_form, |
+ sync_pb::PasswordSpecificsData* password_specifics) { |
+#define CopyField(field) password_specifics->set_##field(password_form.field) |
+#define CopyStringField(field) \ |
+ password_specifics->set_##field(base::UTF16ToUTF8(password_form.field)) |
+ CopyField(scheme); |
+ CopyField(signon_realm); |
+ password_specifics->set_origin(password_form.origin.spec()); |
+ password_specifics->set_action(password_form.action.spec()); |
+ CopyStringField(username_element); |
+ CopyStringField(password_element); |
+ CopyStringField(username_value); |
+ CopyStringField(password_value); |
+ CopyField(preferred); |
+ password_specifics->set_date_created( |
+ password_form.date_created.ToInternalValue()); |
+ password_specifics->set_blacklisted(password_form.blacklisted_by_user); |
+ CopyField(type); |
+ CopyField(times_used); |
+ CopyStringField(display_name); |
+ password_specifics->set_avatar_url(password_form.icon_url.spec()); |
+ password_specifics->set_federation_url( |
+ password_form.federation_origin.unique() |
+ ? std::string() |
+ : password_form.federation_origin.Serialize()); |
+#undef CopyStringField |
+#undef CopyField |
+} |
+ |
+struct AndroidMergeResult { |
+ // New value for Android entry in the correct format. |
+ base::Optional<syncer::SyncData> new_android_correct; |
+ // New value for Android autofill entry. |
+ base::Optional<syncer::SyncData> new_android_incorrect; |
+ // New value for local entry in the correct format. |
+ base::Optional<autofill::PasswordForm> new_local_correct; |
+ // New value for local entry in the Android autofill format. |
+ base::Optional<autofill::PasswordForm> new_local_incorrect; |
+}; |
+ |
+// Perform deduplication of Android credentials saved in the wrong format. As |
+// the result all the four entries should be created and have the same value. |
+AndroidMergeResult Perform4WayMergeAndroidCredentials( |
+ const sync_pb::PasswordSpecificsData* correct_android, |
+ const sync_pb::PasswordSpecificsData* incorrect_android, |
+ const autofill::PasswordForm* correct_local, |
+ const autofill::PasswordForm* incorrect_local) { |
+ AndroidMergeResult result; |
+ |
+ base::Optional<sync_pb::PasswordSpecificsData> local_correct_ps; |
+ if (correct_local) { |
+ local_correct_ps = sync_pb::PasswordSpecificsData(); |
+ PasswordSpecificsFromPassword(*correct_local, &local_correct_ps.value()); |
+ } |
+ |
+ base::Optional<sync_pb::PasswordSpecificsData> local_incorrect_ps; |
+ if (incorrect_local) { |
+ local_incorrect_ps = sync_pb::PasswordSpecificsData(); |
+ PasswordSpecificsFromPassword(*incorrect_local, |
+ &local_incorrect_ps.value()); |
+ } |
+ |
+ const sync_pb::PasswordSpecificsData* all_data[4] = { |
+ correct_android, incorrect_android, |
+ local_correct_ps ? &local_correct_ps.value() : nullptr, |
+ local_incorrect_ps ? &local_incorrect_ps.value() : nullptr}; |
+ |
+ // |newest_data| will point to the newest entry out of all 4. |
+ const sync_pb::PasswordSpecificsData* newest_data = nullptr; |
+ newest_data = |
engedy
2017/07/20 18:34:14
nit: Isn't this actually longer than a good old ra
vasilii
2017/07/20 19:50:17
Done.
|
+ std::accumulate(all_data, all_data + 4, newest_data, |
+ [](const sync_pb::PasswordSpecificsData* newest, |
+ const sync_pb::PasswordSpecificsData* current) { |
+ if (newest && current) { |
+ if (current->date_created() > newest->date_created()) |
+ newest = current; |
+ } else if (current) { |
+ newest = current; |
+ } |
+ return newest; |
+ }); |
+ DCHECK(newest_data); |
+ |
+ const std::string correct_tag = AndroidCorrectSyncTag(*newest_data); |
engedy
2017/07/20 18:34:14
For brevity, have you considered precalculating th
vasilii
2017/07/20 19:50:18
Done.
|
+ const std::string incorrect_tag = AndroidAutofillSyncTag(*newest_data); |
+ // Set the correct Sync entry if needed. |
+ if (newest_data != correct_android && |
+ (!correct_android || |
+ !AreNonSyncTagFieldsEqual(*correct_android, *newest_data))) { |
+ sync_pb::EntitySpecifics password_data; |
+ sync_pb::PasswordSpecificsData* password_specifics = |
+ password_data.mutable_password()->mutable_client_only_encrypted_data(); |
engedy
2017/07/20 18:34:14
To double check: are you familiar with how non-enc
vasilii
2017/07/20 19:50:18
Right, not our business.
|
+ *password_specifics = *newest_data; |
+ password_specifics->set_origin( |
+ GetCorrectAndroidSignonRealm(newest_data->origin())); |
+ password_specifics->set_signon_realm( |
+ GetCorrectAndroidSignonRealm(newest_data->signon_realm())); |
+ result.new_android_correct = syncer::SyncData::CreateLocalData( |
+ correct_tag, correct_tag, password_data); |
+ } |
+ // Set the Andoroid Autofill Sync entry if needed. |
engedy
2017/07/20 18:34:14
optional nit: Consider blank lines between blocks.
vasilii
2017/07/20 19:50:18
Done.
|
+ if (newest_data != incorrect_android && |
+ (!incorrect_android || |
+ !AreNonSyncTagFieldsEqual(*incorrect_android, *newest_data))) { |
+ sync_pb::EntitySpecifics password_data; |
+ sync_pb::PasswordSpecificsData* password_specifics = |
+ password_data.mutable_password()->mutable_client_only_encrypted_data(); |
+ *password_specifics = *newest_data; |
+ password_specifics->set_origin( |
+ GetAndroidAutofillSignonRealm(newest_data->origin())); |
+ password_specifics->set_signon_realm( |
+ GetAndroidAutofillSignonRealm(newest_data->signon_realm())); |
+ result.new_android_incorrect = syncer::SyncData::CreateLocalData( |
+ incorrect_tag, incorrect_tag, password_data); |
+ } |
+ // Set the correct local entry if needed. |
+ if (!local_correct_ps || |
+ (newest_data != &local_correct_ps.value() && |
+ !AreNonSyncTagFieldsEqual(local_correct_ps.value(), *newest_data))) { |
+ result.new_local_correct = PasswordFromSpecifics(*newest_data); |
+ result.new_local_correct.value().origin = |
+ GURL(GetCorrectAndroidSignonRealm(newest_data->origin())); |
+ result.new_local_correct.value().signon_realm = |
+ GetCorrectAndroidSignonRealm(newest_data->signon_realm()); |
+ } |
+ // Set the incorrect local entry if needed. |
+ if (!local_incorrect_ps || |
+ (newest_data != &local_incorrect_ps.value() && |
+ !AreNonSyncTagFieldsEqual(local_incorrect_ps.value(), *newest_data))) { |
+ result.new_local_incorrect = PasswordFromSpecifics(*newest_data); |
+ result.new_local_incorrect.value().origin = |
+ GURL(GetAndroidAutofillSignonRealm(newest_data->origin())); |
+ result.new_local_incorrect.value().signon_realm = |
+ GetAndroidAutofillSignonRealm(newest_data->signon_realm()); |
+ } |
+ return result; |
+} |
+ |
} // namespace |
struct PasswordSyncableService::SyncEntries { |
@@ -124,8 +335,9 @@ struct PasswordSyncableService::SyncEntries { |
PasswordSyncableService::PasswordSyncableService( |
PasswordStoreSync* password_store) |
- : password_store_(password_store), is_processing_sync_changes_(false) { |
-} |
+ : password_store_(password_store), |
+ clock_(new base::DefaultClock), |
+ is_processing_sync_changes_(false) {} |
PasswordSyncableService::~PasswordSyncableService() { |
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
@@ -163,17 +375,12 @@ syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing( |
} |
merge_result.set_num_items_before_association(new_local_entries.size()); |
+ // Changes from Sync to be applied locally. |
SyncEntries sync_entries; |
// Changes from password db that need to be propagated to sync. |
syncer::SyncChangeList updated_db_entries; |
- for (syncer::SyncDataList::const_iterator sync_iter = |
- initial_sync_data.begin(); |
- sync_iter != initial_sync_data.end(); ++sync_iter) { |
- CreateOrUpdateEntry(*sync_iter, |
- &new_local_entries, |
- &sync_entries, |
- &updated_db_entries); |
- } |
+ MergeSyncDataWithLocalData(initial_sync_data, &new_local_entries, |
+ &sync_entries, &updated_db_entries); |
for (PasswordEntryMap::iterator it = new_local_entries.begin(); |
it != new_local_entries.end(); ++it) { |
@@ -237,7 +444,7 @@ syncer::SyncError PasswordSyncableService::ProcessSyncChanges( |
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true); |
SyncEntries sync_entries; |
- base::Time time_now = base::Time::Now(); |
+ base::Time time_now = clock_->Now(); |
for (syncer::SyncChangeList::const_iterator it = change_list.begin(); |
it != change_list.end(); ++it) { |
@@ -250,6 +457,13 @@ syncer::SyncError PasswordSyncableService::ProcessSyncChanges( |
} |
AppendPasswordFromSpecifics( |
specifics.password().client_only_encrypted_data(), time_now, entries); |
+ if (IsValidAndroidFacetURI(entries->back()->signon_realm)) { |
+ // Fix the Android Autofill credentials if needed. |
+ entries->back()->signon_realm = |
+ GetCorrectAndroidSignonRealm(entries->back()->signon_realm); |
+ entries->back()->origin = |
+ GURL(GetCorrectAndroidSignonRealm(entries->back()->origin.spec())); |
+ } |
} |
WriteToPasswordStore(sync_entries); |
@@ -339,21 +553,129 @@ void PasswordSyncableService::WriteToPasswordStore(const SyncEntries& entries) { |
password_store_->NotifyLoginsChanged(changes); |
} |
-// static |
+void PasswordSyncableService::MergeSyncDataWithLocalData( |
+ const syncer::SyncDataList& sync_data, |
+ PasswordEntryMap* unmatched_data_from_password_db, |
+ SyncEntries* sync_entries, |
+ syncer::SyncChangeList* updated_db_entries) { |
+ std::map<std::string, const sync_pb::PasswordSpecificsData*> sync_data_map; |
+ for (const auto& data : sync_data) { |
+ const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); |
+ const sync_pb::PasswordSpecificsData* password_specifics = |
+ &specifics.password().client_only_encrypted_data(); |
+ sync_data_map[MakePasswordSyncTag(*password_specifics)] = |
engedy
2017/07/20 18:34:14
Should we add a DCHECK() that there was no such ke
vasilii
2017/07/20 19:50:18
Done.
|
+ password_specifics; |
+ } |
+ |
+ for (auto it = sync_data_map.begin(); it != sync_data_map.end();) { |
+ if (IsValidAndroidFacetURI(it->second->signon_realm())) { |
+ // Perform deduplication of Android credentials saved in the wrong format. |
+ // An incorrect entry is copied in the correct format so Chrome can make |
engedy
2017/07/20 18:34:14
phrasing nit: For each incorrect entry, a duplicat
vasilii
2017/07/20 19:50:17
Done.
|
+ // use of it. The wrong sync entries are not deleted for now. |
engedy
2017/07/20 18:34:14
phrasing nit: s/wrong/incorrect/
vasilii
2017/07/20 19:50:18
Done.
|
+ std::string incorrect_tag = AndroidAutofillSyncTag(*it->second); |
+ std::string correct_tag = AndroidCorrectSyncTag(*it->second); |
+ auto it_android_incorrect = sync_data_map.find(incorrect_tag); |
engedy
2017/07/20 18:34:14
For extra readability, have you considered s/andro
vasilii
2017/07/20 19:50:17
Done.
|
+ auto it_android_correct = sync_data_map.find(correct_tag); |
+ auto it_local_data_correct = |
+ unmatched_data_from_password_db->find(correct_tag); |
+ auto it_local_data_incorrect = |
+ unmatched_data_from_password_db->find(incorrect_tag); |
engedy
2017/07/20 18:34:14
Should we fall back to default handling if (it !=
vasilii
2017/07/20 19:50:18
Good point.
They claim that they use the same lib
|
+ if (it_android_incorrect == sync_data_map.end() && |
+ it_local_data_incorrect == unmatched_data_from_password_db->end()) { |
+ // Wrong credential don't exist. Just do what Sync would normally do. |
+ CreateOrUpdateEntry(*it->second, unmatched_data_from_password_db, |
+ sync_entries, updated_db_entries); |
+ ++it; |
+ } else { |
+ AndroidMergeResult result = Perform4WayMergeAndroidCredentials( |
+ it_android_correct == sync_data_map.end() |
+ ? nullptr |
+ : it_android_correct->second, |
+ it_android_incorrect == sync_data_map.end() |
+ ? nullptr |
+ : it_android_incorrect->second, |
+ it_local_data_correct == unmatched_data_from_password_db->end() |
+ ? nullptr |
+ : it_local_data_correct->second, |
+ it_local_data_incorrect == unmatched_data_from_password_db->end() |
+ ? nullptr |
+ : it_local_data_incorrect->second); |
+ // Add or update the correct local entry. |
+ if (result.new_local_correct) { |
+ auto* local_changes = sync_entries->EntriesForChangeType( |
+ it_local_data_correct == unmatched_data_from_password_db->end() |
+ ? syncer::SyncChange::ACTION_ADD |
+ : syncer::SyncChange::ACTION_UPDATE); |
+ local_changes->push_back(base::MakeUnique<autofill::PasswordForm>( |
+ result.new_local_correct.value())); |
+ local_changes->back()->date_synced = clock_->Now(); |
+ } |
+ // Add or update the incorrect local entry. |
+ if (result.new_local_incorrect) { |
+ auto* local_changes = sync_entries->EntriesForChangeType( |
+ it_local_data_incorrect == unmatched_data_from_password_db->end() |
+ ? syncer::SyncChange::ACTION_ADD |
+ : syncer::SyncChange::ACTION_UPDATE); |
+ local_changes->push_back(base::MakeUnique<autofill::PasswordForm>( |
+ result.new_local_incorrect.value())); |
+ local_changes->back()->date_synced = clock_->Now(); |
+ } |
+ if (it_local_data_correct != unmatched_data_from_password_db->end()) |
+ unmatched_data_from_password_db->erase(it_local_data_correct); |
+ if (it_local_data_incorrect != unmatched_data_from_password_db->end()) |
+ unmatched_data_from_password_db->erase(it_local_data_incorrect); |
+ // Add or update the correct sync entry. |
+ if (result.new_android_correct) { |
+ updated_db_entries->push_back( |
+ syncer::SyncChange(FROM_HERE, |
+ it_android_correct == sync_data_map.end() |
+ ? syncer::SyncChange::ACTION_ADD |
+ : syncer::SyncChange::ACTION_UPDATE, |
+ result.new_android_correct.value())); |
+ } |
+ // Add or update the Android Autofill sync entry. |
+ if (result.new_android_incorrect) { |
+ updated_db_entries->push_back( |
+ syncer::SyncChange(FROM_HERE, |
+ it_android_incorrect == sync_data_map.end() |
+ ? syncer::SyncChange::ACTION_ADD |
+ : syncer::SyncChange::ACTION_UPDATE, |
+ result.new_android_incorrect.value())); |
+ } |
+ bool increment = true; |
+ for (auto sync_data_it : {it_android_incorrect, it_android_correct}) { |
+ if (sync_data_it != sync_data_map.end()) { |
+ if (sync_data_it == it) { |
+ it = sync_data_map.erase(it); |
+ increment = false; |
+ } else { |
+ sync_data_map.erase(sync_data_it); |
+ } |
+ } |
+ } |
+ if (increment) |
+ ++it; |
+ } |
+ } else { |
+ // Not Android. |
+ CreateOrUpdateEntry(*it->second, unmatched_data_from_password_db, |
+ sync_entries, updated_db_entries); |
+ ++it; |
+ } |
+ } |
+} |
+ |
void PasswordSyncableService::CreateOrUpdateEntry( |
- const syncer::SyncData& data, |
+ const sync_pb::PasswordSpecificsData& password_specifics, |
PasswordEntryMap* unmatched_data_from_password_db, |
SyncEntries* sync_entries, |
syncer::SyncChangeList* updated_db_entries) { |
- const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); |
- const sync_pb::PasswordSpecificsData& password_specifics( |
- specifics.password().client_only_encrypted_data()); |
std::string tag = MakePasswordSyncTag(password_specifics); |
// Check whether the data from sync is already in the password store. |
PasswordEntryMap::iterator existing_local_entry_iter = |
unmatched_data_from_password_db->find(tag); |
- base::Time time_now = base::Time::Now(); |
+ base::Time time_now = clock_->Now(); |
if (existing_local_entry_iter == unmatched_data_from_password_db->end()) { |
// The sync data is not in the password store, so we need to create it in |
// the password store. Add the entry to the new_entries list. |
@@ -401,31 +723,7 @@ syncer::SyncData SyncDataFromPassword( |
sync_pb::EntitySpecifics password_data; |
sync_pb::PasswordSpecificsData* password_specifics = |
password_data.mutable_password()->mutable_client_only_encrypted_data(); |
-#define CopyField(field) password_specifics->set_##field(password_form.field) |
-#define CopyStringField(field) \ |
- password_specifics->set_##field(base::UTF16ToUTF8(password_form.field)) |
- CopyField(scheme); |
- CopyField(signon_realm); |
- password_specifics->set_origin(password_form.origin.spec()); |
- password_specifics->set_action(password_form.action.spec()); |
- CopyStringField(username_element); |
- CopyStringField(password_element); |
- CopyStringField(username_value); |
- CopyStringField(password_value); |
- CopyField(preferred); |
- password_specifics->set_date_created( |
- password_form.date_created.ToInternalValue()); |
- password_specifics->set_blacklisted(password_form.blacklisted_by_user); |
- CopyField(type); |
- CopyField(times_used); |
- CopyStringField(display_name); |
- password_specifics->set_avatar_url(password_form.icon_url.spec()); |
- password_specifics->set_federation_url( |
- password_form.federation_origin.unique() |
- ? std::string() |
- : password_form.federation_origin.Serialize()); |
-#undef CopyStringField |
-#undef CopyField |
+ PasswordSpecificsFromPassword(password_form, password_specifics); |
std::string tag = MakePasswordSyncTag(*password_specifics); |
return syncer::SyncData::CreateLocalData(tag, tag, password_data); |