| 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/autofill/autocomplete_history_manager.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/prefs/pref_service.h" | |
| 10 #include "base/string16.h" | |
| 11 #include "base/strings/string_number_conversions.h" | |
| 12 #include "base/utf_string_conversions.h" | |
| 13 #include "chrome/browser/autofill/autofill_external_delegate.h" | |
| 14 #include "chrome/browser/autofill/validation.h" | |
| 15 #include "components/autofill/common/autofill_messages.h" | |
| 16 #include "components/autofill/common/autofill_pref_names.h" | |
| 17 #include "components/autofill/common/form_data.h" | |
| 18 #include "components/user_prefs/user_prefs.h" | |
| 19 #include "content/public/browser/browser_context.h" | |
| 20 #include "content/public/browser/render_view_host.h" | |
| 21 #include "content/public/browser/web_contents.h" | |
| 22 | |
| 23 using base::StringPiece16; | |
| 24 using content::BrowserContext; | |
| 25 using content::WebContents; | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // Limit on the number of suggestions to appear in the pop-up menu under an | |
| 30 // text input element in a form. | |
| 31 const int kMaxAutocompleteMenuItems = 6; | |
| 32 | |
| 33 // The separator characters for SSNs. | |
| 34 const char16 kSSNSeparators[] = {' ', '-', 0}; | |
| 35 | |
| 36 bool IsSSN(const string16& text) { | |
| 37 string16 number_string; | |
| 38 RemoveChars(text, kSSNSeparators, &number_string); | |
| 39 | |
| 40 // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S = | |
| 41 // serial number). The validation we do here is simply checking if the area, | |
| 42 // group, and serial numbers are valid. | |
| 43 // | |
| 44 // Historically, the area number was assigned per state, with the group number | |
| 45 // ascending in an alternating even/odd sequence. With that scheme it was | |
| 46 // possible to check for validity by referencing a table that had the highest | |
| 47 // group number assigned for a given area number. (This was something that | |
| 48 // Chromium never did though, because the "high group" values were constantly | |
| 49 // changing.) | |
| 50 // | |
| 51 // However, starting on 25 June 2011 the SSA began issuing SSNs randomly from | |
| 52 // all areas and groups. Group numbers and serial numbers of zero remain | |
| 53 // invalid, and areas 000, 666, and 900-999 remain invalid. | |
| 54 // | |
| 55 // References for current practices: | |
| 56 // http://www.socialsecurity.gov/employer/randomization.html | |
| 57 // http://www.socialsecurity.gov/employer/randomizationfaqs.html | |
| 58 // | |
| 59 // References for historic practices: | |
| 60 // http://www.socialsecurity.gov/history/ssn/geocard.html | |
| 61 // http://www.socialsecurity.gov/employer/stateweb.htm | |
| 62 // http://www.socialsecurity.gov/employer/ssnvhighgroup.htm | |
| 63 | |
| 64 if (number_string.length() != 9 || !IsStringASCII(number_string)) | |
| 65 return false; | |
| 66 | |
| 67 int area; | |
| 68 if (!base::StringToInt(StringPiece16(number_string.begin(), | |
| 69 number_string.begin() + 3), | |
| 70 &area)) { | |
| 71 return false; | |
| 72 } | |
| 73 if (area < 1 || | |
| 74 area == 666 || | |
| 75 area >= 900) { | |
| 76 return false; | |
| 77 } | |
| 78 | |
| 79 int group; | |
| 80 if (!base::StringToInt(StringPiece16(number_string.begin() + 3, | |
| 81 number_string.begin() + 5), | |
| 82 &group) | |
| 83 || group == 0) { | |
| 84 return false; | |
| 85 } | |
| 86 | |
| 87 int serial; | |
| 88 if (!base::StringToInt(StringPiece16(number_string.begin() + 5, | |
| 89 number_string.begin() + 9), | |
| 90 &serial) | |
| 91 || serial == 0) { | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 return true; | |
| 96 } | |
| 97 | |
| 98 bool IsTextField(const FormFieldData& field) { | |
| 99 return | |
| 100 field.form_control_type == "text" || | |
| 101 field.form_control_type == "search" || | |
| 102 field.form_control_type == "tel" || | |
| 103 field.form_control_type == "url" || | |
| 104 field.form_control_type == "email" || | |
| 105 field.form_control_type == "text"; | |
| 106 } | |
| 107 | |
| 108 } // namespace | |
| 109 | |
| 110 AutocompleteHistoryManager::AutocompleteHistoryManager( | |
| 111 WebContents* web_contents) | |
| 112 : content::WebContentsObserver(web_contents), | |
| 113 browser_context_(web_contents->GetBrowserContext()), | |
| 114 autofill_data_( | |
| 115 AutofillWebDataService::FromBrowserContext(browser_context_)), | |
| 116 pending_query_handle_(0), | |
| 117 query_id_(0), | |
| 118 external_delegate_(NULL) { | |
| 119 autofill_enabled_.Init( | |
| 120 prefs::kAutofillEnabled, | |
| 121 components::UserPrefs::Get(browser_context_)); | |
| 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 WebDataServiceBase::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 (autofill_data_.get()) { | |
| 187 pending_query_handle_ = autofill_data_->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 (browser_context_->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<FormFieldData> values; | |
| 210 for (std::vector<FormFieldData>::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 !autofill::IsValidCreditCardNumber(iter->value) && | |
| 217 !IsSSN(iter->value)) { | |
| 218 values.push_back(*iter); | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 if (!values.empty() && autofill_data_.get()) | |
| 223 autofill_data_->AddFormFields(values); | |
| 224 } | |
| 225 | |
| 226 void AutocompleteHistoryManager::OnRemoveAutocompleteEntry( | |
| 227 const string16& name, const string16& value) { | |
| 228 if (autofill_data_.get()) | |
| 229 autofill_data_->RemoveFormValueForElementName(name, value); | |
| 230 } | |
| 231 | |
| 232 void AutocompleteHistoryManager::SetExternalDelegate( | |
| 233 AutofillExternalDelegate* delegate) { | |
| 234 external_delegate_ = delegate; | |
| 235 } | |
| 236 | |
| 237 void AutocompleteHistoryManager::CancelPendingQuery() { | |
| 238 if (pending_query_handle_) { | |
| 239 if (autofill_data_) | |
| 240 autofill_data_->CancelRequest(pending_query_handle_); | |
| 241 pending_query_handle_ = 0; | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 void AutocompleteHistoryManager::SendSuggestions( | |
| 246 const std::vector<string16>* suggestions) { | |
| 247 if (suggestions) { | |
| 248 // Combine Autofill and Autocomplete values into values and labels. | |
| 249 for (size_t i = 0; i < suggestions->size(); ++i) { | |
| 250 bool unique = true; | |
| 251 for (size_t j = 0; j < autofill_values_.size(); ++j) { | |
| 252 // Don't add duplicate values. | |
| 253 if (autofill_values_[j] == (*suggestions)[i]) { | |
| 254 unique = false; | |
| 255 break; | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 if (unique) { | |
| 260 autofill_values_.push_back((*suggestions)[i]); | |
| 261 autofill_labels_.push_back(string16()); | |
| 262 autofill_icons_.push_back(string16()); | |
| 263 autofill_unique_ids_.push_back(0); // 0 means no profile. | |
| 264 } | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 if (external_delegate_) { | |
| 269 external_delegate_->OnSuggestionsReturned( | |
| 270 query_id_, | |
| 271 autofill_values_, | |
| 272 autofill_labels_, | |
| 273 autofill_icons_, | |
| 274 autofill_unique_ids_); | |
| 275 } else { | |
| 276 Send(new AutofillMsg_SuggestionsReturned(routing_id(), | |
| 277 query_id_, | |
| 278 autofill_values_, | |
| 279 autofill_labels_, | |
| 280 autofill_icons_, | |
| 281 autofill_unique_ids_)); | |
| 282 } | |
| 283 | |
| 284 query_id_ = 0; | |
| 285 autofill_values_.clear(); | |
| 286 autofill_labels_.clear(); | |
| 287 autofill_icons_.clear(); | |
| 288 autofill_unique_ids_.clear(); | |
| 289 } | |
| OLD | NEW |