Chromium Code Reviews
|
| 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 |