| // Copyright 2013 The Chromium Authors |
| // 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/personal_data_manager.h" |
| |
| #include <stddef.h> |
| |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/containers/contains.h" |
| #include "base/containers/cxx20_erase.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/i18n/case_conversion.h" |
| #include "base/i18n/timezone.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/observer_list.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/uuid.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "components/autofill/core/browser/autofill_data_util.h" |
| #include "components/autofill/core/browser/autofill_download_manager.h" |
| #include "components/autofill/core/browser/autofill_experiments.h" |
| #include "components/autofill/core/browser/autofill_field.h" |
| #include "components/autofill/core/browser/data_model/autofill_profile.h" |
| #include "components/autofill/core/browser/data_model/autofill_profile_comparator.h" |
| #include "components/autofill/core/browser/data_model/credit_card_art_image.h" |
| #include "components/autofill/core/browser/data_model/phone_number.h" |
| #include "components/autofill/core/browser/form_structure.h" |
| #include "components/autofill/core/browser/geo/address_i18n.h" |
| #include "components/autofill/core/browser/geo/autofill_country.h" |
| #include "components/autofill/core/browser/geo/country_data.h" |
| #include "components/autofill/core/browser/geo/phone_number_i18n.h" |
| #include "components/autofill/core/browser/manual_testing_import.h" |
| #include "components/autofill/core/browser/metrics/autofill_metrics.h" |
| #include "components/autofill/core/browser/metrics/payments/iban_metrics.h" |
| #include "components/autofill/core/browser/metrics/payments/offers_metrics.h" |
| #include "components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h" |
| #include "components/autofill/core/browser/metrics/stored_profile_metrics.h" |
| #include "components/autofill/core/browser/personal_data_manager_observer.h" |
| #include "components/autofill/core/browser/strike_databases/autofill_profile_migration_strike_database.h" |
| #include "components/autofill/core/browser/strike_databases/autofill_profile_save_strike_database.h" |
| #include "components/autofill/core/browser/ui/autofill_image_fetcher.h" |
| #include "components/autofill/core/browser/ui/label_formatter.h" |
| #include "components/autofill/core/browser/ui/label_formatter_utils.h" |
| #include "components/autofill/core/browser/ui/suggestion_selection.h" |
| #include "components/autofill/core/browser/validation.h" |
| #include "components/autofill/core/common/autofill_clock.h" |
| #include "components/autofill/core/common/autofill_constants.h" |
| #include "components/autofill/core/common/autofill_features.h" |
| #include "components/autofill/core/common/autofill_payments_features.h" |
| #include "components/autofill/core/common/autofill_prefs.h" |
| #include "components/autofill/core/common/autofill_switches.h" |
| #include "components/autofill/core/common/autofill_util.h" |
| #include "components/history/core/browser/history_service.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/signin/public/base/consent_level.h" |
| #include "components/signin/public/identity_manager/account_info.h" |
| #include "components/signin/public/identity_manager/identity_manager.h" |
| #include "components/sync/base/user_selectable_type.h" |
| #include "components/sync/service/sync_service.h" |
| #include "components/sync/service/sync_service_utils.h" |
| #include "components/sync/service/sync_user_settings.h" |
| #include "components/version_info/version_info.h" |
| #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h" |
| #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h" |
| #include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h" |
| #include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h" |
| |
| namespace { |
| // Checks the order of preference of the `original_card` with the |
| // `duplicate_card` and returns whether to dedupe/erase the `duplicate_card` |
| // based on the order of preference. We assume that both the cards in params are |
| // duplicates of each other. |
| // |
| // This function returns true in the following situations: |
| // Case 1: `original_card` = LOCAL_CARD |
| // `duplicate_card` = MASKED_SERVER_CARD |
| // `should_suggest_server_cards_for_deduped_cards` = false |
| // |
| // Case 2: `original_card` = FULL_SERVER_CARD |
| // `duplicate_card` = LOCAL_CARD |
| // `should_suggest_server_cards_for_deduped_cards` = irrelevant |
| // |
| // Case 3: `original_card` = MASKED_SERVER_CARD |
| // `duplicate_card` = LOCAL_CARD |
| // `should_suggest_server_cards_for_deduped_cards` = true |
| bool ShouldDedupeDuplicateCard(autofill::CreditCard* original_card, |
| autofill::CreditCard* duplicate_card) { |
| // FULL_SERVER_CARDs have the highest priority and should never be removed |
| // from the suggestion list. |
| if (duplicate_card->record_type() == autofill::CreditCard::FULL_SERVER_CARD) { |
| return false; |
| } |
| const bool should_suggest_server_cards_for_deduped_cards = |
| base::FeatureList::IsEnabled( |
| autofill::features::kAutofillSuggestServerCardInsteadOfLocalCard); |
| |
| // Delete duplicated MASKED_SERVER_CARD if the original_card is a LOCAL_CARD |
| // and we are NOT suggesting MASKED_SERVER_CARD for duplicates. |
| if (duplicate_card->record_type() == |
| autofill::CreditCard::MASKED_SERVER_CARD && |
| original_card->record_type() == autofill::CreditCard::LOCAL_CARD && |
| !should_suggest_server_cards_for_deduped_cards) { |
| return true; |
| } |
| // Delete duplicated LOCAL_CARD if the original_card is a FULL_SERVER_CARD |
| // or we are suggesting MASKED_SERVER_CARD for duplicates. |
| if (duplicate_card->record_type() == autofill::CreditCard::LOCAL_CARD && |
| (original_card->record_type() == autofill::CreditCard::FULL_SERVER_CARD || |
| should_suggest_server_cards_for_deduped_cards)) { |
| return true; |
| } |
| return false; |
| } |
| } // namespace |
| |
| namespace autofill { |
| |
| namespace { |
| |
| using ::i18n::addressinput::AddressField; |
| using ::i18n::addressinput::GetStreetAddressLinesAsSingleLine; |
| using ::i18n::addressinput::STREET_ADDRESS; |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| enum class MigrateUserOptedInWalletSyncType { |
| kNotMigrated = 0, |
| kMigratedFromCanonicalEmail = 1, |
| kMigratedFromNonCanonicalEmail = 2, |
| kNotMigratedUnexpectedPrimaryAccountIdWithEmail = 3, |
| kMaxValue = kNotMigratedUnexpectedPrimaryAccountIdWithEmail, |
| }; |
| |
| template <typename T> |
| const T& Deref(T* x) { |
| return *x; |
| } |
| |
| template <typename T> |
| const T& Deref(const std::unique_ptr<T>& x) { |
| return *x; |
| } |
| |
| template <typename T> |
| const T& Deref(const T& x) { |
| return x; |
| } |
| |
| template <typename C, typename StringType> |
| typename C::const_iterator FindElementByGUID(const C& container, |
| const StringType& guid) { |
| return base::ranges::find(container, guid, [](const auto& element) { |
| return Deref(element).guid(); |
| }); |
| } |
| |
| template <typename C, typename StringType> |
| bool FindByGUID(const C& container, const StringType& guid) { |
| return FindElementByGUID(container, guid) != container.end(); |
| } |
| |
| template <typename C, typename T> |
| bool FindByContents(const C& container, const T& needle) { |
| return base::ranges::any_of(container, [&needle](const auto& element) { |
| return element->Compare(needle) == 0; |
| }); |
| } |
| |
| // Receives the loaded profiles from the web data service and stores them in |
| // |*dest|. The pending handle is the address of the pending handle |
| // corresponding to this request type. This function is used to save both server |
| // and local profiles and credit cards. |
| template <typename ValueType> |
| void ReceiveLoadedDbValues(WebDataServiceBase::Handle h, |
| WDTypedResult* result, |
| WebDataServiceBase::Handle* pending_handle, |
| std::vector<std::unique_ptr<ValueType>>* dest) { |
| DCHECK_EQ(*pending_handle, h); |
| *pending_handle = 0; |
| |
| *dest = std::move( |
| static_cast<WDResult<std::vector<std::unique_ptr<ValueType>>>*>(result) |
| ->GetValue()); |
| } |
| |
| // A helper function for finding the maximum value in a string->int map. |
| static bool CompareVotes(const std::pair<std::string, int>& a, |
| const std::pair<std::string, int>& b) { |
| return a.second < b.second; |
| } |
| |
| // Orders all `profiles` by the specified `order` rule. |
| void OrderProfiles(std::vector<AutofillProfile*>& profiles, |
| PersonalDataManager::ProfileOrder order) { |
| switch (order) { |
| case PersonalDataManager::ProfileOrder::kNone: |
| break; |
| case PersonalDataManager::ProfileOrder::kHighestFrecencyDesc: |
| // TODO(crbug.com/1411114): Remove code duplication for sorting profiles. |
| base::ranges::sort(profiles, [comparison_time = AutofillClock::Now()]( |
| AutofillProfile* a, AutofillProfile* b) { |
| return a->HasGreaterRankingThan(b, comparison_time); |
| }); |
| break; |
| case PersonalDataManager::ProfileOrder::kMostRecentlyModifiedDesc: |
| base::ranges::sort(profiles, [](AutofillProfile* a, AutofillProfile* b) { |
| return a->modification_date() > b->modification_date(); |
| }); |
| break; |
| case PersonalDataManager::ProfileOrder::kMostRecentlyUsedFirstDesc: |
| base::ranges::sort(profiles, [](AutofillProfile* a, AutofillProfile* b) { |
| return a->use_date() > b->use_date(); |
| }); |
| break; |
| } |
| } |
| |
| } // namespace |
| |
| // Helper class to abstract the switching between account and profile storage |
| // for server cards away from the rest of PersonalDataManager. |
| class PersonalDatabaseHelper |
| : public AutofillWebDataServiceObserverOnUISequence { |
| public: |
| explicit PersonalDatabaseHelper(PersonalDataManager* personal_data_manager) |
| : personal_data_manager_(personal_data_manager) {} |
| |
| PersonalDatabaseHelper(const PersonalDatabaseHelper&) = delete; |
| PersonalDatabaseHelper& operator=(const PersonalDatabaseHelper&) = delete; |
| |
| void Init(scoped_refptr<AutofillWebDataService> profile_database, |
| scoped_refptr<AutofillWebDataService> account_database) { |
| profile_database_ = profile_database; |
| account_database_ = account_database; |
| |
| if (!profile_database_) { |
| // In some tests, there are no dbs. |
| return; |
| } |
| |
| // Start observing the profile database. Don't observe the account database |
| // until we know that we should use it. |
| profile_database_->AddObserver(personal_data_manager_); |
| |
| // If we don't have an account_database , we always use the profile database |
| // for server data. |
| if (!account_database_) { |
| server_database_ = profile_database_; |
| } else { |
| // Wait for the call to SetUseAccountStorageForServerData to decide |
| // which database to use for server data. |
| server_database_ = nullptr; |
| } |
| } |
| |
| ~PersonalDatabaseHelper() override { |
| if (profile_database_) { |
| profile_database_->RemoveObserver(personal_data_manager_); |
| } |
| |
| // If we have a different server database, also remove its observer. |
| if (server_database_ && server_database_ != profile_database_) { |
| server_database_->RemoveObserver(personal_data_manager_); |
| } |
| } |
| |
| // Returns the database that should be used for storing local data. |
| scoped_refptr<AutofillWebDataService> GetLocalDatabase() { |
| return profile_database_; |
| } |
| |
| // Returns the database that should be used for storing server data. |
| scoped_refptr<AutofillWebDataService> GetServerDatabase() { |
| return server_database_; |
| } |
| |
| // Whether we're currently using the ephemeral account storage for saving |
| // server data. |
| bool IsUsingAccountStorageForServerData() { |
| return server_database_ != profile_database_; |
| } |
| |
| // Set whether this should use the passed in account storage for server |
| // addresses. If false, this will use the profile_storage. |
| // It's an error to call this if no account storage was passed in at |
| // construction time. |
| void SetUseAccountStorageForServerData( |
| bool use_account_storage_for_server_cards) { |
| if (!profile_database_) { |
| // In some tests, there are no dbs. |
| return; |
| } |
| scoped_refptr<AutofillWebDataService> new_server_database = |
| use_account_storage_for_server_cards ? account_database_ |
| : profile_database_; |
| DCHECK(new_server_database != nullptr) |
| << "SetUseAccountStorageForServerData(" |
| << use_account_storage_for_server_cards << "): storage not available."; |
| |
| if (new_server_database == server_database_) { |
| // Nothing to do :) |
| return; |
| } |
| |
| if (server_database_ != nullptr) { |
| if (server_database_ != profile_database_) { |
| // Remove the previous observer if we had any. |
| server_database_->RemoveObserver(personal_data_manager_); |
| } |
| personal_data_manager_->CancelPendingServerQueries(); |
| } |
| server_database_ = new_server_database; |
| // We don't need to add an observer if server_database_ is equal to |
| // profile_database_, because we're already observing that. |
| if (server_database_ != profile_database_) { |
| server_database_->AddObserver(personal_data_manager_); |
| } |
| // Notify the manager that the database changed. |
| personal_data_manager_->Refresh(); |
| } |
| |
| private: |
| scoped_refptr<AutofillWebDataService> profile_database_; |
| scoped_refptr<AutofillWebDataService> account_database_; |
| |
| // The database that should be used for server data. This will always be equal |
| // to either profile_database_, or account_database_. |
| scoped_refptr<AutofillWebDataService> server_database_; |
| |
| raw_ptr<PersonalDataManager> personal_data_manager_; |
| }; |
| |
| PersonalDataManager::PersonalDataManager( |
| const std::string& app_locale, |
| const std::string& variations_country_code) |
| : app_locale_(app_locale), |
| variations_country_code_(variations_country_code) { |
| database_helper_ = std::make_unique<PersonalDatabaseHelper>(this); |
| } |
| |
| PersonalDataManager::PersonalDataManager(const std::string& app_locale) |
| : PersonalDataManager(app_locale, std::string()) {} |
| |
| void PersonalDataManager::Init( |
| scoped_refptr<AutofillWebDataService> profile_database, |
| scoped_refptr<AutofillWebDataService> account_database, |
| PrefService* pref_service, |
| PrefService* local_state, |
| signin::IdentityManager* identity_manager, |
| history::HistoryService* history_service, |
| syncer::SyncService* sync_service, |
| StrikeDatabaseBase* strike_database, |
| AutofillImageFetcher* image_fetcher, |
| bool is_off_the_record) { |
| database_helper_->Init(profile_database, account_database); |
| |
| SetPrefService(pref_service); |
| |
| // Listen for the preference changes. |
| pref_registrar_.Init(pref_service); |
| |
| alternative_state_name_map_updater_ = |
| std::make_unique<AlternativeStateNameMapUpdater>(local_state, this); |
| AddObserver(alternative_state_name_map_updater_.get()); |
| |
| // Listen for URL deletions from browsing history. |
| history_service_ = history_service; |
| if (history_service_) |
| history_service_observation_.Observe(history_service_.get()); |
| |
| // Listen for account cookie deletion by the user. |
| identity_manager_ = identity_manager; |
| if (identity_manager_) |
| identity_manager_->AddObserver(this); |
| |
| SetSyncService(sync_service); |
| |
| image_fetcher_ = image_fetcher; |
| |
| is_off_the_record_ = is_off_the_record; |
| |
| if (!is_off_the_record_) { |
| AutofillMetrics::LogIsAutofillEnabledAtStartup(IsAutofillEnabled()); |
| AutofillMetrics::LogIsAutofillProfileEnabledAtStartup( |
| IsAutofillProfileEnabled()); |
| AutofillMetrics::LogIsAutofillCreditCardEnabledAtStartup( |
| IsAutofillCreditCardEnabled()); |
| } |
| |
| if (strike_database) { |
| profile_migration_strike_database_ = |
| std::make_unique<AutofillProfileMigrationStrikeDatabase>( |
| strike_database); |
| profile_save_strike_database_ = |
| std::make_unique<AutofillProfileSaveStrikeDatabase>(strike_database); |
| profile_update_strike_database_ = |
| std::make_unique<AutofillProfileUpdateStrikeDatabase>(strike_database); |
| } |
| |
| // WebDataService may not be available in tests. |
| if (!database_helper_->GetLocalDatabase()) { |
| return; |
| } |
| |
| // No profile change callbacks are expected in the Incognito mode, this check ensures |
| // that the origin profile (which is actually used) change callback is not overridden. |
| if (!is_off_the_record) { |
| database_helper_->GetLocalDatabase()->SetAutofillProfileChangedCallback( |
| base::BindRepeating(&PersonalDataManager::OnAutofillProfileChanged, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| Refresh(); |
| |
| personal_data_manager_cleaner_ = std::make_unique<PersonalDataManagerCleaner>( |
| this, alternative_state_name_map_updater_.get(), pref_service); |
| |
| // Potentially import profiles for testing. `Init()` is called whenever the |
| // corresponding Chrome profile is created. This is either during start-up or |
| // when the Chrome profile is changed (including incognito mode). |
| MaybeImportDataForManualTesting(weak_factory_.GetWeakPtr()); |
| } |
| |
| PersonalDataManager::~PersonalDataManager() { |
| CancelPendingLocalQuery(&pending_synced_local_profiles_query_); |
| CancelPendingLocalQuery(&pending_creditcards_query_); |
| CancelPendingLocalQuery(&pending_upi_ids_query_); |
| CancelPendingServerQueries(); |
| |
| if (alternative_state_name_map_updater_) |
| RemoveObserver(alternative_state_name_map_updater_.get()); |
| } |
| |
| void PersonalDataManager::Shutdown() { |
| if (sync_service_) |
| sync_service_->RemoveObserver(this); |
| sync_service_ = nullptr; |
| |
| if (history_service_) |
| history_service_observation_.Reset(); |
| history_service_ = nullptr; |
| |
| if (identity_manager_) |
| identity_manager_->RemoveObserver(this); |
| identity_manager_ = nullptr; |
| } |
| |
| void PersonalDataManager::OnURLsDeleted( |
| history::HistoryService* /* history_service */, |
| const history::DeletionInfo& deletion_info) { |
| for (PersonalDataManagerObserver& observer : observers_) { |
| observer.OnBrowsingHistoryCleared(deletion_info); |
| } |
| |
| if (!deletion_info.is_from_expiration() && deletion_info.IsAllHistory()) { |
| AutofillDownloadManager::ClearUploadHistory(pref_service_); |
| } |
| |
| if (profile_save_strike_database_) { |
| if (deletion_info.IsAllHistory()) { |
| // If the whole history is deleted, clear all strikes. |
| profile_save_strike_database_->ClearAllStrikes(); |
| } else { |
| std::set<std::string> deleted_hosts; |
| for (const auto& url_row : deletion_info.deleted_rows()) { |
| deleted_hosts.insert(url_row.url().host()); |
| } |
| if (deletion_info.time_range().IsValid() && |
| !deletion_info.time_range().IsAllTime()) { |
| profile_save_strike_database_->ClearStrikesByOriginAndTimeInternal( |
| deleted_hosts, deletion_info.time_range().begin(), |
| deletion_info.time_range().end()); |
| } else { |
| profile_save_strike_database_->ClearStrikesByOrigin(deleted_hosts); |
| } |
| } |
| } |
| } |
| |
| void PersonalDataManager::OnWebDataServiceRequestDone( |
| WebDataServiceBase::Handle h, |
| std::unique_ptr<WDTypedResult> result) { |
| DCHECK(pending_synced_local_profiles_query_ || |
| pending_account_profiles_query_ || |
| pending_creditcard_billing_addresses_query_ || |
| pending_creditcards_query_ || pending_server_creditcards_query_ || |
| pending_server_creditcard_cloud_token_data_query_ || |
| pending_ibans_query_ || pending_customer_data_query_ || |
| pending_upi_ids_query_ || pending_offer_data_query_ || |
| pending_virtual_card_usage_data_query_); |
| |
| if (!result) { |
| // Error from the web database. |
| if (h == pending_synced_local_profiles_query_) |
| pending_synced_local_profiles_query_ = 0; |
| else if (h == pending_account_profiles_query_) |
| pending_account_profiles_query_ = 0; |
| else if (h == pending_creditcard_billing_addresses_query_) |
| pending_creditcard_billing_addresses_query_ = 0; |
| else if (h == pending_creditcards_query_) |
| pending_creditcards_query_ = 0; |
| else if (h == pending_server_creditcards_query_) |
| pending_server_creditcards_query_ = 0; |
| else if (h == pending_server_creditcard_cloud_token_data_query_) |
| pending_server_creditcard_cloud_token_data_query_ = 0; |
| else if (h == pending_ibans_query_) |
| pending_ibans_query_ = 0; |
| else if (h == pending_customer_data_query_) |
| pending_customer_data_query_ = 0; |
| else if (h == pending_upi_ids_query_) |
| pending_upi_ids_query_ = 0; |
| else if (h == pending_offer_data_query_) |
| pending_offer_data_query_ = 0; |
| else if (h == pending_virtual_card_usage_data_query_) { |
| pending_virtual_card_usage_data_query_ = 0; |
| } |
| } else { |
| switch (result->GetType()) { |
| case AUTOFILL_PROFILES_RESULT: |
| if (h == pending_synced_local_profiles_query_) { |
| ReceiveLoadedDbValues(h, result.get(), |
| &pending_synced_local_profiles_query_, |
| &synced_local_profiles_); |
| } else if (h == pending_account_profiles_query_) { |
| ReceiveLoadedDbValues(h, result.get(), |
| &pending_account_profiles_query_, |
| &account_profiles_); |
| } else { |
| DCHECK_EQ(h, pending_creditcard_billing_addresses_query_) |
| << "received profiles from invalid request."; |
| ReceiveLoadedDbValues(h, result.get(), |
| &pending_creditcard_billing_addresses_query_, |
| &credit_card_billing_addresses_); |
| } |
| break; |
| case AUTOFILL_CREDITCARDS_RESULT: |
| if (h == pending_creditcards_query_) { |
| ReceiveLoadedDbValues(h, result.get(), &pending_creditcards_query_, |
| &local_credit_cards_); |
| } else { |
| DCHECK_EQ(h, pending_server_creditcards_query_) |
| << "received creditcards from invalid request."; |
| ReceiveLoadedDbValues(h, result.get(), |
| &pending_server_creditcards_query_, |
| &server_credit_cards_); |
| OnServerCreditCardsRefreshed(); |
| } |
| break; |
| case AUTOFILL_CLOUDTOKEN_RESULT: |
| DCHECK_EQ(h, pending_server_creditcard_cloud_token_data_query_) |
| << "received credit card cloud token data from invalid request."; |
| ReceiveLoadedDbValues( |
| h, result.get(), &pending_server_creditcard_cloud_token_data_query_, |
| &server_credit_card_cloud_token_data_); |
| break; |
| case AUTOFILL_IBANS_RESULT: |
| DCHECK_EQ(h, pending_ibans_query_) |
| << "received ibans from invalid request."; |
| ReceiveLoadedDbValues(h, result.get(), &pending_ibans_query_, |
| &local_ibans_); |
| break; |
| case AUTOFILL_CUSTOMERDATA_RESULT: |
| DCHECK_EQ(h, pending_customer_data_query_) |
| << "received customer data from invalid request."; |
| pending_customer_data_query_ = 0; |
| |
| payments_customer_data_ = |
| static_cast<WDResult<std::unique_ptr<PaymentsCustomerData>>*>( |
| result.get()) |
| ->GetValue(); |
| break; |
| case AUTOFILL_UPI_RESULT: |
| DCHECK_EQ(h, pending_upi_ids_query_) |
| << "received UPI IDs from invalid request."; |
| pending_upi_ids_query_ = 0; |
| |
| upi_ids_ = |
| static_cast<WDResult<std::vector<std::string>>*>(result.get()) |
| ->GetValue(); |
| break; |
| case AUTOFILL_OFFER_DATA: |
| DCHECK_EQ(h, pending_offer_data_query_) |
| << "received autofill offer data from invalid request."; |
| ReceiveLoadedDbValues(h, result.get(), &pending_offer_data_query_, |
| &autofill_offer_data_); |
| break; |
| case AUTOFILL_VIRTUAL_CARD_USAGE_DATA: |
| DCHECK_EQ(h, pending_virtual_card_usage_data_query_) |
| << "received autofill virtual card usage data from invalid " |
| "request."; |
| ReceiveLoadedDbValues(h, result.get(), |
| &pending_virtual_card_usage_data_query_, |
| &autofill_virtual_card_usage_data_); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| if (HasPendingQueries()) |
| return; |
| |
| if (!database_helper_->GetServerDatabase()) { |
| DLOG(WARNING) << "There are no pending queries but the server database " |
| "wasn't set yet, so some data might be missing. Maybe " |
| "SetSyncService() wasn't called yet."; |
| return; |
| } |
| |
| // All personal data is loaded, notify observers. |is_data_loaded_| is false |
| // if this is the initial load. |
| if (!is_data_loaded_) { |
| is_data_loaded_ = true; |
| personal_data_manager_cleaner_->CleanupDataAndNotifyPersonalDataObservers(); |
| } else { |
| NotifyPersonalDataObserver(); |
| } |
| } |
| |
| void PersonalDataManager::AutofillMultipleChangedBySync() { |
| // After each change coming from sync we go through a two-step process: |
| // - First, we post a task on the DB sequence to (potentially) convert server |
| // addresses to local addresses and update cards accordingly. |
| // - This conversion task is concluded by a |
| // AutofillAddressConversionCompleted() notification. As a second step, we |
| // need to refresh the PDM's view of the data. |
| ConvertWalletAddressesAndUpdateWalletCards(); |
| } |
| |
| void PersonalDataManager::AutofillAddressConversionCompleted() { |
| Refresh(); |
| } |
| |
| void PersonalDataManager::SyncStarted(syncer::ModelType model_type) { |
| personal_data_manager_cleaner_->SyncStarted(model_type); |
| } |
| |
| void PersonalDataManager::OnStateChanged(syncer::SyncService* sync_service) { |
| DCHECK_EQ(sync_service_, sync_service); |
| |
| for (PersonalDataManagerObserver& observer : observers_) { |
| observer.OnPersonalDataSyncStateChanged(); |
| } |
| |
| // Use the ephemeral account storage when the user didn't enable the sync |
| // feature explicitly. `sync_service` is nullptr-checked because this |
| // method can also be used (apart from the Sync service observer's calls) in |
| // SetSyncService() where setting a nullptr is possible. |
| database_helper_->SetUseAccountStorageForServerData( |
| sync_service && !sync_service->IsSyncFeatureEnabled()); |
| } |
| |
| void PersonalDataManager::OnSyncShutdown(syncer::SyncService* sync_service) { |
| DCHECK_EQ(sync_service_, sync_service); |
| sync_service_->RemoveObserver(this); |
| sync_service_ = nullptr; |
| } |
| |
| CoreAccountInfo PersonalDataManager::GetAccountInfoForPaymentsServer() const { |
| // Return the account of the active signed-in user irrespective of whether |
| // they enabled sync or not. |
| return identity_manager_->GetPrimaryAccountInfo( |
| signin::ConsentLevel::kSignin); |
| } |
| |
| bool PersonalDataManager::IsSyncFeatureEnabled() const { |
| return sync_service_ && sync_service_->IsSyncFeatureEnabled(); |
| } |
| |
| void PersonalDataManager::OnAccountsCookieDeletedByUserAction() { |
| // Clear all the Sync Transport feature opt-ins. |
| prefs::ClearSyncTransportOptIns(pref_service_); |
| } |
| |
| absl::optional<CoreAccountInfo> PersonalDataManager::GetPrimaryAccountInfo() |
| const { |
| if (identity_manager_ && |
| identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin)) { |
| return identity_manager_->GetPrimaryAccountInfo( |
| signin::ConsentLevel::kSignin); |
| } |
| |
| return absl::nullopt; |
| } |
| |
| AutofillSyncSigninState PersonalDataManager::GetSyncSigninState() const { |
| // Check if the user is signed out. |
| if (!sync_service_ || !identity_manager_ || |
| sync_service_->GetAccountInfo().IsEmpty()) { |
| return AutofillSyncSigninState::kSignedOut; |
| } |
| |
| if (sync_service_->GetTransportState() == |
| syncer::SyncService::TransportState::PAUSED) { |
| return AutofillSyncSigninState::kSyncPaused; |
| } |
| |
| // Check if the user has turned on sync. |
| if (sync_service_->IsSyncFeatureEnabled()) { |
| return AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled; |
| } |
| |
| // Check if Wallet data types are supported. |
| if (sync_service_->GetActiveDataTypes().Has(syncer::AUTOFILL_WALLET_DATA)) { |
| return AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled; |
| } |
| |
| return AutofillSyncSigninState::kSignedIn; |
| } |
| |
| void PersonalDataManager::AddObserver(PersonalDataManagerObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void PersonalDataManager::RemoveObserver( |
| PersonalDataManagerObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void PersonalDataManager::MarkObserversInsufficientFormDataForImport() { |
| for (PersonalDataManagerObserver& observer : observers_) |
| observer.OnInsufficientFormData(); |
| } |
| |
| void PersonalDataManager::RecordUseOf( |
| absl::variant<const AutofillProfile*, const CreditCard*> |
| profile_or_credit_card) { |
| if (is_off_the_record_) |
| return; |
| |
| if (absl::holds_alternative<const CreditCard*>(profile_or_credit_card)) { |
| CreditCard* credit_card = GetCreditCardByGUID( |
| absl::get<const CreditCard*>(profile_or_credit_card)->guid()); |
| |
| if (credit_card) { |
| credit_card->RecordAndLogUse(); |
| |
| if (credit_card->record_type() == CreditCard::LOCAL_CARD) { |
| // Fail silently if there's no local database, because we need to |
| // support this for tests. |
| if (database_helper_->GetLocalDatabase()) { |
| database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card); |
| } |
| } else { |
| DCHECK(database_helper_->GetServerDatabase()) |
| << "Recording use of server card without server storage."; |
| database_helper_->GetServerDatabase()->UpdateServerCardMetadata( |
| *credit_card); |
| } |
| |
| Refresh(); |
| return; |
| } |
| } |
| |
| if (absl::holds_alternative<const AutofillProfile*>(profile_or_credit_card)) { |
| // TODO(crbug.com/941498): Server profiles are not recorded therefore |
| // GetProfileByGUID returns null for them. |
| AutofillProfile* profile = GetProfileByGUID( |
| absl::get<const AutofillProfile*>(profile_or_credit_card)->guid()); |
| |
| if (profile) { |
| profile->RecordAndLogUse(); |
| |
| switch (profile->record_type()) { |
| case AutofillProfile::LOCAL_PROFILE: |
| UpdateProfileInDB(*profile, /*enforced=*/true); |
| break; |
| case AutofillProfile::SERVER_PROFILE: |
| DCHECK(database_helper_->GetServerDatabase()) |
| << "Recording use of server address without server storage."; |
| database_helper_->GetServerDatabase()->UpdateServerAddressMetadata( |
| *profile); |
| Refresh(); |
| break; |
| } |
| } |
| } |
| } |
| |
| void PersonalDataManager::AddUpiId(const std::string& upi_id) { |
| DCHECK(!upi_id.empty()); |
| if (is_off_the_record_ || !database_helper_->GetLocalDatabase()) |
| return; |
| |
| // Don't add a duplicate. |
| if (base::Contains(upi_ids_, upi_id)) |
| return; |
| |
| database_helper_->GetLocalDatabase()->AddUpiId(upi_id); |
| |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| } |
| |
| std::vector<std::string> PersonalDataManager::GetUpiIds() { |
| return upi_ids_; |
| } |
| |
| void PersonalDataManager::AddProfile(const AutofillProfile& profile) { |
| if (!IsAutofillProfileEnabled()) |
| return; |
| |
| if (is_off_the_record_) |
| return; |
| |
| if (!database_helper_->GetLocalDatabase()) |
| return; |
| |
| AddProfileToDB(profile); |
| } |
| |
| void PersonalDataManager::UpdateProfile(const AutofillProfile& profile) { |
| if (is_off_the_record_) |
| return; |
| |
| if (!database_helper_->GetLocalDatabase()) |
| return; |
| |
| // If the profile is empty, remove it unconditionally. |
| if (profile.IsEmpty(app_locale_)) { |
| RemoveByGUID(profile.guid()); |
| return; |
| } |
| |
| // The profile is a duplicate of an existing profile if it has a distinct GUID |
| // but the same content. |
| // Duplicates can exist across profile sources. |
| const std::vector<std::unique_ptr<AutofillProfile>>& profiles = |
| GetProfileStorage(profile.source()); |
| auto duplicate_profile_iter = |
| base::ranges::find_if(profiles, [&profile](const auto& other_profile) { |
| return profile.guid() != other_profile->guid() && |
| other_profile->Compare(profile) == 0; |
| }); |
| |
| // Remove the profile if it is a duplicate of another already existing |
| // profile. |
| if (duplicate_profile_iter != profiles.end()) { |
| // Keep the more recently used version of the profile. |
| if (profile.use_date() > duplicate_profile_iter->get()->use_date()) { |
| UpdateProfileInDB(profile); |
| RemoveByGUID(duplicate_profile_iter->get()->guid()); |
| } else { |
| RemoveByGUID(profile.guid()); |
| } |
| return; |
| } |
| |
| UpdateProfileInDB(profile); |
| } |
| |
| AutofillProfile* PersonalDataManager::GetProfileByGUID( |
| const std::string& guid) const { |
| // GUIDs are unique among profile sources. |
| std::vector<AutofillProfile*> profiles = GetProfiles(); |
| auto iter = FindElementByGUID(profiles, guid); |
| return iter != profiles.end() ? *iter : nullptr; |
| } |
| |
| bool PersonalDataManager::IsEligibleForAddressAccountStorage() const { |
| // The CONTACT_INFO data type is only running for eligible users. See |
| // ContactInfoModelTypeController. Some additional countries are excluded |
| // based on their GeoIP. |
| return sync_service_ && |
| sync_service_->GetActiveDataTypes().Has(syncer::CONTACT_INFO) && |
| base::FeatureList::IsEnabled( |
| features::kAutofillAccountProfilesUnionView) && |
| base::FeatureList::IsEnabled( |
| features::kAutofillAccountProfileStorage) && |
| (features::kAutofillAccountProfileStorageFromUnsupportedIPs.Get() || |
| IsCountryEligibleForAccountStorage(variations_country_code_)); |
| } |
| |
| bool PersonalDataManager::IsCountryEligibleForAccountStorage( |
| base::StringPiece country_code) const { |
| constexpr char const* kUnsupportedCountries[] = {"CU", "IR", "KP", "SD", |
| "SY"}; |
| return !base::Contains(kUnsupportedCountries, country_code); |
| } |
| |
| std::string PersonalDataManager::AddIBAN(const IBAN& iban) { |
| if (!IsAutofillIBANEnabled()) |
| return std::string(); |
| |
| // Sets the `kAutofillHasSeenIban` pref to true indicating that the user has |
| // added an IBAN via Chrome payment settings page or accepted the save-IBAN |
| // prompt, which indicates that the user is familiar with IBANs as a concept. |
| // We set the pref so that even if the user travels to a country where IBAN |
| // functionality is not typically used, they will still be able to save new |
| // IBANs from the settings page using this pref. |
| SetAutofillHasSeenIban(); |
| |
| // Early exit if `is_off_the_record_` is true, or an IBAN which has the same |
| // guid exists in `local_ibans_`, or fail to get local database. |
| if (is_off_the_record_ || FindByGUID(local_ibans_, iban.guid()) || |
| !database_helper_->GetLocalDatabase()) { |
| return std::string(); |
| } |
| |
| // Search through `local_ibans_` to ensure no IBAN that already saved has the |
| // same value and nickname as `iban`, because we do not want to add two IBANs |
| // with the exact same data. |
| if (base::ranges::any_of( |
| local_ibans_, [&iban](const std::unique_ptr<IBAN>& iban_from_list) { |
| return iban.value().compare(iban_from_list->value()) == 0 && |
| iban.nickname().compare(iban_from_list->nickname()); |
| })) { |
| return std::string(); |
| } |
| |
| // Add the new iban to the web database. |
| database_helper_->GetLocalDatabase()->AddIBAN(iban); |
| |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| return iban.guid(); |
| } |
| |
| std::string PersonalDataManager::UpdateIBAN(const IBAN& iban) { |
| if (is_off_the_record_) |
| return std::string(); |
| |
| if (!database_helper_->GetLocalDatabase()) |
| return std::string(); |
| |
| // Make the update. |
| database_helper_->GetLocalDatabase()->UpdateIBAN(iban); |
| |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| return iban.guid(); |
| } |
| |
| void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) { |
| if (!IsAutofillCreditCardEnabled()) |
| return; |
| |
| if (is_off_the_record_) |
| return; |
| |
| if (credit_card.IsEmpty(app_locale_)) |
| return; |
| |
| if (FindByGUID(local_credit_cards_, credit_card.guid())) |
| return; |
| |
| if (!database_helper_->GetLocalDatabase()) |
| return; |
| |
| // Don't add a duplicate. |
| if (FindByContents(local_credit_cards_, credit_card)) |
| return; |
| |
| // Add the new credit card to the web database. |
| database_helper_->GetLocalDatabase()->AddCreditCard(credit_card); |
| |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| } |
| |
| void PersonalDataManager::DeleteLocalCreditCards( |
| const std::vector<CreditCard>& cards) { |
| DCHECK(database_helper_); |
| DCHECK(database_helper_->GetLocalDatabase()) |
| << "Use of local card without local storage."; |
| |
| for (const auto& card : cards) |
| database_helper_->GetLocalDatabase()->RemoveCreditCard(card.guid()); |
| |
| // Refresh the database, so latest state is reflected in all consumers. |
| if (!cards.empty()) |
| Refresh(); |
| } |
| |
| void PersonalDataManager::UpdateCreditCard(const CreditCard& credit_card) { |
| DCHECK_EQ(CreditCard::LOCAL_CARD, credit_card.record_type()); |
| if (is_off_the_record_) |
| return; |
| |
| CreditCard* existing_credit_card = GetCreditCardByGUID(credit_card.guid()); |
| if (!existing_credit_card) |
| return; |
| |
| // Don't overwrite the origin for a credit card that is already stored. |
| if (existing_credit_card->Compare(credit_card) == 0) |
| return; |
| |
| if (credit_card.IsEmpty(app_locale_)) { |
| RemoveByGUID(credit_card.guid()); |
| return; |
| } |
| |
| // Update the cached version. |
| *existing_credit_card = credit_card; |
| |
| if (!database_helper_->GetLocalDatabase()) |
| return; |
| |
| // Make the update. |
| database_helper_->GetLocalDatabase()->UpdateCreditCard(credit_card); |
| |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| } |
| |
| void PersonalDataManager::AddFullServerCreditCard( |
| const CreditCard& credit_card) { |
| DCHECK_EQ(CreditCard::FULL_SERVER_CARD, credit_card.record_type()); |
| DCHECK(!credit_card.IsEmpty(app_locale_)); |
| DCHECK(!credit_card.server_id().empty()); |
| |
| if (is_off_the_record_) |
| return; |
| |
| DCHECK(database_helper_->GetServerDatabase()) |
| << "Adding server card without server storage."; |
| |
| // Don't add a duplicate. |
| if (FindByGUID(server_credit_cards_, credit_card.guid()) || |
| FindByContents(server_credit_cards_, credit_card)) |
| return; |
| |
| // Add the new credit card to the web database. |
| database_helper_->GetServerDatabase()->AddFullServerCreditCard(credit_card); |
| |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| } |
| |
| void PersonalDataManager::UpdateServerCreditCard( |
| const CreditCard& credit_card) { |
| DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); |
| |
| if (is_off_the_record_ || !database_helper_->GetServerDatabase()) |
| return; |
| |
| // Look up by server id, not GUID. |
| const CreditCard* existing_credit_card = nullptr; |
| for (const auto& server_card : server_credit_cards_) { |
| if (credit_card.server_id() == server_card->server_id()) { |
| existing_credit_card = server_card.get(); |
| break; |
| } |
| } |
| if (!existing_credit_card) |
| return; |
| |
| DCHECK_NE(existing_credit_card->record_type(), credit_card.record_type()); |
| DCHECK_EQ(existing_credit_card->Label(), credit_card.Label()); |
| if (existing_credit_card->record_type() == CreditCard::MASKED_SERVER_CARD) { |
| database_helper_->GetServerDatabase()->UnmaskServerCreditCard( |
| credit_card, credit_card.number()); |
| } else { |
| database_helper_->GetServerDatabase()->MaskServerCreditCard( |
| credit_card.server_id()); |
| } |
| |
| Refresh(); |
| } |
| |
| void PersonalDataManager::UpdateServerCardsMetadata( |
| const std::vector<CreditCard>& credit_cards) { |
| if (is_off_the_record_) |
| return; |
| |
| DCHECK(database_helper_->GetServerDatabase()) |
| << "Updating server card metadata without server storage."; |
| |
| for (const auto& credit_card : credit_cards) { |
| DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); |
| database_helper_->GetServerDatabase()->UpdateServerCardMetadata( |
| credit_card); |
| } |
| |
| Refresh(); |
| } |
| |
| void PersonalDataManager::ResetFullServerCard(const std::string& guid) { |
| for (const auto& card : server_credit_cards_) { |
| if (card->guid() == guid) { |
| DCHECK_EQ(card->record_type(), CreditCard::FULL_SERVER_CARD); |
| CreditCard card_copy = *card; |
| card_copy.set_record_type(CreditCard::MASKED_SERVER_CARD); |
| card_copy.SetNumber(card->LastFourDigits()); |
| UpdateServerCreditCard(card_copy); |
| break; |
| } |
| } |
| } |
| |
| void PersonalDataManager::ResetFullServerCards() { |
| for (const auto& card : server_credit_cards_) { |
| if (card->record_type() == CreditCard::FULL_SERVER_CARD) { |
| CreditCard card_copy = *card; |
| card_copy.set_record_type(CreditCard::MASKED_SERVER_CARD); |
| card_copy.SetNumber(card->LastFourDigits()); |
| UpdateServerCreditCard(card_copy); |
| } |
| } |
| } |
| |
| void PersonalDataManager::ClearAllServerData() { |
| // This could theoretically be called before we get the data back from the |
| // database on startup, and it could get called when the wallet pref is |
| // off (meaning this class won't even query for the server data) so don't |
| // check the server_credit_cards_/profiles_ before posting to the DB. |
| |
| // TODO(crbug.com/864519): Move this nullcheck logic to the database helper. |
| // The server database can be null for a limited amount of time before the |
| // sync service gets initialized. Not clearing it does not matter in that case |
| // since it will not have been created yet (nothing to clear). |
| if (database_helper_->GetServerDatabase()) |
| database_helper_->GetServerDatabase()->ClearAllServerData(); |
| |
| // The above call will eventually clear our server data by notifying us |
| // that the data changed and then this class will re-fetch. Preemptively |
| // clear so that tests can synchronously verify that this data was cleared. |
| server_credit_cards_.clear(); |
| credit_card_billing_addresses_.clear(); |
| payments_customer_data_.reset(); |
| server_credit_card_cloud_token_data_.clear(); |
| autofill_offer_data_.clear(); |
| credit_card_art_images_.clear(); |
| } |
| |
| void PersonalDataManager::ClearAllLocalData() { |
| database_helper_->GetLocalDatabase()->ClearAllLocalData(); |
| local_credit_cards_.clear(); |
| synced_local_profiles_.clear(); |
| // Even though `account_profiles_` are not "local", the local/server |
| // distinction in the PersonalDataManager only exists for historical reasons |
| // and all AutofillProfiles fall in the local category. |
| account_profiles_.clear(); |
| } |
| |
| void PersonalDataManager::AddServerCreditCardForTest( |
| std::unique_ptr<CreditCard> credit_card) { |
| server_credit_cards_.push_back(std::move(credit_card)); |
| } |
| |
| bool PersonalDataManager::IsUsingAccountStorageForServerDataForTest() const { |
| return database_helper_->IsUsingAccountStorageForServerData(); |
| } |
| |
| void PersonalDataManager::AddOfferDataForTest( |
| std::unique_ptr<AutofillOfferData> offer_data) { |
| autofill_offer_data_.push_back(std::move(offer_data)); |
| } |
| |
| void PersonalDataManager::SetSyncServiceForTest( |
| syncer::SyncService* sync_service) { |
| // Before the sync service pointer gets changed, remove the observer. |
| if (sync_service_) { |
| sync_service_->RemoveObserver(this); |
| sync_service_ = nullptr; |
| } |
| SetSyncService(sync_service); |
| } |
| |
| void PersonalDataManager:: |
| RemoveAutofillProfileByGUIDAndBlankCreditCardReference( |
| const std::string& guid) { |
| RemoveProfileFromDB(guid); |
| |
| // Reset the billing_address_id of any card that refered to this profile. |
| for (CreditCard* credit_card : GetCreditCards()) { |
| if (credit_card->billing_address_id() == guid) { |
| credit_card->set_billing_address_id(""); |
| |
| if (credit_card->record_type() == CreditCard::LOCAL_CARD) { |
| database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card); |
| } else { |
| DCHECK(database_helper_->GetServerDatabase()) |
| << "Updating metadata on null server db."; |
| database_helper_->GetServerDatabase()->UpdateServerCardMetadata( |
| *credit_card); |
| } |
| } |
| } |
| } |
| |
| void PersonalDataManager::RemoveByGUID(const std::string& guid) { |
| if (is_off_the_record_) |
| return; |
| |
| if (!database_helper_->GetLocalDatabase()) |
| return; |
| |
| if (FindByGUID(local_credit_cards_, guid)) { |
| database_helper_->GetLocalDatabase()->RemoveCreditCard(guid); |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| } else if (FindByGUID(local_ibans_, guid)) { |
| database_helper_->GetLocalDatabase()->RemoveIBAN(guid); |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| } else { |
| RemoveAutofillProfileByGUIDAndBlankCreditCardReference(guid); |
| } |
| } |
| |
| IBAN* PersonalDataManager::GetIBANByGUID(const std::string& guid) { |
| const std::vector<IBAN*>& ibans = GetLocalIBANs(); |
| auto iter = FindElementByGUID(ibans, guid); |
| return iter != ibans.end() ? *iter : nullptr; |
| } |
| |
| CreditCard* PersonalDataManager::GetCreditCardByGUID(const std::string& guid) { |
| const std::vector<CreditCard*>& credit_cards = GetCreditCards(); |
| auto iter = FindElementByGUID(credit_cards, guid); |
| return iter != credit_cards.end() ? *iter : nullptr; |
| } |
| |
| CreditCard* PersonalDataManager::GetCreditCardByNumber( |
| const std::string& number) { |
| CreditCard numbered_card; |
| numbered_card.SetNumber(base::ASCIIToUTF16(number)); |
| for (CreditCard* credit_card : GetCreditCards()) { |
| DCHECK(credit_card); |
| if (credit_card->MatchingCardDetails(numbered_card)) { |
| return credit_card; |
| } |
| } |
| return nullptr; |
| } |
| |
| CreditCard* PersonalDataManager::GetCreditCardByInstrumentId( |
| int64_t instrument_id) { |
| const std::vector<CreditCard*> credit_cards = GetCreditCards(); |
| for (CreditCard* credit_card : credit_cards) { |
| if (credit_card->instrument_id() == instrument_id) |
| return credit_card; |
| } |
| return nullptr; |
| } |
| |
| CreditCard* PersonalDataManager::GetCreditCardByServerId( |
| const std::string& server_id) { |
| const std::vector<CreditCard*> server_credit_cards = GetServerCreditCards(); |
| for (CreditCard* credit_card : server_credit_cards) { |
| if (credit_card->server_id() == server_id) |
| return credit_card; |
| } |
| return nullptr; |
| } |
| |
| void PersonalDataManager::GetNonEmptyTypes( |
| ServerFieldTypeSet* non_empty_types) const { |
| for (AutofillProfile* profile : GetProfiles()) |
| profile->GetNonEmptyTypes(app_locale_, non_empty_types); |
| for (CreditCard* card : GetCreditCards()) |
| card->GetNonEmptyTypes(app_locale_, non_empty_types); |
| } |
| |
| bool PersonalDataManager::IsDataLoaded() const { |
| return is_data_loaded_; |
| } |
| |
| std::vector<AutofillProfile*> PersonalDataManager::GetProfiles( |
| ProfileOrder order) const { |
| std::vector<AutofillProfile*> a = GetProfilesFromSource( |
| AutofillProfile::Source::kLocalOrSyncable, ProfileOrder::kNone); |
| std::vector<AutofillProfile*> b = GetProfilesFromSource( |
| AutofillProfile::Source::kAccount, ProfileOrder::kNone); |
| a.reserve(a.size() + b.size()); |
| base::ranges::move(b, std::back_inserter(a)); |
| OrderProfiles(a, order); |
| return a; |
| } |
| |
| std::vector<AutofillProfile*> PersonalDataManager::GetProfilesFromSource( |
| AutofillProfile::Source profile_source, |
| ProfileOrder order) const { |
| const std::vector<std::unique_ptr<AutofillProfile>>& profiles = |
| GetProfileStorage(profile_source); |
| std::vector<AutofillProfile*> result; |
| result.reserve(profiles.size()); |
| for (const auto& profile : profiles) |
| result.push_back(profile.get()); |
| OrderProfiles(result, order); |
| return result; |
| } |
| |
| std::vector<AutofillProfile*> PersonalDataManager::GetServerProfiles() const { |
| std::vector<AutofillProfile*> result; |
| if (!IsAutofillProfileEnabled()) |
| return result; |
| result.reserve(credit_card_billing_addresses_.size()); |
| for (const auto& profile : credit_card_billing_addresses_) |
| result.push_back(profile.get()); |
| return result; |
| } |
| |
| std::vector<CreditCard*> PersonalDataManager::GetLocalCreditCards() const { |
| std::vector<CreditCard*> result; |
| result.reserve(local_credit_cards_.size()); |
| for (const auto& card : local_credit_cards_) |
| result.push_back(card.get()); |
| return result; |
| } |
| |
| std::vector<CreditCard*> PersonalDataManager::GetServerCreditCards() const { |
| std::vector<CreditCard*> result; |
| if (!IsAutofillWalletImportEnabled()) |
| return result; |
| |
| result.reserve(server_credit_cards_.size()); |
| for (const auto& card : server_credit_cards_) { |
| result.push_back(card.get()); |
| } |
| return result; |
| } |
| |
| std::vector<CreditCard*> PersonalDataManager::GetCreditCards() const { |
| std::vector<CreditCard*> result; |
| result.reserve(local_credit_cards_.size() + server_credit_cards_.size()); |
| for (const auto& card : local_credit_cards_) |
| result.push_back(card.get()); |
| if (IsAutofillWalletImportEnabled()) { |
| for (const auto& card : server_credit_cards_) { |
| result.push_back(card.get()); |
| } |
| } |
| return result; |
| } |
| |
| std::vector<IBAN*> PersonalDataManager::GetLocalIBANs() const { |
| std::vector<IBAN*> result; |
| result.reserve(local_ibans_.size()); |
| for (const auto& iban : local_ibans_) { |
| result.push_back(iban.get()); |
| } |
| return result; |
| } |
| |
| PaymentsCustomerData* PersonalDataManager::GetPaymentsCustomerData() const { |
| return payments_customer_data_ ? payments_customer_data_.get() : nullptr; |
| } |
| |
| std::vector<CreditCardCloudTokenData*> |
| PersonalDataManager::GetCreditCardCloudTokenData() const { |
| std::vector<CreditCardCloudTokenData*> result; |
| if (!IsAutofillWalletImportEnabled()) |
| return result; |
| |
| result.reserve(server_credit_card_cloud_token_data_.size()); |
| for (const auto& data : server_credit_card_cloud_token_data_) |
| result.push_back(data.get()); |
| return result; |
| } |
| |
| std::vector<AutofillOfferData*> PersonalDataManager::GetAutofillOffers() const { |
| if (!IsAutofillWalletImportEnabled() || !IsAutofillCreditCardEnabled()) |
| return {}; |
| |
| std::vector<AutofillOfferData*> result; |
| result.reserve(autofill_offer_data_.size()); |
| for (const auto& data : autofill_offer_data_) |
| result.push_back(data.get()); |
| return result; |
| } |
| |
| std::vector<const AutofillOfferData*> |
| PersonalDataManager::GetActiveAutofillPromoCodeOffersForOrigin( |
| GURL origin) const { |
| if (!IsAutofillWalletImportEnabled() || !IsAutofillCreditCardEnabled()) |
| return {}; |
| |
| std::vector<const AutofillOfferData*> promo_code_offers_for_origin; |
| base::ranges::for_each( |
| autofill_offer_data_, |
| [&](const std::unique_ptr<AutofillOfferData>& autofill_offer_data) { |
| if (autofill_offer_data.get()->IsPromoCodeOffer() && |
| autofill_offer_data.get()->IsActiveAndEligibleForOrigin(origin)) { |
| promo_code_offers_for_origin.push_back(autofill_offer_data.get()); |
| } |
| }); |
| return promo_code_offers_for_origin; |
| } |
| |
| gfx::Image* PersonalDataManager::GetCreditCardArtImageForUrl( |
| const GURL& card_art_url) const { |
| gfx::Image* cached_image = GetCachedCardArtImageForUrl(card_art_url); |
| if (cached_image) |
| return cached_image; |
| |
| FetchImagesForURLs(base::make_span(&card_art_url, 1u)); |
| return nullptr; |
| } |
| |
| gfx::Image* PersonalDataManager::GetCachedCardArtImageForUrl( |
| const GURL& card_art_url) const { |
| if (!IsAutofillWalletImportEnabled()) |
| return nullptr; |
| |
| if (!card_art_url.is_valid()) |
| return nullptr; |
| |
| auto images_iterator = credit_card_art_images_.find(card_art_url); |
| |
| // If the cache contains the image, return it. |
| if (images_iterator != credit_card_art_images_.end()) { |
| gfx::Image* image = images_iterator->second.get(); |
| if (!image->IsEmpty()) |
| return image; |
| } |
| |
| // The cache does not contain the image, return nullptr. |
| return nullptr; |
| } |
| |
| std::vector<VirtualCardUsageData*> |
| PersonalDataManager::GetVirtualCardUsageData() const { |
| if (!IsAutofillWalletImportEnabled() || !IsAutofillCreditCardEnabled()) { |
| return {}; |
| } |
| |
| std::vector<VirtualCardUsageData*> result; |
| result.reserve(autofill_virtual_card_usage_data_.size()); |
| for (const auto& data : autofill_virtual_card_usage_data_) { |
| result.push_back(data.get()); |
| } |
| return result; |
| } |
| |
| void PersonalDataManager::Refresh() { |
| LoadProfiles(); |
| LoadCreditCards(); |
| LoadCreditCardCloudTokenData(); |
| LoadIBANs(); |
| LoadPaymentsCustomerData(); |
| LoadUpiIds(); |
| LoadAutofillOffers(); |
| LoadVirtualCardUsageData(); |
| } |
| |
| std::vector<AutofillProfile*> PersonalDataManager::GetProfilesToSuggest() |
| const { |
| return IsAutofillProfileEnabled() |
| ? GetProfiles(ProfileOrder::kHighestFrecencyDesc) |
| : std::vector<AutofillProfile*>{}; |
| } |
| |
| std::vector<AutofillProfile*> PersonalDataManager::GetProfilesForSettings() |
| const { |
| return GetProfiles(ProfileOrder::kMostRecentlyModifiedDesc); |
| } |
| |
| std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions( |
| const AutofillType& type, |
| const std::u16string& field_contents, |
| bool field_is_autofilled, |
| const std::vector<ServerFieldType>& field_types) { |
| if (IsInAutofillSuggestionsDisabledExperiment()) |
| return std::vector<Suggestion>(); |
| |
| const AutofillProfileComparator comparator(app_locale_); |
| std::u16string field_contents_canon = |
| comparator.NormalizeForComparison(field_contents); |
| |
| // Get the profiles to suggest, which are already sorted. |
| std::vector<AutofillProfile*> sorted_profiles = GetProfilesToSuggest(); |
| |
| // When suggesting with no prefix to match, suppress disused address |
| // suggestions as well as those based on invalid profile data. |
| if (field_contents_canon.empty()) { |
| const base::Time min_last_used = |
| AutofillClock::Now() - kDisusedDataModelTimeDelta; |
| suggestion_selection::RemoveProfilesNotUsedSinceTimestamp(min_last_used, |
| &sorted_profiles); |
| } |
| |
| std::vector<AutofillProfile*> matched_profiles; |
| std::vector<Suggestion> suggestions = |
| suggestion_selection::GetPrefixMatchedSuggestions( |
| type, field_contents, field_contents_canon, comparator, |
| field_is_autofilled, sorted_profiles, &matched_profiles); |
| |
| // Don't show two suggestions if one is a subset of the other. |
| // Duplicates across sources are resolved in favour of `kAccount` profiles. |
| std::vector<AutofillProfile*> unique_matched_profiles; |
| std::vector<Suggestion> unique_suggestions = |
| suggestion_selection::GetUniqueSuggestions( |
| field_types, comparator, app_locale_, matched_profiles, suggestions, |
| &unique_matched_profiles); |
| |
| std::unique_ptr<LabelFormatter> formatter; |
| bool use_formatter; |
| |
| #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| use_formatter = base::FeatureList::IsEnabled( |
| features::kAutofillUseImprovedLabelDisambiguation); |
| #else |
| use_formatter = base::FeatureList::IsEnabled( |
| features::kAutofillUseMobileLabelDisambiguation); |
| #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| |
| // The formatter stores a constant reference to |unique_matched_profiles|. |
| // This is safe since the formatter is destroyed when this function returns. |
| formatter = use_formatter |
| ? LabelFormatter::Create(unique_matched_profiles, app_locale_, |
| type.GetStorableType(), field_types) |
| : nullptr; |
| |
| // Generate disambiguating labels based on the list of matches. |
| std::vector<std::u16string> labels; |
| if (formatter) { |
| labels = formatter->GetLabels(); |
| } else { |
| AutofillProfile::CreateInferredLabels(unique_matched_profiles, &field_types, |
| type.GetStorableType(), 1, |
| app_locale_, &labels); |
| } |
| |
| if (use_formatter && !unique_suggestions.empty()) { |
| AutofillMetrics::LogProfileSuggestionsMadeWithFormatter(formatter != |
| nullptr); |
| } |
| |
| suggestion_selection::PrepareSuggestions(labels, &unique_suggestions, |
| comparator); |
| |
| // We add an icon to the address (profile) suggestion if there is more than |
| // one profile related field in the form. Returns true if |type| is related to |
| // address profiles. |
| auto is_field_type_profile_related = [](ServerFieldType type) { |
| FieldTypeGroup group = AutofillType(type).group(); |
| return group == FieldTypeGroup::kName || |
| group == FieldTypeGroup::kAddressHome || |
| group == FieldTypeGroup::kPhoneHome || |
| group == FieldTypeGroup::kEmail; |
| }; |
| if (base::ranges::count_if(field_types, is_field_type_profile_related) > 1) { |
| for (auto& suggestion : unique_suggestions) { |
| suggestion.icon = "accountIcon"; |
| } |
| } |
| return unique_suggestions; |
| } |
| |
| const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest() |
| const { |
| if (!IsAutofillCreditCardEnabled()) |
| return std::vector<CreditCard*>{}; |
| |
| std::vector<CreditCard*> credit_cards; |
| if (ShouldSuggestServerCards()) { |
| credit_cards = GetCreditCards(); |
| } else { |
| credit_cards = GetLocalCreditCards(); |
| } |
| |
| std::list<CreditCard*> cards_to_dedupe(credit_cards.begin(), |
| credit_cards.end()); |
| |
| DedupeCreditCardToSuggest(&cards_to_dedupe); |
| |
| std::vector<CreditCard*> cards_to_suggest( |
| std::make_move_iterator(std::begin(cards_to_dedupe)), |
| std::make_move_iterator(std::end(cards_to_dedupe))); |
| |
| // Rank the cards by ranking score (see AutofillDataModel for details). All |
| // expired cards should be suggested last, also by ranking score. |
| base::Time comparison_time = AutofillClock::Now(); |
| if (cards_to_suggest.size() > 1) { |
| std::sort(cards_to_suggest.begin(), cards_to_suggest.end(), |
| [comparison_time](const CreditCard* a, const CreditCard* b) { |
| const bool a_is_expired = a->IsExpired(comparison_time); |
| if (a_is_expired != b->IsExpired(comparison_time)) { |
| return !a_is_expired; |
| } |
| |
| return a->HasGreaterRankingThan(b, comparison_time); |
| }); |
| } |
| |
| return cards_to_suggest; |
| } |
| |
| bool PersonalDataManager::IsAutofillEnabled() const { |
| return IsAutofillProfileEnabled() || IsAutofillCreditCardEnabled() || |
| IsAutofillIBANEnabled(); |
| } |
| |
| bool PersonalDataManager::IsAutofillProfileEnabled() const { |
| return prefs::IsAutofillProfileEnabled(pref_service_); |
| } |
| |
| bool PersonalDataManager::IsAutofillCreditCardEnabled() const { |
| return prefs::IsAutofillCreditCardEnabled(pref_service_); |
| } |
| |
| bool PersonalDataManager::IsAutofillHasSeenIbanPrefEnabled() const { |
| return prefs::HasSeenIban(pref_service_); |
| } |
| |
| void PersonalDataManager::SetAutofillHasSeenIban() { |
| prefs::SetAutofillHasSeenIban(pref_service_); |
| } |
| |
| bool PersonalDataManager::IsAutofillIBANEnabled() const { |
| return prefs::IsAutofillIBANEnabled(pref_service_); |
| } |
| |
| bool PersonalDataManager::IsAutofillWalletImportEnabled() const { |
| return prefs::IsPaymentsIntegrationEnabled(pref_service_); |
| } |
| |
| bool PersonalDataManager::ShouldSuggestServerCards() const { |
| if (!IsAutofillWalletImportEnabled()) |
| return false; |
| |
| if (is_syncing_for_test_) |
| return true; |
| |
| if (!sync_service_) |
| return false; |
| |
| // Check if the user is in sync transport mode for wallet data. |
| if (!sync_service_->IsSyncFeatureEnabled()) { |
| // For SyncTransport, only show server cards if the user has opted in to |
| // seeing them in the dropdown. |
| if (!prefs::IsUserOptedInWalletSyncTransport( |
| pref_service_, sync_service_->GetAccountInfo().account_id)) { |
| return false; |
| } |
| } |
| |
| // Server cards should be suggested if the sync service is active. |
| return sync_service_->GetActiveDataTypes().Has(syncer::AUTOFILL_WALLET_DATA); |
| } |
| |
| std::string PersonalDataManager::CountryCodeForCurrentTimezone() const { |
| return base::CountryCodeForCurrentTimezone(); |
| } |
| |
| void PersonalDataManager::SetPrefService(PrefService* pref_service) { |
| wallet_enabled_pref_ = std::make_unique<BooleanPrefMember>(); |
| profile_enabled_pref_ = std::make_unique<BooleanPrefMember>(); |
| credit_card_enabled_pref_ = std::make_unique<BooleanPrefMember>(); |
| pref_service_ = pref_service; |
| // |pref_service_| can be nullptr in tests. Using base::Unretained(this) is |
| // safe because observer instances are destroyed once |this| is destroyed. |
| if (pref_service_) { |
| credit_card_enabled_pref_->Init( |
| prefs::kAutofillCreditCardEnabled, pref_service_, |
| base::BindRepeating(&PersonalDataManager::EnableAutofillPrefChanged, |
| base::Unretained(this))); |
| profile_enabled_pref_->Init( |
| prefs::kAutofillProfileEnabled, pref_service_, |
| base::BindRepeating(&PersonalDataManager::EnableAutofillPrefChanged, |
| base::Unretained(this))); |
| wallet_enabled_pref_->Init( |
| prefs::kAutofillWalletImportEnabled, pref_service_, |
| base::BindRepeating( |
| &PersonalDataManager::EnableWalletIntegrationPrefChanged, |
| base::Unretained(this))); |
| } |
| } |
| |
| void PersonalDataManager::FetchImagesForURLs( |
| base::span<const GURL> updated_urls) const { |
| if (!image_fetcher_) |
| return; |
| |
| image_fetcher_->FetchImagesForURLs( |
| updated_urls, base::BindOnce(&PersonalDataManager::OnCardArtImagesFetched, |
| weak_factory_.GetMutableWeakPtr())); |
| } |
| |
| const std::vector<std::unique_ptr<AutofillProfile>>& |
| PersonalDataManager::GetProfileStorage(AutofillProfile::Source source) const { |
| switch (source) { |
| case AutofillProfile::Source::kLocalOrSyncable: |
| return synced_local_profiles_; |
| case AutofillProfile::Source::kAccount: |
| return account_profiles_; |
| } |
| NOTREACHED(); |
| } |
| |
| const std::string& PersonalDataManager::GetDefaultCountryCodeForNewAddress() |
| const { |
| if (default_country_code_.empty()) |
| default_country_code_ = MostCommonCountryCodeFromProfiles(); |
| |
| // Failing that, use the country code determined for experiment groups. |
| if (default_country_code_.empty()) |
| default_country_code_ = GetCountryCodeForExperimentGroup(); |
| |
| return default_country_code_; |
| } |
| |
| const std::string& PersonalDataManager::GetCountryCodeForExperimentGroup() |
| const { |
| // Set to |variations_country_code_| if it exists. |
| if (experiment_country_code_.empty()) |
| experiment_country_code_ = variations_country_code_; |
| |
| // Failing that, guess based on system timezone. |
| if (experiment_country_code_.empty()) |
| experiment_country_code_ = CountryCodeForCurrentTimezone(); |
| |
| // Failing that, guess based on locale. This returns "US" if there is no good |
| // guess. |
| if (experiment_country_code_.empty()) { |
| experiment_country_code_ = |
| AutofillCountry::CountryCodeForLocale(app_locale()); |
| } |
| |
| return experiment_country_code_; |
| } |
| |
| // The priority ranking for deduping a duplicate card is: |
| // 1. FULL_SERVER_CARD |
| // 2. LOCAL_CARD |
| // 3. MASKED_SERVER_CARD |
| // Note: 2 & 3 are swapped if experiment |
| // kAutofillSuggestServerCardInsteadOfLocalCard is enabled. |
| // static |
| void PersonalDataManager::DedupeCreditCardToSuggest( |
| std::list<CreditCard*>* cards_to_suggest) { |
| for (auto outer_it = cards_to_suggest->begin(); |
| outer_it != cards_to_suggest->end(); ++outer_it) { |
| for (auto inner_it = cards_to_suggest->begin(); |
| inner_it != cards_to_suggest->end();) { |
| auto inner_it_copy = inner_it++; |
| if (outer_it == inner_it_copy) { |
| continue; |
| } |
| // Check if the cards are local or server duplicate of each other. If yes, |
| // then check if we can dedupe/erase the duplicate card. |
| if ((*inner_it_copy)->IsLocalOrServerDuplicateOf(**outer_it) && |
| ShouldDedupeDuplicateCard(*outer_it, *inner_it_copy)) { |
| cards_to_suggest->erase(inner_it_copy); |
| } |
| } |
| } |
| } |
| |
| bool PersonalDataManager::IsCardPresentAsBothLocalAndServerCards( |
| const CreditCard& credit_card) { |
| for (CreditCard* card_from_list : GetCreditCards()) { |
| if (credit_card.IsLocalOrServerDuplicateOf(*card_from_list)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void PersonalDataManager::SetProfilesForAllSources( |
| std::vector<AutofillProfile>* new_profiles) { |
| if (is_off_the_record_ || !database_helper_->GetLocalDatabase()) |
| return; |
| |
| ClearOnGoingProfileChanges(); |
| |
| const auto split_point = base::ranges::partition( |
| *new_profiles, [](const AutofillProfile& profile) { |
| return profile.source() == AutofillProfile::Source::kLocalOrSyncable; |
| }); |
| bool change_happened = |
| SetProfilesForSource({new_profiles->begin(), split_point}, |
| AutofillProfile::Source::kLocalOrSyncable); |
| change_happened |= SetProfilesForSource({split_point, new_profiles->end()}, |
| AutofillProfile::Source::kAccount); |
| |
| if (!change_happened) { |
| // When a change happens (add, update, remove), we would consequently call |
| // `NotifyPersonalDataChanged()` which notifies the tests to stop waiting. |
| // Otherwise, we need to stop them by calling the function directly. |
| NotifyPersonalDataObserver(); |
| } |
| } |
| |
| bool PersonalDataManager::SetProfilesForSource( |
| base::span<const AutofillProfile> new_profiles, |
| AutofillProfile::Source source) { |
| DCHECK( |
| base::ranges::all_of(new_profiles, [&](const AutofillProfile& profile) { |
| return profile.source() == source; |
| })); |
| |
| // Means that a profile was added, removed or updated. |
| bool change_happened = false; |
| |
| std::vector<std::unique_ptr<AutofillProfile>>& profiles = |
| GetProfileStorage(source); |
| |
| // Any profiles that are not in the new profile list should be removed from |
| // the web database |
| for (const auto& it : profiles) { |
| if (!FindByGUID(new_profiles, it->guid())) { |
| RemoveProfileFromDB(it->guid()); |
| change_happened = true; |
| } |
| } |
| |
| // Update the web database with the new and existing profiles. |
| for (const AutofillProfile& it : new_profiles) { |
| const auto* existing_profile = GetProfileByGUID(it.guid()); |
| // In `SetProfilesForSource()`, exceptionally, profiles are directly added/ |
| // updated on the `profiles` before they are ready to be added or get |
| // updated in the database. Enforce the changes to make sure the database is |
| // also updated. |
| if (existing_profile) { |
| if (!existing_profile->EqualsForUpdatePurposes(it)) { |
| UpdateProfileInDB(it, /*enforced=*/true); |
| change_happened = true; |
| } |
| } else if (!FindByContents(profiles, it)) { |
| AddProfileToDB(it, /*enforced=*/true); |
| change_happened = true; |
| } |
| } |
| |
| if (change_happened) { |
| // Copy in the new profiles. |
| profiles.clear(); |
| for (const AutofillProfile& it : new_profiles) { |
| profiles.push_back(std::make_unique<AutofillProfile>(it)); |
| } |
| } |
| return change_happened; |
| } |
| |
| bool PersonalDataManager::IsProfileMigrationBlocked( |
| const std::string& guid) const { |
| AutofillProfile* profile = GetProfileByGUID(guid); |
| DCHECK(profile == nullptr || |
| profile->source() == AutofillProfile::Source::kLocalOrSyncable); |
| if (!GetProfileMigrationStrikeDatabase()) { |
| return false; |
| } |
| return GetProfileMigrationStrikeDatabase()->ShouldBlockFeature(guid); |
| } |
| |
| void PersonalDataManager::AddStrikeToBlockProfileMigration( |
| const std::string& guid) { |
| if (!GetProfileMigrationStrikeDatabase()) { |
| return; |
| } |
| GetProfileMigrationStrikeDatabase()->AddStrike(guid); |
| } |
| |
| void PersonalDataManager::AddMaxStrikesToBlockProfileMigration( |
| const std::string& guid) { |
| if (AutofillProfileMigrationStrikeDatabase* db = |
| GetProfileMigrationStrikeDatabase()) { |
| db->AddStrikes(db->GetMaxStrikesLimit() - db->GetStrikes(guid), guid); |
| } |
| } |
| |
| void PersonalDataManager::RemoveStrikesToBlockProfileMigration( |
| const std::string& guid) { |
| if (!GetProfileMigrationStrikeDatabase()) { |
| return; |
| } |
| GetProfileMigrationStrikeDatabase()->ClearStrikes(guid); |
| } |
| |
| bool PersonalDataManager::IsNewProfileImportBlockedForDomain( |
| const GURL& url) const { |
| if (!GetProfileSaveStrikeDatabase() || !url.is_valid() || !url.has_host()) { |
| return false; |
| } |
| |
| return GetProfileSaveStrikeDatabase()->ShouldBlockFeature(url.host()); |
| } |
| |
| void PersonalDataManager::AddStrikeToBlockNewProfileImportForDomain( |
| const GURL& url) { |
| if (!GetProfileSaveStrikeDatabase() || !url.is_valid() || !url.has_host()) { |
| return; |
| } |
| GetProfileSaveStrikeDatabase()->AddStrike(url.host()); |
| } |
| |
| void PersonalDataManager::RemoveStrikesToBlockNewProfileImportForDomain( |
| const GURL& url) { |
| if (!GetProfileSaveStrikeDatabase() || !url.is_valid() || !url.has_host()) { |
| return; |
| } |
| GetProfileSaveStrikeDatabase()->ClearStrikes(url.host()); |
| } |
| |
| bool PersonalDataManager::IsProfileUpdateBlocked( |
| const std::string& guid) const { |
| if (!GetProfileUpdateStrikeDatabase()) { |
| return false; |
| } |
| return GetProfileUpdateStrikeDatabase()->ShouldBlockFeature(guid); |
| } |
| |
| void PersonalDataManager::AddStrikeToBlockProfileUpdate( |
| const std::string& guid) { |
| if (!GetProfileUpdateStrikeDatabase()) |
| return; |
| GetProfileUpdateStrikeDatabase()->AddStrike(guid); |
| } |
| |
| void PersonalDataManager::RemoveStrikesToBlockProfileUpdate( |
| const std::string& guid) { |
| if (!GetProfileUpdateStrikeDatabase()) { |
| return; |
| } |
| GetProfileUpdateStrikeDatabase()->ClearStrikes(guid); |
| } |
| |
| bool PersonalDataManager::IsSyncEnabledFor( |
| syncer::UserSelectableType data_type) const { |
| return sync_service_ != nullptr && sync_service_->IsSyncFeatureEnabled() && |
| sync_service_->GetUserSettings()->GetSelectedTypes().Has(data_type); |
| } |
| |
| void PersonalDataManager::SetPaymentMethodsMandatoryReauthEnabled( |
| bool enabled) { |
| prefs::SetPaymentMethodsMandatoryReauthEnabled(pref_service_, enabled); |
| } |
| |
| bool PersonalDataManager::IsPaymentMethodsMandatoryReauthEnabled() { |
| return prefs::IsPaymentMethodsMandatoryReauthEnabled(pref_service_); |
| } |
| |
| bool PersonalDataManager::ShouldShowPaymentMethodsMandatoryReauthPromo() { |
| return prefs::ShouldShowPaymentMethodsMandatoryReauthPromo(pref_service_); |
| } |
| |
| void PersonalDataManager:: |
| IncrementPaymentMethodsMandatoryReauthPromoShownCounter() { |
| prefs::IncrementPaymentMethodsMandatoryReauthPromoShownCounter(pref_service_); |
| } |
| |
| AutofillProfileMigrationStrikeDatabase* |
| PersonalDataManager::GetProfileMigrationStrikeDatabase() { |
| return const_cast<AutofillProfileMigrationStrikeDatabase*>( |
| std::as_const(*this).GetProfileMigrationStrikeDatabase()); |
| } |
| |
| const AutofillProfileMigrationStrikeDatabase* |
| PersonalDataManager::GetProfileMigrationStrikeDatabase() const { |
| return profile_migration_strike_database_.get(); |
| } |
| |
| AutofillProfileSaveStrikeDatabase* |
| PersonalDataManager::GetProfileSaveStrikeDatabase() { |
| return const_cast<AutofillProfileSaveStrikeDatabase*>( |
| std::as_const(*this).GetProfileSaveStrikeDatabase()); |
| } |
| |
| const AutofillProfileSaveStrikeDatabase* |
| PersonalDataManager::GetProfileSaveStrikeDatabase() const { |
| return profile_save_strike_database_.get(); |
| } |
| |
| AutofillProfileUpdateStrikeDatabase* |
| PersonalDataManager::GetProfileUpdateStrikeDatabase() { |
| return const_cast<AutofillProfileUpdateStrikeDatabase*>( |
| std::as_const(*this).GetProfileUpdateStrikeDatabase()); |
| } |
| |
| const AutofillProfileUpdateStrikeDatabase* |
| PersonalDataManager::GetProfileUpdateStrikeDatabase() const { |
| return profile_update_strike_database_.get(); |
| } |
| |
| void PersonalDataManager::SetCreditCards( |
| std::vector<CreditCard>* credit_cards) { |
| if (is_off_the_record_) |
| return; |
| |
| // Remove empty credit cards from input. |
| base::EraseIf(*credit_cards, [this](const CreditCard& credit_card) { |
| return credit_card.IsEmpty(app_locale_); |
| }); |
| |
| if (!database_helper_->GetLocalDatabase()) |
| return; |
| |
| // Any credit cards that are not in the new credit card list should be |
| // removed. |
| for (const auto& card : local_credit_cards_) { |
| if (!FindByGUID(*credit_cards, card->guid())) |
| database_helper_->GetLocalDatabase()->RemoveCreditCard(card->guid()); |
| } |
| |
| // Update the web database with the existing credit cards. |
| for (const CreditCard& card : *credit_cards) { |
| if (FindByGUID(local_credit_cards_, card.guid())) |
| database_helper_->GetLocalDatabase()->UpdateCreditCard(card); |
| } |
| |
| // Add the new credit cards to the web database. Don't add a duplicate. |
| for (const CreditCard& card : *credit_cards) { |
| if (!FindByGUID(local_credit_cards_, card.guid()) && |
| !FindByContents(local_credit_cards_, card)) |
| database_helper_->GetLocalDatabase()->AddCreditCard(card); |
| } |
| |
| // Copy in the new credit cards. |
| local_credit_cards_.clear(); |
| for (const CreditCard& card : *credit_cards) |
| local_credit_cards_.push_back(std::make_unique<CreditCard>(card)); |
| |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| } |
| |
| void PersonalDataManager::LoadProfiles() { |
| if (!database_helper_->GetLocalDatabase()) { |
| NOTREACHED(); |
| return; |
| } |
| |
| CancelPendingLocalQuery(&pending_synced_local_profiles_query_); |
| CancelPendingLocalQuery(&pending_account_profiles_query_); |
| CancelPendingServerQuery(&pending_creditcard_billing_addresses_query_); |
| |
| pending_synced_local_profiles_query_ = |
| database_helper_->GetLocalDatabase()->GetAutofillProfiles( |
| AutofillProfile::Source::kLocalOrSyncable, this); |
| if (base::FeatureList::IsEnabled( |
| features::kAutofillAccountProfilesUnionView)) { |
| pending_account_profiles_query_ = |
| database_helper_->GetLocalDatabase()->GetAutofillProfiles( |
| AutofillProfile::Source::kAccount, this); |
| } |
| if (database_helper_->GetServerDatabase()) { |
| pending_creditcard_billing_addresses_query_ = |
| database_helper_->GetServerDatabase()->GetServerProfiles(this); |
| } |
| } |
| |
| void PersonalDataManager::LoadCreditCards() { |
| if (!database_helper_->GetLocalDatabase()) { |
| NOTREACHED(); |
| return; |
| } |
| |
| CancelPendingLocalQuery(&pending_creditcards_query_); |
| CancelPendingServerQuery(&pending_server_creditcards_query_); |
| |
| pending_creditcards_query_ = |
| database_helper_->GetLocalDatabase()->GetCreditCards(this); |
| if (database_helper_->GetServerDatabase()) { |
| pending_server_creditcards_query_ = |
| database_helper_->GetServerDatabase()->GetServerCreditCards(this); |
| } |
| } |
| |
| void PersonalDataManager::LoadCreditCardCloudTokenData() { |
| if (!database_helper_->GetServerDatabase()) |
| return; |
| |
| CancelPendingServerQuery(&pending_server_creditcard_cloud_token_data_query_); |
| |
| pending_server_creditcard_cloud_token_data_query_ = |
| database_helper_->GetServerDatabase()->GetCreditCardCloudTokenData(this); |
| } |
| |
| void PersonalDataManager::LoadIBANs() { |
| if (!database_helper_->GetLocalDatabase()) { |
| NOTREACHED(); |
| return; |
| } |
| |
| CancelPendingLocalQuery(&pending_ibans_query_); |
| |
| pending_ibans_query_ = database_helper_->GetLocalDatabase()->GetIBANs(this); |
| } |
| |
| void PersonalDataManager::LoadUpiIds() { |
| if (!database_helper_->GetLocalDatabase()) { |
| NOTREACHED(); |
| return; |
| } |
| |
| CancelPendingLocalQuery(&pending_upi_ids_query_); |
| |
| pending_upi_ids_query_ = |
| database_helper_->GetLocalDatabase()->GetAllUpiIds(this); |
| } |
| |
| void PersonalDataManager::LoadAutofillOffers() { |
| if (!database_helper_->GetServerDatabase()) |
| return; |
| |
| CancelPendingServerQuery(&pending_offer_data_query_); |
| |
| pending_offer_data_query_ = |
| database_helper_->GetServerDatabase()->GetAutofillOffers(this); |
| } |
| |
| void PersonalDataManager::LoadVirtualCardUsageData() { |
| if (!database_helper_->GetServerDatabase()) { |
| return; |
| } |
| |
| CancelPendingServerQuery(&pending_virtual_card_usage_data_query_); |
| |
| pending_virtual_card_usage_data_query_ = |
| database_helper_->GetServerDatabase()->GetVirtualCardUsageData(this); |
| } |
| |
| void PersonalDataManager::CancelPendingLocalQuery( |
| WebDataServiceBase::Handle* handle) { |
| if (*handle) { |
| if (!database_helper_->GetLocalDatabase()) { |
| NOTREACHED(); |
| return; |
| } |
| database_helper_->GetLocalDatabase()->CancelRequest(*handle); |
| } |
| *handle = 0; |
| } |
| |
| void PersonalDataManager::CancelPendingServerQuery( |
| WebDataServiceBase::Handle* handle) { |
| if (*handle) { |
| if (!database_helper_->GetServerDatabase()) { |
| NOTREACHED(); |
| return; |
| } |
| database_helper_->GetServerDatabase()->CancelRequest(*handle); |
| } |
| *handle = 0; |
| } |
| |
| void PersonalDataManager::CancelPendingServerQueries() { |
| CancelPendingServerQuery(&pending_creditcard_billing_addresses_query_); |
| CancelPendingServerQuery(&pending_server_creditcards_query_); |
| CancelPendingServerQuery(&pending_customer_data_query_); |
| CancelPendingServerQuery(&pending_server_creditcard_cloud_token_data_query_); |
| CancelPendingServerQuery(&pending_offer_data_query_); |
| CancelPendingServerQuery(&pending_virtual_card_usage_data_query_); |
| } |
| |
| void PersonalDataManager::LoadPaymentsCustomerData() { |
| if (!database_helper_->GetServerDatabase()) |
| return; |
| |
| CancelPendingServerQuery(&pending_customer_data_query_); |
| |
| pending_customer_data_query_ = |
| database_helper_->GetServerDatabase()->GetPaymentsCustomerData(this); |
| } |
| |
| std::string PersonalDataManager::SaveImportedProfile( |
| const AutofillProfile& imported_profile) { |
| if (is_off_the_record_) |
| return std::string(); |
| |
| std::vector<AutofillProfile> profiles; |
| std::string guid = AutofillProfileComparator::MergeProfile( |
| imported_profile, GetProfileStorage(imported_profile.source()), |
| app_locale_, &profiles); |
| // Keep profiles from other sources. `SetProfilesForSource()` cannot be used, |
| // since it doesn't notify observers. |
| for (AutofillProfile* profile : GetProfiles()) { |
| if (profile->source() != imported_profile.source()) { |
| profiles.push_back(*profile); |
| } |
| } |
| SetProfilesForAllSources(&profiles); |
| return guid; |
| } |
| |
| std::string PersonalDataManager::OnAcceptedLocalCreditCardSave( |
| const CreditCard& imported_card) { |
| DCHECK(!imported_card.number().empty()); |
| if (is_off_the_record_) |
| return std::string(); |
| |
| return SaveImportedCreditCard(imported_card); |
| } |
| |
| std::string PersonalDataManager::OnAcceptedLocalIBANSave(IBAN& imported_iban) { |
| DCHECK(!imported_iban.value().empty()); |
| if (is_off_the_record_) |
| return std::string(); |
| |
| return SaveImportedIBAN(imported_iban); |
| } |
| |
| void PersonalDataManager::SetSyncService(syncer::SyncService* sync_service) { |
| CHECK(!sync_service_); |
| |
| sync_service_ = sync_service; |
| if (sync_service_) { |
| sync_service_->AddObserver(this); |
| } |
| |
| // Re-mask all server cards if the upload state is not active. |
| const bool is_upload_not_active = |
| syncer::GetUploadToGoogleState(sync_service_, |
| syncer::ModelType::AUTOFILL_WALLET_DATA) == |
| syncer::UploadState::NOT_ACTIVE; |
| if (is_upload_not_active) { |
| ResetFullServerCards(); |
| } |
| |
| OnStateChanged(sync_service_); |
| } |
| |
| std::string PersonalDataManager::SaveImportedCreditCard( |
| const CreditCard& imported_card) { |
| // Set to true if |imported_card| is merged into the credit card list. |
| bool merged = false; |
| |
| std::string guid = imported_card.guid(); |
| std::vector<CreditCard> credit_cards; |
| for (auto& card : local_credit_cards_) { |
| // If |imported_card| has not yet been merged, check whether it should be |
| // with the current |card|. |
| if (!merged && card->UpdateFromImportedCard(imported_card, app_locale_)) { |
| guid = card->guid(); |
| merged = true; |
| } |
| |
| credit_cards.push_back(*card); |
| } |
| |
| if (!merged) |
| credit_cards.push_back(imported_card); |
| |
| SetCreditCards(&credit_cards); |
| |
| // After a card is saved locally, notifies the observers. |
| OnCreditCardSaved(/*is_local_card=*/true); |
| |
| return guid; |
| } |
| |
| std::string PersonalDataManager::SaveImportedIBAN(IBAN& imported_iban) { |
| // If an existing IBAN is found, call `UpdateIBAN()`, otherwise, `AddIBAN()`. |
| // `local_ibans_` will be in sync with the local web database as of |
| // `Refresh()` which will be called by both `UpdateIBAN()` and `AddIBAN()`. |
| for (auto& iban : local_ibans_) { |
| if (iban->value().compare(imported_iban.value()) == 0) { |
| // Set the GUID of the IBAN to the one that matches it in |
| // `local_ibans_` so that UpdateIBAN() will be able to update the |
| // specific IBAN. |
| imported_iban.set_guid(iban->guid()); |
| return UpdateIBAN(imported_iban); |
| } |
| } |
| return AddIBAN(imported_iban); |
| } |
| |
| void PersonalDataManager::LogStoredDataMetrics() const { |
| if (has_logged_stored_data_metrics_) { |
| return; |
| } |
| // Only log this info once per Chrome user profile load. |
| has_logged_stored_data_metrics_ = true; |
| |
| const std::vector<AutofillProfile*> profiles = GetProfiles(); |
| autofill_metrics::LogStoredProfileMetrics(profiles); |
| if (base::FeatureList::IsEnabled( |
| features::kAutofillAccountProfilesUnionView) && |
| base::FeatureList::IsEnabled(features::kAutofillAccountProfileStorage)) { |
| autofill_metrics::LogLocalProfileSupersetMetrics(std::move(profiles), |
| app_locale_); |
| } |
| |
| AutofillMetrics::LogStoredCreditCardMetrics( |
| local_credit_cards_, server_credit_cards_, |
| GetServerCardWithArtImageCount(), kDisusedDataModelTimeDelta); |
| autofill_metrics::LogStoredIbanMetrics(local_ibans_, |
| kDisusedDataModelTimeDelta); |
| autofill_metrics::LogStoredOfferMetrics(autofill_offer_data_); |
| autofill_metrics::LogStoredVirtualCardUsageCount( |
| autofill_virtual_card_usage_data_.size()); |
| } |
| |
| std::string PersonalDataManager::MostCommonCountryCodeFromProfiles() const { |
| if (!IsAutofillEnabled()) |
| return std::string(); |
| |
| // Count up country codes from existing profiles. |
| std::map<std::string, int> votes; |
| const std::vector<AutofillProfile*>& profiles = GetProfiles(); |
| const std::vector<std::string>& country_codes = |
| CountryDataMap::GetInstance()->country_codes(); |
| for (auto* profile : profiles) { |
| std::string country_code = base::ToUpperASCII( |
| base::UTF16ToASCII(profile->GetRawInfo(ADDRESS_HOME_COUNTRY))); |
| if (base::Contains(country_codes, country_code)) { |
| votes[country_code]++; |
| } |
| } |
| |
| // Take the most common country code. |
| if (!votes.empty()) { |
| auto iter = std::max_element(votes.begin(), votes.end(), CompareVotes); |
| return iter->first; |
| } |
| |
| return std::string(); |
| } |
| |
| void PersonalDataManager::EnableWalletIntegrationPrefChanged() { |
| if (!prefs::IsPaymentsIntegrationEnabled(pref_service_)) { |
| // Re-mask all server cards when the user turns off wallet card |
| // integration. |
| ResetFullServerCards(); |
| NotifyPersonalDataObserver(); |
| } |
| } |
| |
| void PersonalDataManager::EnableAutofillPrefChanged() { |
| default_country_code_.clear(); |
| |
| // Refresh our local cache and send notifications to observers. |
| Refresh(); |
| } |
| |
| bool PersonalDataManager::IsKnownCard(const CreditCard& credit_card) const { |
| const auto stripped_pan = CreditCard::StripSeparators(credit_card.number()); |
| for (const auto& card : local_credit_cards_) { |
| if (stripped_pan == CreditCard::StripSeparators(card->number())) |
| return true; |
| } |
| |
| const auto masked_info = credit_card.NetworkAndLastFourDigits(); |
| for (const auto& card : server_credit_cards_) { |
| switch (card->record_type()) { |
| case CreditCard::FULL_SERVER_CARD: |
| if (stripped_pan == CreditCard::StripSeparators(card->number())) |
| return true; |
| break; |
| case CreditCard::MASKED_SERVER_CARD: |
| if (masked_info == card->NetworkAndLastFourDigits()) |
| return true; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| return false; |
| } |
| |
| bool PersonalDataManager::IsServerCard(const CreditCard* credit_card) const { |
| // Check whether the current card itself is a server card. |
| if (credit_card->record_type() != CreditCard::LOCAL_CARD) |
| return true; |
| |
| std::vector<CreditCard*> server_credit_cards = GetServerCreditCards(); |
| // Check whether the current card is already uploaded. |
| for (const CreditCard* server_card : server_credit_cards) { |
| if (credit_card->MatchingCardDetails(*server_card)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool PersonalDataManager::ShouldShowCardsFromAccountOption() const { |
| // The feature is only for Linux, Windows, Mac, and Fuchsia. |
| // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch |
| // of lacros-chrome is complete. |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) || \ |
| BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA) |
| // This option should only be shown for users that have not enabled the Sync |
| // Feature and that have server credit cards available. |
| if (!sync_service_ || sync_service_->IsSyncFeatureEnabled() || |
| GetServerCreditCards().empty()) { |
| return false; |
| } |
| |
| bool is_opted_in = prefs::IsUserOptedInWalletSyncTransport( |
| pref_service_, sync_service_->GetAccountInfo().account_id); |
| |
| // The option should only be shown if the user has not already opted-in. |
| return !is_opted_in; |
| #else |
| return false; |
| #endif // #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) || |
| // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA) |
| } |
| |
| void PersonalDataManager::OnUserAcceptedCardsFromAccountOption() { |
| DCHECK_EQ(AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled, |
| GetSyncSigninState()); |
| prefs::SetUserOptedInWalletSyncTransport( |
| pref_service_, sync_service_->GetAccountInfo().account_id, |
| /*opted_in=*/true); |
| } |
| |
| void PersonalDataManager::OnAutofillProfileChanged( |
| const AutofillProfileDeepChange& change) { |
| const auto& guid = change.key(); |
| const auto& change_type = change.type(); |
| const auto& profile = *(change.profile()); |
| DCHECK(guid == profile.guid()); |
| // Happens only in tests. |
| if (!ProfileChangesAreOngoing(guid)) { |
| DVLOG(1) << "Received an unexpected response from database."; |
| return; |
| } |
| |
| std::vector<std::unique_ptr<AutofillProfile>>& profiles = |
| GetProfileStorage(profile.source()); |
| const auto* existing_profile = GetProfileByGUID(guid); |
| const bool profile_exists = (existing_profile != nullptr); |
| switch (change_type) { |
| case AutofillProfileChange::ADD: |
| if (!profile_exists && !FindByContents(profiles, profile)) { |
| profiles.push_back(std::make_unique<AutofillProfile>(profile)); |
| } |
| break; |
| case AutofillProfileChange::UPDATE: |
| if (profile_exists && |
| (change.enforced() || |
| !existing_profile->EqualsForUpdatePurposes(profile))) { |
| profiles.erase(FindElementByGUID(profiles, guid)); |
| profiles.push_back(std::make_unique<AutofillProfile>(profile)); |
| } |
| break; |
| case AutofillProfileChange::REMOVE: |
| if (profile_exists) { |
| profiles.erase(FindElementByGUID(profiles, guid)); |
| } |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| OnProfileChangeDone(guid); |
| } |
| |
| void PersonalDataManager::OnCardArtImagesFetched( |
| const std::vector<std::unique_ptr<CreditCardArtImage>>& art_images) { |
| for (auto& art_image : art_images) { |
| if (!art_image->card_art_image.IsEmpty()) { |
| credit_card_art_images_[art_image->card_art_url] = |
| std::make_unique<gfx::Image>(art_image->card_art_image); |
| } |
| } |
| } |
| |
| void PersonalDataManager::LogServerCardLinkClicked() const { |
| AutofillMetrics::LogServerCardLinkClicked(GetSyncSigninState()); |
| } |
| |
| void PersonalDataManager::OnUserAcceptedUpstreamOffer() { |
| // If the user is in sync transport mode for Wallet, record an opt-in. |
| if (GetSyncSigninState() == |
| AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled) { |
| prefs::SetUserOptedInWalletSyncTransport( |
| pref_service_, sync_service_->GetAccountInfo().account_id, |
| /*opted_in=*/true); |
| } |
| } |
| |
| void PersonalDataManager::NotifyPersonalDataObserver() { |
| bool profile_changes_are_ongoing = ProfileChangesAreOngoing(); |
| for (PersonalDataManagerObserver& observer : observers_) { |
| observer.OnPersonalDataChanged(); |
| } |
| if (!profile_changes_are_ongoing) { |
| // Call OnPersonalDataFinishedProfileTasks in a separate loop as |
| // the observers might have removed themselves in OnPersonalDataChanged |
| for (PersonalDataManagerObserver& observer : observers_) { |
| observer.OnPersonalDataFinishedProfileTasks(); |
| } |
| } |
| } |
| |
| void PersonalDataManager::OnCreditCardSaved(bool is_local_card) {} |
| |
| void PersonalDataManager::ConvertWalletAddressesAndUpdateWalletCards() { |
| // If the full Sync feature isn't enabled, then do NOT convert any Wallet |
| // addresses to local ones. |
| // When syncing of account profiles is enabled, converting wallet addresses |
| // is unnecessary, since they are available through the ContactInfoSyncBridge. |
| if (!IsSyncFeatureEnabled() || |
| base::FeatureList::IsEnabled( |
| features::kAutofillAccountProfilesUnionView)) { |
| // PDM expects that each call to |
| // ConvertWalletAddressesAndUpdateWalletCards() is followed by a |
| // AutofillAddressConversionCompleted() notification, simulate the |
| // notification here. |
| AutofillAddressConversionCompleted(); |
| return; |
| } |
| |
| database_helper_->GetServerDatabase() |
| ->ConvertWalletAddressesAndUpdateWalletCards( |
| app_locale_, GetAccountInfoForPaymentsServer().email); |
| } |
| |
| void PersonalDataManager::AddProfileToDB(const AutofillProfile& profile, |
| bool enforced) { |
| if (profile.IsEmpty(app_locale_)) { |
| NotifyPersonalDataObserver(); |
| return; |
| } |
| |
| if (!ProfileChangesAreOngoing(profile.guid())) { |
| const std::vector<std::unique_ptr<AutofillProfile>>& profiles = |
| GetProfileStorage(profile.source()); |
| if (!enforced && (FindByGUID(profiles, profile.guid()) || |
| FindByContents(profiles, profile))) { |
| NotifyPersonalDataObserver(); |
| return; |
| } |
| } |
| ongoing_profile_changes_[profile.guid()].push_back( |
| AutofillProfileDeepChange(AutofillProfileChange::ADD, profile)); |
| if (enforced) |
| ongoing_profile_changes_[profile.guid()].back().set_enforced(); |
| HandleNextProfileChange(profile.guid()); |
| } |
| |
| void PersonalDataManager::UpdateProfileInDB(const AutofillProfile& profile, |
| bool enforced) { |
| // if the update is enforced, don't check if a similar profile already exists |
| // or not. Otherwise, check if updating the profile makes sense. |
| if (!enforced && !ProfileChangesAreOngoing(profile.guid())) { |
| const auto* existing_profile = GetProfileByGUID(profile.guid()); |
| bool profile_exists = (existing_profile != nullptr); |
| if (!profile_exists || existing_profile->EqualsForUpdatePurposes(profile)) { |
| NotifyPersonalDataObserver(); |
| return; |
| } |
| } |
| |
| ongoing_profile_changes_[profile.guid()].push_back( |
| AutofillProfileDeepChange(AutofillProfileChange::UPDATE, profile)); |
| if (enforced) |
| ongoing_profile_changes_[profile.guid()].back().set_enforced(); |
| HandleNextProfileChange(profile.guid()); |
| } |
| |
| void PersonalDataManager::RemoveProfileFromDB(const std::string& guid) { |
| // Find the profile to remove. Since `ongoing_profile_changes_` returns a |
| // `const AutofillProfile*`, this logic is in a separate lambda. |
| const AutofillProfile* profile = [&]() -> const AutofillProfile* { |
| if (AutofillProfile* profile = GetProfileByGUID(guid)) |
| return profile; |
| if (ProfileChangesAreOngoing(guid)) |
| return ongoing_profile_changes_[guid].back().profile(); |
| return nullptr; |
| }(); |
| if (!profile) { |
| NotifyPersonalDataObserver(); |
| return; |
| } |
| AutofillProfileDeepChange change(AutofillProfileChange::REMOVE, *profile); |
| if (!ProfileChangesAreOngoing(guid)) { |
| database_helper_->GetLocalDatabase()->RemoveAutofillProfile( |
| guid, profile->source()); |
| change.set_is_ongoing_on_background(); |
| } |
| ongoing_profile_changes_[guid].push_back(std::move(change)); |
| } |
| |
| void PersonalDataManager::HandleNextProfileChange(const std::string& guid) { |
| if (!ProfileChangesAreOngoing(guid)) |
| return; |
| |
| const auto& change = ongoing_profile_changes_[guid].front(); |
| if (change.is_ongoing_on_background()) |
| return; |
| |
| const auto& change_type = change.type(); |
| const auto* existing_profile = GetProfileByGUID(guid); |
| const bool profile_exists = (existing_profile != nullptr); |
| const auto& profile = *(ongoing_profile_changes_[guid].front().profile()); |
| |
| DCHECK(guid == profile.guid()); |
| |
| if (change_type == AutofillProfileChange::REMOVE) { |
| if (!profile_exists) { |
| OnProfileChangeDone(guid); |
| return; |
| } |
| database_helper_->GetLocalDatabase()->RemoveAutofillProfile( |
| guid, existing_profile->source()); |
| change.set_is_ongoing_on_background(); |
| return; |
| } |
| |
| if (change_type == AutofillProfileChange::ADD) { |
| const std::vector<std::unique_ptr<AutofillProfile>>& profiles = |
| GetProfileStorage(profile.source()); |
| if (!change.enforced() && |
| (profile_exists || FindByContents(profiles, profile))) { |
| OnProfileChangeDone(guid); |
| return; |
| } |
| database_helper_->GetLocalDatabase()->AddAutofillProfile(profile); |
| change.set_is_ongoing_on_background(); |
| return; |
| } |
| |
| if (profile_exists && (change.enforced() || |
| !existing_profile->EqualsForUpdatePurposes(profile))) { |
| database_helper_->GetLocalDatabase()->UpdateAutofillProfile(profile); |
| change.set_is_ongoing_on_background(); |
| } else { |
| OnProfileChangeDone(guid); |
| } |
| } |
| |
| bool PersonalDataManager::ProfileChangesAreOngoing(const std::string& guid) { |
| return ongoing_profile_changes_.find(guid) != |
| ongoing_profile_changes_.end() && |
| !ongoing_profile_changes_[guid].empty(); |
| } |
| |
| bool PersonalDataManager::ProfileChangesAreOngoing() { |
| for (const auto& [guid, change] : ongoing_profile_changes_) { |
| if (ProfileChangesAreOngoing(guid)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void PersonalDataManager::OnProfileChangeDone(const std::string& guid) { |
| ongoing_profile_changes_[guid].pop_front(); |
| |
| if (!ProfileChangesAreOngoing()) { |
| Refresh(); |
| } else { |
| NotifyPersonalDataObserver(); |
| HandleNextProfileChange(guid); |
| } |
| } |
| |
| void PersonalDataManager::ClearOnGoingProfileChanges() { |
| ongoing_profile_changes_.clear(); |
| } |
| |
| bool PersonalDataManager::HasPendingQueries() { |
| return pending_synced_local_profiles_query_ != 0 || |
| pending_account_profiles_query_ != 0 || |
| pending_creditcards_query_ != 0 || |
| pending_creditcard_billing_addresses_query_ != 0 || |
| pending_server_creditcards_query_ != 0 || |
| pending_server_creditcard_cloud_token_data_query_ != 0 || |
| pending_customer_data_query_ != 0 || pending_upi_ids_query_ != 0 || |
| pending_offer_data_query_ != 0 || |
| pending_virtual_card_usage_data_query_ != 0; |
| } |
| |
| scoped_refptr<AutofillWebDataService> PersonalDataManager::GetLocalDatabase() { |
| DCHECK(database_helper_); |
| return database_helper_->GetLocalDatabase(); |
| } |
| |
| void PersonalDataManager::OnServerCreditCardsRefreshed() { |
| ProcessCardArtUrlChanges(); |
| } |
| |
| void PersonalDataManager::ProcessCardArtUrlChanges() { |
| std::vector<GURL> updated_urls; |
| for (auto& card : server_credit_cards_) { |
| if (!card->card_art_url().is_valid()) |
| continue; |
| |
| // Try to find the old entry with the same url. |
| auto it = credit_card_art_images_.find(card->card_art_url()); |
| // No existing entry found. |
| if (it == credit_card_art_images_.end()) |
| updated_urls.emplace_back(card->card_art_url()); |
| } |
| if (!updated_urls.empty()) |
| FetchImagesForURLs(updated_urls); |
| } |
| |
| size_t PersonalDataManager::GetServerCardWithArtImageCount() const { |
| return base::ranges::count_if( |
| server_credit_cards_.begin(), server_credit_cards_.end(), |
| [](const auto& card) { return card->card_art_url().is_valid(); }); |
| } |
| |
| } // namespace autofill |