1 // Copyright 2014 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.
5 #include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.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 "sync/api/sync_error.h"
18 #include "sync/api/sync_error_factory.h"
19 #include "sync/protocol/sync.pb.h"
21 using base::ASCIIToUTF16;
22 using base::UTF8ToUTF16;
23 using base::UTF16ToUTF8;
29 std::string LimitData(const std::string& data) {
30 std::string sanitized_value(data);
31 if (sanitized_value.length() > AutofillTable::kMaxDataLength)
32 sanitized_value.resize(AutofillTable::kMaxDataLength);
33 return sanitized_value;
37 // Use the address of a static that COMDAT folding won't ever fold
38 // with something else.
39 static int user_data_key = 0;
40 return reinterpret_cast<void*>(&user_data_key);
45 const char kAutofillProfileTag[] = "google_chrome_autofill_profiles";
47 AutofillProfileSyncableService::AutofillProfileSyncableService(
48 AutofillWebDataBackend* webdata_backend,
49 const std::string& app_locale)
50 : webdata_backend_(webdata_backend),
51 app_locale_(app_locale),
52 scoped_observer_(this) {
53 DCHECK(webdata_backend_);
55 scoped_observer_.Add(webdata_backend_);
58 AutofillProfileSyncableService::~AutofillProfileSyncableService() {
59 DCHECK(CalledOnValidThread());
63 void AutofillProfileSyncableService::CreateForWebDataServiceAndBackend(
64 AutofillWebDataService* web_data_service,
65 AutofillWebDataBackend* webdata_backend,
66 const std::string& app_locale) {
67 web_data_service->GetDBUserData()->SetUserData(
69 new AutofillProfileSyncableService(webdata_backend, app_locale));
73 AutofillProfileSyncableService*
74 AutofillProfileSyncableService::FromWebDataService(
75 AutofillWebDataService* web_data_service) {
76 return static_cast<AutofillProfileSyncableService*>(
77 web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
80 AutofillProfileSyncableService::AutofillProfileSyncableService()
81 : webdata_backend_(NULL),
82 scoped_observer_(this) {
85 syncer::SyncMergeResult
86 AutofillProfileSyncableService::MergeDataAndStartSyncing(
87 syncer::ModelType type,
88 const syncer::SyncDataList& initial_sync_data,
89 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
90 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
91 DCHECK(CalledOnValidThread());
92 DCHECK(!sync_processor_.get());
93 DCHECK(sync_processor.get());
94 DCHECK(sync_error_factory.get());
95 DVLOG(1) << "Associating Autofill: MergeDataAndStartSyncing";
97 syncer::SyncMergeResult merge_result(type);
98 sync_error_factory_ = sync_error_factory.Pass();
99 if (!LoadAutofillData(&profiles_.get())) {
100 merge_result.set_error(sync_error_factory_->CreateAndUploadError(
101 FROM_HERE, "Could not get the autofill data from WebDatabase."));
105 if (DLOG_IS_ON(INFO)) {
106 DVLOG(2) << "[AUTOFILL MIGRATION]"
107 << "Printing profiles from web db";
109 for (ScopedVector<AutofillProfile>::const_iterator ix =
110 profiles_.begin(); ix != profiles_.end(); ++ix) {
111 AutofillProfile* p = *ix;
112 DVLOG(2) << "[AUTOFILL MIGRATION] "
113 << UTF16ToUTF8(p->GetRawInfo(NAME_FIRST))
114 << UTF16ToUTF8(p->GetRawInfo(NAME_LAST))
119 sync_processor_ = sync_processor.Pass();
121 GUIDToProfileMap remaining_profiles;
122 CreateGUIDToProfileMap(profiles_.get(), &remaining_profiles);
125 // Go through and check for all the profiles that sync already knows about.
126 for (syncer::SyncDataList::const_iterator sync_iter =
127 initial_sync_data.begin();
128 sync_iter != initial_sync_data.end();
130 GUIDToProfileMap::iterator it =
131 CreateOrUpdateProfile(*sync_iter, &remaining_profiles, &bundle);
132 // |it| points to created/updated profile. Add it to the |profiles_map_| and
133 // then remove it from |remaining_profiles|. After this loop is completed
134 // |remaining_profiles| will have only those profiles that are not in the
136 profiles_map_[it->first] = it->second;
137 remaining_profiles.erase(it);
140 // Check for similar unmatched profiles - they are created independently on
141 // two systems, so merge them.
142 for (GUIDToProfileMap::iterator it = bundle.candidates_to_merge.begin();
143 it != bundle.candidates_to_merge.end(); ++it) {
144 GUIDToProfileMap::iterator profile_to_merge =
145 remaining_profiles.find(it->first);
146 if (profile_to_merge != remaining_profiles.end()) {
147 bundle.profiles_to_delete.push_back(profile_to_merge->second->guid());
148 if (MergeProfile(*(profile_to_merge->second), it->second, app_locale_))
149 bundle.profiles_to_sync_back.push_back(it->second);
150 DVLOG(2) << "[AUTOFILL SYNC]"
151 << "Found similar profile in sync db but with a different guid: "
152 << UTF16ToUTF8(it->second->GetRawInfo(NAME_FIRST))
153 << UTF16ToUTF8(it->second->GetRawInfo(NAME_LAST))
154 << "New guid " << it->second->guid()
155 << ". Profile to be deleted "
156 << profile_to_merge->second->guid();
157 remaining_profiles.erase(profile_to_merge);
161 if (!SaveChangesToWebData(bundle)) {
162 merge_result.set_error(sync_error_factory_->CreateAndUploadError(
164 "Failed to update webdata."));
168 syncer::SyncChangeList new_changes;
169 for (GUIDToProfileMap::iterator i = remaining_profiles.begin();
170 i != remaining_profiles.end(); ++i) {
171 new_changes.push_back(
172 syncer::SyncChange(FROM_HERE,
173 syncer::SyncChange::ACTION_ADD,
174 CreateData(*(i->second))));
175 profiles_map_[i->first] = i->second;
178 for (size_t i = 0; i < bundle.profiles_to_sync_back.size(); ++i) {
179 new_changes.push_back(
180 syncer::SyncChange(FROM_HERE,
181 syncer::SyncChange::ACTION_UPDATE,
182 CreateData(*(bundle.profiles_to_sync_back[i]))));
185 if (!new_changes.empty()) {
186 merge_result.set_error(
187 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
190 if (webdata_backend_)
191 webdata_backend_->NotifyOfMultipleAutofillChanges();
196 void AutofillProfileSyncableService::StopSyncing(syncer::ModelType type) {
197 DCHECK(CalledOnValidThread());
198 DCHECK_EQ(type, syncer::AUTOFILL_PROFILE);
200 sync_processor_.reset();
201 sync_error_factory_.reset();
203 profiles_map_.clear();
206 syncer::SyncDataList AutofillProfileSyncableService::GetAllSyncData(
207 syncer::ModelType type) const {
208 DCHECK(CalledOnValidThread());
209 DCHECK(sync_processor_.get());
210 DCHECK_EQ(type, syncer::AUTOFILL_PROFILE);
212 syncer::SyncDataList current_data;
214 for (GUIDToProfileMap::const_iterator i = profiles_map_.begin();
215 i != profiles_map_.end(); ++i) {
216 current_data.push_back(CreateData(*(i->second)));
221 syncer::SyncError AutofillProfileSyncableService::ProcessSyncChanges(
222 const tracked_objects::Location& from_here,
223 const syncer::SyncChangeList& change_list) {
224 DCHECK(CalledOnValidThread());
225 if (!sync_processor_.get()) {
226 syncer::SyncError error(FROM_HERE,
227 syncer::SyncError::DATATYPE_ERROR,
228 "Models not yet associated.",
229 syncer::AUTOFILL_PROFILE);
235 for (syncer::SyncChangeList::const_iterator i = change_list.begin();
236 i != change_list.end(); ++i) {
237 DCHECK(i->IsValid());
238 switch (i->change_type()) {
239 case syncer::SyncChange::ACTION_ADD:
240 case syncer::SyncChange::ACTION_UPDATE:
241 CreateOrUpdateProfile(i->sync_data(), &profiles_map_, &bundle);
243 case syncer::SyncChange::ACTION_DELETE: {
244 std::string guid = i->sync_data().GetSpecifics().
245 autofill_profile().guid();
246 bundle.profiles_to_delete.push_back(guid);
247 profiles_map_.erase(guid);
250 NOTREACHED() << "Unexpected sync change state.";
251 return sync_error_factory_->CreateAndUploadError(
253 "ProcessSyncChanges failed on ChangeType " +
254 syncer::SyncChange::ChangeTypeToString(i->change_type()));
258 if (!SaveChangesToWebData(bundle)) {
259 return sync_error_factory_->CreateAndUploadError(
261 "Failed to update webdata.");
264 if (webdata_backend_)
265 webdata_backend_->NotifyOfMultipleAutofillChanges();
267 return syncer::SyncError();
270 void AutofillProfileSyncableService::AutofillProfileChanged(
271 const AutofillProfileChange& change) {
272 // Check if sync is on. If we receive notification prior to the sync being set
273 // up we are going to process all when MergeData..() is called. If we receive
274 // notification after the sync exited, it will be sinced next time Chrome
276 if (sync_processor_.get()) {
278 } else if (!flare_.is_null()) {
279 flare_.Run(syncer::AUTOFILL_PROFILE);
284 bool AutofillProfileSyncableService::LoadAutofillData(
285 std::vector<AutofillProfile*>* profiles) {
286 return GetAutofillTable()->GetAutofillProfiles(profiles);
289 bool AutofillProfileSyncableService::SaveChangesToWebData(
290 const DataBundle& bundle) {
291 DCHECK(CalledOnValidThread());
293 AutofillTable* autofill_table = GetAutofillTable();
296 for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) {
297 if (!autofill_table->RemoveAutofillProfile(bundle.profiles_to_delete[i]))
301 for (size_t i = 0; i < bundle.profiles_to_add.size(); i++) {
302 if (!autofill_table->AddAutofillProfile(*bundle.profiles_to_add[i]))
306 for (size_t i = 0; i < bundle.profiles_to_update.size(); i++) {
307 if (!autofill_table->UpdateAutofillProfile(*bundle.profiles_to_update[i]))
314 bool AutofillProfileSyncableService::OverwriteProfileWithServerData(
315 const sync_pb::AutofillProfileSpecifics& specifics,
316 AutofillProfile* profile,
317 const std::string& app_locale) {
319 if (specifics.has_origin() && profile->origin() != specifics.origin()) {
320 bool was_verified = profile->IsVerified();
321 profile->set_origin(specifics.origin());
324 // Verified profiles should never be overwritten by unverified ones.
325 DCHECK(!was_verified || profile->IsVerified());
328 // Update all multivalued fields: names, emails, and phones.
329 diff = UpdateMultivaluedField(NAME_FIRST,
330 specifics.name_first(), profile) || diff;
331 diff = UpdateMultivaluedField(NAME_MIDDLE,
332 specifics.name_middle(), profile) || diff;
333 diff = UpdateMultivaluedField(NAME_LAST,
334 specifics.name_last(), profile) || diff;
335 diff = UpdateMultivaluedField(EMAIL_ADDRESS,
336 specifics.email_address(), profile) || diff;
337 diff = UpdateMultivaluedField(PHONE_HOME_WHOLE_NUMBER,
338 specifics.phone_home_whole_number(),
341 // Update all simple single-valued address fields.
342 diff = UpdateField(COMPANY_NAME, specifics.company_name(), profile) || diff;
343 diff = UpdateField(ADDRESS_HOME_CITY,
344 specifics.address_home_city(), profile) || diff;
345 diff = UpdateField(ADDRESS_HOME_STATE,
346 specifics.address_home_state(), profile) || diff;
347 diff = UpdateField(ADDRESS_HOME_ZIP,
348 specifics.address_home_zip(), profile) || diff;
349 diff = UpdateField(ADDRESS_HOME_SORTING_CODE,
350 specifics.address_home_sorting_code(), profile) || diff;
351 diff = UpdateField(ADDRESS_HOME_DEPENDENT_LOCALITY,
352 specifics.address_home_dependent_locality(),
355 // Update the country field, which can contain either a country code (if set
356 // by a newer version of Chrome), or a country name (if set by an older
357 // version of Chrome).
358 base::string16 country_name_or_code =
359 ASCIIToUTF16(specifics.address_home_country());
360 std::string country_code =
361 AutofillCountry::GetCountryCode(country_name_or_code, app_locale);
362 diff = UpdateField(ADDRESS_HOME_COUNTRY, country_code, profile) || diff;
364 // Update the street address. In newer versions of Chrome (M34+), this data
365 // is stored in the |address_home_street_address| field. In older versions,
366 // this data is stored separated out by address line.
367 if (specifics.has_address_home_street_address()) {
368 diff = UpdateField(ADDRESS_HOME_STREET_ADDRESS,
369 specifics.address_home_street_address(),
372 diff = UpdateField(ADDRESS_HOME_LINE1,
373 specifics.address_home_line1(), profile) || diff;
374 diff = UpdateField(ADDRESS_HOME_LINE2,
375 specifics.address_home_line2(), profile) || diff;
378 // Update the BCP 47 language code that can be used to format the address for
380 if (specifics.has_address_home_language_code() &&
381 specifics.address_home_language_code() != profile->language_code()) {
382 profile->set_language_code(specifics.address_home_language_code());
390 void AutofillProfileSyncableService::WriteAutofillProfile(
391 const AutofillProfile& profile,
392 sync_pb::EntitySpecifics* profile_specifics) {
393 sync_pb::AutofillProfileSpecifics* specifics =
394 profile_specifics->mutable_autofill_profile();
396 DCHECK(base::IsValidGUID(profile.guid()));
398 // Reset all multi-valued fields in the protobuf.
399 specifics->clear_name_first();
400 specifics->clear_name_middle();
401 specifics->clear_name_last();
402 specifics->clear_email_address();
403 specifics->clear_phone_home_whole_number();
405 specifics->set_guid(profile.guid());
406 specifics->set_origin(profile.origin());
408 std::vector<base::string16> values;
409 profile.GetRawMultiInfo(NAME_FIRST, &values);
410 for (size_t i = 0; i < values.size(); ++i) {
411 specifics->add_name_first(LimitData(UTF16ToUTF8(values[i])));
414 profile.GetRawMultiInfo(NAME_MIDDLE, &values);
415 for (size_t i = 0; i < values.size(); ++i) {
416 specifics->add_name_middle(LimitData(UTF16ToUTF8(values[i])));
419 profile.GetRawMultiInfo(NAME_LAST, &values);
420 for (size_t i = 0; i < values.size(); ++i) {
421 specifics->add_name_last(LimitData(UTF16ToUTF8(values[i])));
424 specifics->set_address_home_line1(
425 LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE1))));
426 specifics->set_address_home_line2(
427 LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE2))));
428 specifics->set_address_home_city(
429 LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_CITY))));
430 specifics->set_address_home_state(
431 LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STATE))));
432 specifics->set_address_home_zip(
433 LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_ZIP))));
434 specifics->set_address_home_country(
435 LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY))));
436 specifics->set_address_home_street_address(
437 LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS))));
438 specifics->set_address_home_sorting_code(
439 LimitData(UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE))));
440 specifics->set_address_home_dependent_locality(
442 UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY))));
443 specifics->set_address_home_language_code(LimitData(profile.language_code()));
445 profile.GetRawMultiInfo(EMAIL_ADDRESS, &values);
446 for (size_t i = 0; i < values.size(); ++i) {
447 specifics->add_email_address(LimitData(UTF16ToUTF8(values[i])));
450 specifics->set_company_name(
451 LimitData(UTF16ToUTF8(profile.GetRawInfo(COMPANY_NAME))));
453 profile.GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &values);
454 for (size_t i = 0; i < values.size(); ++i) {
455 specifics->add_phone_home_whole_number(LimitData(UTF16ToUTF8(values[i])));
459 void AutofillProfileSyncableService::CreateGUIDToProfileMap(
460 const std::vector<AutofillProfile*>& profiles,
461 GUIDToProfileMap* profile_map) {
463 profile_map->clear();
464 for (size_t i = 0; i < profiles.size(); ++i)
465 (*profile_map)[profiles[i]->guid()] = profiles[i];
468 AutofillProfileSyncableService::GUIDToProfileMap::iterator
469 AutofillProfileSyncableService::CreateOrUpdateProfile(
470 const syncer::SyncData& data,
471 GUIDToProfileMap* profile_map,
472 DataBundle* bundle) {
476 DCHECK_EQ(syncer::AUTOFILL_PROFILE, data.GetDataType());
478 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
479 const sync_pb::AutofillProfileSpecifics& autofill_specifics(
480 specifics.autofill_profile());
482 GUIDToProfileMap::iterator existing_profile = profile_map->find(
483 autofill_specifics.guid());
484 if (existing_profile != profile_map->end()) {
485 // The synced profile already exists locally. It might need to be updated.
486 if (OverwriteProfileWithServerData(
487 autofill_specifics, existing_profile->second, app_locale_)) {
488 bundle->profiles_to_update.push_back(existing_profile->second);
490 return existing_profile;
494 // New profile synced.
495 AutofillProfile* new_profile = new AutofillProfile(
496 autofill_specifics.guid(), autofill_specifics.origin());
497 OverwriteProfileWithServerData(autofill_specifics, new_profile, app_locale_);
499 // Check if profile appears under a different guid. Compares only profile
500 // contents. (Ignores origin and language code in comparison.)
502 // Unverified profiles should never overwrite verified ones.
503 for (GUIDToProfileMap::iterator it = profile_map->begin();
504 it != profile_map->end(); ++it) {
505 AutofillProfile* local_profile = it->second;
506 if (local_profile->Compare(*new_profile) == 0) {
507 // Ensure that a verified profile can never revert back to an unverified
509 if (local_profile->IsVerified() && !new_profile->IsVerified()) {
510 new_profile->set_origin(local_profile->origin());
511 bundle->profiles_to_sync_back.push_back(new_profile);
514 bundle->profiles_to_delete.push_back(local_profile->guid());
515 DVLOG(2) << "[AUTOFILL SYNC]"
516 << "Found in sync db but with a different guid: "
517 << UTF16ToUTF8(local_profile->GetRawInfo(NAME_FIRST))
518 << UTF16ToUTF8(local_profile->GetRawInfo(NAME_LAST))
519 << "New guid " << new_profile->guid()
520 << ". Profile to be deleted " << local_profile->guid();
521 profile_map->erase(it);
523 } else if (!local_profile->IsVerified() &&
524 !new_profile->IsVerified() &&
525 !local_profile->PrimaryValue().empty() &&
526 local_profile->PrimaryValue() == new_profile->PrimaryValue()) {
527 // Add it to candidates for merge - if there is no profile with this
528 // guid we will merge them.
529 bundle->candidates_to_merge.insert(
530 std::make_pair(local_profile->guid(), new_profile));
533 profiles_.push_back(new_profile);
534 bundle->profiles_to_add.push_back(new_profile);
535 return profile_map->insert(std::make_pair(new_profile->guid(),
539 void AutofillProfileSyncableService::ActOnChange(
540 const AutofillProfileChange& change) {
541 DCHECK((change.type() == AutofillProfileChange::REMOVE &&
542 !change.profile()) ||
543 (change.type() != AutofillProfileChange::REMOVE && change.profile()));
544 DCHECK(sync_processor_.get());
545 syncer::SyncChangeList new_changes;
547 switch (change.type()) {
548 case AutofillProfileChange::ADD:
549 new_changes.push_back(
550 syncer::SyncChange(FROM_HERE,
551 syncer::SyncChange::ACTION_ADD,
552 CreateData(*(change.profile()))));
553 DCHECK(profiles_map_.find(change.profile()->guid()) ==
554 profiles_map_.end());
555 profiles_.push_back(new AutofillProfile(*(change.profile())));
556 profiles_map_[change.profile()->guid()] = profiles_.get().back();
558 case AutofillProfileChange::UPDATE: {
559 GUIDToProfileMap::iterator it = profiles_map_.find(
560 change.profile()->guid());
561 DCHECK(it != profiles_map_.end());
562 *(it->second) = *(change.profile());
563 new_changes.push_back(
564 syncer::SyncChange(FROM_HERE,
565 syncer::SyncChange::ACTION_UPDATE,
566 CreateData(*(change.profile()))));
569 case AutofillProfileChange::REMOVE: {
570 AutofillProfile empty_profile(change.key(), std::string());
571 new_changes.push_back(
572 syncer::SyncChange(FROM_HERE,
573 syncer::SyncChange::ACTION_DELETE,
574 CreateData(empty_profile)));
575 profiles_map_.erase(change.key());
581 syncer::SyncError error =
582 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
584 // TODO(isherman): Investigating http://crbug.com/121592
585 VLOG(1) << "[AUTOFILL SYNC] "
586 << "Failed processing change:\n"
587 << " Error: " << error.message() << "\n"
588 << " Guid: " << change.key();
592 syncer::SyncData AutofillProfileSyncableService::CreateData(
593 const AutofillProfile& profile) {
594 sync_pb::EntitySpecifics specifics;
595 WriteAutofillProfile(profile, &specifics);
597 syncer::SyncData::CreateLocalData(
598 profile.guid(), profile.guid(), specifics);
601 bool AutofillProfileSyncableService::UpdateField(
602 ServerFieldType field_type,
603 const std::string& new_value,
604 AutofillProfile* autofill_profile) {
605 if (UTF16ToUTF8(autofill_profile->GetRawInfo(field_type)) == new_value)
607 autofill_profile->SetRawInfo(field_type, UTF8ToUTF16(new_value));
611 bool AutofillProfileSyncableService::UpdateMultivaluedField(
612 ServerFieldType field_type,
613 const ::google::protobuf::RepeatedPtrField<std::string>& new_values,
614 AutofillProfile* autofill_profile) {
615 std::vector<base::string16> values;
616 autofill_profile->GetRawMultiInfo(field_type, &values);
617 bool changed = false;
618 if (static_cast<size_t>(new_values.size()) != values.size()) {
620 values.resize(static_cast<size_t>(new_values.size()));
623 for (size_t i = 0; i < values.size(); ++i) {
624 base::string16 synced_value(
625 UTF8ToUTF16(new_values.Get(static_cast<int>(i))));
626 if (values[i] != synced_value) {
627 values[i] = synced_value;
632 autofill_profile->SetRawMultiInfo(field_type, values);
636 bool AutofillProfileSyncableService::MergeProfile(
637 const AutofillProfile& merge_from,
638 AutofillProfile* merge_into,
639 const std::string& app_locale) {
640 // Overwrites all single values and adds to mutli-values. Does not overwrite
642 merge_into->OverwriteWithOrAddTo(merge_from, app_locale);
643 return !merge_into->EqualsSansGuid(merge_from);
646 AutofillTable* AutofillProfileSyncableService::GetAutofillTable() const {
647 return AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase());
650 void AutofillProfileSyncableService::InjectStartSyncFlare(
651 const syncer::SyncableService::StartSyncFlare& flare) {
655 AutofillProfileSyncableService::DataBundle::DataBundle() {}
657 AutofillProfileSyncableService::DataBundle::~DataBundle() {}
659 } // namespace autofill