Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Unified Diff: chrome/browser/sync/glue/autofill_profile_syncable_service.cc

Issue 7819002: Migrate AutofillProfile sync to new API. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698