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

Side by Side 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, 3 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 unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sync/glue/autofill_profile_syncable_service.h"
6
7 #include "base/tracked.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/sync/api/sync_error.h"
11 #include "chrome/browser/sync/glue/do_optimistic_refresh_task.h"
12 #include "chrome/browser/webdata/autofill_table.h"
13 #include "chrome/browser/webdata/web_database.h"
14 #include "chrome/common/chrome_notification_types.h"
15 #include "chrome/common/guid.h"
16 #include "content/common/notification_service.h"
17
18 namespace {
19
20 // Helper to compare the local value and cloud value of a field, merge into
21 // the local value if they differ, and return whether the merge happened.
22 bool MergeField(FormGroup* form_group,
23 AutofillFieldType field_type,
24 const std::string& specifics_field) {
25 if (UTF16ToUTF8(form_group->GetInfo(field_type)) == specifics_field)
26 return false;
27 form_group->SetInfo(field_type, UTF8ToUTF16(specifics_field));
28 return true;
29 }
30
31 } // namespace
32
33 namespace browser_sync {
34
35 const char kAutofillProfileTag[] = "google_chrome_autofill_profiles";
36
37 AutofillProfileSyncableService::AutofillProfileSyncableService(
38 WebDatabase* web_database,
39 PersonalDataManager* personal_data,
40 Profile* profile)
41 : web_database_(web_database),
42 personal_data_(personal_data),
43 sync_processor_(NULL) {
44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
45 DCHECK(web_database_);
46 DCHECK(personal_data_);
47 DCHECK(profile);
48 notification_registrar_.Add(this,
49 chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED,
50 Source<WebDataService>(
51 profile->GetWebDataService(Profile::EXPLICIT_ACCESS)));
52 }
53
54 AutofillProfileSyncableService::~AutofillProfileSyncableService() {
55 DCHECK(CalledOnValidThread());
56 }
57
58 AutofillProfileSyncableService::AutofillProfileSyncableService()
59 : web_database_(NULL),
60 personal_data_(NULL),
61 sync_processor_(NULL) {
62 }
63
64 SyncError AutofillProfileSyncableService::MergeDataAndStartSyncing(
65 syncable::ModelType type,
66 const SyncDataList& initial_sync_data,
67 SyncChangeProcessor* sync_processor) {
68 DCHECK(CalledOnValidThread());
69 DCHECK(sync_processor_ == NULL);
70 VLOG(1) << "Associating Autofill: MergeDataAndStartSyncing";
71
72 if (!LoadAutofillData(&profiles_.get())) {
73 return SyncError(
74 FROM_HERE, "Could not get the autofill data from WebDatabase.",
75 model_type());
76 }
77
78 if (VLOG_IS_ON(2)) {
79 VLOG(2) << "[AUTOFILL MIGRATION]"
80 << "Printing profiles from web db";
81
82 for (ScopedVector<AutofillProfile>::const_iterator ix =
83 profiles_.begin(); ix != profiles_.end(); ++ix) {
84 AutofillProfile* p = *ix;
85 VLOG(2) << "[AUTOFILL MIGRATION] "
86 << p->GetInfo(NAME_FIRST)
87 << p->GetInfo(NAME_LAST)
88 << p->guid();
89 }
90 }
91
92 sync_processor_ = sync_processor;
93
94 GUIDToProfileMap remaining_profiles;
95 CreateGUIDToProfileMap(profiles_, &remaining_profiles);
96
97 DataBundle bundle;
98 // Go through and check for all the profiles that sync already knows about.
99 for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin();
100 sync_iter != initial_sync_data.end();
101 ++sync_iter) {
102 GUIDToProfileMap:: iterator it =
Ilya Sherman 2011/09/02 21:49:50 nit: Extraneous space after ":: "
GeorgeY 2011/09/03 00:01:29 Done.
103 CreateOrUpdateProfile(*sync_iter, &remaining_profiles, &bundle);
104 profiles_map_[it->first] = it->second;
Ilya Sherman 2011/09/02 21:49:50 Are you sure you want to update the map here, rath
GeorgeY 2011/09/03 00:01:29 Yes. Any changes from Sync service and WDS will be
Ilya Sherman 2011/09/03 00:10:57 But there's a chance that the SaveChangesToWebData
GeorgeY 2011/09/07 00:28:14 Yes, this is a serious problem and breaks us local
Ilya Sherman 2011/09/07 01:03:58 Hmm, not sure I follow why the data would be corre
105 remaining_profiles.erase(it);
106 }
107
108 if (!SaveChangesToWebData(bundle))
109 return SyncError(FROM_HERE, "Failed to update webdata.", model_type());
110
111 SyncChangeList new_changes;
112 for (GUIDToProfileMap::iterator i = remaining_profiles.begin();
113 i != remaining_profiles.end(); ++i) {
114 new_changes.push_back(
115 SyncChange(SyncChange::ACTION_ADD,
116 CreateData(*(profiles_[i->second]))));
117 profiles_map_[i->first] = i->second;
118 }
119
120 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
121
122 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
123 new DoOptimisticRefreshForAutofill(personal_data_));
124
125 return error;
126 }
127
128 void AutofillProfileSyncableService::StopSyncing(syncable::ModelType type) {
129 DCHECK(CalledOnValidThread());
130 DCHECK(sync_processor_ != NULL);
131 DCHECK_EQ(type, syncable::AUTOFILL_PROFILE);
132
133 sync_processor_ = NULL;
134 profiles_.reset();
135 profiles_map_.clear();
136 }
137
138 SyncDataList AutofillProfileSyncableService::GetAllSyncData(
139 syncable::ModelType type) const {
140 DCHECK(CalledOnValidThread());
141 DCHECK(sync_processor_ != NULL);
142 DCHECK_EQ(type, syncable::AUTOFILL_PROFILE);
143
144 SyncDataList current_data;
145
146 for (GUIDToProfileMap::const_iterator i = profiles_map_.begin();
147 i != profiles_map_.end(); ++i) {
148 sync_pb::EntitySpecifics specifics;
149 WriteAutofillProfile(*(profiles_[i->second]), &specifics);
150 current_data.push_back(SyncData::CreateLocalData(
151 profiles_[i->second]->guid(), profiles_[i->second]->guid(), specifics));
Ilya Sherman 2011/09/02 21:49:50 nit: I think you can call into CreateData() here.
GeorgeY 2011/09/03 00:01:29 Done.
152 }
153 return current_data;
154 }
155
156 SyncError AutofillProfileSyncableService::ProcessSyncChanges(
157 const tracked_objects::Location& from_here,
158 const SyncChangeList& change_list) {
159 DCHECK(CalledOnValidThread());
160 DCHECK(sync_processor_ != NULL);
161 if (sync_processor_ == NULL) {
162 SyncError error(FROM_HERE, "Models not yet associated.",
163 syncable::AUTOFILL_PROFILE);
164 return error;
165 }
166
167 DataBundle bundle;
168
169 for (SyncChangeList::const_iterator i = change_list.begin();
170 i != change_list.end(); ++i) {
171 DCHECK(i->IsValid());
172 switch (i->change_type()) {
173 case SyncChange::ACTION_ADD:
174 case SyncChange::ACTION_UPDATE:
175 CreateOrUpdateProfile(i->sync_data(), &profiles_map_, &bundle);
176 break;
177 case SyncChange::ACTION_DELETE: {
178 std::string guid = i->sync_data().GetSpecifics().
179 GetExtension(sync_pb::autofill_profile).guid();
180 bundle.profiles_to_delete.push_back(guid);
181 profiles_map_.erase(guid);
182 } break;
183 default:
184 NOTREACHED() << "Unexpected sync change state.";
185 return SyncError(FROM_HERE, "ProcessSyncChanges failed on ChangeType " +
186 SyncChange::ChangeTypeToString(i->change_type()),
187 syncable::AUTOFILL_PROFILE);
188 }
189 }
190
191 if (!SaveChangesToWebData(bundle))
192 return SyncError(FROM_HERE, "Failed to update webdata.", model_type());
193
194 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
195 new DoOptimisticRefreshForAutofill(personal_data_));
196 return SyncError();
197 }
198
199 void AutofillProfileSyncableService::Observe(int type,
200 const NotificationSource& source,
201 const NotificationDetails& details) {
202 DCHECK_EQ(type, chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED);
203 WebDataService* wds = Source<WebDataService>(source).ptr();
204
205 if (!wds || wds->GetDatabase() != web_database_)
Ilya Sherman 2011/09/02 21:49:50 nit: Can this be a DCHECK instead?
GeorgeY 2011/09/03 00:01:29 sure
206 return;
207
208 AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr();
209
210 ActOnChange(change);
211 }
212
213 bool AutofillProfileSyncableService::LoadAutofillData(
214 std::vector<AutofillProfile*>* profiles) const {
215 // const_cast it as GetAutofillProfiles() is a really const accessor, though
216 // not so declared.
217 if (!const_cast<WebDatabase*>(web_database_)->
218 GetAutofillTable()->GetAutofillProfiles(profiles))
219 return false;
220
221 return true;
222 }
223
224 bool AutofillProfileSyncableService::SaveChangesToWebData(
225 const DataBundle& bundle) {
226 DCHECK(CalledOnValidThread());
227
228 for (size_t i = 0; i < bundle.new_profiles.size(); i++) {
229 if (!web_database_->GetAutofillTable()->AddAutofillProfile(
230 *bundle.new_profiles[i]))
231 return false;
232 }
233
234 for (size_t i = 0; i < bundle.updated_profiles.size(); i++) {
235 if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
236 *bundle.updated_profiles[i]))
237 return false;
238 }
239
240 for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) {
241 if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(
242 bundle.profiles_to_delete[i]))
243 return false;
244 }
245 return true;
246 }
247
248 // static
249 bool AutofillProfileSyncableService::OverwriteProfileWithServerData(
250 const sync_pb::AutofillProfileSpecifics& specifics,
251 AutofillProfile* profile) {
252 bool diff = false;
253 diff = MergeField(profile, NAME_FIRST, specifics.name_first()) || diff;
254 diff = MergeField(profile, NAME_LAST, specifics.name_last()) || diff;
255 diff = MergeField(profile, NAME_MIDDLE, specifics.name_middle()) || diff;
256 diff = MergeField(profile, ADDRESS_HOME_LINE1,
257 specifics.address_home_line1()) || diff;
258 diff = MergeField(profile, ADDRESS_HOME_LINE2,
259 specifics.address_home_line2()) || diff;
260 diff = MergeField(profile, ADDRESS_HOME_CITY,
261 specifics.address_home_city()) || diff;
262 diff = MergeField(profile, ADDRESS_HOME_STATE,
263 specifics.address_home_state()) || diff;
264 diff = MergeField(profile, ADDRESS_HOME_COUNTRY,
265 specifics.address_home_country()) || diff;
266 diff = MergeField(profile, ADDRESS_HOME_ZIP,
267 specifics.address_home_zip()) || diff;
268 diff = MergeField(profile, EMAIL_ADDRESS, specifics.email_address()) || diff;
269 diff = MergeField(profile, COMPANY_NAME, specifics.company_name()) || diff;
270 diff = MergeField(profile, PHONE_FAX_WHOLE_NUMBER,
271 specifics.phone_fax_whole_number()) || diff;
272 diff = MergeField(profile, PHONE_HOME_WHOLE_NUMBER,
273 specifics.phone_home_whole_number()) || diff;
274 return diff;
275 }
276
277 // static
278 void AutofillProfileSyncableService::WriteAutofillProfile(
279 const AutofillProfile& profile,
280 sync_pb::EntitySpecifics* profile_specifics) {
281 sync_pb::AutofillProfileSpecifics* specifics =
282 profile_specifics->MutableExtension(sync_pb::autofill_profile);
283
284 // This would get compiled out in official builds. The caller is expected to
285 // pass in a valid profile object with valid guid.(i.e., the caller might
286 // have to a DCHECK and log before calling. Having to check in 2 places is
287 // not optimal.)
Ilya Sherman 2011/09/02 21:49:50 nit: I do not understand this comment. What's spe
GeorgeY 2011/09/03 00:01:29 Not mine - from the old file. Came with the functi
288 DCHECK(guid::IsValidGUID(profile.guid()));
289
290 specifics->set_guid(profile.guid());
291 specifics->set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST)));
292 specifics->set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE)));
293 specifics->set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST)));
294 specifics->set_address_home_line1(
295 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1)));
296 specifics->set_address_home_line2(
297 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2)));
298 specifics->set_address_home_city(UTF16ToUTF8(profile.GetInfo(
299 ADDRESS_HOME_CITY)));
300 specifics->set_address_home_state(UTF16ToUTF8(profile.GetInfo(
301 ADDRESS_HOME_STATE)));
302 specifics->set_address_home_country(UTF16ToUTF8(profile.GetInfo(
303 ADDRESS_HOME_COUNTRY)));
304 specifics->set_address_home_zip(UTF16ToUTF8(profile.GetInfo(
305 ADDRESS_HOME_ZIP)));
306 specifics->set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS)));
307 specifics->set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME)));
308 specifics->set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo(
309 PHONE_FAX_WHOLE_NUMBER)));
310 specifics->set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo(
311 PHONE_HOME_WHOLE_NUMBER)));
312 }
313
314 void AutofillProfileSyncableService::CreateGUIDToProfileMap(
315 const ScopedVector<AutofillProfile>& profiles,
316 GUIDToProfileMap* profile_map) {
317 DCHECK(profile_map);
318 profile_map->clear();
319 for (size_t i = 0; i < profiles.size(); ++i)
320 (*profile_map)[profiles[i]->guid()] = i;
321 }
322
323 AutofillProfileSyncableService::GUIDToProfileMap::iterator
324 AutofillProfileSyncableService::CreateOrUpdateProfile(
325 const SyncData& data, GUIDToProfileMap* profile_map, DataBundle* bundle) {
326 DCHECK(profile_map);
327 DCHECK(bundle);
328
329 DCHECK_EQ(syncable::AUTOFILL_PROFILE, data.GetDataType());
330
331 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
332 const sync_pb::AutofillProfileSpecifics& autofill_specifics(
333 specifics.GetExtension(sync_pb::autofill_profile));
334
335 GUIDToProfileMap::iterator it = profile_map->find(
336 autofill_specifics.guid());
337 if (it != profile_map->end()) {
338 // Some profile that already present is synced.
339 if (OverwriteProfileWithServerData(autofill_specifics,
340 profiles_[it->second]))
341 bundle->updated_profiles.push_back(profiles_[it->second]);
342 } else {
343 // New profile synced.
344 AutofillProfile* p(new AutofillProfile(autofill_specifics.guid()));
Ilya Sherman 2011/09/02 21:49:50 nit: Please give the variable a more complete name
GeorgeY 2011/09/03 00:01:29 Done.
345 OverwriteProfileWithServerData(autofill_specifics, p);
346
347 // Check if profile appears under a different guid.
348 for (GUIDToProfileMap::iterator i = profile_map->begin();
349 i != profile_map->end(); ++i) {
350 if (profiles_[i->second]->Compare(*p) == 0) {
351 bundle->profiles_to_delete.push_back(profiles_[i->second]->guid());
352 VLOG(2) << "[AUTOFILL SYNC]"
353 << "Found in sync db but with a different guid: "
354 << UTF16ToUTF8(profiles_[it->second]->GetInfo(NAME_FIRST))
355 << UTF16ToUTF8(profiles_[it->second]->GetInfo(NAME_LAST))
356 << "New guid " << p->guid() << ". Profile to be deleted "
357 << profiles_[it->second]->guid();
358 profile_map->erase(i);
359 break;
360 }
361 }
362 profiles_.push_back(p);
363 it = profile_map->insert(
364 std::make_pair(p->guid(), profiles_.size() - 1)).first;
365 bundle->new_profiles.push_back(p);
366 }
367 return it;
368 }
369
370 void AutofillProfileSyncableService::ActOnChange(
371 AutofillProfileChange* change) {
372 DCHECK(change->type() == AutofillProfileChange::REMOVE || change->profile());
373 DCHECK(sync_processor_);
374 SyncChangeList new_changes;
375 DataBundle bundle;
376 switch (change->type()) {
377 case AutofillProfileChange::ADD:
378 new_changes.push_back(
379 SyncChange(SyncChange::ACTION_ADD, CreateData(*(change->profile()))));
380 DCHECK(profiles_map_.find(change->profile()->guid()) ==
381 profiles_map_.end());
382 profiles_.push_back(new AutofillProfile(*(change->profile())));
383 profiles_map_[change->profile()->guid()] = profiles_.size() - 1;
384 break;
385 case AutofillProfileChange::UPDATE:
386 new_changes.push_back(
387 SyncChange(SyncChange::ACTION_UPDATE,
388 CreateData(*(change->profile()))));
Ilya Sherman 2011/09/02 21:49:50 I believe you need to update the cached profiles i
GeorgeY 2011/09/03 00:01:29 true. Fixed.
389 break;
390 case AutofillProfileChange::REMOVE: {
391 AutofillProfile empty_profile(change->key());
392 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE,
393 CreateData(empty_profile)));
394 profiles_map_.erase(change->key());
395 break;
396 }
397 default:
398 NOTREACHED();
399 }
400 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
401 if (error.IsSet()) {
402 VLOG(1) << "[AUTOFILL SYNC]"
Ilya Sherman 2011/09/02 21:49:50 nit: Does this happen commonly enough to where LOG
GeorgeY 2011/09/03 00:01:29 Done.
403 << " Failed processing change:"
404 << " Error:" << error.message()
405 << " Guid:" << change->profile()->guid();
406 }
407 }
408
409 SyncData AutofillProfileSyncableService::CreateData(
410 const AutofillProfile& profile) {
411 sync_pb::EntitySpecifics specifics;
412 WriteAutofillProfile(profile, &specifics);
413 return SyncData::CreateLocalData(profile.guid(), profile.guid(), specifics);
414 }
415
416 AutofillProfileSyncableService::DataBundle::DataBundle() {}
417
418 AutofillProfileSyncableService::DataBundle::~DataBundle() {
419 }
420
421 } // namespace browser_sync
422
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698