1 // Copyright (c) 2012 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/webdata/autofill_profile_syncable_service.h" 6 7 #include "base/guid.h" 8 #include "base/location.h" 9 #include "base/logging.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "components/autofill/core/browser/autofill_country.h" 12 #include "components/autofill/core/browser/autofill_profile.h" 13 #include "components/autofill/core/browser/form_group.h" 14 #include "components/autofill/core/browser/webdata/autofill_table.h" 15 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" 16 #include "components/webdata/common/web_database.h" 17 #include "content/public/browser/browser_thread.h" 18 #include "sync/api/sync_error.h" 19 #include "sync/api/sync_error_factory.h" 20 #include "sync/protocol/sync.pb.h" 21 22 using autofill::AutofillCountry; 23 using autofill::ServerFieldType; 24 using autofill::AutofillProfile; 25 using autofill::AutofillProfileChange; 26 using autofill::AutofillTable; 27 using autofill::AutofillWebDataService; 28 using content::BrowserThread; 29 30 namespace { 31 32 std::string LimitData(const std::string& data) { 33 std::string sanitized_value(data); 34 if (sanitized_value.length() > AutofillTable::kMaxDataLength) 35 sanitized_value.resize(AutofillTable::kMaxDataLength); 36 return sanitized_value; 37 } 38 39 void* UserDataKey() { 40 // Use the address of a static that COMDAT folding won't ever fold 41 // with something else. 42 static int user_data_key = 0; 43 return reinterpret_cast<void*>(&user_data_key); 44 } 45 46 } // namespace 47 48 const char kAutofillProfileTag[] = "google_chrome_autofill_profiles"; 49 50 AutofillProfileSyncableService::AutofillProfileSyncableService( 51 autofill::AutofillWebDataBackend* webdata_backend, 52 const std::string& app_locale) 53 : webdata_backend_(webdata_backend), 54 app_locale_(app_locale), 55 scoped_observer_(this) { 56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 57 DCHECK(webdata_backend_); 58 59 scoped_observer_.Add(webdata_backend_); 60 } 61 62 AutofillProfileSyncableService::~AutofillProfileSyncableService() { 63 DCHECK(CalledOnValidThread()); 64 } 65 66 // static 67 void AutofillProfileSyncableService::CreateForWebDataServiceAndBackend( 68 AutofillWebDataService* web_data_service, 69 autofill::AutofillWebDataBackend* webdata_backend, 70 const std::string& app_locale) { 71 web_data_service->GetDBUserData()->SetUserData( 72 UserDataKey(), 73 new AutofillProfileSyncableService(webdata_backend, app_locale)); 74 } 75 76 // static 77 AutofillProfileSyncableService* 78 AutofillProfileSyncableService::FromWebDataService( 79 AutofillWebDataService* web_data_service) { 80 return static_cast<AutofillProfileSyncableService*>( 81 web_data_service->GetDBUserData()->GetUserData(UserDataKey())); 82 } 83 84 AutofillProfileSyncableService::AutofillProfileSyncableService() 85 : webdata_backend_(NULL), 86 scoped_observer_(this) { 87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 88 } 89 90 syncer::SyncMergeResult 91 AutofillProfileSyncableService::MergeDataAndStartSyncing( 92 syncer::ModelType type, 93 const syncer::SyncDataList& initial_sync_data, 94 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, 95 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) { 96 DCHECK(CalledOnValidThread()); 97 DCHECK(!sync_processor_.get()); 98 DCHECK(sync_processor.get()); 99 DCHECK(sync_error_factory.get()); 100 DVLOG(1) << "Associating Autofill: MergeDataAndStartSyncing"; 101 102 syncer::SyncMergeResult merge_result(type); 103 sync_error_factory_ = sync_error_factory.Pass(); 104 if (!LoadAutofillData(&profiles_.get())) { 105 merge_result.set_error(sync_error_factory_->CreateAndUploadError( 106 FROM_HERE, "Could not get the autofill data from WebDatabase.")); 107 return merge_result; 108 } 109 110 if (DLOG_IS_ON(INFO)) { 111 DVLOG(2) << "[AUTOFILL MIGRATION]" 112 << "Printing profiles from web db"; 113 114 for (ScopedVector<AutofillProfile>::const_iterator ix = 115 profiles_.begin(); ix != profiles_.end(); ++ix) { 116 AutofillProfile* p = *ix; 117 DVLOG(2) << "[AUTOFILL MIGRATION] " 118 << p->GetRawInfo(autofill::NAME_FIRST) 119 << p->GetRawInfo(autofill::NAME_LAST) 120 << p->guid(); 121 } 122 } 123 124 sync_processor_ = sync_processor.Pass(); 125 126 GUIDToProfileMap remaining_profiles; 127 CreateGUIDToProfileMap(profiles_.get(), &remaining_profiles); 128 129 DataBundle bundle; 130 // Go through and check for all the profiles that sync already knows about. 131 for (syncer::SyncDataList::const_iterator sync_iter = 132 initial_sync_data.begin(); 133 sync_iter != initial_sync_data.end(); 134 ++sync_iter) { 135 GUIDToProfileMap::iterator it = 136 CreateOrUpdateProfile(*sync_iter, &remaining_profiles, &bundle); 137 // |it| points to created/updated profile. Add it to the |profiles_map_| and 138 // then remove it from |remaining_profiles|. After this loop is completed 139 // |remaining_profiles| will have only those profiles that are not in the 140 // sync. 141 profiles_map_[it->first] = it->second; 142 remaining_profiles.erase(it); 143 } 144 145 // Check for similar unmatched profiles - they are created independently on 146 // two systems, so merge them. 147 for (GUIDToProfileMap::iterator it = bundle.candidates_to_merge.begin(); 148 it != bundle.candidates_to_merge.end(); ++it) { 149 GUIDToProfileMap::iterator profile_to_merge = 150 remaining_profiles.find(it->first); 151 if (profile_to_merge != remaining_profiles.end()) { 152 bundle.profiles_to_delete.push_back(profile_to_merge->second->guid()); 153 if (MergeProfile(*(profile_to_merge->second), it->second, app_locale_)) 154 bundle.profiles_to_sync_back.push_back(it->second); 155 DVLOG(2) << "[AUTOFILL SYNC]" 156 << "Found similar profile in sync db but with a different guid: " 157 << UTF16ToUTF8(it->second->GetRawInfo(autofill::NAME_FIRST)) 158 << UTF16ToUTF8(it->second->GetRawInfo(autofill::NAME_LAST)) 159 << "New guid " << it->second->guid() 160 << ". Profile to be deleted " 161 << profile_to_merge->second->guid(); 162 remaining_profiles.erase(profile_to_merge); 163 } 164 } 165 166 if (!SaveChangesToWebData(bundle)) { 167 merge_result.set_error(sync_error_factory_->CreateAndUploadError( 168 FROM_HERE, 169 "Failed to update webdata.")); 170 return merge_result; 171 } 172 173 syncer::SyncChangeList new_changes; 174 for (GUIDToProfileMap::iterator i = remaining_profiles.begin(); 175 i != remaining_profiles.end(); ++i) { 176 new_changes.push_back( 177 syncer::SyncChange(FROM_HERE, 178 syncer::SyncChange::ACTION_ADD, 179 CreateData(*(i->second)))); 180 profiles_map_[i->first] = i->second; 181 } 182 183 for (size_t i = 0; i < bundle.profiles_to_sync_back.size(); ++i) { 184 new_changes.push_back( 185 syncer::SyncChange(FROM_HERE, 186 syncer::SyncChange::ACTION_UPDATE, 187 CreateData(*(bundle.profiles_to_sync_back[i])))); 188 } 189 190 if (!new_changes.empty()) { 191 merge_result.set_error( 192 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes)); 193 } 194 195 if (webdata_backend_) 196 webdata_backend_->NotifyOfMultipleAutofillChanges(); 197 198 return merge_result; 199 } 200 201 void AutofillProfileSyncableService::StopSyncing(syncer::ModelType type) { 202 DCHECK(CalledOnValidThread()); 203 DCHECK_EQ(type, syncer::AUTOFILL_PROFILE); 204 205 sync_processor_.reset(); 206 sync_error_factory_.reset(); 207 profiles_.clear(); 208 profiles_map_.clear(); 209 } 210 211 syncer::SyncDataList AutofillProfileSyncableService::GetAllSyncData( 212 syncer::ModelType type) const { 213 DCHECK(CalledOnValidThread()); 214 DCHECK(sync_processor_.get()); 215 DCHECK_EQ(type, syncer::AUTOFILL_PROFILE); 216 217 syncer::SyncDataList current_data; 218 219 for (GUIDToProfileMap::const_iterator i = profiles_map_.begin(); 220 i != profiles_map_.end(); ++i) { 221 current_data.push_back(CreateData(*(i->second))); 222 } 223 return current_data; 224 } 225 226 syncer::SyncError AutofillProfileSyncableService::ProcessSyncChanges( 227 const tracked_objects::Location& from_here, 228 const syncer::SyncChangeList& change_list) { 229 DCHECK(CalledOnValidThread()); 230 if (!sync_processor_.get()) { 231 syncer::SyncError error(FROM_HERE, 232 syncer::SyncError::DATATYPE_ERROR, 233 "Models not yet associated.", 234 syncer::AUTOFILL_PROFILE); 235 return error; 236 } 237 238 DataBundle bundle; 239 240 for (syncer::SyncChangeList::const_iterator i = change_list.begin(); 241 i != change_list.end(); ++i) { 242 DCHECK(i->IsValid()); 243 switch (i->change_type()) { 244 case syncer::SyncChange::ACTION_ADD: 245 case syncer::SyncChange::ACTION_UPDATE: 246 CreateOrUpdateProfile(i->sync_data(), &profiles_map_, &bundle); 247 break; 248 case syncer::SyncChange::ACTION_DELETE: { 249 std::string guid = i->sync_data().GetSpecifics(). 250 autofill_profile().guid(); 251 bundle.profiles_to_delete.push_back(guid); 252 profiles_map_.erase(guid); 253 } break; 254 default: 255 NOTREACHED() << "Unexpected sync change state."; 256 return sync_error_factory_->CreateAndUploadError( 257 FROM_HERE, 258 "ProcessSyncChanges failed on ChangeType " + 259 syncer::SyncChange::ChangeTypeToString(i->change_type())); 260 } 261 } 262 263 if (!SaveChangesToWebData(bundle)) { 264 return sync_error_factory_->CreateAndUploadError( 265 FROM_HERE, 266 "Failed to update webdata."); 267 } 268 269 if (webdata_backend_) 270 webdata_backend_->NotifyOfMultipleAutofillChanges(); 271 272 return syncer::SyncError(); 273 } 274 275 void AutofillProfileSyncableService::AutofillProfileChanged( 276 const AutofillProfileChange& change) { 277 // Check if sync is on. If we receive notification prior to the sync being set 278 // up we are going to process all when MergeData..() is called. If we receive 279 // notification after the sync exited, it will be sinced next time Chrome 280 // starts. 281 if (sync_processor_.get()) { 282 ActOnChange(change); 283 } else if (!flare_.is_null()) { 284 flare_.Run(syncer::AUTOFILL_PROFILE); 285 flare_.Reset(); 286 } 287 } 288 289 bool AutofillProfileSyncableService::LoadAutofillData( 290 std::vector<AutofillProfile*>* profiles) { 291 return GetAutofillTable()->GetAutofillProfiles(profiles); 292 } 293 294 bool AutofillProfileSyncableService::SaveChangesToWebData( 295 const DataBundle& bundle) { 296 DCHECK(CalledOnValidThread()); 297 298 autofill::AutofillTable* autofill_table = GetAutofillTable(); 299 300 bool success = true; 301 for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) { 302 if (!autofill_table->RemoveAutofillProfile(bundle.profiles_to_delete[i])) 303 success = false; 304 } 305 306 for (size_t i = 0; i < bundle.profiles_to_add.size(); i++) { 307 if (!autofill_table->AddAutofillProfile(*bundle.profiles_to_add[i])) 308 success = false; 309 } 310 311 for (size_t i = 0; i < bundle.profiles_to_update.size(); i++) { 312 if (!autofill_table->UpdateAutofillProfile(*bundle.profiles_to_update[i])) 313 success = false; 314 } 315 return success; 316 } 317 318 // static 319 bool AutofillProfileSyncableService::OverwriteProfileWithServerData( 320 const sync_pb::AutofillProfileSpecifics& specifics, 321 AutofillProfile* profile, 322 const std::string& app_locale) { 323 bool diff = false; 324 if (profile->origin() != specifics.origin()) { 325 bool was_verified = profile->IsVerified(); 326 profile->set_origin(specifics.origin()); 327 diff = true; 328 329 // Verified profiles should never be overwritten by unverified ones. 330 DCHECK(!was_verified || profile->IsVerified()); 331 } 332 333 diff = UpdateMultivaluedField(autofill::NAME_FIRST, 334 specifics.name_first(), profile) || diff; 335 diff = UpdateMultivaluedField(autofill::NAME_MIDDLE, 336 specifics.name_middle(), profile) || diff; 337 diff = UpdateMultivaluedField(autofill::NAME_LAST, 338 specifics.name_last(), profile) || diff; 339 diff = UpdateField(autofill::ADDRESS_HOME_LINE1, 340 specifics.address_home_line1(), profile) || diff; 341 diff = UpdateField(autofill::ADDRESS_HOME_LINE2, 342 specifics.address_home_line2(), profile) || diff; 343 diff = UpdateField(autofill::ADDRESS_HOME_CITY, 344 specifics.address_home_city(), profile) || diff; 345 diff = UpdateField(autofill::ADDRESS_HOME_STATE, 346 specifics.address_home_state(), profile) || diff; 347 string16 country_name_or_code = 348 ASCIIToUTF16(specifics.address_home_country()); 349 std::string country_code = AutofillCountry::GetCountryCode( 350 country_name_or_code, app_locale); 351 diff = UpdateField( 352 autofill::ADDRESS_HOME_COUNTRY, country_code, profile) || diff; 353 diff = UpdateField(autofill::ADDRESS_HOME_ZIP, 354 specifics.address_home_zip(), profile) || diff; 355 diff = UpdateMultivaluedField(autofill::EMAIL_ADDRESS, 356 specifics.email_address(), profile) || diff; 357 diff = UpdateField( 358 autofill::COMPANY_NAME, specifics.company_name(), profile) || diff; 359 diff = UpdateMultivaluedField(autofill::PHONE_HOME_WHOLE_NUMBER, 360 specifics.phone_home_whole_number(), 361 profile) || diff; 362 return diff; 363 } 364 365 // static 366 void AutofillProfileSyncableService::WriteAutofillProfile( 367 const AutofillProfile& profile, 368 sync_pb::EntitySpecifics* profile_specifics) { 369 sync_pb::AutofillProfileSpecifics* specifics = 370 profile_specifics->mutable_autofill_profile(); 371 372 DCHECK(base::IsValidGUID(profile.guid())); 373 374 // Reset all multi-valued fields in the protobuf. 375 specifics->clear_name_first(); 376 specifics->clear_name_middle(); 377 specifics->clear_name_last(); 378 specifics->clear_email_address(); 379 specifics->clear_phone_home_whole_number(); 380 381 specifics->set_guid(profile.guid()); 382 specifics->set_origin(profile.origin()); 383 384 std::vector<string16> values; 385 profile.GetRawMultiInfo(autofill::NAME_FIRST, &values); 386 for (size_t i = 0; i < values.size(); ++i) { 387 specifics->add_name_first(LimitData(UTF16ToUTF8(values[i]))); 388 } 389 390 profile.GetRawMultiInfo(autofill::NAME_MIDDLE, &values); 391 for (size_t i = 0; i < values.size(); ++i) { 392 specifics->add_name_middle(LimitData(UTF16ToUTF8(values[i]))); 393 } 394 395 profile.GetRawMultiInfo(autofill::NAME_LAST, &values); 396 for (size_t i = 0; i < values.size(); ++i) { 397 specifics->add_name_last(LimitData(UTF16ToUTF8(values[i]))); 398 } 399 400 specifics->set_address_home_line1( 401 LimitData(UTF16ToUTF8(profile.GetRawInfo(autofill::ADDRESS_HOME_LINE1)))); 402 specifics->set_address_home_line2( 403 LimitData(UTF16ToUTF8(profile.GetRawInfo(autofill::ADDRESS_HOME_LINE2)))); 404 specifics->set_address_home_city( 405 LimitData(UTF16ToUTF8(profile.GetRawInfo(autofill::ADDRESS_HOME_CITY)))); 406 specifics->set_address_home_state( 407 LimitData(UTF16ToUTF8(profile.GetRawInfo(autofill::ADDRESS_HOME_STATE)))); 408 specifics->set_address_home_country( 409 LimitData( 410 UTF16ToUTF8(profile.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY)))); 411 specifics->set_address_home_zip( 412 LimitData(UTF16ToUTF8(profile.GetRawInfo(autofill::ADDRESS_HOME_ZIP)))); 413 414 profile.GetRawMultiInfo(autofill::EMAIL_ADDRESS, &values); 415 for (size_t i = 0; i < values.size(); ++i) { 416 specifics->add_email_address(LimitData(UTF16ToUTF8(values[i]))); 417 } 418 419 specifics->set_company_name( 420 LimitData(UTF16ToUTF8(profile.GetRawInfo(autofill::COMPANY_NAME)))); 421 422 profile.GetRawMultiInfo(autofill::PHONE_HOME_WHOLE_NUMBER, &values); 423 for (size_t i = 0; i < values.size(); ++i) { 424 specifics->add_phone_home_whole_number(LimitData(UTF16ToUTF8(values[i]))); 425 } 426 } 427 428 void AutofillProfileSyncableService::CreateGUIDToProfileMap( 429 const std::vector<AutofillProfile*>& profiles, 430 GUIDToProfileMap* profile_map) { 431 DCHECK(profile_map); 432 profile_map->clear(); 433 for (size_t i = 0; i < profiles.size(); ++i) 434 (*profile_map)[profiles[i]->guid()] = profiles[i]; 435 } 436 437 AutofillProfileSyncableService::GUIDToProfileMap::iterator 438 AutofillProfileSyncableService::CreateOrUpdateProfile( 439 const syncer::SyncData& data, 440 GUIDToProfileMap* profile_map, 441 DataBundle* bundle) { 442 DCHECK(profile_map); 443 DCHECK(bundle); 444 445 DCHECK_EQ(syncer::AUTOFILL_PROFILE, data.GetDataType()); 446 447 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); 448 const sync_pb::AutofillProfileSpecifics& autofill_specifics( 449 specifics.autofill_profile()); 450 451 GUIDToProfileMap::iterator existing_profile = profile_map->find( 452 autofill_specifics.guid()); 453 if (existing_profile != profile_map->end()) { 454 // The synced profile already exists locally. It might need to be updated. 455 if (OverwriteProfileWithServerData( 456 autofill_specifics, existing_profile->second, app_locale_)) { 457 bundle->profiles_to_update.push_back(existing_profile->second); 458 } 459 return existing_profile; 460 } 461 462 463 // New profile synced. 464 AutofillProfile* new_profile = new AutofillProfile( 465 autofill_specifics.guid(), autofill_specifics.origin()); 466 OverwriteProfileWithServerData(autofill_specifics, new_profile, app_locale_); 467 468 // Check if profile appears under a different guid. 469 // Unverified profiles should never overwrite verified ones. 470 for (GUIDToProfileMap::iterator it = profile_map->begin(); 471 it != profile_map->end(); ++it) { 472 AutofillProfile* local_profile = it->second; 473 if (local_profile->Compare(*new_profile) == 0) { 474 // Ensure that a verified profile can never revert back to an unverified 475 // one. 476 if (local_profile->IsVerified() && !new_profile->IsVerified()) { 477 new_profile->set_origin(local_profile->origin()); 478 bundle->profiles_to_sync_back.push_back(new_profile); 479 } 480 481 bundle->profiles_to_delete.push_back(local_profile->guid()); 482 DVLOG(2) << "[AUTOFILL SYNC]" 483 << "Found in sync db but with a different guid: " 484 << UTF16ToUTF8(local_profile->GetRawInfo(autofill::NAME_FIRST)) 485 << UTF16ToUTF8(local_profile->GetRawInfo(autofill::NAME_LAST)) 486 << "New guid " << new_profile->guid() 487 << ". Profile to be deleted " << local_profile->guid(); 488 profile_map->erase(it); 489 break; 490 } else if (!local_profile->IsVerified() && 491 !new_profile->IsVerified() && 492 !local_profile->PrimaryValue().empty() && 493 local_profile->PrimaryValue() == new_profile->PrimaryValue()) { 494 // Add it to candidates for merge - if there is no profile with this 495 // guid we will merge them. 496 bundle->candidates_to_merge.insert( 497 std::make_pair(local_profile->guid(), new_profile)); 498 } 499 } 500 profiles_.push_back(new_profile); 501 bundle->profiles_to_add.push_back(new_profile); 502 return profile_map->insert(std::make_pair(new_profile->guid(), 503 new_profile)).first; 504 } 505 506 void AutofillProfileSyncableService::ActOnChange( 507 const AutofillProfileChange& change) { 508 DCHECK((change.type() == AutofillProfileChange::REMOVE && 509 !change.profile()) || 510 (change.type() != AutofillProfileChange::REMOVE && change.profile())); 511 DCHECK(sync_processor_.get()); 512 syncer::SyncChangeList new_changes; 513 DataBundle bundle; 514 switch (change.type()) { 515 case AutofillProfileChange::ADD: 516 new_changes.push_back( 517 syncer::SyncChange(FROM_HERE, 518 syncer::SyncChange::ACTION_ADD, 519 CreateData(*(change.profile())))); 520 DCHECK(profiles_map_.find(change.profile()->guid()) == 521 profiles_map_.end()); 522 profiles_.push_back(new AutofillProfile(*(change.profile()))); 523 profiles_map_[change.profile()->guid()] = profiles_.get().back(); 524 break; 525 case AutofillProfileChange::UPDATE: { 526 GUIDToProfileMap::iterator it = profiles_map_.find( 527 change.profile()->guid()); 528 DCHECK(it != profiles_map_.end()); 529 *(it->second) = *(change.profile()); 530 new_changes.push_back( 531 syncer::SyncChange(FROM_HERE, 532 syncer::SyncChange::ACTION_UPDATE, 533 CreateData(*(change.profile())))); 534 break; 535 } 536 case AutofillProfileChange::REMOVE: { 537 AutofillProfile empty_profile(change.key(), std::string()); 538 new_changes.push_back( 539 syncer::SyncChange(FROM_HERE, 540 syncer::SyncChange::ACTION_DELETE, 541 CreateData(empty_profile))); 542 profiles_map_.erase(change.key()); 543 break; 544 } 545 default: 546 NOTREACHED(); 547 } 548 syncer::SyncError error = 549 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); 550 if (error.IsSet()) { 551 // TODO(isherman): Investigating http://crbug.com/121592 552 VLOG(1) << "[AUTOFILL SYNC] " 553 << "Failed processing change:\n" 554 << " Error: " << error.message() << "\n" 555 << " Guid: " << change.key(); 556 } 557 } 558 559 syncer::SyncData AutofillProfileSyncableService::CreateData( 560 const AutofillProfile& profile) { 561 sync_pb::EntitySpecifics specifics; 562 WriteAutofillProfile(profile, &specifics); 563 return 564 syncer::SyncData::CreateLocalData( 565 profile.guid(), profile.guid(), specifics); 566 } 567 568 bool AutofillProfileSyncableService::UpdateField( 569 ServerFieldType field_type, 570 const std::string& new_value, 571 AutofillProfile* autofill_profile) { 572 if (UTF16ToUTF8(autofill_profile->GetRawInfo(field_type)) == new_value) 573 return false; 574 autofill_profile->SetRawInfo(field_type, UTF8ToUTF16(new_value)); 575 return true; 576 } 577 578 bool AutofillProfileSyncableService::UpdateMultivaluedField( 579 ServerFieldType field_type, 580 const ::google::protobuf::RepeatedPtrField<std::string>& new_values, 581 AutofillProfile* autofill_profile) { 582 std::vector<string16> values; 583 autofill_profile->GetRawMultiInfo(field_type, &values); 584 bool changed = false; 585 if (static_cast<size_t>(new_values.size()) != values.size()) { 586 values.clear(); 587 values.resize(static_cast<size_t>(new_values.size())); 588 changed = true; 589 } 590 for (size_t i = 0; i < values.size(); ++i) { 591 string16 synced_value( 592 UTF8ToUTF16(new_values.Get(static_cast<int>(i)))); 593 if (values[i] != synced_value) { 594 values[i] = synced_value; 595 changed = true; 596 } 597 } 598 if (changed) 599 autofill_profile->SetRawMultiInfo(field_type, values); 600 return changed; 601 } 602 603 bool AutofillProfileSyncableService::MergeProfile( 604 const AutofillProfile& merge_from, 605 AutofillProfile* merge_into, 606 const std::string& app_locale) { 607 merge_into->OverwriteWithOrAddTo(merge_from, app_locale); 608 return (merge_into->Compare(merge_from) != 0 || 609 merge_into->origin() != merge_from.origin()); 610 } 611 612 AutofillTable* AutofillProfileSyncableService::GetAutofillTable() const { 613 return AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase()); 614 } 615 616 void AutofillProfileSyncableService::InjectStartSyncFlare( 617 const syncer::SyncableService::StartSyncFlare& flare) { 618 flare_ = flare; 619 } 620 621 AutofillProfileSyncableService::DataBundle::DataBundle() {} 622 623 AutofillProfileSyncableService::DataBundle::~DataBundle() {} 624