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, 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, |
| 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 // Check if sync is on. If we receive notification prior to the sync being set |
| 201 // up we are going to process all when MergeData..() is called. If we receive |
| 202 // notification after the sync exited, it will be sinced next time Chrome |
| 203 // starts. |
| 204 if (!sync_processor_) |
| 205 return; |
| 206 WebDataService* wds = Source<WebDataService>(source).ptr(); |
| 207 |
| 208 DCHECK(wds && wds->GetDatabase() == web_database_); |
| 209 |
| 210 AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr(); |
| 211 |
| 212 ActOnChange(*change); |
| 213 } |
| 214 |
| 215 bool AutofillProfileSyncableService::LoadAutofillData( |
| 216 std::vector<AutofillProfile*>* profiles) { |
| 217 return web_database_->GetAutofillTable()->GetAutofillProfiles(profiles); |
| 218 } |
| 219 |
| 220 bool AutofillProfileSyncableService::SaveChangesToWebData( |
| 221 const DataBundle& bundle) { |
| 222 DCHECK(CalledOnValidThread()); |
| 223 |
| 224 for (size_t i = 0; i < bundle.profiles_to_add.size(); i++) { |
| 225 if (!web_database_->GetAutofillTable()->AddAutofillProfile( |
| 226 *bundle.profiles_to_add[i])) |
| 227 return false; |
| 228 } |
| 229 |
| 230 for (size_t i = 0; i < bundle.profiles_to_update.size(); i++) { |
| 231 if (!web_database_->GetAutofillTable()->UpdateAutofillProfile( |
| 232 *bundle.profiles_to_update[i])) |
| 233 return false; |
| 234 } |
| 235 |
| 236 for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) { |
| 237 if (!web_database_->GetAutofillTable()->RemoveAutofillProfile( |
| 238 bundle.profiles_to_delete[i])) |
| 239 return false; |
| 240 } |
| 241 return true; |
| 242 } |
| 243 |
| 244 // static |
| 245 bool AutofillProfileSyncableService::OverwriteProfileWithServerData( |
| 246 const sync_pb::AutofillProfileSpecifics& specifics, |
| 247 AutofillProfile* profile) { |
| 248 bool diff = false; |
| 249 diff = MergeField(profile, NAME_FIRST, specifics.name_first()) || diff; |
| 250 diff = MergeField(profile, NAME_LAST, specifics.name_last()) || diff; |
| 251 diff = MergeField(profile, NAME_MIDDLE, specifics.name_middle()) || diff; |
| 252 diff = MergeField(profile, ADDRESS_HOME_LINE1, |
| 253 specifics.address_home_line1()) || diff; |
| 254 diff = MergeField(profile, ADDRESS_HOME_LINE2, |
| 255 specifics.address_home_line2()) || diff; |
| 256 diff = MergeField(profile, ADDRESS_HOME_CITY, |
| 257 specifics.address_home_city()) || diff; |
| 258 diff = MergeField(profile, ADDRESS_HOME_STATE, |
| 259 specifics.address_home_state()) || diff; |
| 260 diff = MergeField(profile, ADDRESS_HOME_COUNTRY, |
| 261 specifics.address_home_country()) || diff; |
| 262 diff = MergeField(profile, ADDRESS_HOME_ZIP, |
| 263 specifics.address_home_zip()) || diff; |
| 264 diff = MergeField(profile, EMAIL_ADDRESS, specifics.email_address()) || diff; |
| 265 diff = MergeField(profile, COMPANY_NAME, specifics.company_name()) || diff; |
| 266 diff = MergeField(profile, PHONE_FAX_WHOLE_NUMBER, |
| 267 specifics.phone_fax_whole_number()) || diff; |
| 268 diff = MergeField(profile, PHONE_HOME_WHOLE_NUMBER, |
| 269 specifics.phone_home_whole_number()) || diff; |
| 270 return diff; |
| 271 } |
| 272 |
| 273 // static |
| 274 void AutofillProfileSyncableService::WriteAutofillProfile( |
| 275 const AutofillProfile& profile, |
| 276 sync_pb::EntitySpecifics* profile_specifics) { |
| 277 sync_pb::AutofillProfileSpecifics* specifics = |
| 278 profile_specifics->MutableExtension(sync_pb::autofill_profile); |
| 279 |
| 280 DCHECK(guid::IsValidGUID(profile.guid())); |
| 281 |
| 282 specifics->set_guid(profile.guid()); |
| 283 specifics->set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST))); |
| 284 specifics->set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE))); |
| 285 specifics->set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST))); |
| 286 specifics->set_address_home_line1( |
| 287 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1))); |
| 288 specifics->set_address_home_line2( |
| 289 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2))); |
| 290 specifics->set_address_home_city(UTF16ToUTF8(profile.GetInfo( |
| 291 ADDRESS_HOME_CITY))); |
| 292 specifics->set_address_home_state(UTF16ToUTF8(profile.GetInfo( |
| 293 ADDRESS_HOME_STATE))); |
| 294 specifics->set_address_home_country(UTF16ToUTF8(profile.GetInfo( |
| 295 ADDRESS_HOME_COUNTRY))); |
| 296 specifics->set_address_home_zip(UTF16ToUTF8(profile.GetInfo( |
| 297 ADDRESS_HOME_ZIP))); |
| 298 specifics->set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS))); |
| 299 specifics->set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME))); |
| 300 specifics->set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo( |
| 301 PHONE_FAX_WHOLE_NUMBER))); |
| 302 specifics->set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo( |
| 303 PHONE_HOME_WHOLE_NUMBER))); |
| 304 } |
| 305 |
| 306 void AutofillProfileSyncableService::CreateGUIDToProfileMap( |
| 307 const std::vector<AutofillProfile*>& profiles, |
| 308 GUIDToProfileMap* profile_map) { |
| 309 DCHECK(profile_map); |
| 310 profile_map->clear(); |
| 311 for (size_t i = 0; i < profiles.size(); ++i) |
| 312 (*profile_map)[profiles[i]->guid()] = profiles[i]; |
| 313 } |
| 314 |
| 315 AutofillProfileSyncableService::GUIDToProfileMap::iterator |
| 316 AutofillProfileSyncableService::CreateOrUpdateProfile( |
| 317 const SyncData& data, GUIDToProfileMap* profile_map, DataBundle* bundle) { |
| 318 DCHECK(profile_map); |
| 319 DCHECK(bundle); |
| 320 |
| 321 DCHECK_EQ(syncable::AUTOFILL_PROFILE, data.GetDataType()); |
| 322 |
| 323 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); |
| 324 const sync_pb::AutofillProfileSpecifics& autofill_specifics( |
| 325 specifics.GetExtension(sync_pb::autofill_profile)); |
| 326 |
| 327 GUIDToProfileMap::iterator it = profile_map->find( |
| 328 autofill_specifics.guid()); |
| 329 if (it != profile_map->end()) { |
| 330 // Some profile that already present is synced. |
| 331 if (OverwriteProfileWithServerData(autofill_specifics, it->second)) |
| 332 bundle->profiles_to_update.push_back(it->second); |
| 333 } else { |
| 334 // New profile synced. |
| 335 AutofillProfile* new_profile( |
| 336 new AutofillProfile(autofill_specifics.guid())); |
| 337 OverwriteProfileWithServerData(autofill_specifics, new_profile); |
| 338 |
| 339 // Check if profile appears under a different guid. |
| 340 for (GUIDToProfileMap::iterator i = profile_map->begin(); |
| 341 i != profile_map->end(); ++i) { |
| 342 if (i->second->Compare(*new_profile) == 0) { |
| 343 bundle->profiles_to_delete.push_back(i->second->guid()); |
| 344 VLOG(2) << "[AUTOFILL SYNC]" |
| 345 << "Found in sync db but with a different guid: " |
| 346 << UTF16ToUTF8(it->second->GetInfo(NAME_FIRST)) |
| 347 << UTF16ToUTF8(it->second->GetInfo(NAME_LAST)) |
| 348 << "New guid " << new_profile->guid() |
| 349 << ". Profile to be deleted " << it->second->guid(); |
| 350 profile_map->erase(i); |
| 351 break; |
| 352 } |
| 353 } |
| 354 profiles_.push_back(new_profile); |
| 355 it = profile_map->insert(std::make_pair(new_profile->guid(), |
| 356 new_profile)).first; |
| 357 bundle->profiles_to_add.push_back(new_profile); |
| 358 } |
| 359 return it; |
| 360 } |
| 361 |
| 362 void AutofillProfileSyncableService::ActOnChange( |
| 363 const AutofillProfileChange& change) { |
| 364 DCHECK((change.type() == AutofillProfileChange::REMOVE && |
| 365 !change.profile()) || |
| 366 (change.type() != AutofillProfileChange::REMOVE && change.profile())); |
| 367 DCHECK(sync_processor_); |
| 368 SyncChangeList new_changes; |
| 369 DataBundle bundle; |
| 370 switch (change.type()) { |
| 371 case AutofillProfileChange::ADD: |
| 372 new_changes.push_back( |
| 373 SyncChange(SyncChange::ACTION_ADD, CreateData(*(change.profile())))); |
| 374 DCHECK(profiles_map_.find(change.profile()->guid()) == |
| 375 profiles_map_.end()); |
| 376 profiles_.push_back(new AutofillProfile(*(change.profile()))); |
| 377 profiles_map_[change.profile()->guid()] = profiles_.get().back(); |
| 378 break; |
| 379 case AutofillProfileChange::UPDATE: { |
| 380 GUIDToProfileMap::iterator it = profiles_map_.find( |
| 381 change.profile()->guid()); |
| 382 DCHECK(it != profiles_map_.end()); |
| 383 *(it->second) = *(change.profile()); |
| 384 new_changes.push_back( |
| 385 SyncChange(SyncChange::ACTION_UPDATE, |
| 386 CreateData(*(change.profile())))); |
| 387 break; |
| 388 } |
| 389 case AutofillProfileChange::REMOVE: { |
| 390 AutofillProfile empty_profile(change.key()); |
| 391 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, |
| 392 CreateData(empty_profile))); |
| 393 profiles_map_.erase(change.key()); |
| 394 break; |
| 395 } |
| 396 default: |
| 397 NOTREACHED(); |
| 398 } |
| 399 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); |
| 400 if (error.IsSet()) { |
| 401 LOG(WARNING) << "[AUTOFILL SYNC]" |
| 402 << " Failed processing change:" |
| 403 << " Error:" << error.message() |
| 404 << " Guid:" << change.profile()->guid(); |
| 405 } |
| 406 } |
| 407 |
| 408 SyncData AutofillProfileSyncableService::CreateData( |
| 409 const AutofillProfile& profile) { |
| 410 sync_pb::EntitySpecifics specifics; |
| 411 WriteAutofillProfile(profile, &specifics); |
| 412 return SyncData::CreateLocalData(profile.guid(), profile.guid(), specifics); |
| 413 } |
| 414 |
| 415 AutofillProfileSyncableService::DataBundle::DataBundle() {} |
| 416 |
| 417 AutofillProfileSyncableService::DataBundle::~DataBundle() { |
| 418 } |
| 419 |
| 420 } // namespace browser_sync |
| 421 |
OLD | NEW |