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,421 @@ |
+// 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(*(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(*(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); |
+ // Check if sync is on. If we receive notification prior to the sync being set |
+ // up we are going to process all when MergeData..() is called. If we receive |
+ // notification after the sync exited, it will be sinced next time Chrome |
+ // starts. |
+ if (!sync_processor_) |
+ return; |
+ 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) { |
+ return web_database_->GetAutofillTable()->GetAutofillProfiles(profiles); |
+} |
+ |
+bool AutofillProfileSyncableService::SaveChangesToWebData( |
+ const DataBundle& bundle) { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ for (size_t i = 0; i < bundle.profiles_to_add.size(); i++) { |
+ if (!web_database_->GetAutofillTable()->AddAutofillProfile( |
+ *bundle.profiles_to_add[i])) |
+ return false; |
+ } |
+ |
+ for (size_t i = 0; i < bundle.profiles_to_update.size(); i++) { |
+ if (!web_database_->GetAutofillTable()->UpdateAutofillProfile( |
+ *bundle.profiles_to_update[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, |
+ GUIDToProfileMap* profile_map) { |
+ DCHECK(profile_map); |
+ profile_map->clear(); |
+ for (size_t i = 0; i < profiles.size(); ++i) |
+ (*profile_map)[profiles[i]->guid()] = profiles[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, it->second)) |
+ bundle->profiles_to_update.push_back(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 (i->second->Compare(*new_profile) == 0) { |
+ bundle->profiles_to_delete.push_back(i->second->guid()); |
+ VLOG(2) << "[AUTOFILL SYNC]" |
+ << "Found in sync db but with a different guid: " |
+ << UTF16ToUTF8(it->second->GetInfo(NAME_FIRST)) |
+ << UTF16ToUTF8(it->second->GetInfo(NAME_LAST)) |
+ << "New guid " << new_profile->guid() |
+ << ". Profile to be deleted " << it->second->guid(); |
+ profile_map->erase(i); |
+ break; |
+ } |
+ } |
+ profiles_.push_back(new_profile); |
+ it = profile_map->insert(std::make_pair(new_profile->guid(), |
+ new_profile)).first; |
+ bundle->profiles_to_add.push_back(new_profile); |
+ } |
+ return it; |
+} |
+ |
+void AutofillProfileSyncableService::ActOnChange( |
+ const AutofillProfileChange& change) { |
+ DCHECK((change.type() == AutofillProfileChange::REMOVE && |
+ !change.profile()) || |
+ (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_.get().back(); |
+ break; |
+ case AutofillProfileChange::UPDATE: { |
+ GUIDToProfileMap::iterator it = profiles_map_.find( |
+ change.profile()->guid()); |
+ DCHECK(it != profiles_map_.end()); |
+ *(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 |