// 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/autocomplete_history_manager.h" #include #include "base/string16.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_external_delegate.h" #include "chrome/browser/autofill/credit_card.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/autofill_messages.h" #include "chrome/common/pref_names.h" #include "content/browser/renderer_host/render_view_host.h" #include "content/public/browser/web_contents.h" #include "webkit/forms/form_data.h" using base::StringPiece16; using content::WebContents; using webkit::forms::FormData; using webkit::forms::FormField; namespace { // Limit on the number of suggestions to appear in the pop-up menu under an // text input element in a form. const int kMaxAutocompleteMenuItems = 6; // The separator characters for SSNs. const char16 kSSNSeparators[] = {' ', '-', 0}; bool IsSSN(const string16& text) { string16 number_string; RemoveChars(text, kSSNSeparators, &number_string); // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S = // serial number). The validation we do here is simply checking if the area, // group, and serial numbers are valid. // // Historically, the area number was assigned per state, with the group number // ascending in an alternating even/odd sequence. With that scheme it was // possible to check for validity by referencing a table that had the highest // group number assigned for a given area number. (This was something that // Chromium never did though, because the "high group" values were constantly // changing.) // // However, starting on 25 June 2011 the SSA began issuing SSNs randomly from // all areas and groups. Group numbers and serial numbers of zero remain // invalid, and areas 000, 666, and 900-999 remain invalid. // // References for current practices: // http://www.socialsecurity.gov/employer/randomization.html // http://www.socialsecurity.gov/employer/randomizationfaqs.html // // References for historic practices: // http://www.socialsecurity.gov/history/ssn/geocard.html // http://www.socialsecurity.gov/employer/stateweb.htm // http://www.socialsecurity.gov/employer/ssnvhighgroup.htm if (number_string.length() != 9 || !IsStringASCII(number_string)) return false; int area; if (!base::StringToInt(StringPiece16(number_string.begin(), number_string.begin() + 3), &area)) { return false; } if (area < 1 || area == 666 || area >= 900) { return false; } int group; if (!base::StringToInt(StringPiece16(number_string.begin() + 3, number_string.begin() + 5), &group) || group == 0) { return false; } int serial; if (!base::StringToInt(StringPiece16(number_string.begin() + 5, number_string.begin() + 9), &serial) || serial == 0) { return false; } return true; } bool IsTextField(const FormField& field) { return field.form_control_type == ASCIIToUTF16("text") || field.form_control_type == ASCIIToUTF16("search") || field.form_control_type == ASCIIToUTF16("tel") || field.form_control_type == ASCIIToUTF16("url") || field.form_control_type == ASCIIToUTF16("email") || field.form_control_type == ASCIIToUTF16("text"); } } // namespace AutocompleteHistoryManager::AutocompleteHistoryManager( WebContents* web_contents) : content::WebContentsObserver(web_contents), pending_query_handle_(0), query_id_(0), external_delegate_(NULL) { profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); // May be NULL in unit tests. web_data_service_ = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); autofill_enabled_.Init(prefs::kAutofillEnabled, profile_->GetPrefs(), NULL); } AutocompleteHistoryManager::~AutocompleteHistoryManager() { CancelPendingQuery(); } bool AutocompleteHistoryManager::OnMessageReceived( const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(AutocompleteHistoryManager, message) IPC_MESSAGE_HANDLER(AutofillHostMsg_RemoveAutocompleteEntry, OnRemoveAutocompleteEntry) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void AutocompleteHistoryManager::OnFormSubmitted(const FormData& form) { if (!*autofill_enabled_) return; if (profile_->IsOffTheRecord()) return; // Don't save data that was submitted through JavaScript. if (!form.user_submitted) return; // We put the following restriction on stored FormFields: // - non-empty name // - non-empty value // - text field // - value is not a credit card number // - value is not a SSN std::vector values; for (std::vector::const_iterator iter = form.fields.begin(); iter != form.fields.end(); ++iter) { if (!iter->value.empty() && !iter->name.empty() && IsTextField(*iter) && !CreditCard::IsValidCreditCardNumber(iter->value) && !IsSSN(iter->value)) { values.push_back(*iter); } } if (!values.empty() && web_data_service_.get()) web_data_service_->AddFormFields(values); } void AutocompleteHistoryManager::OnRemoveAutocompleteEntry( const string16& name, const string16& value) { if (web_data_service_.get()) web_data_service_->RemoveFormValueForElementName(name, value); } void AutocompleteHistoryManager::OnGetAutocompleteSuggestions( int query_id, const string16& name, const string16& prefix, const std::vector& autofill_values, const std::vector& autofill_labels, const std::vector& autofill_icons, const std::vector& autofill_unique_ids) { CancelPendingQuery(); query_id_ = query_id; autofill_values_ = autofill_values; autofill_labels_ = autofill_labels; autofill_icons_ = autofill_icons; autofill_unique_ids_ = autofill_unique_ids; if (!*autofill_enabled_) { SendSuggestions(NULL); return; } if (web_data_service_.get()) { pending_query_handle_ = web_data_service_->GetFormValuesForElementName( name, prefix, kMaxAutocompleteMenuItems, this); } } void AutocompleteHistoryManager::OnWebDataServiceRequestDone( WebDataService::Handle h, const WDTypedResult* result) { DCHECK(pending_query_handle_); pending_query_handle_ = 0; if (!*autofill_enabled_) { SendSuggestions(NULL); return; } DCHECK(result); // Returning early here if |result| is NULL. We've seen this happen on // Linux due to NFS dismounting and causing sql failures. // See http://crbug.com/68783. if (!result) { SendSuggestions(NULL); return; } DCHECK_EQ(AUTOFILL_VALUE_RESULT, result->GetType()); const WDResult >* autofill_result = static_cast >*>(result); std::vector suggestions = autofill_result->GetValue(); SendSuggestions(&suggestions); } void AutocompleteHistoryManager::SetExternalDelegate( AutofillExternalDelegate* delegate) { external_delegate_ = delegate; } AutocompleteHistoryManager::AutocompleteHistoryManager( WebContents* web_contents, Profile* profile, WebDataService* wds) : content::WebContentsObserver(web_contents), profile_(profile), web_data_service_(wds), pending_query_handle_(0), query_id_(0), external_delegate_(NULL) { autofill_enabled_.Init( prefs::kAutofillEnabled, profile_->GetPrefs(), NULL); } void AutocompleteHistoryManager::CancelPendingQuery() { if (pending_query_handle_) { SendSuggestions(NULL); if (web_data_service_.get()) web_data_service_->CancelRequest(pending_query_handle_); pending_query_handle_ = 0; } } void AutocompleteHistoryManager::SendSuggestions( const std::vector* suggestions) { if (suggestions) { // Combine Autofill and Autocomplete values into values and labels. for (size_t i = 0; i < suggestions->size(); ++i) { bool unique = true; for (size_t j = 0; j < autofill_values_.size(); ++j) { // Don't add duplicate values. if (autofill_values_[j] == (*suggestions)[i]) { unique = false; break; } } if (unique) { autofill_values_.push_back((*suggestions)[i]); autofill_labels_.push_back(string16()); autofill_icons_.push_back(string16()); autofill_unique_ids_.push_back(0); // 0 means no profile. } } } if (external_delegate_) { external_delegate_->OnSuggestionsReturned( query_id_, autofill_values_, autofill_labels_, autofill_icons_, autofill_unique_ids_); } else { Send(new AutofillMsg_SuggestionsReturned(routing_id(), query_id_, autofill_values_, autofill_labels_, autofill_icons_, autofill_unique_ids_)); } query_id_ = 0; autofill_values_.clear(); autofill_labels_.clear(); autofill_icons_.clear(); autofill_unique_ids_.clear(); }