| 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 7f3710913247228c337653f1ce81ecd083fb20ba..7525bbee83e0c1ab9e964f81ca5d681b1cb0f1bc 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) {
|
| @@ -36,19 +97,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;
|
| }
|
|
|
| @@ -81,72 +205,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) {
|
| 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();
|
| }
|
| }
|
|
|
| -syncer::SyncData PasswordSyncableService::CreateSyncData(
|
| +void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges() {
|
| + password_store_->PostNotifyLoginsChanged();
|
| +}
|
| +
|
| +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 =
|
| @@ -169,6 +308,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());
|
| +}
|
|
|