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, |
| 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, |
| 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, |
| 117 CreateData(*(profiles_[i->second])))); |
| 118 profiles_map_[i->first] = i->second; |
| 119 } |
| 120 |
| 121 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); |
| 122 |
| 123 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 124 new DoOptimisticRefreshForAutofill(personal_data_)); |
| 125 |
| 126 return error; |
| 127 } |
| 128 |
| 129 void AutofillProfileSyncableService::StopSyncing(syncable::ModelType type) { |
| 130 DCHECK(CalledOnValidThread()); |
| 131 DCHECK(sync_processor_ != NULL); |
| 132 DCHECK_EQ(type, syncable::AUTOFILL_PROFILE); |
| 133 |
| 134 sync_processor_ = NULL; |
| 135 profiles_.reset(); |
| 136 profiles_map_.clear(); |
| 137 } |
| 138 |
| 139 SyncDataList AutofillProfileSyncableService::GetAllSyncData( |
| 140 syncable::ModelType type) const { |
| 141 DCHECK(CalledOnValidThread()); |
| 142 DCHECK(sync_processor_ != NULL); |
| 143 DCHECK_EQ(type, syncable::AUTOFILL_PROFILE); |
| 144 |
| 145 SyncDataList current_data; |
| 146 |
| 147 for (GUIDToProfileMap::const_iterator i = profiles_map_.begin(); |
| 148 i != profiles_map_.end(); ++i) { |
| 149 current_data.push_back(CreateData(*(profiles_[i->second]))); |
| 150 } |
| 151 return current_data; |
| 152 } |
| 153 |
| 154 SyncError AutofillProfileSyncableService::ProcessSyncChanges( |
| 155 const tracked_objects::Location& from_here, |
| 156 const SyncChangeList& change_list) { |
| 157 DCHECK(CalledOnValidThread()); |
| 158 DCHECK(sync_processor_ != NULL); |
| 159 if (sync_processor_ == NULL) { |
| 160 SyncError error(FROM_HERE, "Models not yet associated.", |
| 161 syncable::AUTOFILL_PROFILE); |
| 162 return error; |
| 163 } |
| 164 |
| 165 DataBundle bundle; |
| 166 |
| 167 for (SyncChangeList::const_iterator i = change_list.begin(); |
| 168 i != change_list.end(); ++i) { |
| 169 DCHECK(i->IsValid()); |
| 170 switch (i->change_type()) { |
| 171 case SyncChange::ACTION_ADD: |
| 172 case SyncChange::ACTION_UPDATE: |
| 173 CreateOrUpdateProfile(i->sync_data(), &profiles_map_, &bundle); |
| 174 break; |
| 175 case SyncChange::ACTION_DELETE: { |
| 176 std::string guid = i->sync_data().GetSpecifics(). |
| 177 GetExtension(sync_pb::autofill_profile).guid(); |
| 178 bundle.profiles_to_delete.push_back(guid); |
| 179 profiles_map_.erase(guid); |
| 180 } break; |
| 181 default: |
| 182 NOTREACHED() << "Unexpected sync change state."; |
| 183 return SyncError(FROM_HERE, "ProcessSyncChanges failed on ChangeType " + |
| 184 SyncChange::ChangeTypeToString(i->change_type()), |
| 185 syncable::AUTOFILL_PROFILE); |
| 186 } |
| 187 } |
| 188 |
| 189 if (!SaveChangesToWebData(bundle)) |
| 190 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); |
| 191 |
| 192 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 193 new DoOptimisticRefreshForAutofill(personal_data_)); |
| 194 return SyncError(); |
| 195 } |
| 196 |
| 197 void AutofillProfileSyncableService::Observe(int type, |
| 198 const NotificationSource& source, |
| 199 const NotificationDetails& details) { |
| 200 DCHECK_EQ(type, chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED); |
| 201 WebDataService* wds = Source<WebDataService>(source).ptr(); |
| 202 |
| 203 DCHECK(wds && wds->GetDatabase() == web_database_); |
| 204 |
| 205 AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr(); |
| 206 |
| 207 ActOnChange(*change); |
| 208 } |
| 209 |
| 210 bool AutofillProfileSyncableService::LoadAutofillData( |
| 211 std::vector<AutofillProfile*>* profiles) { |
| 212 if (!web_database_->GetAutofillTable()->GetAutofillProfiles(profiles)) |
| 213 return false; |
| 214 |
| 215 return true; |
| 216 } |
| 217 |
| 218 bool AutofillProfileSyncableService::SaveChangesToWebData( |
| 219 const DataBundle& bundle) { |
| 220 DCHECK(CalledOnValidThread()); |
| 221 |
| 222 for (size_t i = 0; i < bundle.new_profiles.size(); i++) { |
| 223 if (!web_database_->GetAutofillTable()->AddAutofillProfile( |
| 224 *bundle.new_profiles[i])) |
| 225 return false; |
| 226 } |
| 227 |
| 228 for (size_t i = 0; i < bundle.updated_profiles.size(); i++) { |
| 229 if (!web_database_->GetAutofillTable()->UpdateAutofillProfile( |
| 230 *bundle.updated_profiles[i])) |
| 231 return false; |
| 232 } |
| 233 |
| 234 for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) { |
| 235 if (!web_database_->GetAutofillTable()->RemoveAutofillProfile( |
| 236 bundle.profiles_to_delete[i])) |
| 237 return false; |
| 238 } |
| 239 return true; |
| 240 } |
| 241 |
| 242 // static |
| 243 bool AutofillProfileSyncableService::OverwriteProfileWithServerData( |
| 244 const sync_pb::AutofillProfileSpecifics& specifics, |
| 245 AutofillProfile* profile) { |
| 246 bool diff = false; |
| 247 diff = MergeField(profile, NAME_FIRST, specifics.name_first()) || diff; |
| 248 diff = MergeField(profile, NAME_LAST, specifics.name_last()) || diff; |
| 249 diff = MergeField(profile, NAME_MIDDLE, specifics.name_middle()) || diff; |
| 250 diff = MergeField(profile, ADDRESS_HOME_LINE1, |
| 251 specifics.address_home_line1()) || diff; |
| 252 diff = MergeField(profile, ADDRESS_HOME_LINE2, |
| 253 specifics.address_home_line2()) || diff; |
| 254 diff = MergeField(profile, ADDRESS_HOME_CITY, |
| 255 specifics.address_home_city()) || diff; |
| 256 diff = MergeField(profile, ADDRESS_HOME_STATE, |
| 257 specifics.address_home_state()) || diff; |
| 258 diff = MergeField(profile, ADDRESS_HOME_COUNTRY, |
| 259 specifics.address_home_country()) || diff; |
| 260 diff = MergeField(profile, ADDRESS_HOME_ZIP, |
| 261 specifics.address_home_zip()) || diff; |
| 262 diff = MergeField(profile, EMAIL_ADDRESS, specifics.email_address()) || diff; |
| 263 diff = MergeField(profile, COMPANY_NAME, specifics.company_name()) || diff; |
| 264 diff = MergeField(profile, PHONE_FAX_WHOLE_NUMBER, |
| 265 specifics.phone_fax_whole_number()) || diff; |
| 266 diff = MergeField(profile, PHONE_HOME_WHOLE_NUMBER, |
| 267 specifics.phone_home_whole_number()) || diff; |
| 268 return diff; |
| 269 } |
| 270 |
| 271 // static |
| 272 void AutofillProfileSyncableService::WriteAutofillProfile( |
| 273 const AutofillProfile& profile, |
| 274 sync_pb::EntitySpecifics* profile_specifics) { |
| 275 sync_pb::AutofillProfileSpecifics* specifics = |
| 276 profile_specifics->MutableExtension(sync_pb::autofill_profile); |
| 277 |
| 278 DCHECK(guid::IsValidGUID(profile.guid())); |
| 279 |
| 280 specifics->set_guid(profile.guid()); |
| 281 specifics->set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST))); |
| 282 specifics->set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE))); |
| 283 specifics->set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST))); |
| 284 specifics->set_address_home_line1( |
| 285 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1))); |
| 286 specifics->set_address_home_line2( |
| 287 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2))); |
| 288 specifics->set_address_home_city(UTF16ToUTF8(profile.GetInfo( |
| 289 ADDRESS_HOME_CITY))); |
| 290 specifics->set_address_home_state(UTF16ToUTF8(profile.GetInfo( |
| 291 ADDRESS_HOME_STATE))); |
| 292 specifics->set_address_home_country(UTF16ToUTF8(profile.GetInfo( |
| 293 ADDRESS_HOME_COUNTRY))); |
| 294 specifics->set_address_home_zip(UTF16ToUTF8(profile.GetInfo( |
| 295 ADDRESS_HOME_ZIP))); |
| 296 specifics->set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS))); |
| 297 specifics->set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME))); |
| 298 specifics->set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo( |
| 299 PHONE_FAX_WHOLE_NUMBER))); |
| 300 specifics->set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo( |
| 301 PHONE_HOME_WHOLE_NUMBER))); |
| 302 } |
| 303 |
| 304 void AutofillProfileSyncableService::CreateGUIDToProfileMap( |
| 305 const std::vector<AutofillProfile *>& profiles, |
| 306 GUIDToProfileMap* profile_map) { |
| 307 DCHECK(profile_map); |
| 308 profile_map->clear(); |
| 309 for (size_t i = 0; i < profiles.size(); ++i) |
| 310 (*profile_map)[profiles[i]->guid()] = i; |
| 311 } |
| 312 |
| 313 AutofillProfileSyncableService::GUIDToProfileMap::iterator |
| 314 AutofillProfileSyncableService::CreateOrUpdateProfile( |
| 315 const SyncData& data, GUIDToProfileMap* profile_map, DataBundle* bundle) { |
| 316 DCHECK(profile_map); |
| 317 DCHECK(bundle); |
| 318 |
| 319 DCHECK_EQ(syncable::AUTOFILL_PROFILE, data.GetDataType()); |
| 320 |
| 321 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); |
| 322 const sync_pb::AutofillProfileSpecifics& autofill_specifics( |
| 323 specifics.GetExtension(sync_pb::autofill_profile)); |
| 324 |
| 325 GUIDToProfileMap::iterator it = profile_map->find( |
| 326 autofill_specifics.guid()); |
| 327 if (it != profile_map->end()) { |
| 328 // Some profile that already present is synced. |
| 329 if (OverwriteProfileWithServerData(autofill_specifics, |
| 330 profiles_[it->second])) |
| 331 bundle->updated_profiles.push_back(profiles_[it->second]); |
| 332 } else { |
| 333 // New profile synced. |
| 334 AutofillProfile* new_profile( |
| 335 new AutofillProfile(autofill_specifics.guid())); |
| 336 OverwriteProfileWithServerData(autofill_specifics, new_profile); |
| 337 |
| 338 // Check if profile appears under a different guid. |
| 339 for (GUIDToProfileMap::iterator i = profile_map->begin(); |
| 340 i != profile_map->end(); ++i) { |
| 341 if (profiles_[i->second]->Compare(*new_profile) == 0) { |
| 342 bundle->profiles_to_delete.push_back(profiles_[i->second]->guid()); |
| 343 VLOG(2) << "[AUTOFILL SYNC]" |
| 344 << "Found in sync db but with a different guid: " |
| 345 << UTF16ToUTF8(profiles_[it->second]->GetInfo(NAME_FIRST)) |
| 346 << UTF16ToUTF8(profiles_[it->second]->GetInfo(NAME_LAST)) |
| 347 << "New guid " << new_profile->guid() |
| 348 << ". Profile to be deleted " << profiles_[it->second]->guid(); |
| 349 profile_map->erase(i); |
| 350 break; |
| 351 } |
| 352 } |
| 353 profiles_.push_back(new_profile); |
| 354 it = profile_map->insert( |
| 355 std::make_pair(new_profile->guid(), profiles_.size() - 1)).first; |
| 356 bundle->new_profiles.push_back(new_profile); |
| 357 } |
| 358 return it; |
| 359 } |
| 360 |
| 361 void AutofillProfileSyncableService::ActOnChange( |
| 362 const AutofillProfileChange& change) { |
| 363 DCHECK(change.type() == AutofillProfileChange::REMOVE || change.profile()); |
| 364 DCHECK(sync_processor_); |
| 365 SyncChangeList new_changes; |
| 366 DataBundle bundle; |
| 367 switch (change.type()) { |
| 368 case AutofillProfileChange::ADD: |
| 369 new_changes.push_back( |
| 370 SyncChange(SyncChange::ACTION_ADD, CreateData(*(change.profile())))); |
| 371 DCHECK(profiles_map_.find(change.profile()->guid()) == |
| 372 profiles_map_.end()); |
| 373 profiles_.push_back(new AutofillProfile(*(change.profile()))); |
| 374 profiles_map_[change.profile()->guid()] = profiles_.size() - 1; |
| 375 break; |
| 376 case AutofillProfileChange::UPDATE: { |
| 377 GUIDToProfileMap::iterator it = profiles_map_.find( |
| 378 change.profile()->guid()); |
| 379 DCHECK(it != profiles_map_.end()); |
| 380 *profiles_[it->second] = *(change.profile()); |
| 381 new_changes.push_back( |
| 382 SyncChange(SyncChange::ACTION_UPDATE, |
| 383 CreateData(*(change.profile())))); |
| 384 break; |
| 385 } |
| 386 case AutofillProfileChange::REMOVE: { |
| 387 AutofillProfile empty_profile(change.key()); |
| 388 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, |
| 389 CreateData(empty_profile))); |
| 390 profiles_map_.erase(change.key()); |
| 391 break; |
| 392 } |
| 393 default: |
| 394 NOTREACHED(); |
| 395 } |
| 396 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); |
| 397 if (error.IsSet()) { |
| 398 LOG(WARNING) << "[AUTOFILL SYNC]" |
| 399 << " Failed processing change:" |
| 400 << " Error:" << error.message() |
| 401 << " Guid:" << change.profile()->guid(); |
| 402 } |
| 403 } |
| 404 |
| 405 SyncData AutofillProfileSyncableService::CreateData( |
| 406 const AutofillProfile& profile) { |
| 407 sync_pb::EntitySpecifics specifics; |
| 408 WriteAutofillProfile(profile, &specifics); |
| 409 return SyncData::CreateLocalData(profile.guid(), profile.guid(), specifics); |
| 410 } |
| 411 |
| 412 AutofillProfileSyncableService::DataBundle::DataBundle() {} |
| 413 |
| 414 AutofillProfileSyncableService::DataBundle::~DataBundle() { |
| 415 } |
| 416 |
| 417 } // namespace browser_sync |
| 418 |
OLD | NEW |