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