[email protected] | c4a9e93 | 2011-03-05 04:05:55 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 5 | #include "chrome/renderer/autofill/autofill_agent.h" |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 6 | |
[email protected] | a1cb57f | 2011-09-28 22:41:29 | [diff] [blame] | 7 | #include "base/bind.h" |
[email protected] | 8704f89b | 2011-04-15 00:30:05 | [diff] [blame] | 8 | #include "base/message_loop.h" |
[email protected] | 1d14f58 | 2011-09-02 20:42:04 | [diff] [blame] | 9 | #include "base/time.h" |
[email protected] | 2377cafe | 2010-10-27 01:19:08 | [diff] [blame] | 10 | #include "base/utf_string_conversions.h" |
[email protected] | 19d6e1e8 | 2011-01-26 05:08:58 | [diff] [blame] | 11 | #include "chrome/common/autofill_messages.h" |
[email protected] | 2377cafe | 2010-10-27 01:19:08 | [diff] [blame] | 12 | #include "chrome/common/chrome_constants.h" |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 13 | #include "chrome/renderer/autofill/form_autofill_util.h" |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 14 | #include "chrome/renderer/autofill/password_autofill_manager.h" |
[email protected] | a2ef54c | 2011-10-10 16:20:31 | [diff] [blame] | 15 | #include "content/public/renderer/render_view.h" |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 16 | #include "grit/generated_resources.h" |
[email protected] | 8bd0fe6 | 2011-01-17 06:44:37 | [diff] [blame] | 17 | #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" |
| 18 | #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h" |
| 19 | #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
[email protected] | a22f7e0 | 2011-02-09 07:15:35 | [diff] [blame] | 20 | #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 21 | #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" |
[email protected] | 9718305 | 2011-11-17 16:35:22 | [diff] [blame^] | 22 | #include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h" |
[email protected] | 8bd0fe6 | 2011-01-17 06:44:37 | [diff] [blame] | 23 | #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" |
[email protected] | b6d8126 | 2011-01-13 17:36:09 | [diff] [blame] | 24 | #include "ui/base/keycodes/keyboard_codes.h" |
[email protected] | c051a1b | 2011-01-21 23:30:17 | [diff] [blame] | 25 | #include "ui/base/l10n/l10n_util.h" |
[email protected] | 6a8ddba5 | 2010-09-05 04:38:06 | [diff] [blame] | 26 | #include "webkit/glue/form_data.h" |
[email protected] | 3eb5728c | 2011-06-20 22:32:24 | [diff] [blame] | 27 | #include "webkit/glue/form_data_predictions.h" |
[email protected] | 6a8ddba5 | 2010-09-05 04:38:06 | [diff] [blame] | 28 | #include "webkit/glue/form_field.h" |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 29 | #include "webkit/glue/password_form.h" |
| 30 | |
| 31 | using WebKit::WebFormControlElement; |
| 32 | using WebKit::WebFormElement; |
| 33 | using WebKit::WebFrame; |
| 34 | using WebKit::WebInputElement; |
[email protected] | 7aab84b | 2010-11-20 01:04:26 | [diff] [blame] | 35 | using WebKit::WebKeyboardEvent; |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 36 | using WebKit::WebNode; |
| 37 | using WebKit::WebString; |
[email protected] | 2a5b173 | 2011-04-01 23:55:55 | [diff] [blame] | 38 | using webkit_glue::FormData; |
[email protected] | 3eb5728c | 2011-06-20 22:32:24 | [diff] [blame] | 39 | using webkit_glue::FormDataPredictions; |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 40 | |
[email protected] | e47aec5 | 2010-08-12 00:50:30 | [diff] [blame] | 41 | namespace { |
| 42 | |
| 43 | // The size above which we stop triggering autofill for an input text field |
| 44 | // (so to avoid sending long strings through IPC). |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 45 | const size_t kMaximumTextSizeForAutofill = 1000; |
[email protected] | e47aec5 | 2010-08-12 00:50:30 | [diff] [blame] | 46 | |
| 47 | } // namespace |
| 48 | |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 49 | namespace autofill { |
| 50 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 51 | AutofillAgent::AutofillAgent( |
[email protected] | a2ef54c | 2011-10-10 16:20:31 | [diff] [blame] | 52 | content::RenderView* render_view, |
[email protected] | c4a9e93 | 2011-03-05 04:05:55 | [diff] [blame] | 53 | PasswordAutofillManager* password_autofill_manager) |
[email protected] | 3a034ebb | 2011-10-03 19:19:44 | [diff] [blame] | 54 | : content::RenderViewObserver(render_view), |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 55 | password_autofill_manager_(password_autofill_manager), |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 56 | autofill_query_id_(0), |
| 57 | autofill_action_(AUTOFILL_NONE), |
[email protected] | 6bcd58a | 2010-11-25 02:31:20 | [diff] [blame] | 58 | display_warning_if_disabled_(false), |
[email protected] | d77ddc806 | 2010-11-24 01:14:06 | [diff] [blame] | 59 | was_query_node_autofilled_(false), |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 60 | suggestions_clear_index_(-1), |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 61 | suggestions_options_index_(-1), |
[email protected] | e4dce0af | 2011-09-19 22:22:28 | [diff] [blame] | 62 | has_shown_autofill_popup_for_current_edit_(false), |
[email protected] | a1cb57f | 2011-09-28 22:41:29 | [diff] [blame] | 63 | ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
[email protected] | a2ef54c | 2011-10-10 16:20:31 | [diff] [blame] | 64 | render_view->GetWebView()->setAutofillClient(this); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 65 | } |
| 66 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 67 | AutofillAgent::~AutofillAgent() {} |
[email protected] | d2f05d0 | 2011-01-27 18:51:01 | [diff] [blame] | 68 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 69 | bool AutofillAgent::OnMessageReceived(const IPC::Message& message) { |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 70 | bool handled = true; |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 71 | IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message) |
| 72 | IPC_MESSAGE_HANDLER(AutofillMsg_SuggestionsReturned, OnSuggestionsReturned) |
| 73 | IPC_MESSAGE_HANDLER(AutofillMsg_FormDataFilled, OnFormDataFilled) |
[email protected] | 3eb5728c | 2011-06-20 22:32:24 | [diff] [blame] | 74 | IPC_MESSAGE_HANDLER(AutofillMsg_FieldTypePredictionsAvailable, |
| 75 | OnFieldTypePredictionsAvailable) |
[email protected] | 3fd0400 | 2011-10-29 00:51:04 | [diff] [blame] | 76 | IPC_MESSAGE_HANDLER(AutofillMsg_SelectAutofillSuggestionAtIndex, |
| 77 | OnSelectAutofillSuggestionAtIndex) |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 78 | IPC_MESSAGE_UNHANDLED(handled = false) |
| 79 | IPC_END_MESSAGE_MAP() |
| 80 | return handled; |
| 81 | } |
| 82 | |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 83 | void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) { |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 84 | // The document has now been fully loaded. Scan for forms to be sent up to |
| 85 | // the browser. |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 86 | std::vector<webkit_glue::FormData> forms; |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 87 | form_cache_.ExtractForms(*frame, &forms); |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 88 | |
[email protected] | 1d14f58 | 2011-09-02 20:42:04 | [diff] [blame] | 89 | if (!forms.empty()) { |
| 90 | Send(new AutofillHostMsg_FormsSeen(routing_id(), forms, |
| 91 | base::TimeTicks::Now())); |
| 92 | } |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 93 | } |
| 94 | |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 95 | void AutofillAgent::FrameDetached(WebFrame* frame) { |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 96 | form_cache_.ResetFrame(*frame); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 97 | } |
| 98 | |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 99 | void AutofillAgent::FrameWillClose(WebFrame* frame) { |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 100 | form_cache_.ResetFrame(*frame); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 101 | } |
| 102 | |
[email protected] | 2a5b173 | 2011-04-01 23:55:55 | [diff] [blame] | 103 | void AutofillAgent::WillSubmitForm(WebFrame* frame, |
| 104 | const WebFormElement& form) { |
| 105 | FormData form_data; |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 106 | if (WebFormElementToFormData(form, |
| 107 | WebFormControlElement(), |
| 108 | REQUIRE_AUTOCOMPLETE, |
| 109 | static_cast<ExtractMask>( |
| 110 | EXTRACT_VALUE | EXTRACT_OPTION_TEXT), |
| 111 | &form_data, |
| 112 | NULL)) { |
[email protected] | 1d14f58 | 2011-09-02 20:42:04 | [diff] [blame] | 113 | Send(new AutofillHostMsg_FormSubmitted(routing_id(), form_data, |
| 114 | base::TimeTicks::Now())); |
[email protected] | 2a5b173 | 2011-04-01 23:55:55 | [diff] [blame] | 115 | } |
| 116 | } |
| 117 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 118 | bool AutofillAgent::InputElementClicked(const WebInputElement& element, |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 119 | bool was_focused, |
| 120 | bool is_focused) { |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 121 | if (was_focused) |
| 122 | ShowSuggestions(element, true, false, true); |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 123 | |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 124 | return false; |
| 125 | } |
| 126 | |
[email protected] | 65ec8db | 2011-11-04 14:50:44 | [diff] [blame] | 127 | bool AutofillAgent::InputElementLostFocus() { |
[email protected] | 9718305 | 2011-11-17 16:35:22 | [diff] [blame^] | 128 | Send(new AutofillHostMsg_HideAutofillPopup(routing_id())); |
| 129 | |
[email protected] | 65ec8db | 2011-11-04 14:50:44 | [diff] [blame] | 130 | return false; |
| 131 | } |
| 132 | |
[email protected] | 3d07d6b | 2011-07-13 00:25:00 | [diff] [blame] | 133 | void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node, |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 134 | const WebString& value, |
| 135 | const WebString& label, |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 136 | int unique_id, |
| 137 | unsigned index) { |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 138 | if (password_autofill_manager_->DidAcceptAutofillSuggestion(node, value)) |
[email protected] | b43c135 | 2011-03-04 23:55:34 | [diff] [blame] | 139 | return; |
| 140 | |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 141 | if (suggestions_options_index_ != -1 && |
| 142 | index == static_cast<unsigned>(suggestions_options_index_)) { |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 143 | // User selected 'Autofill Options'. |
| 144 | Send(new AutofillHostMsg_ShowAutofillDialog(routing_id())); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 145 | } else if (suggestions_clear_index_ != -1 && |
| 146 | index == static_cast<unsigned>(suggestions_clear_index_)) { |
| 147 | // User selected 'Clear form'. |
[email protected] | 829245b | 2011-09-07 21:18:32 | [diff] [blame] | 148 | DCHECK(node == autofill_query_element_); |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 149 | form_cache_.ClearFormWithElement(autofill_query_element_); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 150 | } else if (!unique_id) { |
| 151 | // User selected an Autocomplete entry, so we fill directly. |
| 152 | WebInputElement element = node.toConst<WebInputElement>(); |
| 153 | |
| 154 | string16 substring = value; |
| 155 | substring = substring.substr(0, element.maxLength()); |
[email protected] | 3b3b94d | 2011-03-10 09:52:20 | [diff] [blame] | 156 | element.setValue(substring, true); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 157 | } else { |
| 158 | // Fill the values for the whole form. |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 159 | FillAutofillFormData(node, unique_id, AUTOFILL_FILL); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | suggestions_clear_index_ = -1; |
| 163 | suggestions_options_index_ = -1; |
| 164 | } |
| 165 | |
[email protected] | 3d07d6b | 2011-07-13 00:25:00 | [diff] [blame] | 166 | void AutofillAgent::didSelectAutofillSuggestion(const WebNode& node, |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 167 | const WebString& value, |
| 168 | const WebString& label, |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 169 | int unique_id) { |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 170 | DCHECK_GE(unique_id, 0); |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 171 | if (password_autofill_manager_->DidSelectAutofillSuggestion(node)) |
[email protected] | fc1964a | 2011-01-20 19:33:07 | [diff] [blame] | 172 | return; |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 173 | |
[email protected] | 3d07d6b | 2011-07-13 00:25:00 | [diff] [blame] | 174 | didClearAutofillSelection(node); |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 175 | FillAutofillFormData(node, unique_id, AUTOFILL_PREVIEW); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 176 | } |
| 177 | |
[email protected] | 3d07d6b | 2011-07-13 00:25:00 | [diff] [blame] | 178 | void AutofillAgent::didClearAutofillSelection(const WebNode& node) { |
[email protected] | 6075d050 | 2011-09-13 02:10:21 | [diff] [blame] | 179 | if (password_autofill_manager_->DidClearAutofillSelection(node)) |
| 180 | return; |
| 181 | |
[email protected] | 091b222 | 2011-09-14 02:52:55 | [diff] [blame] | 182 | if (!autofill_query_element_.isNull() && node == autofill_query_element_) { |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 183 | ClearPreviewedFormWithElement(autofill_query_element_, |
| 184 | was_query_node_autofilled_); |
[email protected] | 091b222 | 2011-09-14 02:52:55 | [diff] [blame] | 185 | } else { |
| 186 | // TODO(isherman): There seem to be rare cases where this code *is* |
| 187 | // reachable: see [ http://crbug.com/96321#c6 ]. Ideally we would |
| 188 | // understand those cases and fix the code to avoid them. However, so far I |
| 189 | // have been unable to reproduce such a case locally. If you hit this |
| 190 | // NOTREACHED(), please file a bug against me. |
| 191 | NOTREACHED(); |
| 192 | } |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 193 | } |
| 194 | |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 195 | void AutofillAgent::removeAutocompleteSuggestion(const WebString& name, |
| 196 | const WebString& value) { |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 197 | // The index of clear & options will have shifted down. |
| 198 | if (suggestions_clear_index_ != -1) |
| 199 | suggestions_clear_index_--; |
| 200 | if (suggestions_options_index_ != -1) |
| 201 | suggestions_options_index_--; |
| 202 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 203 | Send(new AutofillHostMsg_RemoveAutocompleteEntry(routing_id(), name, value)); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 204 | } |
| 205 | |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 206 | void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) { |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 207 | password_autofill_manager_->TextFieldDidEndEditing(element); |
[email protected] | 7d24db7 | 2011-08-26 06:02:31 | [diff] [blame] | 208 | has_shown_autofill_popup_for_current_edit_ = false; |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 209 | } |
| 210 | |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 211 | void AutofillAgent::textFieldDidChange(const WebInputElement& element) { |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 212 | // We post a task for doing the Autofill as the caret position is not set |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 213 | // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and |
| 214 | // it is needed to trigger autofill. |
[email protected] | a1cb57f | 2011-09-28 22:41:29 | [diff] [blame] | 215 | weak_ptr_factory_.InvalidateWeakPtrs(); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 216 | MessageLoop::current()->PostTask( |
| 217 | FROM_HERE, |
[email protected] | a1cb57f | 2011-09-28 22:41:29 | [diff] [blame] | 218 | base::Bind(&AutofillAgent::TextFieldDidChangeImpl, |
| 219 | weak_ptr_factory_.GetWeakPtr(), element)); |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 220 | } |
| 221 | |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 222 | void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) { |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 223 | if (password_autofill_manager_->TextDidChangeInTextField(element)) |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 224 | return; |
| 225 | |
| 226 | ShowSuggestions(element, false, true, false); |
[email protected] | 7d24db7 | 2011-08-26 06:02:31 | [diff] [blame] | 227 | |
| 228 | webkit_glue::FormData form; |
| 229 | webkit_glue::FormField field; |
[email protected] | 283ba4ca | 2011-09-22 05:25:08 | [diff] [blame] | 230 | if (FindFormAndFieldForInputElement(element, &form, &field, REQUIRE_NONE)) { |
[email protected] | 1d14f58 | 2011-09-02 20:42:04 | [diff] [blame] | 231 | Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field, |
| 232 | base::TimeTicks::Now())); |
| 233 | } |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 234 | } |
| 235 | |
[email protected] | 2fa18c2 | 2011-06-14 23:40:43 | [diff] [blame] | 236 | void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element, |
| 237 | const WebKeyboardEvent& event) { |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 238 | if (password_autofill_manager_->TextFieldHandlingKeyDown(element, event)) |
[email protected] | fc1964a | 2011-01-20 19:33:07 | [diff] [blame] | 239 | return; |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 240 | |
| 241 | if (event.windowsKeyCode == ui::VKEY_DOWN || |
| 242 | event.windowsKeyCode == ui::VKEY_UP) |
| 243 | ShowSuggestions(element, true, true, true); |
| 244 | } |
| 245 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 246 | void AutofillAgent::OnSuggestionsReturned(int query_id, |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 247 | const std::vector<string16>& values, |
| 248 | const std::vector<string16>& labels, |
| 249 | const std::vector<string16>& icons, |
| 250 | const std::vector<int>& unique_ids) { |
[email protected] | a2ef54c | 2011-10-10 16:20:31 | [diff] [blame] | 251 | WebKit::WebView* web_view = render_view()->GetWebView(); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 252 | if (!web_view || query_id != autofill_query_id_) |
| 253 | return; |
| 254 | |
[email protected] | 4313847 | 2010-08-17 22:45:45 | [diff] [blame] | 255 | if (values.empty()) { |
| 256 | // No suggestions, any popup currently showing is obsolete. |
| 257 | web_view->hidePopups(); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 258 | return; |
[email protected] | 4313847 | 2010-08-17 22:45:45 | [diff] [blame] | 259 | } |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 260 | |
| 261 | std::vector<string16> v(values); |
| 262 | std::vector<string16> l(labels); |
[email protected] | a53e0683 | 2010-07-28 22:46:41 | [diff] [blame] | 263 | std::vector<string16> i(icons); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 264 | std::vector<int> ids(unique_ids); |
| 265 | int separator_index = -1; |
| 266 | |
[email protected] | 7d24db7 | 2011-08-26 06:02:31 | [diff] [blame] | 267 | DCHECK_GT(ids.size(), 0U); |
[email protected] | cd0a3b7 | 2011-08-02 06:16:19 | [diff] [blame] | 268 | if (!autofill_query_element_.isNull() && |
| 269 | !autofill_query_element_.autoComplete()) { |
| 270 | // If autofill is disabled and we had suggestions, show a warning instead. |
| 271 | v.assign(1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)); |
| 272 | l.assign(1, string16()); |
| 273 | i.assign(1, string16()); |
| 274 | ids.assign(1, -1); |
| 275 | } else if (ids[0] < 0 && ids.size() > 1) { |
[email protected] | 1866d06 | 2010-11-16 06:19:56 | [diff] [blame] | 276 | // If we received a warning instead of suggestions from autofill but regular |
| 277 | // suggestions from autocomplete, don't show the autofill warning. |
| 278 | v.erase(v.begin()); |
| 279 | l.erase(l.begin()); |
| 280 | i.erase(i.begin()); |
| 281 | ids.erase(ids.begin()); |
| 282 | } |
| 283 | |
[email protected] | 6bcd58a | 2010-11-25 02:31:20 | [diff] [blame] | 284 | // If we were about to show a warning and we shouldn't, don't. |
| 285 | if (ids[0] < 0 && !display_warning_if_disabled_) |
| 286 | return; |
| 287 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 288 | // Only include "Autofill Options" special menu item if we have Autofill |
[email protected] | 4f03a2d3 | 2010-12-01 01:49:36 | [diff] [blame] | 289 | // items, identified by |unique_ids| having at least one valid value. |
| 290 | bool has_autofill_item = false; |
| 291 | for (size_t i = 0; i < ids.size(); ++i) { |
| 292 | if (ids[i] > 0) { |
| 293 | has_autofill_item = true; |
| 294 | break; |
| 295 | } |
| 296 | } |
| 297 | |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 298 | // The form has been auto-filled, so give the user the chance to clear the |
| 299 | // form. Append the 'Clear form' menu item. |
[email protected] | 4f03a2d3 | 2010-12-01 01:49:36 | [diff] [blame] | 300 | if (has_autofill_item && |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 301 | FormWithElementIsAutofilled(autofill_query_element_)) { |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 302 | v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM)); |
| 303 | l.push_back(string16()); |
[email protected] | a53e0683 | 2010-07-28 22:46:41 | [diff] [blame] | 304 | i.push_back(string16()); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 305 | ids.push_back(0); |
| 306 | suggestions_clear_index_ = v.size() - 1; |
[email protected] | 1866d06 | 2010-11-16 06:19:56 | [diff] [blame] | 307 | separator_index = v.size() - 1; |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 308 | } |
| 309 | |
[email protected] | 4f03a2d3 | 2010-12-01 01:49:36 | [diff] [blame] | 310 | if (has_autofill_item) { |
[email protected] | 2377cafe | 2010-10-27 01:19:08 | [diff] [blame] | 311 | // Append the 'Chrome Autofill options' menu item; |
| 312 | v.push_back(l10n_util::GetStringFUTF16(IDS_AUTOFILL_OPTIONS_POPUP, |
| 313 | WideToUTF16(chrome::kBrowserAppName))); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 314 | l.push_back(string16()); |
[email protected] | a53e0683 | 2010-07-28 22:46:41 | [diff] [blame] | 315 | i.push_back(string16()); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 316 | ids.push_back(0); |
| 317 | suggestions_options_index_ = v.size() - 1; |
| 318 | separator_index = values.size(); |
| 319 | } |
| 320 | |
| 321 | // Send to WebKit for display. |
[email protected] | cd0a3b7 | 2011-08-02 06:16:19 | [diff] [blame] | 322 | if (!v.empty() && !autofill_query_element_.isNull() && |
| 323 | autofill_query_element_.isFocusable()) { |
[email protected] | 3d07d6b | 2011-07-13 00:25:00 | [diff] [blame] | 324 | web_view->applyAutofillSuggestions( |
[email protected] | cd0a3b7 | 2011-08-02 06:16:19 | [diff] [blame] | 325 | autofill_query_element_, v, l, i, ids, separator_index); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 326 | } |
[email protected] | 468327f | 2010-10-04 20:02:29 | [diff] [blame] | 327 | |
[email protected] | 7d24db7 | 2011-08-26 06:02:31 | [diff] [blame] | 328 | Send(new AutofillHostMsg_DidShowAutofillSuggestions( |
| 329 | routing_id(), |
| 330 | has_autofill_item && !has_shown_autofill_popup_for_current_edit_)); |
| 331 | has_shown_autofill_popup_for_current_edit_ |= has_autofill_item; |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 332 | } |
| 333 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 334 | void AutofillAgent::OnFormDataFilled(int query_id, |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 335 | const webkit_glue::FormData& form) { |
[email protected] | a2ef54c | 2011-10-10 16:20:31 | [diff] [blame] | 336 | if (!render_view()->GetWebView() || query_id != autofill_query_id_) |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 337 | return; |
| 338 | |
| 339 | switch (autofill_action_) { |
| 340 | case AUTOFILL_FILL: |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 341 | FillForm(form, autofill_query_element_); |
[email protected] | 1d14f58 | 2011-09-02 20:42:04 | [diff] [blame] | 342 | Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id(), |
| 343 | base::TimeTicks::Now())); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 344 | break; |
| 345 | case AUTOFILL_PREVIEW: |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 346 | PreviewForm(form, autofill_query_element_); |
[email protected] | 7d24db7 | 2011-08-26 06:02:31 | [diff] [blame] | 347 | Send(new AutofillHostMsg_DidPreviewAutofillFormData(routing_id())); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 348 | break; |
| 349 | default: |
| 350 | NOTREACHED(); |
| 351 | } |
| 352 | autofill_action_ = AUTOFILL_NONE; |
[email protected] | e47aec5 | 2010-08-12 00:50:30 | [diff] [blame] | 353 | } |
| 354 | |
[email protected] | 3eb5728c | 2011-06-20 22:32:24 | [diff] [blame] | 355 | void AutofillAgent::OnFieldTypePredictionsAvailable( |
| 356 | const std::vector<FormDataPredictions>& forms) { |
| 357 | for (size_t i = 0; i < forms.size(); ++i) { |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 358 | form_cache_.ShowPredictions(forms[i]); |
[email protected] | 3eb5728c | 2011-06-20 22:32:24 | [diff] [blame] | 359 | } |
| 360 | } |
| 361 | |
[email protected] | 3fd0400 | 2011-10-29 00:51:04 | [diff] [blame] | 362 | void AutofillAgent::OnSelectAutofillSuggestionAtIndex(int listIndex) { |
| 363 | NOTIMPLEMENTED(); |
| 364 | // TODO(jrg): enable once changes land in WebKit |
| 365 | // render_view()->webview()->selectAutofillSuggestionAtIndex(listIndex); |
| 366 | } |
| 367 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 368 | void AutofillAgent::ShowSuggestions(const WebInputElement& element, |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 369 | bool autofill_on_empty_values, |
| 370 | bool requires_caret_at_end, |
| 371 | bool display_warning_if_disabled) { |
[email protected] | cd0a3b7 | 2011-08-02 06:16:19 | [diff] [blame] | 372 | // If autocomplete is disabled at the form level, then we might want to show |
| 373 | // a warning in place of suggestions. However, if autocomplete is disabled |
| 374 | // specifically for this field, we never want to show a warning. Otherwise, |
| 375 | // we might interfere with custom popups (e.g. search suggestions) used by |
| 376 | // the website. |
| 377 | const WebFormElement form = element.form(); |
| 378 | if (!element.isEnabled() || element.isReadOnly() || |
[email protected] | a2f4889 | 2011-08-19 23:04:42 | [diff] [blame] | 379 | (!element.autoComplete() && (form.isNull() || form.autoComplete())) || |
[email protected] | 6244581 | 2011-05-03 22:41:32 | [diff] [blame] | 380 | !element.isTextField() || element.isPasswordField() || |
| 381 | !element.suggestedValue().isEmpty()) |
[email protected] | dc9144f | 2010-11-16 02:18:28 | [diff] [blame] | 382 | return; |
[email protected] | dc9144f | 2010-11-16 02:18:28 | [diff] [blame] | 383 | |
[email protected] | 1866d06 | 2010-11-16 06:19:56 | [diff] [blame] | 384 | // If the field has no name, then we won't have values. |
| 385 | if (element.nameForAutofill().isEmpty()) |
[email protected] | e47aec5 | 2010-08-12 00:50:30 | [diff] [blame] | 386 | return; |
| 387 | |
| 388 | // Don't attempt to autofill with values that are too large. |
| 389 | WebString value = element.value(); |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 390 | if (value.length() > kMaximumTextSizeForAutofill) |
[email protected] | e47aec5 | 2010-08-12 00:50:30 | [diff] [blame] | 391 | return; |
| 392 | |
| 393 | if (!autofill_on_empty_values && value.isEmpty()) |
| 394 | return; |
| 395 | |
| 396 | if (requires_caret_at_end && |
| 397 | (element.selectionStart() != element.selectionEnd() || |
[email protected] | 1866d06 | 2010-11-16 06:19:56 | [diff] [blame] | 398 | element.selectionEnd() != static_cast<int>(value.length()))) |
[email protected] | e47aec5 | 2010-08-12 00:50:30 | [diff] [blame] | 399 | return; |
[email protected] | e47aec5 | 2010-08-12 00:50:30 | [diff] [blame] | 400 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 401 | QueryAutofillSuggestions(element, display_warning_if_disabled); |
[email protected] | 77bb0da | 2010-11-20 01:55:30 | [diff] [blame] | 402 | } |
| 403 | |
[email protected] | cd0a3b7 | 2011-08-02 06:16:19 | [diff] [blame] | 404 | void AutofillAgent::QueryAutofillSuggestions(const WebInputElement& element, |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 405 | bool display_warning_if_disabled) { |
[email protected] | 77bb0da | 2010-11-20 01:55:30 | [diff] [blame] | 406 | static int query_counter = 0; |
| 407 | autofill_query_id_ = query_counter++; |
[email protected] | cd0a3b7 | 2011-08-02 06:16:19 | [diff] [blame] | 408 | autofill_query_element_ = element; |
[email protected] | 6bcd58a | 2010-11-25 02:31:20 | [diff] [blame] | 409 | display_warning_if_disabled_ = display_warning_if_disabled; |
[email protected] | 77bb0da | 2010-11-20 01:55:30 | [diff] [blame] | 410 | |
[email protected] | 3bb80e4 | 2010-11-29 22:27:16 | [diff] [blame] | 411 | webkit_glue::FormData form; |
[email protected] | 77bb0da | 2010-11-20 01:55:30 | [diff] [blame] | 412 | webkit_glue::FormField field; |
[email protected] | 283ba4ca | 2011-09-22 05:25:08 | [diff] [blame] | 413 | if (!FindFormAndFieldForInputElement(element, &form, &field, |
| 414 | REQUIRE_AUTOCOMPLETE)) { |
[email protected] | 7837be6 | 2011-01-18 23:45:08 | [diff] [blame] | 415 | // If we didn't find the cached form, at least let autocomplete have a shot |
| 416 | // at providing suggestions. |
[email protected] | aaa80c9 | 2011-09-16 00:23:54 | [diff] [blame] | 417 | WebFormControlElementToFormField(element, EXTRACT_VALUE, &field); |
[email protected] | 7837be6 | 2011-01-18 23:45:08 | [diff] [blame] | 418 | } |
[email protected] | 77bb0da | 2010-11-20 01:55:30 | [diff] [blame] | 419 | |
[email protected] | 9718305 | 2011-11-17 16:35:22 | [diff] [blame^] | 420 | // TODO(csharp): Stop using the hardcoded value once the WebKit change to |
| 421 | // expose the position lands. |
| 422 | // gfx::Rect bounding_box(autofill_query_element_.boundsInRootViewSpace()); |
| 423 | gfx::Rect bounding_box(26, 51, 155, 22); |
| 424 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 425 | Send(new AutofillHostMsg_QueryFormFieldAutofill( |
[email protected] | 9718305 | 2011-11-17 16:35:22 | [diff] [blame^] | 426 | routing_id(), autofill_query_id_, form, field, bounding_box)); |
[email protected] | e47aec5 | 2010-08-12 00:50:30 | [diff] [blame] | 427 | } |
| 428 | |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 429 | void AutofillAgent::FillAutofillFormData(const WebNode& node, |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 430 | int unique_id, |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 431 | AutofillAction action) { |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 432 | static int query_counter = 0; |
| 433 | autofill_query_id_ = query_counter++; |
| 434 | |
| 435 | webkit_glue::FormData form; |
[email protected] | 3bb80e4 | 2010-11-29 22:27:16 | [diff] [blame] | 436 | webkit_glue::FormField field; |
[email protected] | 283ba4ca | 2011-09-22 05:25:08 | [diff] [blame] | 437 | if (!FindFormAndFieldForInputElement(node.toConst<WebInputElement>(), &form, |
| 438 | &field, REQUIRE_AUTOCOMPLETE)) { |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 439 | return; |
[email protected] | 283ba4ca | 2011-09-22 05:25:08 | [diff] [blame] | 440 | } |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 441 | |
| 442 | autofill_action_ = action; |
[email protected] | e2aaa81 | 2011-03-08 18:46:04 | [diff] [blame] | 443 | was_query_node_autofilled_ = field.is_autofilled; |
[email protected] | 663bd9e | 2011-03-21 01:07:01 | [diff] [blame] | 444 | Send(new AutofillHostMsg_FillAutofillFormData( |
[email protected] | 676126f7 | 2011-01-15 00:03:51 | [diff] [blame] | 445 | routing_id(), autofill_query_id_, form, field, unique_id)); |
[email protected] | 679f128f | 2010-07-22 22:57:44 | [diff] [blame] | 446 | } |
| 447 | |
[email protected] | 7819208 | 2011-01-29 05:43:44 | [diff] [blame] | 448 | } // namespace autofill |