Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1344)

Unified Diff: chrome/browser/password_manager/password_syncable_service.cc

Issue 131003006: Password sync refactoring: implemented |MergeDataAndStartSyncing| and unit_tests (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge with trunk Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/password_manager/password_syncable_service.cc
diff --git a/chrome/browser/password_manager/password_syncable_service.cc b/chrome/browser/password_manager/password_syncable_service.cc
index aab4b79fba9a0a1049d7583d9771d59ebdeb38f5..aa4b3aaf14921b1ff13a9288406f2ea41be08715 100644
--- a/chrome/browser/password_manager/password_syncable_service.cc
+++ b/chrome/browser/password_manager/password_syncable_service.cc
@@ -5,6 +5,8 @@
#include "chrome/browser/password_manager/password_syncable_service.h"
#include "base/location.h"
+#include "base/memory/scoped_vector.h"
+#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/password_manager/password_store.h"
#include "components/autofill/core/common/password_form.h"
@@ -13,6 +15,65 @@
namespace {
+// Merges the local and sync passwords and outputs the entry into
+// |new_password_form|. Returns true if the local and the sync passwords differ.
+// Returns false if they are identical.
+bool MergeLocalAndSyncPasswords(
+ const sync_pb::PasswordSpecificsData& password_specifics,
+ const autofill::PasswordForm& password_form,
+ autofill::PasswordForm* new_password_form) {
+ DCHECK(new_password_form);
+ if (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.ssl_valid == password_specifics.ssl_valid() &&
+ password_form.preferred == password_specifics.preferred() &&
+ password_form.date_created.ToInternalValue() ==
+ password_specifics.date_created() &&
+ password_form.blacklisted_by_user == password_specifics.blacklisted()) {
+ return false;
+ }
+
+ // If the passwords differ, take the one that was created more recently.
+ if (base::Time::FromInternalValue(password_specifics.date_created()) <=
+ password_form.date_created) {
+ *new_password_form = password_form;
+ } else {
+ PasswordFromSpecifics(password_specifics, new_password_form);
+ }
+
+ return true;
+}
+
+std::string MakePasswordSyncTag(const std::string& origin_url,
+ const std::string& username_element,
+ const std::string& username_value,
+ const std::string& password_element,
+ const std::string& signon_realm) {
+ return net::EscapePath(origin_url) + "|" +
+ net::EscapePath(username_element) + "|" +
+ net::EscapePath(username_value) + "|" +
+ net::EscapePath(password_element) + "|" +
+ net::EscapePath(signon_realm);
+}
+
+std::string MakePasswordSyncTag(const autofill::PasswordForm& password) {
+ return MakePasswordSyncTag(password.origin.spec(),
+ base::UTF16ToUTF8(password.username_element),
+ base::UTF16ToUTF8(password.username_value),
+ base::UTF16ToUTF8(password.password_element),
+ password.signon_realm);
+}
+
syncer::SyncChange::SyncChangeType GetSyncChangeType(
PasswordStoreChange::Type type) {
switch (type) {
@@ -30,8 +91,8 @@ syncer::SyncChange::SyncChangeType GetSyncChangeType(
void AppendChanges(const PasswordStoreChangeList& new_changes,
PasswordStoreChangeList* all_changes) {
all_changes->insert(all_changes->end(),
- new_changes.begin(),
- new_changes.end());
+ new_changes.begin(),
+ new_changes.end());
}
} // namespace
@@ -43,19 +104,82 @@ PasswordSyncableService::PasswordSyncableService(
PasswordSyncableService::~PasswordSyncableService() {}
-syncer::SyncMergeResult
-PasswordSyncableService::MergeDataAndStartSyncing(
+syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
syncer::ModelType type,
const syncer::SyncDataList& initial_sync_data,
scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
+ DCHECK_EQ(syncer::PASSWORDS, type);
syncer::SyncMergeResult merge_result(type);
sync_error_factory_ = sync_error_factory.Pass();
sync_processor_ = sync_processor.Pass();
- merge_result.set_error(sync_error_factory->CreateAndUploadError(
- FROM_HERE,
- "Password Syncable Service Not Implemented."));
+ ScopedVector<autofill::PasswordForm> password_entries;
+ if (!password_store_->FillAutofillableLogins(&password_entries.get())) {
+ // Password store often fails to load passwords. Track failures with UMA.
+ // (http://crbug.com/249000)
+ UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad",
+ syncer::PASSWORDS,
+ syncer::MODEL_TYPE_COUNT);
+ merge_result.set_error(sync_error_factory_->CreateAndUploadError(
+ FROM_HERE,
+ "Failed to get passwords from store."));
+ return merge_result;
+ }
+
+ PasswordEntryMap new_local_entries;
+ for (PasswordForms::iterator it = password_entries.begin();
+ it != password_entries.end(); ++it) {
+ autofill::PasswordForm* password_form = *it;
+ // We add all the db entries as |new_local_entries| initially. During
+ // model association entries that match a sync entry will be
+ // removed and this list will only contain entries that are not in sync.
+ new_local_entries.insert(
+ std::make_pair(MakePasswordSyncTag(*password_form), password_form));
+ }
+
+ merge_result.set_num_items_before_association(new_local_entries.size());
+
+ // List that contains the entries that are known only to sync.
+ ScopedVector<autofill::PasswordForm> new_sync_entries;
+
+ // List that contains the entries that are known to both sync and db but
+ // have updates in sync. They need to be updated in the passwords db.
+ ScopedVector<autofill::PasswordForm> updated_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,
+ &new_sync_entries,
+ &updated_sync_entries,
+ &updated_db_entries);
+ }
+
+ WriteToPasswordStore(new_sync_entries.get(),
+ updated_sync_entries.get());
+
+ merge_result.set_num_items_after_association(
+ merge_result.num_items_before_association() + new_sync_entries.size());
+
+ merge_result.set_num_items_added(new_sync_entries.size());
+
+ merge_result.set_num_items_modified(updated_sync_entries.size());
+
+ for (PasswordEntryMap::iterator it = new_local_entries.begin();
+ it != new_local_entries.end();
+ ++it) {
+ updated_db_entries.push_back(
+ syncer::SyncChange(FROM_HERE,
+ syncer::SyncChange::ACTION_ADD,
+ SyncDataFromPassword(*it->second)));
+ }
+
+ merge_result.set_error(
+ sync_processor_->ProcessSyncChanges(FROM_HERE, updated_db_entries));
return merge_result;
}
@@ -88,71 +212,87 @@ void PasswordSyncableService::ActOnPasswordStoreChanges(
for (PasswordStoreChangeList::const_iterator it = local_changes.begin();
it != local_changes.end();
++it) {
- sync_changes.push_back(syncer::SyncChange(FROM_HERE,
- GetSyncChangeType(it->type()),
- CreateSyncData(it->form())));
+ sync_changes.push_back(
+ syncer::SyncChange(FROM_HERE,
+ GetSyncChangeType(it->type()),
+ SyncDataFromPassword(it->form())));
}
sync_processor_->ProcessSyncChanges(FROM_HERE, sync_changes);
}
-// static
-std::string PasswordSyncableService::MakeTag(
- const std::string& origin_url,
- const std::string& username_element,
- const std::string& username_value,
- const std::string& password_element,
- const std::string& signon_realm) {
- return net::EscapePath(origin_url) + "|" +
- net::EscapePath(username_element) + "|" +
- net::EscapePath(username_value) + "|" +
- net::EscapePath(password_element) + "|" +
- net::EscapePath(signon_realm);
-}
-
-// static
-std::string PasswordSyncableService::MakeTag(
- const autofill::PasswordForm& password) {
- return MakeTag(password.origin.spec(),
- base::UTF16ToUTF8(password.username_element),
- base::UTF16ToUTF8(password.username_value),
- base::UTF16ToUTF8(password.password_element),
- password.signon_realm);
-}
-
-// static
-std::string PasswordSyncableService::MakeTag(
- const sync_pb::PasswordSpecificsData& password) {
- return MakeTag(password.origin(),
- password.username_element(),
- password.username_value(),
- password.password_element(),
- password.signon_realm());
-}
-
void PasswordSyncableService::WriteToPasswordStore(
- PasswordForms* new_entries,
- PasswordForms* updated_entries) {
+ const PasswordForms& new_entries,
+ const PasswordForms& updated_entries) {
PasswordStoreChangeList changes;
for (std::vector<autofill::PasswordForm*>::const_iterator it =
- new_entries->begin();
- it != new_entries->end();
+ new_entries.begin();
+ it != new_entries.end();
++it) {
AppendChanges(password_store_->AddLoginImpl(**it), &changes);
}
for (std::vector<autofill::PasswordForm*>::const_iterator it =
- updated_entries->begin();
- it != updated_entries->end();
+ updated_entries.begin();
+ it != updated_entries.end();
++it) {
AppendChanges(password_store_->UpdateLoginImpl(**it), &changes);
}
// We have to notify password store observers of the change by hand since
// we use internal password store interfaces to make changes synchronously.
+ NotifyPasswordStoreOfLoginChanges(changes);
+}
+
+void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges(
+ const PasswordStoreChangeList& changes) {
password_store_->NotifyLoginsChanged(changes);
}
-syncer::SyncData PasswordSyncableService::CreateSyncData(
+void PasswordSyncableService::CreateOrUpdateEntry(
+ const syncer::SyncData& data,
+ PasswordEntryMap* umatched_data_from_password_db,
+ ScopedVector<autofill::PasswordForm>* new_sync_entries,
+ ScopedVector<autofill::PasswordForm>* updated_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 =
+ umatched_data_from_password_db->find(tag);
+ if (existing_local_entry_iter == umatched_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.
+ scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm);
+ PasswordFromSpecifics(password_specifics, new_password.get());
+ new_sync_entries->push_back(new_password.release());
+ } else {
+ // The entry is in password store. If the entries are not identical, then
+ // the entries need to be merged.
+ scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm);
+ if (MergeLocalAndSyncPasswords(password_specifics,
+ *existing_local_entry_iter->second,
+ new_password.get())) {
+ // Rather than checking which database -- sync or local -- needs updating,
+ // simply push an update to both. This will end up being a noop for the
+ // database that didn't need an update.
+ updated_db_entries->push_back(
+ syncer::SyncChange(FROM_HERE,
+ syncer::SyncChange::ACTION_UPDATE,
+ SyncDataFromPassword(*new_password)));
+
+ updated_sync_entries->push_back(new_password.release());
+ }
+ // Remove the entry from the entry map to indicate a match has been found.
+ // Entries that remain in the map at the end of associating all sync entries
+ // will be treated as additions that need to be propagated to sync.
+ umatched_data_from_password_db->erase(existing_local_entry_iter);
+ }
+}
+
+syncer::SyncData SyncDataFromPassword(
const autofill::PasswordForm& password_form) {
sync_pb::EntitySpecifics password_data;
sync_pb::PasswordSpecificsData* password_specifics =
@@ -175,6 +315,35 @@ syncer::SyncData PasswordSyncableService::CreateSyncData(
password_form.date_created.ToInternalValue());
password_specifics->set_blacklisted(password_form.blacklisted_by_user);
- std::string tag = MakeTag(*password_specifics);
+ std::string tag = MakePasswordSyncTag(*password_specifics);
return syncer::SyncData::CreateLocalData(tag, tag, password_data);
}
+
+void PasswordFromSpecifics(const sync_pb::PasswordSpecificsData& password,
+ autofill::PasswordForm* new_password) {
+ new_password->scheme =
+ static_cast<autofill::PasswordForm::Scheme>(password.scheme());
+ new_password->signon_realm = password.signon_realm();
+ new_password->origin = GURL(password.origin());
+ new_password->action = GURL(password.action());
+ new_password->username_element =
+ base::UTF8ToUTF16(password.username_element());
+ new_password->password_element =
+ base::UTF8ToUTF16(password.password_element());
+ new_password->username_value = base::UTF8ToUTF16(password.username_value());
+ new_password->password_value = base::UTF8ToUTF16(password.password_value());
+ new_password->ssl_valid = password.ssl_valid();
+ new_password->preferred = password.preferred();
+ new_password->date_created =
+ base::Time::FromInternalValue(password.date_created());
+ new_password->blacklisted_by_user = password.blacklisted();
+}
+
+std::string MakePasswordSyncTag(
+ const sync_pb::PasswordSpecificsData& password) {
+ return MakePasswordSyncTag(password.origin(),
+ password.username_element(),
+ password.username_value(),
+ password.password_element(),
+ password.signon_realm());
+}

Powered by Google App Engine
This is Rietveld 408576698