|
OLD | NEW |
---|---|
(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 | |
OLD | NEW |