Chromium Code Reviews| 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 97e83414412fa89268967858c515ef6c78b39cf1..9f3eb1836c6690c885f949bc9e836a17576d99b3 100644 |
| --- a/chrome/browser/password_manager/password_syncable_service.cc |
| +++ b/chrome/browser/password_manager/password_syncable_service.cc |
| @@ -5,12 +5,53 @@ |
| #include "chrome/browser/password_manager/password_syncable_service.h" |
| #include "base/location.h" |
| +#include "base/memory/scoped_vector.h" |
|
Ilya Sherman
2013/12/05 07:01:54
nit: Redundant with the header's include.
|
| +#include "base/metrics/histogram.h" |
|
Ilya Sherman
2013/12/05 07:01:54
nit: unused.
|
| +#include "base/stl_util.h" |
|
Ilya Sherman
2013/12/05 07:01:54
nit: unused?
|
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/password_manager/password_store.h" |
| #include "components/autofill/core/common/password_form.h" |
| #include "net/base/escape.h" |
| #include "sync/api/sync_error_factory.h" |
| +bool PasswordSyncableService::MergeLocalAndSyncPasswords( |
| + const sync_pb::PasswordSpecificsData& password_specifics, |
| + const autofill::PasswordForm& password_form, |
| + autofill::PasswordForm* new_password_form) { |
| + if (password_specifics.scheme() == password_form.scheme && |
| + password_form.signon_realm == password_specifics.signon_realm() && |
| + password_form.origin.spec() == password_specifics.origin() && |
| + password_form.action.spec() == password_specifics.action() && |
| + UTF16ToUTF8(password_form.username_element) == |
| + password_specifics.username_element() && |
| + UTF16ToUTF8(password_form.password_element) == |
| + password_specifics.password_element() && |
| + UTF16ToUTF8(password_form.username_value) == |
| + password_specifics.username_value() && |
| + UTF16ToUTF8(password_form.password_value) == |
| + password_specifics.password_value() && |
| + password_specifics.ssl_valid() == password_form.ssl_valid && |
| + password_specifics.preferred() == password_form.preferred && |
| + password_specifics.date_created() == |
| + password_form.date_created.ToInternalValue() && |
| + password_specifics.blacklisted() == |
| + password_form.blacklisted_by_user) { |
| + 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 { |
| + PasswordSyncableService::ExtractPasswordFromSpecifics( |
| + password_specifics, |
| + new_password_form); |
| + } |
| + |
| + return true; |
| +} |
|
Ilya Sherman
2013/12/05 07:01:54
nit: Please arrange the code in the implementation
|
| + |
| PasswordSyncableService::PasswordSyncableService( |
| scoped_refptr<PasswordStore> password_store) |
| : password_store_(password_store) { |
| @@ -18,6 +59,53 @@ PasswordSyncableService::PasswordSyncableService( |
| PasswordSyncableService::~PasswordSyncableService() {} |
| +void PasswordSyncableService::CreateOrUpdateEntry( |
| + const syncer::SyncData& data, |
| + PasswordEntryMap* umatched_data_from_password_db, |
| + ScopedVector<autofill::PasswordForm>* new_entries, |
| + ScopedVector<autofill::PasswordForm>* updated_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 = MakeTag(password_specifics); |
| + |
| + // Check whether the data from sync is already in password store. |
|
Ilya Sherman
2013/12/05 07:01:54
nit: "in password store" -> "in the password store
|
| + PasswordEntryMap::iterator umatched_data_from_password_db_iter = |
|
Ilya Sherman
2013/12/05 07:01:54
nit: Perhaps name this something more like "existi
|
| + umatched_data_from_password_db->find(tag); |
| + if (umatched_data_from_password_db_iter == |
| + umatched_data_from_password_db->end()) { |
|
Ilya Sherman
2013/12/05 07:01:54
nit: Please indent this line four more spaces.
|
| + // 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()); |
| + ExtractPasswordFromSpecifics(password_specifics, new_password.get()); |
| + new_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, |
| + *(umatched_data_from_password_db_iter->second), |
| + new_password.get())) { |
| + // It is possible that both the sync database and the passwords |
| + // database need to be updated to reflect the merged data. Rather |
| + // than checking we simply update both. |
|
Ilya Sherman
2013/12/05 07:01:54
Is it actually possible that both need to be updat
|
| + updated_db_entries->push_back( |
| + syncer::SyncChange(FROM_HERE, |
| + syncer::SyncChange::ACTION_UPDATE, |
| + CreateSyncData(*(new_password.get())))); |
| + |
| + updated_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(umatched_data_from_password_db_iter); |
| + } |
| +} |
| + |
| syncer::SyncMergeResult |
| PasswordSyncableService::MergeDataAndStartSyncing( |
| syncer::ModelType type, |
| @@ -28,9 +116,69 @@ PasswordSyncableService::MergeDataAndStartSyncing( |
| 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) |
| + syncer::SyncError::LogDataLoadFailure(syncer::PASSWORDS); |
| + 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[MakeTag(*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 needs to be propagated to sync. |
|
Ilya Sherman
2013/12/05 07:01:54
nit: "needs" -> "need"
|
| + syncer::SyncChangeList db_changes; |
| + 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, |
| + &db_changes); |
| + } |
| + |
| + 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) { |
| + db_changes.push_back(syncer::SyncChange(FROM_HERE, |
| + syncer::SyncChange::ACTION_ADD, |
| + CreateSyncData(*(it->second)))); |
|
Ilya Sherman
2013/12/05 07:01:54
nit: Please align all three arguments to syncer::S
|
| + } |
| + |
| + merge_result.set_error( |
| + sync_processor_->ProcessSyncChanges(FROM_HERE, db_changes)); |
| return merge_result; |
| } |
| @@ -54,29 +202,33 @@ syncer::SyncError PasswordSyncableService::ProcessSyncChanges( |
| } |
| void PasswordSyncableService::WriteToPasswordStore( |
| - PasswordForms* new_entries, |
| - PasswordForms* updated_entries) { |
| + const PasswordForms& new_entries, |
| + const PasswordForms& updated_entries) { |
| for (std::vector<autofill::PasswordForm*>::const_iterator it = |
| - new_entries->begin(); |
| - it != new_entries->end(); |
| + new_entries.begin(); |
| + it != new_entries.end(); |
| ++it) { |
| password_store_->AddLoginImpl(**it); |
| } |
| for (std::vector<autofill::PasswordForm*>::const_iterator it = |
| - updated_entries->begin(); |
| - it != updated_entries->end(); |
| + updated_entries.begin(); |
| + it != updated_entries.end(); |
| ++it) { |
| password_store_->UpdateLoginImpl(**it); |
| } |
| - if (!new_entries->empty() || !updated_entries->empty()) { |
| + if (!new_entries.empty() || !updated_entries.empty()) { |
| // We have to notify password store observers of the change by hand since |
| // we use internal password store interfaces to make changes synchronously. |
| - password_store_->PostNotifyLoginsChanged(); |
| + NotifyPasswordStoreOfLoginChanges(); |
| } |
| } |
| +void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges() { |
| + password_store_->PostNotifyLoginsChanged(); |
| +} |
| + |
| syncer::SyncData PasswordSyncableService::CreateSyncData( |
| const autofill::PasswordForm& password_form) { |
| sync_pb::EntitySpecifics password_data; |
| @@ -138,3 +290,27 @@ std::string PasswordSyncableService::MakeTag( |
| password.signon_realm()); |
| } |
| +// static |
| +void PasswordSyncableService::ExtractPasswordFromSpecifics( |
| + 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 = |
| + UTF8ToUTF16(password.username_element()); |
| + new_password->password_element = |
| + UTF8ToUTF16(password.password_element()); |
| + new_password->username_value = |
| + UTF8ToUTF16(password.username_value()); |
| + new_password->password_value = |
| + 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(); |
| +} |