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/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 |