// Copyright (c) 2010 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_field.h" #include "chrome/browser/autofill/autofill-inl.h" #include "chrome/browser/autofill/form_structure.h" #include "chrome/browser/autofill/phone_number.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/webdata/web_data_service.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/common/pref_names.h" namespace { // The minimum number of fields that must contain user data and have known types // before AutoFill will attempt to import the data into a profile or a credit // card. const int kMinProfileImportSize = 3; const int kMinCreditCardImportSize = 2; 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; } } // namespace PersonalDataManager::~PersonalDataManager() { CancelPendingQuery(&pending_profiles_query_); CancelPendingQuery(&pending_creditcards_query_); } void PersonalDataManager::OnWebDataServiceRequestDone( WebDataService::Handle h, const WDTypedResult* result) { // Error from the web database. if (!result) return; DCHECK(pending_profiles_query_ || pending_creditcards_query_); 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(Observer, observers_, OnPersonalDataLoaded()); } } ///////////////////////////////////////////////////////////////////////////// // PersonalDataManager, // views::ButtonListener implementations void PersonalDataManager::OnAutoFillDialogApply( std::vector* profiles, std::vector* credit_cards) { // |profiles| may be NULL. // |credit_cards| may be NULL. if (profiles) { CancelPendingQuery(&pending_profiles_query_); SetProfiles(profiles); } if (credit_cards) { CancelPendingQuery(&pending_creditcards_query_); SetCreditCards(credit_cards); } } void PersonalDataManager::SetObserver(PersonalDataManager::Observer* observer) { // TODO: RemoveObserver is for compatibility with old code, it should be // nuked. observers_.RemoveObserver(observer); observers_.AddObserver(observer); } void PersonalDataManager::RemoveObserver( PersonalDataManager::Observer* observer) { observers_.RemoveObserver(observer); } bool PersonalDataManager::ImportFormData( const std::vector& form_structures) { // Parse the form and construct a profile based on the information that is // possible to import. int importable_fields = 0; int importable_credit_card_fields = 0; imported_profile_.reset(new AutoFillProfile); // TODO(jhawkins): Use a hash of the CC# instead of a list of unique IDs? imported_credit_card_.reset(new CreditCard); std::vector::const_iterator iter; for (iter = form_structures.begin(); iter != form_structures.end(); ++iter) { const FormStructure* form = *iter; 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; AutoFillType field_type(field->type()); FieldTypeGroup group(field_type.group()); if (group == AutoFillType::CREDIT_CARD) { // If the user has a password set, we have no way of setting credit // card numbers. if (!HasPassword()) { imported_credit_card_->SetInfo(AutoFillType(field_type.field_type()), value); ++importable_credit_card_fields; } } else { // In the case of a phone number, if the whole phone number was entered // into a single field, then parse it and set the sub components. if (field_type.subgroup() == AutoFillType::PHONE_WHOLE_NUMBER) { string16 number; string16 city_code; string16 country_code; PhoneNumber::ParsePhoneNumber(value, &number, &city_code, &country_code); if (number.empty()) continue; if (group == AutoFillType::PHONE_HOME) { imported_profile_->SetInfo(AutoFillType(PHONE_HOME_COUNTRY_CODE), country_code); imported_profile_->SetInfo(AutoFillType(PHONE_HOME_CITY_CODE), city_code); imported_profile_->SetInfo(AutoFillType(PHONE_HOME_NUMBER), number); } else if (group == AutoFillType::PHONE_FAX) { imported_profile_->SetInfo(AutoFillType(PHONE_FAX_COUNTRY_CODE), country_code); imported_profile_->SetInfo(AutoFillType(PHONE_FAX_CITY_CODE), city_code); imported_profile_->SetInfo(AutoFillType(PHONE_FAX_NUMBER), number); } continue; } // Phone and fax numbers can be split across multiple fields, so we // might have already stored the prefix, and now be at the suffix. // If so, combine them to form the full number. if (group == AutoFillType::PHONE_HOME || group == AutoFillType::PHONE_FAX) { AutoFillType number_type(PHONE_HOME_NUMBER); if (group == AutoFillType::PHONE_FAX) number_type = AutoFillType(PHONE_FAX_NUMBER); string16 stored_number = imported_profile_->GetFieldText(number_type); if (stored_number.size() == static_cast(PhoneNumber::kPrefixLength) && value.size() == static_cast(PhoneNumber::kSuffixLength)) { value = stored_number + value; } } imported_profile_->SetInfo(AutoFillType(field_type.field_type()), value); ++importable_fields; } } } // If the user did not enter enough information on the page then don't bother // importing the data. if (importable_fields < kMinProfileImportSize) imported_profile_.reset(); if (importable_credit_card_fields < kMinCreditCardImportSize) imported_credit_card_.reset(); if (imported_credit_card_.get()) { if (!CreditCard::IsCreditCardNumber(imported_credit_card_->GetFieldText( AutoFillType(CREDIT_CARD_NUMBER)))) { imported_credit_card_.reset(); } } // Don't import if we already have this info. if (imported_credit_card_.get()) { for (std::vector::const_iterator iter = credit_cards_.begin(); iter != credit_cards_.end(); ++iter) { if (imported_credit_card_->IsSubsetOf(**iter)) { imported_credit_card_.reset(); break; } } } if (imported_profile_.get()) { // We always save imported profiles. SaveImportedProfile(); } return imported_profile_.get() || imported_credit_card_.get(); } void PersonalDataManager::GetImportedFormData(AutoFillProfile** profile, CreditCard** credit_card) { DCHECK(profile); DCHECK(credit_card); *profile = imported_profile_.get(); *credit_card = imported_credit_card_.get(); } 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->RemoveAutoFillProfileGUID((*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->UpdateAutoFillProfileGUID(*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->AddAutoFillProfileGUID(*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)); } // Read our writes to ensure consistency with the database. Refresh(); FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); } 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()); SetUniqueCreditCardLabels(credit_cards); 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->RemoveCreditCardGUID((*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->UpdateCreditCardGUID(*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->AddCreditCardGUID(*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)); } // Read our writes to ensure consistency with the database. Refresh(); FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); } // TODO(jhawkins): Refactor SetProfiles so this isn't so hacky. void PersonalDataManager::AddProfile(const AutoFillProfile& profile) { // 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 (profile.IsSubsetOf(**iter)) return; } // Set to true if |profile| is merged into the profile list. bool merged = false; // First preference is to add missing values to an existing profile. // Only merge with the first match. std::vector profiles; for (std::vector::const_iterator iter = web_profiles_.begin(); iter != web_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); } } 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) { profiles.clear(); for (std::vector::const_iterator iter = web_profiles_.begin(); iter != web_profiles_.end(); ++iter) { if (!merged) { if (!profile.PrimaryValue().empty() && (*iter)->PrimaryValue() == profile.PrimaryValue()) { merged = true; (*iter)->OverwriteWith(profile); } } 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) profiles.push_back(profile); SetProfiles(&profiles); } void PersonalDataManager::UpdateProfile(const AutoFillProfile& profile) { WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Update the cached profile. for (std::vector::iterator iter = web_profiles_->begin(); iter != web_profiles_->end(); ++iter) { if ((*iter)->guid() == profile.guid()) { delete *iter; *iter = new AutoFillProfile(profile); break; } } // Ensure that profile labels are up to date. AutoFillProfile::AdjustInferredLabels(&web_profiles_.get()); wds->UpdateAutoFillProfileGUID(profile); FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); } void PersonalDataManager::RemoveProfile(const std::string& guid) { // TODO(jhawkins): Refactor SetProfiles so this isn't so hacky. std::vector profiles(web_profiles_.size()); std::transform(web_profiles_.begin(), web_profiles_.end(), profiles.begin(), DereferenceFunctor()); // Remove the profile that matches |guid|. profiles.erase( std::remove_if(profiles.begin(), profiles.end(), FormGroupMatchesByGUIDFunctor(guid)), profiles.end()); SetProfiles(&profiles); } 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; } // TODO(jhawkins): Refactor SetCreditCards so this isn't so hacky. void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) { std::vector credit_cards(credit_cards_.size()); std::transform(credit_cards_.begin(), credit_cards_.end(), credit_cards.begin(), DereferenceFunctor()); credit_cards.push_back(credit_card); SetCreditCards(&credit_cards); } void PersonalDataManager::UpdateCreditCard(const CreditCard& credit_card) { WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); if (!wds) return; // Update the cached credit card. for (std::vector::iterator iter = credit_cards_->begin(); iter != credit_cards_->end(); ++iter) { if ((*iter)->guid() == credit_card.guid()) { delete *iter; *iter = new CreditCard(credit_card); break; } } wds->UpdateCreditCardGUID(credit_card); FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); } void PersonalDataManager::RemoveCreditCard(const std::string& guid) { // TODO(jhawkins): Refactor SetCreditCards so this isn't so hacky. std::vector credit_cards(credit_cards_.size()); std::transform(credit_cards_.begin(), credit_cards_.end(), credit_cards.begin(), DereferenceFunctor()); // Remove the credit card that matches |guid|. credit_cards.erase( std::remove_if(credit_cards.begin(), credit_cards.end(), FormGroupMatchesByGUIDFunctor(guid)), credit_cards.end()); SetCreditCards(&credit_cards); } 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::GetPossibleFieldTypes(const string16& text, FieldTypeSet* possible_types) { string16 clean_info = StringToLowerASCII(CollapseWhitespace(text, false)); if (clean_info.empty()) { possible_types->insert(EMPTY_TYPE); return; } const std::vector& profiles = this->profiles(); for (std::vector::const_iterator iter = profiles.begin(); iter != profiles.end(); ++iter) { const FormGroup* profile = *iter; if (!profile) { DLOG(ERROR) << "NULL information in profiles list"; continue; } profile->GetPossibleFieldTypes(clean_info, possible_types); } for (ScopedVector::iterator iter = credit_cards_.begin(); iter != credit_cards_.end(); ++iter) { const FormGroup* credit_card = *iter; if (!credit_card) { DLOG(ERROR) << "NULL information in credit cards list"; continue; } credit_card->GetPossibleFieldTypes(clean_info, possible_types); } if (possible_types->empty()) possible_types->insert(UNKNOWN_TYPE); } bool PersonalDataManager::HasPassword() { return !password_hash_.empty(); } bool PersonalDataManager::IsDataLoaded() const { return is_data_loaded_; } const std::vector& PersonalDataManager::profiles() { // |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() { return web_profiles_.get(); } const std::vector& PersonalDataManager::credit_cards() { return credit_cards_.get(); } AutoFillProfile* PersonalDataManager::CreateNewEmptyAutoFillProfileForDBThread( const string16& label) { // See comment in header for thread details. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); AutoFillProfile* p = new AutoFillProfile; p->set_label(label); return p; } void PersonalDataManager::Refresh() { LoadProfiles(); LoadCreditCards(); } PersonalDataManager::PersonalDataManager() : profile_(NULL), is_data_loaded_(false), pending_profiles_query_(0), pending_creditcards_query_(0) { } void PersonalDataManager::Init(Profile* profile) { profile_ = profile; LoadProfiles(); LoadCreditCards(); } 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() { } #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); } } 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::SetUniqueCreditCardLabels( std::vector* credit_cards) { std::map > label_map; for (std::vector::iterator iter = credit_cards->begin(); iter != credit_cards->end(); ++iter) { label_map[iter->Label()].push_back(&(*iter)); } for (std::map >::iterator iter = label_map.begin(); iter != label_map.end(); ++iter) { // Start at the second element because the first label should not be // renamed. The appended label number starts at 2, because the first label // has an implicit index of 1. for (size_t i = 1; i < iter->second.size(); ++i) { string16 newlabel = iter->second[i]->Label() + base::UintToString16(static_cast(i + 1)); iter->second[i]->set_label(newlabel); } } } void PersonalDataManager::SaveImportedProfile() { if (profile_->IsOffTheRecord()) return; if (!imported_profile_.get()) return; AddProfile(*imported_profile_); } // TODO(jhawkins): Refactor and merge this with SaveImportedProfile. void PersonalDataManager::SaveImportedCreditCard() { if (profile_->IsOffTheRecord()) return; if (!imported_credit_card_.get()) return; // Set to true if |imported_credit_card_| is merged into the profile 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_profile| contains all of the data in this profile, 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); }