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 |