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