OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/renderer/autofill/autofill_agent.h" | 5 #include "chrome/renderer/autofill/autofill_agent.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
9 #include "base/time.h" | 9 #include "base/time.h" |
10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
11 #include "chrome/common/autofill_messages.h" | 11 #include "chrome/common/autofill_messages.h" |
12 #include "chrome/common/chrome_constants.h" | 12 #include "chrome/common/chrome_constants.h" |
13 #include "chrome/renderer/autofill/form_autofill_util.h" | 13 #include "chrome/renderer/autofill/form_autofill_util.h" |
14 #include "chrome/renderer/autofill/password_autofill_manager.h" | 14 #include "chrome/renderer/autofill/password_autofill_manager.h" |
15 #include "content/public/renderer/render_view.h" | 15 #include "content/public/renderer/render_view.h" |
16 #include "grit/chromium_strings.h" | 16 #include "grit/chromium_strings.h" |
17 #include "grit/generated_resources.h" | 17 #include "grit/generated_resources.h" |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h" | |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement .h" | 20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement .h" |
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | 21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | 22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" |
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" | 23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" |
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeCollection.h" | |
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebOptionElement.h" | |
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | 26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" |
24 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h" | |
25 #include "ui/base/keycodes/keyboard_codes.h" | 27 #include "ui/base/keycodes/keyboard_codes.h" |
26 #include "ui/base/l10n/l10n_util.h" | 28 #include "ui/base/l10n/l10n_util.h" |
27 #include "webkit/forms/form_data.h" | 29 #include "webkit/forms/form_data.h" |
28 #include "webkit/forms/form_data_predictions.h" | 30 #include "webkit/forms/form_data_predictions.h" |
29 #include "webkit/forms/form_field.h" | 31 #include "webkit/forms/form_field.h" |
30 #include "webkit/forms/password_form.h" | 32 #include "webkit/forms/password_form.h" |
31 | 33 |
34 using WebKit::WebAutofillClient; | |
32 using WebKit::WebFormControlElement; | 35 using WebKit::WebFormControlElement; |
33 using WebKit::WebFormElement; | 36 using WebKit::WebFormElement; |
34 using WebKit::WebFrame; | 37 using WebKit::WebFrame; |
35 using WebKit::WebInputElement; | 38 using WebKit::WebInputElement; |
36 using WebKit::WebKeyboardEvent; | 39 using WebKit::WebKeyboardEvent; |
37 using WebKit::WebNode; | 40 using WebKit::WebNode; |
41 using WebKit::WebNodeCollection; | |
42 using WebKit::WebOptionElement; | |
38 using WebKit::WebString; | 43 using WebKit::WebString; |
39 using webkit::forms::FormData; | 44 using webkit::forms::FormData; |
40 using webkit::forms::FormDataPredictions; | 45 using webkit::forms::FormDataPredictions; |
41 | 46 |
42 namespace { | 47 namespace { |
43 | 48 |
44 // The size above which we stop triggering autofill for an input text field | 49 // The size above which we stop triggering autofill for an input text field |
45 // (so to avoid sending long strings through IPC). | 50 // (so to avoid sending long strings through IPC). |
46 const size_t kMaximumTextSizeForAutofill = 1000; | 51 const size_t kMaximumTextSizeForAutofill = 1000; |
47 | 52 |
53 void AppendDataListSuggestions(const WebKit::WebInputElement& element, | |
54 std::vector<string16>* values, | |
55 std::vector<string16>* labels, | |
56 std::vector<string16>* icons, | |
57 std::vector<int>* item_ids) { | |
58 WebNodeCollection options = element.dataListOptions(); | |
59 if (options.isNull()) | |
60 return; | |
61 | |
62 WebOptionElement option = options.firstItem().to<WebOptionElement>(); | |
63 while (!option.isNull()) { | |
64 values->push_back(option.value()); | |
65 if (option.value() != option.label()) | |
66 labels->push_back(option.label()); | |
67 else | |
68 labels->push_back(string16()); | |
69 icons->push_back(string16()); | |
70 item_ids->push_back(WebAutofillClient::DataListEntryMenuItemID); | |
71 option = options.nextItem().to<WebOptionElement>(); | |
72 } | |
73 } | |
74 | |
48 } // namespace | 75 } // namespace |
49 | 76 |
50 namespace autofill { | 77 namespace autofill { |
51 | 78 |
52 AutofillAgent::AutofillAgent( | 79 AutofillAgent::AutofillAgent( |
53 content::RenderView* render_view, | 80 content::RenderView* render_view, |
54 PasswordAutofillManager* password_autofill_manager) | 81 PasswordAutofillManager* password_autofill_manager) |
55 : content::RenderViewObserver(render_view), | 82 : content::RenderViewObserver(render_view), |
56 password_autofill_manager_(password_autofill_manager), | 83 password_autofill_manager_(password_autofill_manager), |
57 autofill_query_id_(0), | 84 autofill_query_id_(0), |
58 autofill_action_(AUTOFILL_NONE), | 85 autofill_action_(AUTOFILL_NONE), |
59 display_warning_if_disabled_(false), | 86 display_warning_if_disabled_(false), |
60 was_query_node_autofilled_(false), | 87 was_query_node_autofilled_(false), |
61 suggestions_clear_index_(-1), | |
62 suggestions_options_index_(-1), | |
63 has_shown_autofill_popup_for_current_edit_(false), | 88 has_shown_autofill_popup_for_current_edit_(false), |
64 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | 89 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
65 render_view->GetWebView()->setAutofillClient(this); | 90 render_view->GetWebView()->setAutofillClient(this); |
66 } | 91 } |
67 | 92 |
68 AutofillAgent::~AutofillAgent() {} | 93 AutofillAgent::~AutofillAgent() {} |
69 | 94 |
70 bool AutofillAgent::OnMessageReceived(const IPC::Message& message) { | 95 bool AutofillAgent::OnMessageReceived(const IPC::Message& message) { |
71 bool handled = true; | 96 bool handled = true; |
72 IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message) | 97 IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message) |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
139 | 164 |
140 bool AutofillAgent::InputElementLostFocus() { | 165 bool AutofillAgent::InputElementLostFocus() { |
141 Send(new AutofillHostMsg_HideAutofillPopup(routing_id())); | 166 Send(new AutofillHostMsg_HideAutofillPopup(routing_id())); |
142 | 167 |
143 return false; | 168 return false; |
144 } | 169 } |
145 | 170 |
146 void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node, | 171 void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node, |
147 const WebString& value, | 172 const WebString& value, |
148 const WebString& label, | 173 const WebString& label, |
149 int unique_id, | 174 int item_id, |
150 unsigned index) { | 175 unsigned index) { |
151 if (password_autofill_manager_->DidAcceptAutofillSuggestion(node, value)) | 176 if (password_autofill_manager_->DidAcceptAutofillSuggestion(node, value)) |
152 return; | 177 return; |
153 | 178 |
154 DCHECK(node == autofill_query_element_); | 179 DCHECK(node == element_); |
155 | 180 |
156 if (suggestions_options_index_ != -1 && | 181 switch (item_id) { |
157 index == static_cast<unsigned>(suggestions_options_index_)) { | 182 case WebAutofillClient::WarningMessageMenuItemID: |
158 // User selected 'Autofill Options'. | 183 case WebAutofillClient::SeparatorMenuItemID: |
159 Send(new AutofillHostMsg_ShowAutofillDialog(routing_id())); | 184 NOTREACHED(); |
160 } else if (suggestions_clear_index_ != -1 && | 185 break; |
161 index == static_cast<unsigned>(suggestions_clear_index_)) { | 186 case WebAutofillClient::AutofillOptionsMenuItemID: |
162 // User selected 'Clear form'. | 187 // User selected 'Autofill Options'. |
163 form_cache_.ClearFormWithElement(autofill_query_element_); | 188 Send(new AutofillHostMsg_ShowAutofillDialog(routing_id())); |
164 } else if (!unique_id) { | 189 break; |
165 // User selected an Autocomplete entry, so we fill directly. | 190 case WebAutofillClient::ClearFormMenuItemID: |
166 WebInputElement element = node.toConst<WebInputElement>(); | 191 // User selected 'Clear form'. |
167 SetNodeText(value, &element); | 192 form_cache_.ClearFormWithElement(element_); |
168 } else { | 193 break; |
169 // Fill the values for the whole form. | 194 case WebAutofillClient::AutocompleteEntryMenuItemID: |
170 FillAutofillFormData(node, unique_id, AUTOFILL_FILL); | 195 case WebAutofillClient::PasswordEntryMenuItemID: |
196 case WebAutofillClient::DataListEntryMenuItemID: | |
197 // User selected an Autocomplete or password or datalist entry, so we | |
198 // fill directly. | |
199 SetNodeText(value, &element_); | |
200 break; | |
201 default: | |
202 // A positive item_id is a unique id for an autofill (vs. autocomplete) | |
203 // suggestion. | |
204 DCHECK_GT(item_id, 0); | |
205 // Fill the values for the whole form. | |
206 FillAutofillFormData(node, item_id, AUTOFILL_FILL); | |
171 } | 207 } |
172 | |
173 suggestions_clear_index_ = -1; | |
174 suggestions_options_index_ = -1; | |
175 } | 208 } |
176 | 209 |
177 void AutofillAgent::didSelectAutofillSuggestion(const WebNode& node, | 210 void AutofillAgent::didSelectAutofillSuggestion(const WebNode& node, |
178 const WebString& value, | 211 const WebString& value, |
179 const WebString& label, | 212 const WebString& label, |
180 int unique_id) { | 213 int item_id) { |
181 DCHECK_GE(unique_id, 0); | |
182 if (password_autofill_manager_->DidSelectAutofillSuggestion(node)) | 214 if (password_autofill_manager_->DidSelectAutofillSuggestion(node)) |
183 return; | 215 return; |
184 | 216 |
185 didClearAutofillSelection(node); | 217 didClearAutofillSelection(node); |
186 FillAutofillFormData(node, unique_id, AUTOFILL_PREVIEW); | 218 |
219 if (item_id > 0) | |
220 FillAutofillFormData(node, item_id, AUTOFILL_PREVIEW); | |
187 } | 221 } |
188 | 222 |
189 void AutofillAgent::didClearAutofillSelection(const WebNode& node) { | 223 void AutofillAgent::didClearAutofillSelection(const WebNode& node) { |
190 if (password_autofill_manager_->DidClearAutofillSelection(node)) | 224 if (password_autofill_manager_->DidClearAutofillSelection(node)) |
191 return; | 225 return; |
192 | 226 |
193 if (!autofill_query_element_.isNull() && node == autofill_query_element_) { | 227 if (!element_.isNull() && node == element_) { |
194 ClearPreviewedFormWithElement(autofill_query_element_, | 228 ClearPreviewedFormWithElement(element_, was_query_node_autofilled_); |
195 was_query_node_autofilled_); | |
196 } else { | 229 } else { |
197 // TODO(isherman): There seem to be rare cases where this code *is* | 230 // TODO(isherman): There seem to be rare cases where this code *is* |
198 // reachable: see [ http://crbug.com/96321#c6 ]. Ideally we would | 231 // reachable: see [ http://crbug.com/96321#c6 ]. Ideally we would |
199 // understand those cases and fix the code to avoid them. However, so far I | 232 // understand those cases and fix the code to avoid them. However, so far I |
200 // have been unable to reproduce such a case locally. If you hit this | 233 // have been unable to reproduce such a case locally. If you hit this |
201 // NOTREACHED(), please file a bug against me. | 234 // NOTREACHED(), please file a bug against me. |
202 NOTREACHED(); | 235 NOTREACHED(); |
203 } | 236 } |
204 } | 237 } |
205 | 238 |
206 void AutofillAgent::removeAutocompleteSuggestion(const WebString& name, | 239 void AutofillAgent::removeAutocompleteSuggestion(const WebString& name, |
207 const WebString& value) { | 240 const WebString& value) { |
208 // The index of clear & options will have shifted down. | |
209 if (suggestions_clear_index_ != -1) | |
210 suggestions_clear_index_--; | |
211 if (suggestions_options_index_ != -1) | |
212 suggestions_options_index_--; | |
213 | |
214 Send(new AutofillHostMsg_RemoveAutocompleteEntry(routing_id(), name, value)); | 241 Send(new AutofillHostMsg_RemoveAutocompleteEntry(routing_id(), name, value)); |
215 } | 242 } |
216 | 243 |
217 void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) { | 244 void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) { |
218 password_autofill_manager_->TextFieldDidEndEditing(element); | 245 password_autofill_manager_->TextFieldDidEndEditing(element); |
219 has_shown_autofill_popup_for_current_edit_ = false; | 246 has_shown_autofill_popup_for_current_edit_ = false; |
220 Send(new AutofillHostMsg_DidEndTextFieldEditing(routing_id())); | 247 Send(new AutofillHostMsg_DidEndTextFieldEditing(routing_id())); |
221 } | 248 } |
222 | 249 |
223 void AutofillAgent::textFieldDidChange(const WebInputElement& element) { | 250 void AutofillAgent::textFieldDidChange(const WebInputElement& element) { |
224 // We post a task for doing the Autofill as the caret position is not set | 251 // We post a task for doing the Autofill as the caret position is not set |
225 // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and | 252 // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and |
226 // it is needed to trigger autofill. | 253 // it is needed to trigger autofill. |
227 weak_ptr_factory_.InvalidateWeakPtrs(); | 254 weak_ptr_factory_.InvalidateWeakPtrs(); |
228 MessageLoop::current()->PostTask( | 255 MessageLoop::current()->PostTask( |
229 FROM_HERE, | 256 FROM_HERE, |
230 base::Bind(&AutofillAgent::TextFieldDidChangeImpl, | 257 base::Bind(&AutofillAgent::TextFieldDidChangeImpl, |
231 weak_ptr_factory_.GetWeakPtr(), element)); | 258 weak_ptr_factory_.GetWeakPtr(), element)); |
232 } | 259 } |
233 | 260 |
234 void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) { | 261 void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) { |
235 if (password_autofill_manager_->TextDidChangeInTextField(element)) { | 262 if (password_autofill_manager_->TextDidChangeInTextField(element)) { |
236 autofill_query_element_ = element; | 263 element_ = element; |
237 return; | 264 return; |
238 } | 265 } |
239 | 266 |
240 ShowSuggestions(element, false, true, false); | 267 ShowSuggestions(element, false, true, false); |
241 | 268 |
242 webkit::forms::FormData form; | 269 webkit::forms::FormData form; |
243 webkit::forms::FormField field; | 270 webkit::forms::FormField field; |
244 if (FindFormAndFieldForInputElement(element, &form, &field, REQUIRE_NONE)) { | 271 if (FindFormAndFieldForInputElement(element, &form, &field, REQUIRE_NONE)) { |
245 Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field, | 272 Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field, |
246 base::TimeTicks::Now())); | 273 base::TimeTicks::Now())); |
247 } | 274 } |
248 } | 275 } |
249 | 276 |
250 void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element, | 277 void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element, |
251 const WebKeyboardEvent& event) { | 278 const WebKeyboardEvent& event) { |
252 if (password_autofill_manager_->TextFieldHandlingKeyDown(element, event)) { | 279 if (password_autofill_manager_->TextFieldHandlingKeyDown(element, event)) { |
253 autofill_query_element_ = element; | 280 element_ = element; |
254 return; | 281 return; |
255 } | 282 } |
256 | 283 |
257 if (event.windowsKeyCode == ui::VKEY_DOWN || | 284 if (event.windowsKeyCode == ui::VKEY_DOWN || |
258 event.windowsKeyCode == ui::VKEY_UP) | 285 event.windowsKeyCode == ui::VKEY_UP) |
259 ShowSuggestions(element, true, true, true); | 286 ShowSuggestions(element, true, true, true); |
260 } | 287 } |
261 | 288 |
262 void AutofillAgent::OnSuggestionsReturned(int query_id, | 289 void AutofillAgent::OnSuggestionsReturned(int query_id, |
263 const std::vector<string16>& values, | 290 const std::vector<string16>& values, |
264 const std::vector<string16>& labels, | 291 const std::vector<string16>& labels, |
265 const std::vector<string16>& icons, | 292 const std::vector<string16>& icons, |
266 const std::vector<int>& unique_ids) { | 293 const std::vector<int>& unique_ids) { |
267 WebKit::WebView* web_view = render_view()->GetWebView(); | 294 if (query_id != autofill_query_id_) |
268 if (!web_view || query_id != autofill_query_id_) | |
269 return; | 295 return; |
270 | 296 |
271 if (values.empty()) { | |
272 // No suggestions, any popup currently showing is obsolete. | |
273 web_view->hidePopups(); | |
274 return; | |
275 } | |
276 | |
277 std::vector<string16> v(values); | 297 std::vector<string16> v(values); |
278 std::vector<string16> l(labels); | 298 std::vector<string16> l(labels); |
279 std::vector<string16> i(icons); | 299 std::vector<string16> i(icons); |
280 std::vector<int> ids(unique_ids); | 300 std::vector<int> ids(unique_ids); |
281 int separator_index = -1; | |
282 | 301 |
283 DCHECK_GT(ids.size(), 0U); | 302 // Only include "Autofill Options" special menu item if we have Autofill |
284 if (!autofill_query_element_.isNull() && | 303 // items, identified by |unique_ids| having at least one valid value. |
285 !autofill_query_element_.autoComplete()) { | 304 bool has_autofill_item = false; |
305 bool has_warning_item = false; | |
306 for (size_t j = 0; j < ids.size(); ++j) { | |
307 if (ids[j] > 0) | |
308 has_autofill_item = true; | |
309 else if (ids[j] == WebAutofillClient::WarningMessageMenuItemID) | |
310 has_warning_item = true; | |
311 if (has_autofill_item || has_warning_item) | |
312 break; | |
313 } | |
314 | |
315 if (!element_.isNull() && !element_.autoComplete() && !v.empty()) { | |
286 // If autofill is disabled and we had suggestions, show a warning instead. | 316 // If autofill is disabled and we had suggestions, show a warning instead. |
287 v.assign(1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)); | 317 v.assign(1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)); |
288 l.assign(1, string16()); | 318 l.assign(1, string16()); |
289 i.assign(1, string16()); | 319 i.assign(1, string16()); |
290 ids.assign(1, -1); | 320 ids.assign(1, WebAutofillClient::WarningMessageMenuItemID); |
291 } else if (ids[0] < 0 && ids.size() > 1) { | 321 } else if (ids.size() > 1 && |
292 // If we received a warning instead of suggestions from autofill but regular | 322 ids[0] == WebAutofillClient::WarningMessageMenuItemID) { |
293 // suggestions from autocomplete, don't show the autofill warning. | 323 // If we received an autofill warning plus some autocomplete suggestions, |
324 // remove the autofill warning. | |
294 v.erase(v.begin()); | 325 v.erase(v.begin()); |
295 l.erase(l.begin()); | 326 l.erase(l.begin()); |
296 i.erase(i.begin()); | 327 i.erase(i.begin()); |
297 ids.erase(ids.begin()); | 328 ids.erase(ids.begin()); |
298 } | 329 } |
Ilya Sherman
2012/04/13 21:39:47
If you keep the |has_warning_item| logic, these if
| |
299 | 330 |
300 // If we were about to show a warning and we shouldn't, don't. | 331 // If we were about to show a warning and we shouldn't, don't. |
301 if (ids[0] < 0 && !display_warning_if_disabled_) | 332 if (!display_warning_if_disabled_ && has_warning_item) { |
Ilya Sherman
2012/04/13 21:39:47
Rather than checking |has_warning_item|, you can c
keishi
2012/04/16 11:14:36
Done.
| |
302 return; | 333 v.clear(); |
303 | 334 l.clear(); |
304 // Only include "Autofill Options" special menu item if we have Autofill | 335 i.clear(); |
305 // items, identified by |unique_ids| having at least one valid value. | 336 ids.clear(); |
306 bool has_autofill_item = false; | |
307 for (size_t i = 0; i < ids.size(); ++i) { | |
308 if (ids[i] > 0) { | |
309 has_autofill_item = true; | |
310 break; | |
311 } | |
312 } | |
313 | |
314 // The form has been auto-filled, so give the user the chance to clear the | |
315 // form. Append the 'Clear form' menu item. | |
316 if (has_autofill_item && | |
317 FormWithElementIsAutofilled(autofill_query_element_)) { | |
318 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM)); | |
319 l.push_back(string16()); | |
320 i.push_back(string16()); | |
321 ids.push_back(0); | |
322 suggestions_clear_index_ = v.size() - 1; | |
323 separator_index = v.size() - 1; | |
324 } | 337 } |
325 | 338 |
326 if (has_autofill_item) { | 339 if (has_autofill_item) { |
340 v.push_back(string16()); | |
341 l.push_back(string16()); | |
342 i.push_back(string16()); | |
343 ids.push_back(WebAutofillClient::SeparatorMenuItemID); | |
344 | |
345 if (FormWithElementIsAutofilled(element_)) { | |
346 // The form has been auto-filled, so give the user the chance to clear the | |
347 // form. Append the 'Clear form' menu item. | |
348 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM)); | |
349 l.push_back(string16()); | |
350 i.push_back(string16()); | |
351 ids.push_back(WebAutofillClient::ClearFormMenuItemID); | |
352 } | |
353 | |
327 // Append the 'Chrome Autofill options' menu item; | 354 // Append the 'Chrome Autofill options' menu item; |
328 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP)); | 355 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP)); |
329 l.push_back(string16()); | 356 l.push_back(string16()); |
330 i.push_back(string16()); | 357 i.push_back(string16()); |
331 ids.push_back(0); | 358 ids.push_back(WebAutofillClient::AutofillOptionsMenuItemID); |
332 suggestions_options_index_ = v.size() - 1; | 359 } |
333 separator_index = values.size(); | 360 |
361 if (!element_.isNull() && element_.isFocusable()) | |
362 CombineDataListEntriesAndShow(element_, v, l, i, ids, has_autofill_item); | |
363 } | |
364 | |
365 void AutofillAgent::CombineDataListEntriesAndShow( | |
366 const WebKit::WebInputElement& element, | |
367 const std::vector<string16>& values, | |
368 const std::vector<string16>& labels, | |
369 const std::vector<string16>& icons, | |
370 const std::vector<int>& item_ids, | |
371 bool has_autofill_item) { | |
372 std::vector<string16> v; | |
373 std::vector<string16> l; | |
374 std::vector<string16> i; | |
375 std::vector<int> ids; | |
376 | |
377 AppendDataListSuggestions(element, &v, &l, &i, &ids); | |
378 | |
379 // If there are both <datalist> items and Autofill suggestions, add a | |
380 // separator between them. | |
381 if (!v.empty() && !values.empty()) { | |
382 v.push_back(string16()); | |
383 l.push_back(string16()); | |
384 i.push_back(string16()); | |
385 ids.push_back(WebAutofillClient::SeparatorMenuItemID); | |
386 } | |
387 | |
388 v.insert(v.end(), values.begin(), values.end()); | |
389 l.insert(l.end(), labels.begin(), labels.end()); | |
390 i.insert(i.end(), icons.begin(), icons.end()); | |
391 ids.insert(ids.end(), item_ids.begin(), item_ids.end()); | |
392 | |
393 WebKit::WebView* web_view = render_view()->GetWebView(); | |
394 if (!web_view) | |
395 return; | |
396 | |
397 if (v.empty()) { | |
398 // No suggestions, any popup currently showing is obsolete. | |
399 web_view->hidePopups(); | |
400 return; | |
334 } | 401 } |
335 | 402 |
336 // Send to WebKit for display. | 403 // Send to WebKit for display. |
337 if (!v.empty() && !autofill_query_element_.isNull() && | 404 web_view->applyAutofillSuggestions(element, v, l, i, ids); |
338 autofill_query_element_.isFocusable()) { | |
339 web_view->applyAutofillSuggestions( | |
340 autofill_query_element_, v, l, i, ids, separator_index); | |
341 } | |
342 | 405 |
343 Send(new AutofillHostMsg_DidShowAutofillSuggestions( | 406 Send(new AutofillHostMsg_DidShowAutofillSuggestions( |
344 routing_id(), | 407 routing_id(), |
345 has_autofill_item && !has_shown_autofill_popup_for_current_edit_)); | 408 has_autofill_item && !has_shown_autofill_popup_for_current_edit_)); |
346 has_shown_autofill_popup_for_current_edit_ |= has_autofill_item; | 409 has_shown_autofill_popup_for_current_edit_ |= has_autofill_item; |
347 } | 410 } |
348 | 411 |
349 void AutofillAgent::OnFormDataFilled(int query_id, | 412 void AutofillAgent::OnFormDataFilled(int query_id, |
350 const webkit::forms::FormData& form) { | 413 const webkit::forms::FormData& form) { |
351 if (!render_view()->GetWebView() || query_id != autofill_query_id_) | 414 if (!render_view()->GetWebView() || query_id != autofill_query_id_) |
352 return; | 415 return; |
353 | 416 |
354 was_query_node_autofilled_ = autofill_query_element_.isAutofilled(); | 417 was_query_node_autofilled_ = element_.isAutofilled(); |
355 | 418 |
356 switch (autofill_action_) { | 419 switch (autofill_action_) { |
357 case AUTOFILL_FILL: | 420 case AUTOFILL_FILL: |
358 FillForm(form, autofill_query_element_); | 421 FillForm(form, element_); |
359 Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id(), | 422 Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id(), |
360 base::TimeTicks::Now())); | 423 base::TimeTicks::Now())); |
361 break; | 424 break; |
362 case AUTOFILL_PREVIEW: | 425 case AUTOFILL_PREVIEW: |
363 didClearAutofillSelection(autofill_query_element_); | 426 didClearAutofillSelection(element_); |
364 | 427 |
365 PreviewForm(form, autofill_query_element_); | 428 PreviewForm(form, element_); |
366 Send(new AutofillHostMsg_DidPreviewAutofillFormData(routing_id())); | 429 Send(new AutofillHostMsg_DidPreviewAutofillFormData(routing_id())); |
367 break; | 430 break; |
368 default: | 431 default: |
369 NOTREACHED(); | 432 NOTREACHED(); |
370 } | 433 } |
371 autofill_action_ = AUTOFILL_NONE; | 434 autofill_action_ = AUTOFILL_NONE; |
372 } | 435 } |
373 | 436 |
374 void AutofillAgent::OnFieldTypePredictionsAvailable( | 437 void AutofillAgent::OnFieldTypePredictionsAvailable( |
375 const std::vector<FormDataPredictions>& forms) { | 438 const std::vector<FormDataPredictions>& forms) { |
376 for (size_t i = 0; i < forms.size(); ++i) { | 439 for (size_t i = 0; i < forms.size(); ++i) { |
377 form_cache_.ShowPredictions(forms[i]); | 440 form_cache_.ShowPredictions(forms[i]); |
378 } | 441 } |
379 } | 442 } |
380 | 443 |
381 void AutofillAgent::OnSelectAutofillSuggestionAtIndex(int listIndex) { | 444 void AutofillAgent::OnSelectAutofillSuggestionAtIndex(int listIndex) { |
382 NOTIMPLEMENTED(); | 445 NOTIMPLEMENTED(); |
383 // TODO(jrg): enable once changes land in WebKit | 446 // TODO(jrg): enable once changes land in WebKit |
384 // render_view()->webview()->selectAutofillSuggestionAtIndex(listIndex); | 447 // render_view()->webview()->selectAutofillSuggestionAtIndex(listIndex); |
385 } | 448 } |
386 | 449 |
387 void AutofillAgent::OnSetAutofillActionFill() { | 450 void AutofillAgent::OnSetAutofillActionFill() { |
388 autofill_action_ = AUTOFILL_FILL; | 451 autofill_action_ = AUTOFILL_FILL; |
389 } | 452 } |
390 | 453 |
391 void AutofillAgent::OnClearForm() { | 454 void AutofillAgent::OnClearForm() { |
392 form_cache_.ClearFormWithElement(autofill_query_element_); | 455 form_cache_.ClearFormWithElement(element_); |
393 } | 456 } |
394 | 457 |
395 void AutofillAgent::OnSetAutofillActionPreview() { | 458 void AutofillAgent::OnSetAutofillActionPreview() { |
396 autofill_action_ = AUTOFILL_PREVIEW; | 459 autofill_action_ = AUTOFILL_PREVIEW; |
397 } | 460 } |
398 | 461 |
399 void AutofillAgent::OnClearPreviewedForm() { | 462 void AutofillAgent::OnClearPreviewedForm() { |
400 didClearAutofillSelection(autofill_query_element_); | 463 didClearAutofillSelection(element_); |
401 } | 464 } |
402 | 465 |
403 void AutofillAgent::OnSetNodeText(const string16& value) { | 466 void AutofillAgent::OnSetNodeText(const string16& value) { |
404 SetNodeText(value, &autofill_query_element_); | 467 SetNodeText(value, &element_); |
405 } | 468 } |
406 | 469 |
407 void AutofillAgent::OnAcceptPasswordAutofillSuggestion(const string16& value) { | 470 void AutofillAgent::OnAcceptPasswordAutofillSuggestion(const string16& value) { |
408 // We need to make sure this is handled here because the browser process | 471 // We need to make sure this is handled here because the browser process |
409 // skipped it handling because it believed it would be handled here. If it | 472 // skipped it handling because it believed it would be handled here. If it |
410 // isn't handled here then the browser logic needs to be updated. | 473 // isn't handled here then the browser logic needs to be updated. |
411 bool handled = password_autofill_manager_->DidAcceptAutofillSuggestion( | 474 bool handled = password_autofill_manager_->DidAcceptAutofillSuggestion( |
412 autofill_query_element_, | 475 element_, |
413 value); | 476 value); |
414 DCHECK(handled); | 477 DCHECK(handled); |
415 } | 478 } |
416 | 479 |
417 void AutofillAgent::ShowSuggestions(const WebInputElement& element, | 480 void AutofillAgent::ShowSuggestions(const WebInputElement& element, |
418 bool autofill_on_empty_values, | 481 bool autofill_on_empty_values, |
419 bool requires_caret_at_end, | 482 bool requires_caret_at_end, |
420 bool display_warning_if_disabled) { | 483 bool display_warning_if_disabled) { |
421 // If autocomplete is disabled at the form level, then we might want to show | 484 if (!element.isEnabled() || element.isReadOnly() || !element.isTextField() || |
422 // a warning in place of suggestions. However, if autocomplete is disabled | 485 element.isPasswordField() || !element.suggestedValue().isEmpty()) |
423 // specifically for this field, we never want to show a warning. Otherwise, | |
424 // we might interfere with custom popups (e.g. search suggestions) used by | |
425 // the website. | |
426 const WebFormElement form = element.form(); | |
427 if (!element.isEnabled() || element.isReadOnly() || | |
428 (!element.autoComplete() && (form.isNull() || form.autoComplete())) || | |
429 !element.isTextField() || element.isPasswordField() || | |
430 !element.suggestedValue().isEmpty()) | |
431 return; | |
432 | |
433 // If the field has no name, then we won't have values. | |
434 if (element.nameForAutofill().isEmpty()) | |
435 return; | 486 return; |
436 | 487 |
437 // Don't attempt to autofill with values that are too large or if filling | 488 // Don't attempt to autofill with values that are too large or if filling |
438 // criteria are not met. | 489 // criteria are not met. |
439 WebString value = element.value(); | 490 WebString value = element.value(); |
440 if (value.length() > kMaximumTextSizeForAutofill || | 491 if (value.length() > kMaximumTextSizeForAutofill || |
441 (!autofill_on_empty_values && value.isEmpty()) || | 492 (!autofill_on_empty_values && value.isEmpty()) || |
442 (requires_caret_at_end && | 493 (requires_caret_at_end && |
443 (element.selectionStart() != element.selectionEnd() || | 494 (element.selectionStart() != element.selectionEnd() || |
444 element.selectionEnd() != static_cast<int>(value.length())))) { | 495 element.selectionEnd() != static_cast<int>(value.length())))) { |
445 // Any popup currently showing is obsolete. | 496 // Any popup currently showing is obsolete. |
446 WebKit::WebView* web_view = render_view()->GetWebView(); | 497 WebKit::WebView* web_view = render_view()->GetWebView(); |
447 if (web_view) | 498 if (web_view) |
448 web_view->hidePopups(); | 499 web_view->hidePopups(); |
449 | 500 |
450 return; | 501 return; |
451 } | 502 } |
452 | 503 |
504 element_ = element; | |
505 | |
506 // If autocomplete is disabled at the form level, then we might want to show | |
507 // a warning in place of suggestions. However, if autocomplete is disabled | |
508 // specifically for this field, we never want to show a warning. Otherwise, | |
509 // we might interfere with custom popups (e.g. search suggestions) used by | |
510 // the website. Also, if the field has no name, then we won't have values. | |
511 const WebFormElement form = element.form(); | |
512 if ((!element.autoComplete() && (form.isNull() || form.autoComplete())) || | |
513 element.nameForAutofill().isEmpty()) { | |
514 CombineDataListEntriesAndShow(element, std::vector<string16>(), | |
515 std::vector<string16>(), | |
516 std::vector<string16>(), | |
517 std::vector<int>(), false); | |
518 return; | |
519 } | |
520 | |
453 QueryAutofillSuggestions(element, display_warning_if_disabled); | 521 QueryAutofillSuggestions(element, display_warning_if_disabled); |
454 } | 522 } |
455 | 523 |
456 void AutofillAgent::QueryAutofillSuggestions(const WebInputElement& element, | 524 void AutofillAgent::QueryAutofillSuggestions(const WebInputElement& element, |
457 bool display_warning_if_disabled) { | 525 bool display_warning_if_disabled) { |
458 static int query_counter = 0; | 526 static int query_counter = 0; |
459 autofill_query_id_ = query_counter++; | 527 autofill_query_id_ = query_counter++; |
460 autofill_query_element_ = element; | |
461 display_warning_if_disabled_ = display_warning_if_disabled; | 528 display_warning_if_disabled_ = display_warning_if_disabled; |
462 | 529 |
463 webkit::forms::FormData form; | 530 webkit::forms::FormData form; |
464 webkit::forms::FormField field; | 531 webkit::forms::FormField field; |
465 if (!FindFormAndFieldForInputElement(element, &form, &field, | 532 if (!FindFormAndFieldForInputElement(element, &form, &field, |
466 REQUIRE_AUTOCOMPLETE)) { | 533 REQUIRE_AUTOCOMPLETE)) { |
467 // If we didn't find the cached form, at least let autocomplete have a shot | 534 // If we didn't find the cached form, at least let autocomplete have a shot |
468 // at providing suggestions. | 535 // at providing suggestions. |
469 WebFormControlElementToFormField(element, EXTRACT_VALUE, &field); | 536 WebFormControlElementToFormField(element, EXTRACT_VALUE, &field); |
470 } | 537 } |
471 | 538 |
472 gfx::Rect bounding_box(autofill_query_element_.boundsInViewportSpace()); | 539 gfx::Rect bounding_box(element_.boundsInViewportSpace()); |
473 | 540 |
474 Send(new AutofillHostMsg_QueryFormFieldAutofill(routing_id(), | 541 Send(new AutofillHostMsg_QueryFormFieldAutofill(routing_id(), |
475 autofill_query_id_, | 542 autofill_query_id_, |
476 form, | 543 form, |
477 field, | 544 field, |
478 bounding_box, | 545 bounding_box, |
479 display_warning_if_disabled)); | 546 display_warning_if_disabled)); |
480 } | 547 } |
481 | 548 |
482 void AutofillAgent::FillAutofillFormData(const WebNode& node, | 549 void AutofillAgent::FillAutofillFormData(const WebNode& node, |
483 int unique_id, | 550 int unique_id, |
484 AutofillAction action) { | 551 AutofillAction action) { |
552 DCHECK_GT(unique_id, 0); | |
553 | |
485 static int query_counter = 0; | 554 static int query_counter = 0; |
486 autofill_query_id_ = query_counter++; | 555 autofill_query_id_ = query_counter++; |
487 | 556 |
488 webkit::forms::FormData form; | 557 webkit::forms::FormData form; |
489 webkit::forms::FormField field; | 558 webkit::forms::FormField field; |
490 if (!FindFormAndFieldForInputElement(node.toConst<WebInputElement>(), &form, | 559 if (!FindFormAndFieldForInputElement(node.toConst<WebInputElement>(), &form, |
491 &field, REQUIRE_AUTOCOMPLETE)) { | 560 &field, REQUIRE_AUTOCOMPLETE)) { |
492 return; | 561 return; |
493 } | 562 } |
494 | 563 |
495 autofill_action_ = action; | 564 autofill_action_ = action; |
496 Send(new AutofillHostMsg_FillAutofillFormData( | 565 Send(new AutofillHostMsg_FillAutofillFormData( |
497 routing_id(), autofill_query_id_, form, field, unique_id)); | 566 routing_id(), autofill_query_id_, form, field, unique_id)); |
498 } | 567 } |
499 | 568 |
500 void AutofillAgent::SetNodeText(const string16& value, | 569 void AutofillAgent::SetNodeText(const string16& value, |
501 WebKit::WebInputElement* node) { | 570 WebKit::WebInputElement* node) { |
502 string16 substring = value; | 571 string16 substring = value; |
503 substring = substring.substr(0, node->maxLength()); | 572 substring = substring.substr(0, node->maxLength()); |
504 | 573 |
505 node->setValue(substring, true); | 574 node->setValue(substring, true); |
506 } | 575 } |
507 | 576 |
508 } // namespace autofill | 577 } // namespace autofill |
OLD | NEW |