| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/autocomplete_history_manager.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/string16.h" | |
| 10 #include "base/string_number_conversions.h" | |
| 11 #include "base/utf_string_conversions.h" | |
| 12 #include "chrome/browser/autofill/autofill_external_delegate.h" | |
| 13 #include "chrome/browser/autofill/credit_card.h" | |
| 14 #include "chrome/browser/prefs/pref_service.h" | |
| 15 #include "chrome/browser/profiles/profile.h" | |
| 16 #include "chrome/browser/webdata/web_data_service_factory.h" | |
| 17 #include "chrome/common/autofill_messages.h" | |
| 18 #include "chrome/common/pref_names.h" | |
| 19 #include "content/public/browser/render_view_host.h" | |
| 20 #include "content/public/browser/web_contents.h" | |
| 21 #include "webkit/forms/form_data.h" | |
| 22 | |
| 23 using base::StringPiece16; | |
| 24 using content::WebContents; | |
| 25 using webkit::forms::FormData; | |
| 26 using webkit::forms::FormField; | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 // Limit on the number of suggestions to appear in the pop-up menu under an | |
| 31 // text input element in a form. | |
| 32 const int kMaxAutocompleteMenuItems = 6; | |
| 33 | |
| 34 // The separator characters for SSNs. | |
| 35 const char16 kSSNSeparators[] = {' ', '-', 0}; | |
| 36 | |
| 37 bool IsSSN(const string16& text) { | |
| 38 string16 number_string; | |
| 39 RemoveChars(text, kSSNSeparators, &number_string); | |
| 40 | |
| 41 // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S = | |
| 42 // serial number). The validation we do here is simply checking if the area, | |
| 43 // group, and serial numbers are valid. | |
| 44 // | |
| 45 // Historically, the area number was assigned per state, with the group number | |
| 46 // ascending in an alternating even/odd sequence. With that scheme it was | |
| 47 // possible to check for validity by referencing a table that had the highest | |
| 48 // group number assigned for a given area number. (This was something that | |
| 49 // Chromium never did though, because the "high group" values were constantly | |
| 50 // changing.) | |
| 51 // | |
| 52 // However, starting on 25 June 2011 the SSA began issuing SSNs randomly from | |
| 53 // all areas and groups. Group numbers and serial numbers of zero remain | |
| 54 // invalid, and areas 000, 666, and 900-999 remain invalid. | |
| 55 // | |
| 56 // References for current practices: | |
| 57 // http://www.socialsecurity.gov/employer/randomization.html | |
| 58 // http://www.socialsecurity.gov/employer/randomizationfaqs.html | |
| 59 // | |
| 60 // References for historic practices: | |
| 61 // http://www.socialsecurity.gov/history/ssn/geocard.html | |
| 62 // http://www.socialsecurity.gov/employer/stateweb.htm | |
| 63 // http://www.socialsecurity.gov/employer/ssnvhighgroup.htm | |
| 64 | |
| 65 if (number_string.length() != 9 || !IsStringASCII(number_string)) | |
| 66 return false; | |
| 67 | |
| 68 int area; | |
| 69 if (!base::StringToInt(StringPiece16(number_string.begin(), | |
| 70 number_string.begin() + 3), | |
| 71 &area)) { | |
| 72 return false; | |
| 73 } | |
| 74 if (area < 1 || | |
| 75 area == 666 || | |
| 76 area >= 900) { | |
| 77 return false; | |
| 78 } | |
| 79 | |
| 80 int group; | |
| 81 if (!base::StringToInt(StringPiece16(number_string.begin() + 3, | |
| 82 number_string.begin() + 5), | |
| 83 &group) | |
| 84 || group == 0) { | |
| 85 return false; | |
| 86 } | |
| 87 | |
| 88 int serial; | |
| 89 if (!base::StringToInt(StringPiece16(number_string.begin() + 5, | |
| 90 number_string.begin() + 9), | |
| 91 &serial) | |
| 92 || serial == 0) { | |
| 93 return false; | |
| 94 } | |
| 95 | |
| 96 return true; | |
| 97 } | |
| 98 | |
| 99 bool IsTextField(const FormField& field) { | |
| 100 return | |
| 101 field.form_control_type == ASCIIToUTF16("text") || | |
| 102 field.form_control_type == ASCIIToUTF16("search") || | |
| 103 field.form_control_type == ASCIIToUTF16("tel") || | |
| 104 field.form_control_type == ASCIIToUTF16("url") || | |
| 105 field.form_control_type == ASCIIToUTF16("email") || | |
| 106 field.form_control_type == ASCIIToUTF16("text"); | |
| 107 } | |
| 108 | |
| 109 } // namespace | |
| 110 | |
| 111 AutocompleteHistoryManager::AutocompleteHistoryManager( | |
| 112 WebContents* web_contents) | |
| 113 : content::WebContentsObserver(web_contents), | |
| 114 pending_query_handle_(0), | |
| 115 query_id_(0), | |
| 116 external_delegate_(NULL) { | |
| 117 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
| 118 // May be NULL in unit tests. | |
| 119 web_data_service_ = WebDataServiceFactory::GetForProfile( | |
| 120 profile_, Profile::EXPLICIT_ACCESS); | |
| 121 autofill_enabled_.Init(prefs::kAutofillEnabled, profile_->GetPrefs(), NULL); | |
| 122 } | |
| 123 | |
| 124 AutocompleteHistoryManager::~AutocompleteHistoryManager() { | |
| 125 CancelPendingQuery(); | |
| 126 } | |
| 127 | |
| 128 bool AutocompleteHistoryManager::OnMessageReceived( | |
| 129 const IPC::Message& message) { | |
| 130 bool handled = true; | |
| 131 IPC_BEGIN_MESSAGE_MAP(AutocompleteHistoryManager, message) | |
| 132 IPC_MESSAGE_HANDLER(AutofillHostMsg_RemoveAutocompleteEntry, | |
| 133 OnRemoveAutocompleteEntry) | |
| 134 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 135 IPC_END_MESSAGE_MAP() | |
| 136 return handled; | |
| 137 } | |
| 138 | |
| 139 void AutocompleteHistoryManager::OnWebDataServiceRequestDone( | |
| 140 WebDataService::Handle h, | |
| 141 const WDTypedResult* result) { | |
| 142 DCHECK(pending_query_handle_); | |
| 143 pending_query_handle_ = 0; | |
| 144 | |
| 145 if (!*autofill_enabled_) { | |
| 146 SendSuggestions(NULL); | |
| 147 return; | |
| 148 } | |
| 149 | |
| 150 DCHECK(result); | |
| 151 // Returning early here if |result| is NULL. We've seen this happen on | |
| 152 // Linux due to NFS dismounting and causing sql failures. | |
| 153 // See http://crbug.com/68783. | |
| 154 if (!result) { | |
| 155 SendSuggestions(NULL); | |
| 156 return; | |
| 157 } | |
| 158 | |
| 159 DCHECK_EQ(AUTOFILL_VALUE_RESULT, result->GetType()); | |
| 160 const WDResult<std::vector<string16> >* autofill_result = | |
| 161 static_cast<const WDResult<std::vector<string16> >*>(result); | |
| 162 std::vector<string16> suggestions = autofill_result->GetValue(); | |
| 163 SendSuggestions(&suggestions); | |
| 164 } | |
| 165 | |
| 166 void AutocompleteHistoryManager::OnGetAutocompleteSuggestions( | |
| 167 int query_id, | |
| 168 const string16& name, | |
| 169 const string16& prefix, | |
| 170 const std::vector<string16>& autofill_values, | |
| 171 const std::vector<string16>& autofill_labels, | |
| 172 const std::vector<string16>& autofill_icons, | |
| 173 const std::vector<int>& autofill_unique_ids) { | |
| 174 CancelPendingQuery(); | |
| 175 | |
| 176 query_id_ = query_id; | |
| 177 autofill_values_ = autofill_values; | |
| 178 autofill_labels_ = autofill_labels; | |
| 179 autofill_icons_ = autofill_icons; | |
| 180 autofill_unique_ids_ = autofill_unique_ids; | |
| 181 if (!*autofill_enabled_) { | |
| 182 SendSuggestions(NULL); | |
| 183 return; | |
| 184 } | |
| 185 | |
| 186 if (web_data_service_.get()) { | |
| 187 pending_query_handle_ = web_data_service_->GetFormValuesForElementName( | |
| 188 name, prefix, kMaxAutocompleteMenuItems, this); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 void AutocompleteHistoryManager::OnFormSubmitted(const FormData& form) { | |
| 193 if (!*autofill_enabled_) | |
| 194 return; | |
| 195 | |
| 196 if (profile_->IsOffTheRecord()) | |
| 197 return; | |
| 198 | |
| 199 // Don't save data that was submitted through JavaScript. | |
| 200 if (!form.user_submitted) | |
| 201 return; | |
| 202 | |
| 203 // We put the following restriction on stored FormFields: | |
| 204 // - non-empty name | |
| 205 // - non-empty value | |
| 206 // - text field | |
| 207 // - value is not a credit card number | |
| 208 // - value is not a SSN | |
| 209 std::vector<FormField> values; | |
| 210 for (std::vector<FormField>::const_iterator iter = | |
| 211 form.fields.begin(); | |
| 212 iter != form.fields.end(); ++iter) { | |
| 213 if (!iter->value.empty() && | |
| 214 !iter->name.empty() && | |
| 215 IsTextField(*iter) && | |
| 216 !CreditCard::IsValidCreditCardNumber(iter->value) && | |
| 217 !IsSSN(iter->value)) { | |
| 218 values.push_back(*iter); | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 if (!values.empty() && web_data_service_.get()) | |
| 223 web_data_service_->AddFormFields(values); | |
| 224 } | |
| 225 | |
| 226 void AutocompleteHistoryManager::OnRemoveAutocompleteEntry( | |
| 227 const string16& name, const string16& value) { | |
| 228 if (web_data_service_.get()) | |
| 229 web_data_service_->RemoveFormValueForElementName(name, value); | |
| 230 } | |
| 231 | |
| 232 void AutocompleteHistoryManager::SetExternalDelegate( | |
| 233 AutofillExternalDelegate* delegate) { | |
| 234 external_delegate_ = delegate; | |
| 235 } | |
| 236 | |
| 237 AutocompleteHistoryManager::AutocompleteHistoryManager( | |
| 238 WebContents* web_contents, | |
| 239 Profile* profile, | |
| 240 WebDataService* wds) | |
| 241 : content::WebContentsObserver(web_contents), | |
| 242 profile_(profile), | |
| 243 web_data_service_(wds), | |
| 244 pending_query_handle_(0), | |
| 245 query_id_(0), | |
| 246 external_delegate_(NULL) { | |
| 247 autofill_enabled_.Init( | |
| 248 prefs::kAutofillEnabled, profile_->GetPrefs(), NULL); | |
| 249 } | |
| 250 | |
| 251 void AutocompleteHistoryManager::CancelPendingQuery() { | |
| 252 if (pending_query_handle_) { | |
| 253 SendSuggestions(NULL); | |
| 254 if (web_data_service_.get()) | |
| 255 web_data_service_->CancelRequest(pending_query_handle_); | |
| 256 pending_query_handle_ = 0; | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 void AutocompleteHistoryManager::SendSuggestions( | |
| 261 const std::vector<string16>* suggestions) { | |
| 262 if (suggestions) { | |
| 263 // Combine Autofill and Autocomplete values into values and labels. | |
| 264 for (size_t i = 0; i < suggestions->size(); ++i) { | |
| 265 bool unique = true; | |
| 266 for (size_t j = 0; j < autofill_values_.size(); ++j) { | |
| 267 // Don't add duplicate values. | |
| 268 if (autofill_values_[j] == (*suggestions)[i]) { | |
| 269 unique = false; | |
| 270 break; | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 if (unique) { | |
| 275 autofill_values_.push_back((*suggestions)[i]); | |
| 276 autofill_labels_.push_back(string16()); | |
| 277 autofill_icons_.push_back(string16()); | |
| 278 autofill_unique_ids_.push_back(0); // 0 means no profile. | |
| 279 } | |
| 280 } | |
| 281 } | |
| 282 | |
| 283 if (external_delegate_) { | |
| 284 external_delegate_->OnSuggestionsReturned( | |
| 285 query_id_, | |
| 286 autofill_values_, | |
| 287 autofill_labels_, | |
| 288 autofill_icons_, | |
| 289 autofill_unique_ids_); | |
| 290 } else { | |
| 291 Send(new AutofillMsg_SuggestionsReturned(routing_id(), | |
| 292 query_id_, | |
| 293 autofill_values_, | |
| 294 autofill_labels_, | |
| 295 autofill_icons_, | |
| 296 autofill_unique_ids_)); | |
| 297 } | |
| 298 | |
| 299 query_id_ = 0; | |
| 300 autofill_values_.clear(); | |
| 301 autofill_labels_.clear(); | |
| 302 autofill_icons_.clear(); | |
| 303 autofill_unique_ids_.clear(); | |
| 304 } | |
| OLD | NEW |