| // 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 <algorithm> |
| #include <iterator> |
| |
| #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/form_field.h" |
| #include "chrome/browser/autofill/form_structure.h" |
| #include "chrome/browser/autofill/phone_number.h" |
| #include "chrome/browser/autofill/select_control_handler.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #ifndef ANDROID |
| #include "chrome/browser/sync/profile_sync_service.h" |
| #endif |
| #include "chrome/browser/webdata/web_data_service.h" |
| #include "chrome/common/pref_names.h" |
| #ifndef ANDROID |
| #include "content/browser/browser_thread.h" |
| #endif |
| |
| namespace { |
| |
| // The minimum number of fields that must contain relevant user data before |
| // Autofill will attempt to import the data into a credit card. |
| const int kMinCreditCardImportSize = 2; |
| |
| template<typename T> |
| 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<typename T, typename C> |
| bool FindByGUID(const C& container, const std::string& guid) { |
| return std::find_if( |
| container.begin(), |
| container.end(), |
| FormGroupMatchesByGUIDFunctor<T>(guid)) != container.end(); |
| } |
| |
| template<typename T> |
| class DereferenceFunctor { |
| public: |
| template<typename T_Iterator> |
| const T& operator()(const T_Iterator& iterator) { |
| return *iterator; |
| } |
| }; |
| |
| template<typename T> |
| 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::MatchString(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::MatchString(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(); |
| } |
| |
| } // 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<AutofillProfile*> 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::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); |
| } |
| |
| // 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() { |
| #ifdef ANDROID |
| return; |
| #else |
| 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); |
| } |
| #endif |
| } |
| |
| bool PersonalDataManager::ImportFormData( |
| const std::vector<const FormStructure*>& form_structures, |
| const CreditCard** imported_credit_card) { |
| #ifdef ANDROID |
| // TODO: Is this the funcionality that tries to create a profile for the user |
| // based on what they've entered into forms? |
| return false; |
| #else |
| scoped_ptr<AutofillProfile> imported_profile(new AutoFillProfile); |
| scoped_ptr<CreditCard> 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 FormStructure*>::const_iterator iter; |
| |
| // Detect and discard forms with multiple fields of the same type. |
| std::set<AutofillFieldType> types_seen; |
| |
| 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; |
| |
| AutofillFieldType field_type = field->type(); |
| FieldTypeGroup group(AutofillType(field_type).group()); |
| |
| // 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) { |
| imported_profile.reset(); |
| local_imported_credit_card.reset(); |
| break; |
| } else { |
| types_seen.insert(field_type); |
| } |
| |
| if (group == AutofillType::CREDIT_CARD) { |
| // If the user has a password set, we have no way of setting credit |
| // card numbers. |
| if (!HasPassword()) { |
| if (LowerCaseEqualsASCII(field->form_control_type, "month")) { |
| DCHECK_EQ(CREDIT_CARD_EXP_MONTH, field_type); |
| local_imported_credit_card->SetInfoForMonthInputType(value); |
| } else { |
| if (field_type == CREDIT_CARD_NUMBER) { |
| // Clean up any imported credit card numbers. |
| value = CreditCard::StripSeparators(value); |
| } |
| local_imported_credit_card->SetInfo(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 (AutofillType(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(PHONE_HOME_COUNTRY_CODE, country_code); |
| imported_profile->SetInfo(PHONE_HOME_CITY_CODE, city_code); |
| imported_profile->SetInfo(PHONE_HOME_NUMBER, number); |
| } else if (group == AutofillType::PHONE_FAX) { |
| imported_profile->SetInfo(PHONE_FAX_COUNTRY_CODE, country_code); |
| imported_profile->SetInfo(PHONE_FAX_CITY_CODE, city_code); |
| imported_profile->SetInfo(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) { |
| AutofillFieldType number_type = PHONE_HOME_NUMBER; |
| if (group == AutofillType::PHONE_FAX) |
| number_type = PHONE_FAX_NUMBER; |
| |
| string16 stored_number = imported_profile->GetInfo(number_type); |
| if (stored_number.size() == |
| static_cast<size_t>(PhoneNumber::kPrefixLength) && |
| value.size() == static_cast<size_t>(PhoneNumber::kSuffixLength)) { |
| value = stored_number + value; |
| } |
| } |
| |
| imported_profile->SetInfo(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; |
| } |
| } |
| } |
| } |
| |
| // 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() && |
| (importable_credit_card_fields < kMinCreditCardImportSize || |
| !CreditCard::IsValidCreditCardNumber( |
| local_imported_credit_card->GetInfo(CREDIT_CARD_NUMBER)))) { |
| local_imported_credit_card.reset(); |
| } |
| |
| // Don't import if we already have this info. |
| if (local_imported_credit_card.get()) { |
| for (std::vector<CreditCard*>::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(); |
| |
| return imported_profile.get() || *imported_credit_card; |
| #endif |
| } |
| |
| void PersonalDataManager::SetProfiles(std::vector<AutofillProfile>* 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()); |
| |
| #ifndef ANDROID |
| // 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<AutofillProfile*> profile_pointers(profiles->size()); |
| std::transform(profiles->begin(), profiles->end(), profile_pointers.begin(), |
| address_of<AutofillProfile>); |
| 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<AutofillProfile*>::const_iterator iter = |
| web_profiles_.begin(); |
| iter != web_profiles_.end(); ++iter) { |
| if (!FindByGUID<AutofillProfile>(*profiles, (*iter)->guid())) |
| wds->RemoveAutofillProfile((*iter)->guid()); |
| } |
| |
| // Update the web database with the existing profiles. |
| for (std::vector<AutofillProfile>::iterator iter = profiles->begin(); |
| iter != profiles->end(); ++iter) { |
| if (FindByGUID<AutofillProfile>(web_profiles_, iter->guid())) |
| wds->UpdateAutofillProfile(*iter); |
| } |
| |
| // Add the new profiles to the web database. Don't add a duplicate. |
| for (std::vector<AutofillProfile>::iterator iter = profiles->begin(); |
| iter != profiles->end(); ++iter) { |
| if (!FindByGUID<AutofillProfile>(web_profiles_, iter->guid()) && |
| !FindByContents(web_profiles_, *iter)) |
| wds->AddAutofillProfile(*iter); |
| } |
| #endif |
| |
| // Copy in the new profiles. |
| web_profiles_.reset(); |
| for (std::vector<AutofillProfile>::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<CreditCard>* credit_cards) { |
| #ifndef ANDROID |
| // Android does not do credit cards and does not have a WebDataService. |
| 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<CreditCard*>::const_iterator iter = credit_cards_.begin(); |
| iter != credit_cards_.end(); ++iter) { |
| if (!FindByGUID<CreditCard>(*credit_cards, (*iter)->guid())) |
| wds->RemoveCreditCard((*iter)->guid()); |
| } |
| |
| // Update the web database with the existing credit cards. |
| for (std::vector<CreditCard>::iterator iter = credit_cards->begin(); |
| iter != credit_cards->end(); ++iter) { |
| if (FindByGUID<CreditCard>(credit_cards_, iter->guid())) |
| wds->UpdateCreditCard(*iter); |
| } |
| |
| // Add the new credit cards to the web database. Don't add a duplicate. |
| for (std::vector<CreditCard>::iterator iter = credit_cards->begin(); |
| iter != credit_cards->end(); ++iter) { |
| if (!FindByGUID<CreditCard>(credit_cards_, iter->guid()) && |
| !FindByContents(credit_cards_, *iter)) |
| wds->AddCreditCard(*iter); |
| } |
| |
| // Copy in the new credit cards. |
| credit_cards_.reset(); |
| for (std::vector<CreditCard>::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()); |
| #endif |
| } |
| |
| // 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<AutofillProfile*>::const_iterator iter = |
| auxiliary_profiles_.begin(); |
| iter != auxiliary_profiles_.end(); ++iter) { |
| if (profile.IsSubsetOf(**iter)) |
| return; |
| } |
| |
| std::vector<AutofillProfile> profiles; |
| MergeProfile(profile, web_profiles_.get(), &profiles); |
| SetProfiles(&profiles); |
| } |
| |
| void PersonalDataManager::UpdateProfile(const AutofillProfile& profile) { |
| #ifndef ANDROID |
| WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); |
| if (!wds) |
| return; |
| |
| // Update the cached profile. |
| for (std::vector<AutofillProfile*>::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->UpdateAutofillProfile(profile); |
| FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); |
| #endif |
| } |
| |
| void PersonalDataManager::RemoveProfile(const std::string& guid) { |
| // TODO(jhawkins): Refactor SetProfiles so this isn't so hacky. |
| std::vector<AutofillProfile> profiles(web_profiles_.size()); |
| std::transform(web_profiles_.begin(), web_profiles_.end(), |
| profiles.begin(), |
| DereferenceFunctor<AutofillProfile>()); |
| |
| // Remove the profile that matches |guid|. |
| profiles.erase( |
| std::remove_if(profiles.begin(), profiles.end(), |
| FormGroupMatchesByGUIDFunctor<AutofillProfile>(guid)), |
| profiles.end()); |
| |
| SetProfiles(&profiles); |
| } |
| |
| AutofillProfile* PersonalDataManager::GetProfileByGUID( |
| const std::string& guid) { |
| for (std::vector<AutofillProfile*>::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<CreditCard> credit_cards(credit_cards_.size()); |
| std::transform(credit_cards_.begin(), credit_cards_.end(), |
| credit_cards.begin(), |
| DereferenceFunctor<CreditCard>()); |
| |
| credit_cards.push_back(credit_card); |
| SetCreditCards(&credit_cards); |
| } |
| |
| void PersonalDataManager::UpdateCreditCard(const CreditCard& credit_card) { |
| #ifndef ANDROID |
| WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); |
| if (!wds) |
| return; |
| |
| // Update the cached credit card. |
| for (std::vector<CreditCard*>::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->UpdateCreditCard(credit_card); |
| FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged()); |
| #endif |
| } |
| |
| void PersonalDataManager::RemoveCreditCard(const std::string& guid) { |
| // TODO(jhawkins): Refactor SetCreditCards so this isn't so hacky. |
| std::vector<CreditCard> credit_cards(credit_cards_.size()); |
| std::transform(credit_cards_.begin(), credit_cards_.end(), |
| credit_cards.begin(), |
| DereferenceFunctor<CreditCard>()); |
| |
| // Remove the credit card that matches |guid|. |
| credit_cards.erase( |
| std::remove_if(credit_cards.begin(), credit_cards.end(), |
| FormGroupMatchesByGUIDFunctor<CreditCard>(guid)), |
| credit_cards.end()); |
| |
| SetCreditCards(&credit_cards); |
| } |
| |
| CreditCard* PersonalDataManager::GetCreditCardByGUID(const std::string& guid) { |
| for (std::vector<CreditCard*>::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<AutofillProfile*>& profiles = this->profiles(); |
| for (std::vector<AutofillProfile*>::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<CreditCard>::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<AutofillProfile*>& PersonalDataManager::profiles() { |
| // |profile_| is NULL in AutofillManagerTest. |
| #ifdef ANDROID |
| bool auxiliary_profiles_enabled = false; |
| #else |
| bool auxiliary_profiles_enabled = profile_ ? profile_->GetPrefs()->GetBoolean( |
| prefs::kAutofillAuxiliaryProfilesEnabled) : false; |
| #endif |
| 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<AutofillProfile*>& PersonalDataManager::web_profiles() { |
| return web_profiles_.get(); |
| } |
| |
| const std::vector<CreditCard*>& PersonalDataManager::credit_cards() { |
| 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 { |
| #ifdef ANDROID |
| return true; |
| #else |
| return profile_->GetPrefs()->GetBoolean(prefs::kAutofillEnabled); |
| #endif |
| } |
| |
| // 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<AutofillProfile*>& existing_profiles, |
| std::vector<AutofillProfile>* 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<AutofillProfile*>::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<AutofillProfile*>::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::LoadProfiles() { |
| #ifdef ANDROID |
| // This shoud request the profile(s) from java land on Android. |
| // Call to a java class that would read/write the data in a database. |
| // WebAutoFillClientAndroid will inject a profile while we're testing. |
| #else |
| 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); |
| #endif |
| } |
| |
| // 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() { |
| #ifndef ANDROID |
| // Need a web database service on Android |
| 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); |
| #endif |
| } |
| |
| void PersonalDataManager::ReceiveLoadedProfiles(WebDataService::Handle h, |
| const WDTypedResult* result) { |
| DCHECK_EQ(pending_profiles_query_, h); |
| |
| pending_profiles_query_ = 0; |
| web_profiles_.reset(); |
| |
| const WDResult<std::vector<AutofillProfile*> >* r = |
| static_cast<const WDResult<std::vector<AutofillProfile*> >*>(result); |
| |
| std::vector<AutofillProfile*> profiles = r->GetValue(); |
| for (std::vector<AutofillProfile*>::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<std::vector<CreditCard*> >* r = |
| static_cast<const WDResult<std::vector<CreditCard*> >*>(result); |
| |
| std::vector<CreditCard*> credit_cards = r->GetValue(); |
| for (std::vector<CreditCard*>::iterator iter = credit_cards.begin(); |
| iter != credit_cards.end(); ++iter) { |
| credit_cards_.push_back(*iter); |
| } |
| } |
| |
| void PersonalDataManager::CancelPendingQuery(WebDataService::Handle* handle) { |
| #ifndef ANDROID |
| // TODO: We need to come up with a web data service class for Android |
| if (*handle) { |
| WebDataService* web_data_service = |
| profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); |
| if (!web_data_service) { |
| NOTREACHED(); |
| return; |
| } |
| web_data_service->CancelRequest(*handle); |
| } |
| *handle = 0; |
| #endif |
| } |
| |
| void PersonalDataManager::SaveImportedProfile( |
| const AutofillProfile& imported_profile) { |
| #ifdef ANDROID |
| // TODO: This should update the profile in Java land. |
| return; |
| #else |
| if (profile_->IsOffTheRecord()) |
| return; |
| |
| AddProfile(imported_profile); |
| #endif |
| } |
| |
| |
| 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<CreditCard> creditcards; |
| for (std::vector<CreditCard*>::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() { |
| #ifdef ANDROID |
| return; |
| #else |
| 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); |
| } |
| #endif |
| } |
| |
| 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); |
| } |