// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/autofill/personal_data_manager.h" #include #include #include "base/logging.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill-inl.h" #include "chrome/browser/autofill/autofill_field.h" #include "chrome/browser/autofill/autofill_metrics.h" #include "chrome/browser/autofill/autofill_regexes.h" #include "chrome/browser/autofill/form_structure.h" #include "chrome/browser/autofill/personal_data_manager_observer.h" #include "chrome/browser/autofill/phone_number.h" #include "chrome/browser/autofill/phone_number_i18n.h" #include "chrome/browser/autofill/select_control_handler.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/webdata/web_data_service.h" #include "chrome/common/pref_names.h" #include "content/browser/browser_thread.h" namespace { template class FormGroupMatchesByGUIDFunctor { public: explicit FormGroupMatchesByGUIDFunctor(const std::string& guid) : guid_(guid) { } bool operator()(const T& form_group) { return form_group.guid() == guid_; } bool operator()(const T* form_group) { return form_group->guid() == guid_; } private: std::string guid_; }; template bool FindByGUID(const C& container, const std::string& guid) { return std::find_if( container.begin(), container.end(), FormGroupMatchesByGUIDFunctor(guid)) != container.end(); } template class DereferenceFunctor { public: template const T& operator()(const T_Iterator& iterator) { return *iterator; } }; template T* address_of(T& v) { return &v; } bool IsValidEmail(const string16& value) { // This regex is more permissive than the official rfc2822 spec on the // subject, but it does reject obvious non-email addresses. const string16 kEmailPattern = ASCIIToUTF16("^[^@]+@[^@]+\\.[a-z]{2,6}$"); return autofill::MatchesPattern(value, kEmailPattern); } // Valid for US zip codes only. bool IsValidZip(const string16& value) { // Basic US zip code matching. const string16 kZipPattern = ASCIIToUTF16("^\\d{5}(-\\d{4})?$"); return autofill::MatchesPattern(value, kZipPattern); } // Returns true if minimum requirements for import of a given |profile| have // been met. An address submitted via a form must have at least these fields // filled. No verification of validity of the contents is preformed. This is // and existence check only. bool IsMinimumAddress(const AutofillProfile& profile) { return !profile.GetInfo(ADDRESS_HOME_LINE1).empty() && !profile.GetInfo(ADDRESS_HOME_CITY).empty() && !profile.GetInfo(ADDRESS_HOME_STATE).empty() && !profile.GetInfo(ADDRESS_HOME_ZIP).empty(); } // Return true if the |field_type| and |value| are valid within the context // of importing a form. bool IsValidFieldTypeAndValue(const std::set& types_seen, AutofillFieldType field_type, const string16& value) { // Abandon the import if two fields of the same type are encountered. // This indicates ambiguous data or miscategorization of types. // Make an exception for PHONE_HOME_NUMBER however as both prefix and // suffix are stored against this type. if (types_seen.count(field_type) && field_type != PHONE_HOME_NUMBER && field_type != PHONE_FAX_NUMBER) { return false; } // Abandon the import if an email address value shows up in a field that is // not an email address. if (field_type != EMAIL_ADDRESS && IsValidEmail(value)) return false; return true; } } // namespace PersonalDataManager::~PersonalDataManager() { CancelPendingQuery(&pending_profiles_query_); CancelPendingQuery(&pending_creditcards_query_); } void PersonalDataManager::OnWebDataServiceRequestDone( WebDataService::Handle h, const WDTypedResult* result) { DCHECK(pending_profiles_query_ || pending_creditcards_query_); if (!result) { // Error from the web database. if (h == pending_creditcards_query_) pending_creditcards_query_ = 0; else if (h == pending_profiles_query_) pending_profiles_query_ = 0; return; } DCHECK(result->GetType() == AUTOFILL_PROFILES_RESULT || result->GetType() == AUTOFILL_CREDITCARDS_RESULT); switch (result->GetType()) { case AUTOFILL_PROFILES_RESULT: ReceiveLoadedProfiles(h, result); break; case AUTOFILL_CREDITCARDS_RESULT: ReceiveLoadedCreditCards(h, result); break; default: NOTREACHED(); } // If both requests have responded, then all personal data is loaded. if (pending_profiles_query_ == 0 && pending_creditcards_query_ == 0) { is_data_loaded_ = true; std::vector profile_pointers(web_profiles_.size()); std::copy(web_profiles_.begin(), web_profiles_.end(), profile_pointers.begin()); AutofillProfile::AdjustInferredLabels(&profile_pointers); FOR_EACH_OBSERVER(PersonalDataManagerObserver, observers_, OnPersonalDataChanged()); } } void PersonalDataManager::SetObserver(PersonalDataManagerObserver* observer) { // TODO(dhollowa): RemoveObserver is for compatibility with old code, it // should be nuked. observers_.RemoveObserver(observer); observers_.AddObserver(observer); } void PersonalDataManager::RemoveObserver( PersonalDataManagerObserver* observer) { observers_.RemoveObserver(observer); } // The |PersonalDataManager| is set up as a listener of the sync service in // |EmptyMigrationTrash| in the case where sync is not yet ready to receive // changes. This method, |OnStateChange| acts as a deferred call to // |EmptyMigrationTrash| once the sync service becomes available. void PersonalDataManager::OnStateChanged() { if (!profile_ || profile_->IsOffTheRecord()) return; WebDataService* web_data_service = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!web_data_service) { NOTREACHED(); return; } ProfileSyncService* sync_service = profile_->GetProfileSyncService(); if (!sync_service) return; if (sync_service->ShouldPushChanges()) { web_data_service->EmptyMigrationTrash(true); sync_service->RemoveObserver(this); } } bool PersonalDataManager::ImportFormData( const FormStructure& form, const CreditCard** imported_credit_card) { scoped_ptr imported_profile(new AutofillProfile); scoped_ptr local_imported_credit_card(new CreditCard); // Parse the form and construct a profile based on the information that is // possible to import. int importable_credit_card_fields = 0; std::vector::const_iterator iter; // Detect and discard forms with multiple fields of the same type. std::set types_seen; // We only set complete phone, so aggregate phone parts in these vars and set // complete at the end. PhoneNumber::PhoneCombineHelper home(AutofillType::PHONE_HOME); PhoneNumber::PhoneCombineHelper fax(AutofillType::PHONE_FAX); for (size_t i = 0; i < form.field_count(); ++i) { const AutofillField* field = form.field(i); string16 value = CollapseWhitespace(field->value, false); // If we don't know the type of the field, or the user hasn't entered any // information into the field, then skip it. if (!field->IsFieldFillable() || value.empty()) continue; AutofillFieldType field_type = field->type(); FieldTypeGroup group(AutofillType(field_type).group()); // If the |field_type| and |value| don't pass basic validity checks then // abandon the import. if (!IsValidFieldTypeAndValue(types_seen, field_type, value)) { imported_profile.reset(); local_imported_credit_card.reset(); break; } types_seen.insert(field_type); if (group == AutofillType::CREDIT_CARD) { if (LowerCaseEqualsASCII(field->form_control_type, "month")) { DCHECK_EQ(CREDIT_CARD_EXP_MONTH, field_type); local_imported_credit_card->SetInfoForMonthInputType(value); } else { local_imported_credit_card->SetCanonicalizedInfo(field_type, value); } ++importable_credit_card_fields; } else { // We need to store phone data in the variables, before building the whole // number at the end. The rest of the fields are set "as is". // If the fields are not the phone fields in question both home.SetInfo() // and fax.SetInfo() are going to return false. if (!home.SetInfo(field_type, value) && !fax.SetInfo(field_type, value)) imported_profile->SetCanonicalizedInfo(field_type, value); // Reject profiles with invalid country information. if (field_type == ADDRESS_HOME_COUNTRY && !value.empty() && imported_profile->CountryCode().empty()) { imported_profile.reset(); break; } } } // Construct the phone and fax numbers. Reject the profile if either number // is invalid. if (imported_profile.get() && !home.IsEmpty()) { string16 constructed_number; if (!home.ParseNumber(imported_profile->CountryCode(), &constructed_number) || !imported_profile->SetCanonicalizedInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number)) { imported_profile.reset(); } } if (imported_profile.get() && !fax.IsEmpty()) { string16 constructed_number; if (!fax.ParseNumber(imported_profile->CountryCode(), &constructed_number) || !imported_profile->SetCanonicalizedInfo(PHONE_FAX_WHOLE_NUMBER, constructed_number)) { imported_profile.reset(); } } // Reject the profile if minimum address and validation requirements are not // met. if (imported_profile.get() && !IsValidLearnableProfile(*imported_profile)) imported_profile.reset(); // Reject the credit card if we did not detect enough filled credit card // fields or if the credit card number does not seem to be valid. if (local_imported_credit_card.get() && !local_imported_credit_card->IsComplete()) { local_imported_credit_card.reset(); } // Don't import if we already have this info. if (local_imported_credit_card.get()) { for (std::vector::const_iterator iter = credit_cards_.begin(); iter != credit_cards_.end(); ++iter) { if (local_imported_credit_card->IsSubsetOf(**iter)) { local_imported_credit_card.reset(); break; } } } if (imported_profile.get()) { // We always save imported profiles. SaveImportedProfile(*imported_profile); } *imported_credit_card = local_imported_credit_card.release(); if (imported_profile.get() || *imported_credit_card) { return true; } else { FOR_EACH_OBSERVER(PersonalDataManagerObserver, observers_, OnInsufficientFormData()); return false; } } void PersonalDataManager::AddProfile(const AutofillProfile& profile) { if (profile_->IsOffTheRecord()) return; if (profile.IsEmpty()) return; // Don't add an existing profile. if (FindByGUID(web_profiles_, profile.guid())) return; WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Don't add a duplicate. if (FindByContents(web_profiles_, profile)) return; // Add the new profile to the web database. wds->AddAutofillProfile(profile); // Refresh our local cache and send notifications to observers. Refresh(); } void PersonalDataManager::UpdateProfile(const AutofillProfile& profile) { if (profile_->IsOffTheRecord()) return; if (!FindByGUID(web_profiles_, profile.guid())) return; if (profile.IsEmpty()) { RemoveProfile(profile.guid()); return; } WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Make the update. wds->UpdateAutofillProfile(profile); // Refresh our local cache and send notifications to observers. Refresh(); } void PersonalDataManager::RemoveProfile(const std::string& guid) { if (profile_->IsOffTheRecord()) return; if (!FindByGUID(web_profiles_, guid)) return; WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Remove the profile. wds->RemoveAutofillProfile(guid); // Refresh our local cache and send notifications to observers. Refresh(); } AutofillProfile* PersonalDataManager::GetProfileByGUID( const std::string& guid) { for (std::vector::iterator iter = web_profiles_->begin(); iter != web_profiles_->end(); ++iter) { if ((*iter)->guid() == guid) return *iter; } return NULL; } void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) { if (profile_->IsOffTheRecord()) return; if (credit_card.IsEmpty()) return; if (FindByGUID(credit_cards_, credit_card.guid())) return; WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Don't add a duplicate. if (FindByContents(credit_cards_, credit_card)) return; // Add the new credit card to the web database. wds->AddCreditCard(credit_card); // Refresh our local cache and send notifications to observers. Refresh(); } void PersonalDataManager::UpdateCreditCard(const CreditCard& credit_card) { if (profile_->IsOffTheRecord()) return; if (!FindByGUID(credit_cards_, credit_card.guid())) return; if (credit_card.IsEmpty()) { RemoveCreditCard(credit_card.guid()); return; } WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Make the update. wds->UpdateCreditCard(credit_card); // Refresh our local cache and send notifications to observers. Refresh(); } void PersonalDataManager::RemoveCreditCard(const std::string& guid) { if (profile_->IsOffTheRecord()) return; if (!FindByGUID(credit_cards_, guid)) return; WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Remove the credit card. wds->RemoveCreditCard(guid); // Refresh our local cache and send notifications to observers. Refresh(); } CreditCard* PersonalDataManager::GetCreditCardByGUID(const std::string& guid) { for (std::vector::iterator iter = credit_cards_.begin(); iter != credit_cards_.end(); ++iter) { if ((*iter)->guid() == guid) return *iter; } return NULL; } void PersonalDataManager::GetNonEmptyTypes( FieldTypeSet* non_empty_types) const { const std::vector& profiles = this->profiles(); for (std::vector::const_iterator iter = profiles.begin(); iter != profiles.end(); ++iter) { (*iter)->GetNonEmptyTypes(non_empty_types); } for (ScopedVector::const_iterator iter = credit_cards_.begin(); iter != credit_cards_.end(); ++iter) { (*iter)->GetNonEmptyTypes(non_empty_types); } } bool PersonalDataManager::IsDataLoaded() const { return is_data_loaded_; } const std::vector& PersonalDataManager::profiles() const { // |profile_| is NULL in AutofillManagerTest. bool auxiliary_profiles_enabled = profile_ ? profile_->GetPrefs()->GetBoolean( prefs::kAutofillAuxiliaryProfilesEnabled) : false; if (!auxiliary_profiles_enabled) return web_profiles(); #if !defined(OS_MACOSX) NOTREACHED() << "Auxiliary profiles supported on Mac only"; #endif profiles_.clear(); // Populates |auxiliary_profiles_|. LoadAuxiliaryProfiles(); profiles_.insert(profiles_.end(), web_profiles_.begin(), web_profiles_.end()); profiles_.insert(profiles_.end(), auxiliary_profiles_.begin(), auxiliary_profiles_.end()); return profiles_; } const std::vector& PersonalDataManager::web_profiles() const { return web_profiles_.get(); } const std::vector& PersonalDataManager::credit_cards() const { return credit_cards_.get(); } void PersonalDataManager::Refresh() { LoadProfiles(); LoadCreditCards(); } PersonalDataManager::PersonalDataManager() : profile_(NULL), is_data_loaded_(false), pending_profiles_query_(0), pending_creditcards_query_(0), metric_logger_(new AutofillMetrics), has_logged_profile_count_(false) { } void PersonalDataManager::Init(Profile* profile) { profile_ = profile; metric_logger_->LogIsAutofillEnabledAtStartup(IsAutofillEnabled()); LoadProfiles(); LoadCreditCards(); } bool PersonalDataManager::IsAutofillEnabled() const { return profile_->GetPrefs()->GetBoolean(prefs::kAutofillEnabled); } // static bool PersonalDataManager::IsValidLearnableProfile( const AutofillProfile& profile) { if (!IsMinimumAddress(profile)) return false; string16 email = profile.GetInfo(EMAIL_ADDRESS); if (!email.empty() && !IsValidEmail(email)) return false; // Reject profiles with invalid US state information. string16 state = profile.GetInfo(ADDRESS_HOME_STATE); if (profile.CountryCode() == "US" && !state.empty() && !autofill::IsValidState(state)) { return false; } // Reject profiles with invalid US zip information. string16 zip = profile.GetInfo(ADDRESS_HOME_ZIP); if (profile.CountryCode() == "US" && !zip.empty() && !IsValidZip(zip)) return false; return true; } // static bool PersonalDataManager::MergeProfile( const AutofillProfile& profile, const std::vector& existing_profiles, std::vector* merged_profiles) { DCHECK(merged_profiles); merged_profiles->clear(); // Set to true if |profile| is merged into |existing_profiles|. bool merged = false; // First preference is to add missing values to an existing profile. // Only merge with the first match. for (std::vector::const_iterator iter = existing_profiles.begin(); iter != existing_profiles.end(); ++iter) { if (!merged) { if (profile.IsSubsetOf(**iter)) { // In this case, the existing profile already contains all of the data // in |profile|, so consider the profiles already merged. merged = true; } else if ((*iter)->IntersectionOfTypesHasEqualValues(profile)) { // |profile| contains all of the data in this profile, plus more. merged = true; (*iter)->MergeWith(profile); } } merged_profiles->push_back(**iter); } // The second preference, if not merged above, is to alter non-primary values // where the primary values match. // Again, only merge with the first match. if (!merged) { merged_profiles->clear(); for (std::vector::const_iterator iter = existing_profiles.begin(); iter != existing_profiles.end(); ++iter) { if (!merged) { if (!profile.PrimaryValue().empty() && StringToLowerASCII((*iter)->PrimaryValue()) == StringToLowerASCII(profile.PrimaryValue())) { merged = true; (*iter)->OverwriteWithOrAddTo(profile); } } merged_profiles->push_back(**iter); } } // Finally, if the new profile was not merged with an existing profile then // add the new profile to the list. if (!merged) merged_profiles->push_back(profile); return merged; } void PersonalDataManager::SetProfiles(std::vector* profiles) { if (profile_->IsOffTheRecord()) return; // Remove empty profiles from input. profiles->erase( std::remove_if(profiles->begin(), profiles->end(), std::mem_fun_ref(&AutofillProfile::IsEmpty)), profiles->end()); // Ensure that profile labels are up to date. Currently, sync relies on // labels to identify a profile. // TODO(dhollowa): We need to deprecate labels and update the way sync // identifies profiles. std::vector profile_pointers(profiles->size()); std::transform(profiles->begin(), profiles->end(), profile_pointers.begin(), address_of); AutofillProfile::AdjustInferredLabels(&profile_pointers); WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Any profiles that are not in the new profile list should be removed from // the web database. for (std::vector::const_iterator iter = web_profiles_.begin(); iter != web_profiles_.end(); ++iter) { if (!FindByGUID(*profiles, (*iter)->guid())) wds->RemoveAutofillProfile((*iter)->guid()); } // Update the web database with the existing profiles. for (std::vector::iterator iter = profiles->begin(); iter != profiles->end(); ++iter) { if (FindByGUID(web_profiles_, iter->guid())) wds->UpdateAutofillProfile(*iter); } // Add the new profiles to the web database. Don't add a duplicate. for (std::vector::iterator iter = profiles->begin(); iter != profiles->end(); ++iter) { if (!FindByGUID(web_profiles_, iter->guid()) && !FindByContents(web_profiles_, *iter)) wds->AddAutofillProfile(*iter); } // Copy in the new profiles. web_profiles_.reset(); for (std::vector::iterator iter = profiles->begin(); iter != profiles->end(); ++iter) { web_profiles_.push_back(new AutofillProfile(*iter)); } // Refresh our local cache and send notifications to observers. Refresh(); } void PersonalDataManager::SetCreditCards( std::vector* credit_cards) { if (profile_->IsOffTheRecord()) return; // Remove empty credit cards from input. credit_cards->erase( std::remove_if( credit_cards->begin(), credit_cards->end(), std::mem_fun_ref(&CreditCard::IsEmpty)), credit_cards->end()); WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Any credit cards that are not in the new credit card list should be // removed. for (std::vector::const_iterator iter = credit_cards_.begin(); iter != credit_cards_.end(); ++iter) { if (!FindByGUID(*credit_cards, (*iter)->guid())) wds->RemoveCreditCard((*iter)->guid()); } // Update the web database with the existing credit cards. for (std::vector::iterator iter = credit_cards->begin(); iter != credit_cards->end(); ++iter) { if (FindByGUID(credit_cards_, iter->guid())) wds->UpdateCreditCard(*iter); } // Add the new credit cards to the web database. Don't add a duplicate. for (std::vector::iterator iter = credit_cards->begin(); iter != credit_cards->end(); ++iter) { if (!FindByGUID(credit_cards_, iter->guid()) && !FindByContents(credit_cards_, *iter)) wds->AddCreditCard(*iter); } // Copy in the new credit cards. credit_cards_.reset(); for (std::vector::iterator iter = credit_cards->begin(); iter != credit_cards->end(); ++iter) { credit_cards_.push_back(new CreditCard(*iter)); } // Refresh our local cache and send notifications to observers. Refresh(); } void PersonalDataManager::LoadProfiles() { WebDataService* web_data_service = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!web_data_service) { NOTREACHED(); return; } CancelPendingQuery(&pending_profiles_query_); pending_profiles_query_ = web_data_service->GetAutofillProfiles(this); } // Win and Linux implementations do nothing. Mac implementation fills in the // contents of |auxiliary_profiles_|. #if !defined(OS_MACOSX) void PersonalDataManager::LoadAuxiliaryProfiles() const { } #endif void PersonalDataManager::LoadCreditCards() { WebDataService* web_data_service = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!web_data_service) { NOTREACHED(); return; } CancelPendingQuery(&pending_creditcards_query_); pending_creditcards_query_ = web_data_service->GetCreditCards(this); } void PersonalDataManager::ReceiveLoadedProfiles(WebDataService::Handle h, const WDTypedResult* result) { DCHECK_EQ(pending_profiles_query_, h); pending_profiles_query_ = 0; web_profiles_.reset(); const WDResult >* r = static_cast >*>(result); std::vector profiles = r->GetValue(); for (std::vector::iterator iter = profiles.begin(); iter != profiles.end(); ++iter) { web_profiles_.push_back(*iter); } LogProfileCount(); EmptyMigrationTrash(); } void PersonalDataManager::ReceiveLoadedCreditCards( WebDataService::Handle h, const WDTypedResult* result) { DCHECK_EQ(pending_creditcards_query_, h); pending_creditcards_query_ = 0; credit_cards_.reset(); const WDResult >* r = static_cast >*>(result); std::vector credit_cards = r->GetValue(); for (std::vector::iterator iter = credit_cards.begin(); iter != credit_cards.end(); ++iter) { credit_cards_.push_back(*iter); } } void PersonalDataManager::CancelPendingQuery(WebDataService::Handle* handle) { if (*handle) { WebDataService* web_data_service = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!web_data_service) { NOTREACHED(); return; } web_data_service->CancelRequest(*handle); } *handle = 0; } void PersonalDataManager::SaveImportedProfile( const AutofillProfile& imported_profile) { if (profile_->IsOffTheRecord()) return; // Don't save a web profile if the data in the profile is a subset of an // auxiliary profile. for (std::vector::const_iterator iter = auxiliary_profiles_.begin(); iter != auxiliary_profiles_.end(); ++iter) { if (imported_profile.IsSubsetOf(**iter)) return; } std::vector profiles; MergeProfile(imported_profile, web_profiles_.get(), &profiles); SetProfiles(&profiles); } void PersonalDataManager::SaveImportedCreditCard( const CreditCard& imported_credit_card) { if (profile_->IsOffTheRecord()) return; // Set to true if |imported_credit_card| is merged into the credit card list. bool merged = false; std::vector creditcards; for (std::vector::const_iterator iter = credit_cards_.begin(); iter != credit_cards_.end(); ++iter) { if (imported_credit_card.IsSubsetOf(**iter)) { // In this case, the existing credit card already contains all of the data // in |imported_credit_card|, so consider the credit cards already // merged. merged = true; } else if ((*iter)->IntersectionOfTypesHasEqualValues( imported_credit_card)) { // |imported_credit_card| contains all of the data in this credit card, // plus more. merged = true; (*iter)->MergeWith(imported_credit_card); } else if (!imported_credit_card.number().empty() && (*iter)->number() == imported_credit_card.number()) { merged = true; (*iter)->OverwriteWith(imported_credit_card); } creditcards.push_back(**iter); } if (!merged) creditcards.push_back(imported_credit_card); SetCreditCards(&creditcards); } void PersonalDataManager::EmptyMigrationTrash() { if (!profile_ || profile_->IsOffTheRecord()) return; WebDataService* web_data_service = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!web_data_service) { NOTREACHED(); return; } ProfileSyncService* sync_service = profile_->GetProfileSyncService(); if (!sync_service) return; if (!sync_service->HasSyncSetupCompleted()) { web_data_service->EmptyMigrationTrash(false); } else if (sync_service->ShouldPushChanges()) { web_data_service->EmptyMigrationTrash(true); } else { // Install ourself as a listener so we can empty the trash once the // sync service becomes available. if (!sync_service->HasObserver(this)) sync_service->AddObserver(this); } } void PersonalDataManager::LogProfileCount() const { if (!has_logged_profile_count_) { metric_logger_->LogStoredProfileCount(web_profiles_.size()); has_logged_profile_count_ = true; } } const AutofillMetrics* PersonalDataManager::metric_logger() const { return metric_logger_.get(); } void PersonalDataManager::set_metric_logger( const AutofillMetrics* metric_logger) { metric_logger_.reset(metric_logger); }