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

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

Powered by Google App Engine
This is Rietveld 408576698