Chromium Code Reviews| Index: chrome/browser/sync/glue/autofill_profile_syncable_service.cc |
| =================================================================== |
| --- chrome/browser/sync/glue/autofill_profile_syncable_service.cc (revision 0) |
| +++ chrome/browser/sync/glue/autofill_profile_syncable_service.cc (revision 0) |
| @@ -0,0 +1,418 @@ |
| +// Copyright (c) 2011 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 "chrome/browser/sync/glue/autofill_profile_syncable_service.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/tracked.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/sync/api/sync_error.h" |
| +#include "chrome/browser/sync/glue/do_optimistic_refresh_task.h" |
| +#include "chrome/browser/webdata/autofill_table.h" |
| +#include "chrome/browser/webdata/web_database.h" |
| +#include "chrome/common/chrome_notification_types.h" |
| +#include "chrome/common/guid.h" |
| +#include "content/common/notification_service.h" |
| + |
| +namespace { |
| + |
| +// Helper to compare the local value and cloud value of a field, merge into |
| +// the local value if they differ, and return whether the merge happened. |
| +bool MergeField(FormGroup* form_group, |
| + AutofillFieldType field_type, |
| + const std::string& specifics_field) { |
| + if (UTF16ToUTF8(form_group->GetInfo(field_type)) == specifics_field) |
| + return false; |
| + form_group->SetInfo(field_type, UTF8ToUTF16(specifics_field)); |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace browser_sync { |
| + |
| +const char kAutofillProfileTag[] = "google_chrome_autofill_profiles"; |
| + |
| +AutofillProfileSyncableService::AutofillProfileSyncableService( |
| + WebDatabase* web_database, |
| + PersonalDataManager* personal_data, |
| + Profile* profile) |
| + : web_database_(web_database), |
| + personal_data_(personal_data), |
| + sync_processor_(NULL) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| + DCHECK(web_database_); |
| + DCHECK(personal_data_); |
| + DCHECK(profile); |
| + notification_registrar_.Add(this, |
| + chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED, |
| + Source<WebDataService>( |
| + profile->GetWebDataService(Profile::EXPLICIT_ACCESS))); |
| +} |
| + |
| +AutofillProfileSyncableService::~AutofillProfileSyncableService() { |
| + DCHECK(CalledOnValidThread()); |
| +} |
| + |
| +AutofillProfileSyncableService::AutofillProfileSyncableService() |
| + : web_database_(NULL), |
| + personal_data_(NULL), |
| + sync_processor_(NULL) { |
| +} |
| + |
| +SyncError AutofillProfileSyncableService::MergeDataAndStartSyncing( |
| + syncable::ModelType type, |
| + const SyncDataList& initial_sync_data, |
| + SyncChangeProcessor* sync_processor) { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(sync_processor_ == NULL); |
| + VLOG(1) << "Associating Autofill: MergeDataAndStartSyncing"; |
| + |
| + if (!LoadAutofillData(&profiles_.get())) { |
| + return SyncError( |
| + FROM_HERE, "Could not get the autofill data from WebDatabase.", |
| + model_type()); |
| + } |
| + |
| + if (VLOG_IS_ON(2)) { |
| + VLOG(2) << "[AUTOFILL MIGRATION]" |
| + << "Printing profiles from web db"; |
| + |
| + for (ScopedVector<AutofillProfile>::const_iterator ix = |
| + profiles_.begin(); ix != profiles_.end(); ++ix) { |
| + AutofillProfile* p = *ix; |
| + VLOG(2) << "[AUTOFILL MIGRATION] " |
| + << p->GetInfo(NAME_FIRST) |
| + << p->GetInfo(NAME_LAST) |
| + << p->guid(); |
| + } |
| + } |
| + |
| + sync_processor_ = sync_processor; |
| + |
| + GUIDToProfileMap remaining_profiles; |
| + CreateGUIDToProfileMap(profiles_.get(), &remaining_profiles); |
| + |
| + DataBundle bundle; |
| + // Go through and check for all the profiles that sync already knows about. |
| + for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin(); |
| + sync_iter != initial_sync_data.end(); |
| + ++sync_iter) { |
| + GUIDToProfileMap::iterator it = |
| + CreateOrUpdateProfile(*sync_iter, &remaining_profiles, &bundle); |
| + profiles_map_[it->first] = it->second; |
| + remaining_profiles.erase(it); |
| + } |
| + |
| + if (!SaveChangesToWebData(bundle)) |
| + return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); |
| + |
| + SyncChangeList new_changes; |
| + for (GUIDToProfileMap::iterator i = remaining_profiles.begin(); |
| + i != remaining_profiles.end(); ++i) { |
| + new_changes.push_back( |
| + SyncChange(SyncChange::ACTION_ADD, |
| + CreateData(*(profiles_[i->second])))); |
| + profiles_map_[i->first] = i->second; |
| + } |
| + |
| + SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); |
| + |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| + new DoOptimisticRefreshForAutofill(personal_data_)); |
| + |
| + return error; |
| +} |
| + |
| +void AutofillProfileSyncableService::StopSyncing(syncable::ModelType type) { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(sync_processor_ != NULL); |
| + DCHECK_EQ(type, syncable::AUTOFILL_PROFILE); |
| + |
| + sync_processor_ = NULL; |
| + profiles_.reset(); |
| + profiles_map_.clear(); |
| +} |
| + |
| +SyncDataList AutofillProfileSyncableService::GetAllSyncData( |
| + syncable::ModelType type) const { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(sync_processor_ != NULL); |
| + DCHECK_EQ(type, syncable::AUTOFILL_PROFILE); |
| + |
| + SyncDataList current_data; |
| + |
| + for (GUIDToProfileMap::const_iterator i = profiles_map_.begin(); |
| + i != profiles_map_.end(); ++i) { |
| + current_data.push_back(CreateData(*(profiles_[i->second]))); |
| + } |
| + return current_data; |
| +} |
| + |
| +SyncError AutofillProfileSyncableService::ProcessSyncChanges( |
| + const tracked_objects::Location& from_here, |
| + const SyncChangeList& change_list) { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(sync_processor_ != NULL); |
| + if (sync_processor_ == NULL) { |
| + SyncError error(FROM_HERE, "Models not yet associated.", |
| + syncable::AUTOFILL_PROFILE); |
| + return error; |
| + } |
| + |
| + DataBundle bundle; |
| + |
| + for (SyncChangeList::const_iterator i = change_list.begin(); |
| + i != change_list.end(); ++i) { |
| + DCHECK(i->IsValid()); |
| + switch (i->change_type()) { |
| + case SyncChange::ACTION_ADD: |
| + case SyncChange::ACTION_UPDATE: |
| + CreateOrUpdateProfile(i->sync_data(), &profiles_map_, &bundle); |
| + break; |
| + case SyncChange::ACTION_DELETE: { |
| + std::string guid = i->sync_data().GetSpecifics(). |
| + GetExtension(sync_pb::autofill_profile).guid(); |
| + bundle.profiles_to_delete.push_back(guid); |
| + profiles_map_.erase(guid); |
| + } break; |
| + default: |
| + NOTREACHED() << "Unexpected sync change state."; |
| + return SyncError(FROM_HERE, "ProcessSyncChanges failed on ChangeType " + |
| + SyncChange::ChangeTypeToString(i->change_type()), |
| + syncable::AUTOFILL_PROFILE); |
| + } |
| + } |
| + |
| + if (!SaveChangesToWebData(bundle)) |
| + return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); |
| + |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| + new DoOptimisticRefreshForAutofill(personal_data_)); |
| + return SyncError(); |
| +} |
| + |
| +void AutofillProfileSyncableService::Observe(int type, |
| + const NotificationSource& source, |
| + const NotificationDetails& details) { |
| + DCHECK_EQ(type, chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED); |
| + WebDataService* wds = Source<WebDataService>(source).ptr(); |
| + |
| + DCHECK(wds && wds->GetDatabase() == web_database_); |
| + |
| + AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr(); |
| + |
| + ActOnChange(*change); |
| +} |
| + |
| +bool AutofillProfileSyncableService::LoadAutofillData( |
| + std::vector<AutofillProfile*>* profiles) { |
| + if (!web_database_->GetAutofillTable()->GetAutofillProfiles(profiles)) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +bool AutofillProfileSyncableService::SaveChangesToWebData( |
| + const DataBundle& bundle) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + for (size_t i = 0; i < bundle.new_profiles.size(); i++) { |
| + if (!web_database_->GetAutofillTable()->AddAutofillProfile( |
| + *bundle.new_profiles[i])) |
| + return false; |
| + } |
| + |
| + for (size_t i = 0; i < bundle.updated_profiles.size(); i++) { |
| + if (!web_database_->GetAutofillTable()->UpdateAutofillProfile( |
| + *bundle.updated_profiles[i])) |
| + return false; |
| + } |
| + |
| + for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) { |
| + if (!web_database_->GetAutofillTable()->RemoveAutofillProfile( |
| + bundle.profiles_to_delete[i])) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +// static |
| +bool AutofillProfileSyncableService::OverwriteProfileWithServerData( |
| + const sync_pb::AutofillProfileSpecifics& specifics, |
| + AutofillProfile* profile) { |
| + bool diff = false; |
| + diff = MergeField(profile, NAME_FIRST, specifics.name_first()) || diff; |
| + diff = MergeField(profile, NAME_LAST, specifics.name_last()) || diff; |
| + diff = MergeField(profile, NAME_MIDDLE, specifics.name_middle()) || diff; |
| + diff = MergeField(profile, ADDRESS_HOME_LINE1, |
| + specifics.address_home_line1()) || diff; |
| + diff = MergeField(profile, ADDRESS_HOME_LINE2, |
| + specifics.address_home_line2()) || diff; |
| + diff = MergeField(profile, ADDRESS_HOME_CITY, |
| + specifics.address_home_city()) || diff; |
| + diff = MergeField(profile, ADDRESS_HOME_STATE, |
| + specifics.address_home_state()) || diff; |
| + diff = MergeField(profile, ADDRESS_HOME_COUNTRY, |
| + specifics.address_home_country()) || diff; |
| + diff = MergeField(profile, ADDRESS_HOME_ZIP, |
| + specifics.address_home_zip()) || diff; |
| + diff = MergeField(profile, EMAIL_ADDRESS, specifics.email_address()) || diff; |
| + diff = MergeField(profile, COMPANY_NAME, specifics.company_name()) || diff; |
| + diff = MergeField(profile, PHONE_FAX_WHOLE_NUMBER, |
| + specifics.phone_fax_whole_number()) || diff; |
| + diff = MergeField(profile, PHONE_HOME_WHOLE_NUMBER, |
| + specifics.phone_home_whole_number()) || diff; |
| + return diff; |
| +} |
| + |
| +// static |
| +void AutofillProfileSyncableService::WriteAutofillProfile( |
| + const AutofillProfile& profile, |
| + sync_pb::EntitySpecifics* profile_specifics) { |
| + sync_pb::AutofillProfileSpecifics* specifics = |
| + profile_specifics->MutableExtension(sync_pb::autofill_profile); |
| + |
| + DCHECK(guid::IsValidGUID(profile.guid())); |
| + |
| + specifics->set_guid(profile.guid()); |
| + specifics->set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST))); |
| + specifics->set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE))); |
| + specifics->set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST))); |
| + specifics->set_address_home_line1( |
| + UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1))); |
| + specifics->set_address_home_line2( |
| + UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2))); |
| + specifics->set_address_home_city(UTF16ToUTF8(profile.GetInfo( |
| + ADDRESS_HOME_CITY))); |
| + specifics->set_address_home_state(UTF16ToUTF8(profile.GetInfo( |
| + ADDRESS_HOME_STATE))); |
| + specifics->set_address_home_country(UTF16ToUTF8(profile.GetInfo( |
| + ADDRESS_HOME_COUNTRY))); |
| + specifics->set_address_home_zip(UTF16ToUTF8(profile.GetInfo( |
| + ADDRESS_HOME_ZIP))); |
| + specifics->set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS))); |
| + specifics->set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME))); |
| + specifics->set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo( |
| + PHONE_FAX_WHOLE_NUMBER))); |
| + specifics->set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo( |
| + PHONE_HOME_WHOLE_NUMBER))); |
| +} |
| + |
| +void AutofillProfileSyncableService::CreateGUIDToProfileMap( |
| + const std::vector<AutofillProfile *>& profiles, |
|
Ilya Sherman
2011/09/03 00:10:57
Nit: No space before the "*"
GeorgeY
2011/09/07 00:28:14
Done.
|
| + GUIDToProfileMap* profile_map) { |
| + DCHECK(profile_map); |
| + profile_map->clear(); |
| + for (size_t i = 0; i < profiles.size(); ++i) |
| + (*profile_map)[profiles[i]->guid()] = i; |
| +} |
| + |
| +AutofillProfileSyncableService::GUIDToProfileMap::iterator |
| +AutofillProfileSyncableService::CreateOrUpdateProfile( |
| + const SyncData& data, GUIDToProfileMap* profile_map, DataBundle* bundle) { |
| + DCHECK(profile_map); |
| + DCHECK(bundle); |
| + |
| + DCHECK_EQ(syncable::AUTOFILL_PROFILE, data.GetDataType()); |
| + |
| + const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); |
| + const sync_pb::AutofillProfileSpecifics& autofill_specifics( |
| + specifics.GetExtension(sync_pb::autofill_profile)); |
| + |
| + GUIDToProfileMap::iterator it = profile_map->find( |
| + autofill_specifics.guid()); |
| + if (it != profile_map->end()) { |
| + // Some profile that already present is synced. |
| + if (OverwriteProfileWithServerData(autofill_specifics, |
| + profiles_[it->second])) |
| + bundle->updated_profiles.push_back(profiles_[it->second]); |
| + } else { |
| + // New profile synced. |
| + AutofillProfile* new_profile( |
| + new AutofillProfile(autofill_specifics.guid())); |
| + OverwriteProfileWithServerData(autofill_specifics, new_profile); |
| + |
| + // Check if profile appears under a different guid. |
| + for (GUIDToProfileMap::iterator i = profile_map->begin(); |
| + i != profile_map->end(); ++i) { |
| + if (profiles_[i->second]->Compare(*new_profile) == 0) { |
| + bundle->profiles_to_delete.push_back(profiles_[i->second]->guid()); |
| + VLOG(2) << "[AUTOFILL SYNC]" |
| + << "Found in sync db but with a different guid: " |
| + << UTF16ToUTF8(profiles_[it->second]->GetInfo(NAME_FIRST)) |
| + << UTF16ToUTF8(profiles_[it->second]->GetInfo(NAME_LAST)) |
| + << "New guid " << new_profile->guid() |
| + << ". Profile to be deleted " << profiles_[it->second]->guid(); |
| + profile_map->erase(i); |
| + break; |
| + } |
| + } |
| + profiles_.push_back(new_profile); |
| + it = profile_map->insert( |
| + std::make_pair(new_profile->guid(), profiles_.size() - 1)).first; |
| + bundle->new_profiles.push_back(new_profile); |
| + } |
| + return it; |
| +} |
| + |
| +void AutofillProfileSyncableService::ActOnChange( |
| + const AutofillProfileChange& change) { |
| + DCHECK(change.type() == AutofillProfileChange::REMOVE || change.profile()); |
| + DCHECK(sync_processor_); |
| + SyncChangeList new_changes; |
| + DataBundle bundle; |
| + switch (change.type()) { |
| + case AutofillProfileChange::ADD: |
| + new_changes.push_back( |
| + SyncChange(SyncChange::ACTION_ADD, CreateData(*(change.profile())))); |
| + DCHECK(profiles_map_.find(change.profile()->guid()) == |
| + profiles_map_.end()); |
| + profiles_.push_back(new AutofillProfile(*(change.profile()))); |
| + profiles_map_[change.profile()->guid()] = profiles_.size() - 1; |
| + break; |
| + case AutofillProfileChange::UPDATE: { |
| + GUIDToProfileMap::iterator it = profiles_map_.find( |
| + change.profile()->guid()); |
| + DCHECK(it != profiles_map_.end()); |
| + *profiles_[it->second] = *(change.profile()); |
| + new_changes.push_back( |
| + SyncChange(SyncChange::ACTION_UPDATE, |
| + CreateData(*(change.profile())))); |
| + break; |
| + } |
| + case AutofillProfileChange::REMOVE: { |
| + AutofillProfile empty_profile(change.key()); |
| + new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, |
| + CreateData(empty_profile))); |
| + profiles_map_.erase(change.key()); |
| + break; |
| + } |
| + default: |
| + NOTREACHED(); |
| + } |
| + SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); |
| + if (error.IsSet()) { |
| + LOG(WARNING) << "[AUTOFILL SYNC]" |
| + << " Failed processing change:" |
| + << " Error:" << error.message() |
| + << " Guid:" << change.profile()->guid(); |
| + } |
| +} |
| + |
| +SyncData AutofillProfileSyncableService::CreateData( |
| + const AutofillProfile& profile) { |
| + sync_pb::EntitySpecifics specifics; |
| + WriteAutofillProfile(profile, &specifics); |
| + return SyncData::CreateLocalData(profile.guid(), profile.guid(), specifics); |
| +} |
| + |
| +AutofillProfileSyncableService::DataBundle::DataBundle() {} |
| + |
| +AutofillProfileSyncableService::DataBundle::~DataBundle() { |
| +} |
| + |
| +} // namespace browser_sync |
| + |
| Property changes on: chrome\browser\sync\glue\autofill_profile_syncable_service.cc |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |