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,430 @@ |
| +// 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/tracked.h" |
| +#include "base/utf_string_conversions.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 browser_sync { |
| + |
| +const char kAutofillProfileTag[] = "google_chrome_autofill_profiles"; |
| + |
| +AutofillProfileSyncableService::AutofillProfileSyncableService( |
| + WebDatabase* web_database, |
| + PersonalDataManager* personal_data) |
| + : web_database_(web_database), |
| + personal_data_(personal_data), |
| + sync_processor_(NULL), |
| + number_of_profiles_created_(0) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| + DCHECK(web_database_); |
| + DCHECK(personal_data_); |
| + notification_registrar_.Add(this, |
| + chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED, |
| + NotificationService::AllSources()); |
|
Ilya Sherman
2011/09/01 07:22:31
Are you sure this should be AllSources() rather th
dhollowa
2011/09/01 23:11:46
Yes, it seems like this code should only listen/re
GeorgeY
2011/09/02 04:34:12
That call is moved from original change processor,
|
| +} |
| + |
| +AutofillProfileSyncableService::~AutofillProfileSyncableService() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
|
dhollowa
2011/09/01 23:11:46
It is fine to leave this here... but I believe it
GeorgeY
2011/09/02 04:34:12
Yes, could be changed to DCHECK(CalledOnValidThrea
|
| +} |
| + |
| +AutofillProfileSyncableService::AutofillProfileSyncableService() |
| + : web_database_(NULL), |
| + personal_data_(NULL), |
| + sync_processor_(NULL), |
| + number_of_profiles_created_(0) { |
| + notification_registrar_.Add(this, |
| + chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED, |
| + NotificationService::AllSources()); |
|
Ilya Sherman
2011/09/01 07:22:31
Ditto.
GeorgeY
2011/09/02 04:34:12
Ditto.
|
| +} |
| + |
| +bool AutofillProfileSyncableService::LoadAutofillData( |
| + std::vector<AutofillProfile*>* profiles) const { |
| + // const_cast it as GetAutofillProfiles() is a really const accessor, though |
| + // not so declared. |
|
Ilya Sherman
2011/09/01 07:22:31
nit: Can we propagate const properly rather than c
dhollowa
2011/09/01 23:11:46
+1
GeorgeY
2011/09/02 04:34:12
Didn't do it because:
#1. The internal state of db
|
| + if (!const_cast<WebDatabase*>(web_database_)-> |
|
dhollowa
2011/09/01 23:11:46
nit: how about simply:
return web_database_->...
dhollowa
2011/09/07 01:48:15
Ping.
GeorgeY
2011/09/07 20:55:07
sure
|
| + GetAutofillTable()->GetAutofillProfiles(profiles)) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +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"; |
| + |
| + ScopedVector<AutofillProfile> profiles; |
| + |
| + 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, &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) |
| + CreateOrUpdateProfile(*sync_iter, &profiles, &remaining_profiles, &bundle); |
|
dhollowa
2011/09/01 23:11:46
nit: style guide requires { } for multi-line for()
GeorgeY
2011/09/02 04:34:12
AFAIK, {} are required *only* if executing statem
dhollowa
2011/09/07 01:48:15
src/base/... is littered with examples of this. A
GeorgeY
2011/09/07 20:55:07
Have you checked the last code - it is already th
dhollowa
2011/09/07 21:10:24
It is helpful to the reviewer if you mark "Done".
GeorgeY
2011/09/07 23:26:24
Done. :)
|
| + |
| + 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( |
| + CreateChange(SyncChange::ACTION_ADD, *(*(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; |
| +} |
| + |
| +SyncDataList AutofillProfileSyncableService::GetAllSyncData( |
| + syncable::ModelType type) const { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(sync_processor_ != NULL); |
| + DCHECK_EQ(type, syncable::AUTOFILL_PROFILE); |
| + |
| + SyncDataList current_data; |
| + |
| + ScopedVector<AutofillProfile> profiles; |
| + |
| + if (!LoadAutofillData(&profiles.get())) |
| + return current_data; |
| + |
| + for (ScopedVector<AutofillProfile>::iterator i = profiles.begin(); |
| + i != profiles.end(); ++i) { |
| + sync_pb::EntitySpecifics specifics; |
| + WriteAutofillProfile(*(*i), &specifics); |
| + current_data.push_back(SyncData::CreateLocalData( |
| + (*i)->guid(), (*i)->guid(), specifics)); |
| + } |
| + 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; |
| + } |
|
lipalani1
2011/09/01 05:06:02
I am concerned about the perf impact here Cam you
GeorgeY
2011/09/02 04:34:12
Done.
|
| + |
| + ScopedVector<AutofillProfile> profiles; |
| + if (!LoadAutofillData(&profiles.get())) { |
| + SyncError error(FROM_HERE, "Failed to read Web DB.", |
| + syncable::AUTOFILL_PROFILE); |
| + return error; |
| + } |
| + |
| + GuidToProfileMap remaining_profiles; |
| + CreateGuidToProfileMap(&profiles, &remaining_profiles); |
| + |
| + std::vector<std::string> profiles_to_delete; |
| + 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, &remaining_profiles, |
| + &bundle); |
| + break; |
| + case SyncChange::ACTION_DELETE: |
| + bundle.profiles_to_delete.push_back( |
| + i->sync_data().GetSpecifics(). |
| + GetExtension(sync_pb::autofill_profile).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(); |
| + |
| + if (!wds || wds->GetDatabase() != web_database_) |
| + return; |
| + |
| + AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr(); |
| + |
| + ActOnChange(change); |
| +} |
| + |
| +// 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 AutofillProfileSyncableService::MergeField(FormGroup* f, |
| + AutofillFieldType t, |
| + const std::string& specifics_field) { |
| + if (UTF16ToUTF8(f->GetInfo(t)) == specifics_field) |
| + return false; |
| + f->SetInfo(t, UTF8ToUTF16(specifics_field)); |
| + return true; |
| +} |
| + |
| +// static |
| +bool AutofillProfileSyncableService::OverwriteProfileWithServerData( |
| + AutofillProfile* merge_into, |
|
dhollowa
2011/09/01 20:17:49
Rename |merge_into| as |profile| and get rid of te
GeorgeY
2011/09/02 04:34:12
Done.
|
| + const sync_pb::AutofillProfileSpecifics& specifics) { |
| + bool diff = false; |
| + AutofillProfile* p = merge_into; |
| + const sync_pb::AutofillProfileSpecifics& s(specifics); |
| + diff = MergeField(p, NAME_FIRST, s.name_first()) || diff; |
| + diff = MergeField(p, NAME_LAST, s.name_last()) || diff; |
| + diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff; |
| + diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff; |
| + diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff; |
| + diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff; |
| + diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff; |
| + diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff; |
| + diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff; |
| + diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff; |
| + diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff; |
| + diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number()) |
| + || diff; |
| + diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number()) |
| + || diff; |
| + return diff; |
| +} |
| + |
| +SyncChange AutofillProfileSyncableService::CreateChange( |
| + SyncChange::SyncChangeType action, |
| + const AutofillProfile& profile) { |
| + return SyncChange(action, CreateData(profile)); |
| +} |
| + |
| +SyncChange AutofillProfileSyncableService::CreateDeleteChange( |
| + const std::string& guid) { |
| + AutofillProfile empty_profile(guid); |
| + return SyncChange(SyncChange::ACTION_DELETE, CreateData(empty_profile)); |
| +} |
| + |
| +SyncData AutofillProfileSyncableService::CreateData( |
| + const AutofillProfile& profile) { |
| + sync_pb::EntitySpecifics specifics; |
| + WriteAutofillProfile(profile, &specifics); |
| + return SyncData::CreateLocalData(profile.guid(), profile.guid(), specifics); |
| +} |
| + |
| +// static |
| +void AutofillProfileSyncableService::WriteAutofillProfile( |
|
dhollowa
2011/09/01 20:17:49
nit: please reorder method to match the .h file.
GeorgeY
2011/09/02 04:34:12
Done.
|
| + const AutofillProfile& profile, |
| + sync_pb::EntitySpecifics* profile_specifics) { |
| + sync_pb::AutofillProfileSpecifics* specifics = |
| + profile_specifics->MutableExtension(sync_pb::autofill_profile); |
| + |
| + // This would get compiled out in official builds. The caller is expected to |
| + // pass in a valid profile object with valid guid.(i.e., the caller might |
| + // have to a DCHECK and log before calling. Having to check in 2 places is |
| + // not optimal.) |
| + 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( |
| + ScopedVector<AutofillProfile>* profiles, GuidToProfileMap* profile_map) { |
|
Ilya Sherman
2011/09/01 07:22:31
nit: |profiles| should probably be passed by const
GeorgeY
2011/09/02 04:34:12
Nope. I needed ::iterator, not ::const_iterator.
C
|
| + DCHECK(profile_map); |
| + profile_map->clear(); |
| + for (ScopedVector<AutofillProfile>::iterator i = profiles->begin(); |
| + i != profiles->end(); ++i) |
| + (*profile_map)[(*i)->guid()] = i; |
| +} |
| + |
| +bool AutofillProfileSyncableService::CreateOrUpdateProfile( |
|
dhollowa
2011/09/01 23:11:46
The name of this function doesn't really reflect w
|
| + const SyncData& data, ScopedVector<AutofillProfile>* profiles, |
|
Ilya Sherman
2011/09/01 07:22:31
nit: |profiles| does not seem to be used in this f
GeorgeY
2011/09/02 04:34:12
Was left from older version - removed.
|
| + GuidToProfileMap* profile_map, DataBundle *bundle) { |
|
dhollowa
2011/09/01 20:17:49
nit: s/ */* /
GeorgeY
2011/09/02 04:34:12
Done.
|
| + DCHECK(profiles); |
| + 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)); |
| + |
| + bool added_new_profile = false; |
| + |
| + GuidToProfileMap::iterator it = profile_map->find( |
| + autofill_specifics.guid()); |
| + if (it != profile_map->end()) { |
| + // Some profile that already present is synced. |
| + if (OverwriteProfileWithServerData(*(it->second), autofill_specifics)) |
| + bundle->updated_profiles.push_back(*(it->second)); |
| + profile_map->erase(autofill_specifics.guid()); |
| + } else { |
| + // New profile synced. |
| + AutofillProfile *p(new AutofillProfile(autofill_specifics.guid())); |
|
dhollowa
2011/09/01 20:17:49
nit: s/ */* /
GeorgeY
2011/09/02 04:34:12
Done.
|
| + OverwriteProfileWithServerData(p, autofill_specifics); |
| + |
| + // Check if profile appears under a different guid. |
| + GuidToProfileMap::iterator i; |
| + for (i = profile_map->begin(); i != profile_map->end(); ++i) { |
| + if ((*(i->second))->Compare(*p) == 0) { |
| + bundle->profiles_to_delete.push_back((*(i->second))->guid()); |
| + VLOG(2) << "[AUTOFILL SYNC]" |
| + << "Found in sync db but with a different guid: " |
| + << UTF16ToUTF8((*(i->second))->GetInfo(NAME_FIRST)) |
| + << UTF16ToUTF8((*(i->second))->GetInfo(NAME_LAST)) |
| + << "New guid " << p->guid() << ". Profile to be deleted " |
| + << (*(i->second))->guid(); |
| + profile_map->erase(i); |
|
lipalani1
2011/09/01 05:06:02
Profile is only erased from the map. Who is erasin
GeorgeY
2011/09/02 04:34:12
It is erased from web_db with a call to DataBundle
|
| + break; |
| + } |
| + } |
| + // Bundle takes ownership of new profiles only. |
| + bundle->new_profiles.push_back(p); |
| + added_new_profile = true; |
|
Ilya Sherman
2011/09/01 07:22:31
I'm a little confused here: If the profile appear
GeorgeY
2011/09/02 04:34:12
We do not want to have multiple copies of a profil
|
| + } |
| + return added_new_profile; |
| +} |
| + |
| +void AutofillProfileSyncableService::ActOnChange( |
| + AutofillProfileChange* change) { |
| + DCHECK(change->type() == AutofillProfileChange::REMOVE || change->profile()); |
|
dhollowa
2011/09/01 23:11:46
This DCHECK is not quite accurate. Better would b
GeorgeY
2011/09/02 04:34:12
Further down we assert on any change that is not A
dhollowa
2011/09/07 01:48:15
Yes, but your checks wouldn't catch the case where
GeorgeY
2011/09/07 20:55:07
But that case is not and error - AFAIK, profile is
dhollowa
2011/09/07 21:10:24
That is not correct. See autofill_change.h:51
GeorgeY
2011/09/07 23:26:24
Done.
|
| + DCHECK(sync_processor_); |
| + SyncChangeList new_changes; |
| + switch (change->type()) { |
| + case AutofillProfileChange::ADD: |
| + new_changes.push_back( |
| + CreateChange(SyncChange::ACTION_ADD, *(change->profile()))); |
|
dhollowa
2011/09/01 23:11:46
These CreateChange and CreateDeleteChange helpers
GeorgeY
2011/09/02 04:34:12
sure
|
| + break; |
| + case AutofillProfileChange::UPDATE: |
| + new_changes.push_back( |
| + CreateChange(SyncChange::ACTION_UPDATE, *(change->profile()))); |
| + break; |
| + case AutofillProfileChange::REMOVE: |
| + new_changes.push_back(CreateDeleteChange(change->key())); |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| + SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); |
| + if (error.IsSet()) { |
|
lipalani1
2011/09/01 05:06:02
may be this is a vlog(warning)?
GeorgeY
2011/09/02 04:34:12
Sure.
|
| + VLOG(2) << "[AUTOFILL SYNC]" |
| + << " Failed processing change:" |
| + << " Error:" << error.message() |
| + << " Guid:" << change->profile()->guid(); |
| + } |
| +} |
| + |
| +bool AutofillProfileSyncableService::SaveChangesToWebData( |
| + const DataBundle& bundle) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| + |
| + 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; |
| +} |
| + |
| +AutofillProfileSyncableService::DataBundle::DataBundle() {} |
| + |
| +AutofillProfileSyncableService::DataBundle::~DataBundle() { |
| + STLDeleteElements(&new_profiles); |
| +} |
| + |
| +} // namespace browser_sync |
| + |
| Property changes on: chrome\browser\sync\glue\autofill_profile_syncable_service.cc |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |