| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h" |
| |
| #include <memory> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/guid.h" |
| #include "base/stl_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/autofill/core/browser/autofill_profile.h" |
| #include "components/autofill/core/browser/autofill_profile_sync_util.h" |
| #include "components/autofill/core/browser/country_names.h" |
| #include "components/autofill/core/browser/field_types.h" |
| #include "components/autofill/core/browser/proto/autofill_sync.pb.h" |
| #include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h" |
| #include "components/autofill/core/browser/webdata/autofill_table.h" |
| #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" |
| #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" |
| #include "components/sync/model/entity_data.h" |
| #include "components/sync/model/metadata_change_list.h" |
| #include "components/sync/model/model_error.h" |
| #include "components/sync/model/model_type_change_processor.h" |
| #include "components/sync/model/mutable_data_batch.h" |
| #include "components/sync/model_impl/client_tag_based_model_type_processor.h" |
| #include "components/sync/model_impl/sync_metadata_store_change_list.h" |
| |
| using base::Optional; |
| using base::UTF16ToUTF8; |
| using sync_pb::AutofillProfileSpecifics; |
| using syncer::EntityData; |
| using syncer::MetadataChangeList; |
| using syncer::ModelError; |
| |
| namespace autofill { |
| |
| namespace { |
| |
| // Simplify checking for optional errors and returning only when present. |
| #define RETURN_IF_ERROR(x) \ |
| if (Optional<ModelError> ret_val = x) { \ |
| return ret_val; \ |
| } |
| |
| // Address to this variable used as the user data key. |
| static int kAutofillProfileSyncBridgeUserDataKey = 0; |
| |
| } // namespace |
| |
| // static |
| void AutofillProfileSyncBridge::CreateForWebDataServiceAndBackend( |
| const std::string& app_locale, |
| AutofillWebDataBackend* web_data_backend, |
| AutofillWebDataService* web_data_service) { |
| web_data_service->GetDBUserData()->SetUserData( |
| &kAutofillProfileSyncBridgeUserDataKey, |
| std::make_unique<AutofillProfileSyncBridge>( |
| std::make_unique<syncer::ClientTagBasedModelTypeProcessor>( |
| syncer::AUTOFILL_PROFILE, |
| /*dump_stack=*/base::RepeatingClosure()), |
| app_locale, web_data_backend)); |
| } |
| |
| // static |
| syncer::ModelTypeSyncBridge* AutofillProfileSyncBridge::FromWebDataService( |
| AutofillWebDataService* web_data_service) { |
| return static_cast<AutofillProfileSyncBridge*>( |
| web_data_service->GetDBUserData()->GetUserData( |
| &kAutofillProfileSyncBridgeUserDataKey)); |
| } |
| |
| AutofillProfileSyncBridge::AutofillProfileSyncBridge( |
| std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor, |
| const std::string& app_locale, |
| AutofillWebDataBackend* backend) |
| : syncer::ModelTypeSyncBridge(std::move(change_processor)), |
| app_locale_(app_locale), |
| web_data_backend_(backend), |
| scoped_observer_(this) { |
| DCHECK(web_data_backend_); |
| |
| scoped_observer_.Add(web_data_backend_); |
| |
| LoadMetadata(); |
| } |
| |
| AutofillProfileSyncBridge::~AutofillProfileSyncBridge() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| } |
| |
| std::unique_ptr<MetadataChangeList> |
| AutofillProfileSyncBridge::CreateMetadataChangeList() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| return std::make_unique<syncer::SyncMetadataStoreChangeList>( |
| GetAutofillTable(), syncer::AUTOFILL_PROFILE); |
| } |
| |
| Optional<syncer::ModelError> AutofillProfileSyncBridge::MergeSyncData( |
| std::unique_ptr<MetadataChangeList> metadata_change_list, |
| syncer::EntityChangeList entity_data) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| AutofillProfileInitialSyncDifferenceTracker initial_sync_tracker( |
| GetAutofillTable()); |
| |
| for (const auto& change : entity_data) { |
| DCHECK(change.data().specifics.has_autofill_profile()); |
| std::unique_ptr<AutofillProfile> remote = |
| CreateAutofillProfileFromSpecifics( |
| change.data().specifics.autofill_profile()); |
| if (!remote) { |
| DVLOG(2) << "[AUTOFILL SYNC] Invalid remote specifics " |
| << change.data().specifics.autofill_profile().SerializeAsString() |
| << " received from the server in an initial sync."; |
| continue; |
| } |
| RETURN_IF_ERROR( |
| initial_sync_tracker.IncorporateRemoteProfile(std::move(remote))); |
| } |
| |
| RETURN_IF_ERROR( |
| initial_sync_tracker.MergeSimilarEntriesForInitialSync(app_locale_)); |
| RETURN_IF_ERROR( |
| FlushSyncTracker(std::move(metadata_change_list), &initial_sync_tracker)); |
| |
| web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL_PROFILE); |
| return base::nullopt; |
| } |
| |
| Optional<ModelError> AutofillProfileSyncBridge::ApplySyncChanges( |
| std::unique_ptr<MetadataChangeList> metadata_change_list, |
| syncer::EntityChangeList entity_changes) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| AutofillProfileSyncDifferenceTracker tracker(GetAutofillTable()); |
| for (const syncer::EntityChange& change : entity_changes) { |
| if (change.type() == syncer::EntityChange::ACTION_DELETE) { |
| RETURN_IF_ERROR(tracker.IncorporateRemoteDelete(change.storage_key())); |
| } else { |
| DCHECK(change.data().specifics.has_autofill_profile()); |
| std::unique_ptr<AutofillProfile> remote = |
| CreateAutofillProfileFromSpecifics( |
| change.data().specifics.autofill_profile()); |
| if (!remote) { |
| DVLOG(2) |
| << "[AUTOFILL SYNC] Invalid remote specifics " |
| << change.data().specifics.autofill_profile().SerializeAsString() |
| << " received from the server in an initial sync."; |
| continue; |
| } |
| RETURN_IF_ERROR(tracker.IncorporateRemoteProfile(std::move(remote))); |
| } |
| } |
| |
| return FlushSyncTracker(std::move(metadata_change_list), &tracker); |
| } |
| |
| void AutofillProfileSyncBridge::GetData(StorageKeyList storage_keys, |
| DataCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| std::vector<std::unique_ptr<AutofillProfile>> entries; |
| if (!GetAutofillTable()->GetAutofillProfiles(&entries)) { |
| change_processor()->ReportError( |
| {FROM_HERE, "Failed to load entries from table."}); |
| return; |
| } |
| |
| std::unordered_set<std::string> keys_set(storage_keys.begin(), |
| storage_keys.end()); |
| auto batch = std::make_unique<syncer::MutableDataBatch>(); |
| for (const std::unique_ptr<AutofillProfile>& entry : entries) { |
| std::string key = GetStorageKeyFromAutofillProfile(*entry); |
| if (base::ContainsKey(keys_set, key)) { |
| batch->Put(key, CreateEntityDataFromAutofillProfile(*entry)); |
| } |
| } |
| std::move(callback).Run(std::move(batch)); |
| } |
| |
| void AutofillProfileSyncBridge::GetAllDataForDebugging(DataCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| std::vector<std::unique_ptr<AutofillProfile>> entries; |
| if (!GetAutofillTable()->GetAutofillProfiles(&entries)) { |
| change_processor()->ReportError( |
| {FROM_HERE, "Failed to load entries from table."}); |
| return; |
| } |
| |
| auto batch = std::make_unique<syncer::MutableDataBatch>(); |
| for (const std::unique_ptr<AutofillProfile>& entry : entries) { |
| batch->Put(GetStorageKeyFromAutofillProfile(*entry), |
| CreateEntityDataFromAutofillProfile(*entry)); |
| } |
| std::move(callback).Run(std::move(batch)); |
| } |
| |
| void AutofillProfileSyncBridge::ActOnLocalChange( |
| const AutofillProfileChange& change) { |
| DCHECK((change.type() == AutofillProfileChange::REMOVE) == |
| (change.data_model() == nullptr)); |
| if (!change_processor()->IsTrackingMetadata()) { |
| return; |
| } |
| if (change.data_model() && |
| change.data_model()->record_type() != AutofillProfile::LOCAL_PROFILE) { |
| return; |
| } |
| |
| auto metadata_change_list = |
| std::make_unique<syncer::SyncMetadataStoreChangeList>( |
| GetAutofillTable(), syncer::AUTOFILL_PROFILE); |
| |
| switch (change.type()) { |
| case AutofillChange::ADD: |
| case AutofillChange::UPDATE: |
| change_processor()->Put( |
| change.key(), |
| CreateEntityDataFromAutofillProfile(*change.data_model()), |
| metadata_change_list.get()); |
| break; |
| case AutofillChange::REMOVE: |
| // Removals have no data_model() so this change can still be for a |
| // SERVER_PROFILE. We have no simple way to rule it out. For the time |
| // being we rely on the processor ignoring deletions for storage keys it |
| // does not know. |
| // TODO(jkrcal): implement a hash map of known storage_keys and use it |
| // here. |
| change_processor()->Delete(change.key(), metadata_change_list.get()); |
| break; |
| } |
| |
| if (Optional<ModelError> error = metadata_change_list->TakeError()) { |
| change_processor()->ReportError(*error); |
| } |
| } |
| |
| base::Optional<syncer::ModelError> AutofillProfileSyncBridge::FlushSyncTracker( |
| std::unique_ptr<MetadataChangeList> metadata_change_list, |
| AutofillProfileSyncDifferenceTracker* tracker) { |
| DCHECK(tracker); |
| |
| RETURN_IF_ERROR(tracker->FlushToLocal( |
| base::BindOnce(&AutofillWebDataBackend::NotifyOfMultipleAutofillChanges, |
| base::Unretained(web_data_backend_)))); |
| |
| std::vector<std::unique_ptr<AutofillProfile>> profiles_to_upload_to_sync; |
| RETURN_IF_ERROR(tracker->FlushToSync(&profiles_to_upload_to_sync)); |
| for (const std::unique_ptr<AutofillProfile>& entry : |
| profiles_to_upload_to_sync) { |
| change_processor()->Put(GetStorageKeyFromAutofillProfile(*entry), |
| CreateEntityDataFromAutofillProfile(*entry), |
| metadata_change_list.get()); |
| } |
| |
| return static_cast<syncer::SyncMetadataStoreChangeList*>( |
| metadata_change_list.get()) |
| ->TakeError(); |
| } |
| |
| void AutofillProfileSyncBridge::LoadMetadata() { |
| if (!web_data_backend_ || !web_data_backend_->GetDatabase() || |
| !GetAutofillTable()) { |
| change_processor()->ReportError( |
| {FROM_HERE, "Failed to load AutofillWebDatabase."}); |
| return; |
| } |
| |
| auto batch = std::make_unique<syncer::MetadataBatch>(); |
| if (!GetAutofillTable()->GetAllSyncMetadata(syncer::AUTOFILL_PROFILE, |
| batch.get())) { |
| change_processor()->ReportError( |
| {FROM_HERE, "Failed reading autofill metadata from WebDatabase."}); |
| return; |
| } |
| change_processor()->ModelReadyToSync(std::move(batch)); |
| } |
| |
| std::string AutofillProfileSyncBridge::GetClientTag( |
| const EntityData& entity_data) { |
| DCHECK(entity_data.specifics.has_autofill_profile()); |
| // Must equal to guid of the entry. This is to maintain compatibility with the |
| // previous sync integration (Directory and SyncableService). |
| return entity_data.specifics.autofill_profile().guid(); |
| } |
| |
| std::string AutofillProfileSyncBridge::GetStorageKey( |
| const EntityData& entity_data) { |
| DCHECK(entity_data.specifics.has_autofill_profile()); |
| return GetStorageKeyFromAutofillProfileSpecifics( |
| entity_data.specifics.autofill_profile()); |
| } |
| |
| void AutofillProfileSyncBridge::AutofillProfileChanged( |
| const AutofillProfileChange& change) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| ActOnLocalChange(change); |
| } |
| |
| AutofillTable* AutofillProfileSyncBridge::GetAutofillTable() { |
| return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase()); |
| } |
| |
| } // namespace autofill |