| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/autofill/renderer/autofill_agent.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/command_line.h" | |
| 9 #include "base/message_loop.h" | |
| 10 #include "base/string_util.h" | |
| 11 #include "base/strings/string_split.h" | |
| 12 #include "base/time.h" | |
| 13 #include "base/utf_string_conversions.h" | |
| 14 #include "components/autofill/common/autocheckout_status.h" | |
| 15 #include "components/autofill/common/autofill_constants.h" | |
| 16 #include "components/autofill/common/autofill_messages.h" | |
| 17 #include "components/autofill/common/autofill_switches.h" | |
| 18 #include "components/autofill/common/form_data.h" | |
| 19 #include "components/autofill/common/form_data_predictions.h" | |
| 20 #include "components/autofill/common/form_field_data.h" | |
| 21 #include "components/autofill/common/web_element_descriptor.h" | |
| 22 #include "components/autofill/renderer/form_autofill_util.h" | |
| 23 #include "components/autofill/renderer/page_click_tracker.h" | |
| 24 #include "components/autofill/renderer/password_autofill_agent.h" | |
| 25 #include "content/public/common/password_form.h" | |
| 26 #include "content/public/common/ssl_status.h" | |
| 27 #include "content/public/renderer/render_view.h" | |
| 28 #include "grit/component_resources.h" | |
| 29 #include "third_party/WebKit/public/platform/WebRect.h" | |
| 30 #include "third_party/WebKit/public/platform/WebURLRequest.h" | |
| 31 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" | |
| 32 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
| 33 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement
.h" | |
| 34 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h" | |
| 35 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
| 36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
| 37 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" | |
| 38 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeCollection.h" | |
| 39 #include "third_party/WebKit/Source/WebKit/chromium/public/WebOptionElement.h" | |
| 40 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
| 41 #include "ui/base/keycodes/keyboard_codes.h" | |
| 42 #include "ui/base/l10n/l10n_util.h" | |
| 43 | |
| 44 using WebKit::WebAutofillClient; | |
| 45 using WebKit::WebFormControlElement; | |
| 46 using WebKit::WebFormElement; | |
| 47 using WebKit::WebFrame; | |
| 48 using WebKit::WebInputElement; | |
| 49 using WebKit::WebKeyboardEvent; | |
| 50 using WebKit::WebNode; | |
| 51 using WebKit::WebNodeCollection; | |
| 52 using WebKit::WebOptionElement; | |
| 53 using WebKit::WebString; | |
| 54 | |
| 55 namespace { | |
| 56 | |
| 57 // The size above which we stop triggering autofill for an input text field | |
| 58 // (so to avoid sending long strings through IPC). | |
| 59 const size_t kMaximumTextSizeForAutofill = 1000; | |
| 60 | |
| 61 // The maximum number of data list elements to send to the browser process | |
| 62 // via IPC (to prevent long IPC messages). | |
| 63 const size_t kMaximumDataListSizeForAutofill = 30; | |
| 64 | |
| 65 const int kAutocheckoutClickTimeout = 3; | |
| 66 | |
| 67 void AppendDataListSuggestions(const WebKit::WebInputElement& element, | |
| 68 std::vector<base::string16>* values, | |
| 69 std::vector<base::string16>* labels, | |
| 70 std::vector<base::string16>* icons, | |
| 71 std::vector<int>* item_ids) { | |
| 72 WebNodeCollection options = element.dataListOptions(); | |
| 73 if (options.isNull()) | |
| 74 return; | |
| 75 | |
| 76 base::string16 prefix = element.editingValue(); | |
| 77 if (element.isMultiple() && | |
| 78 element.formControlType() == WebString::fromUTF8("email")) { | |
| 79 std::vector<base::string16> parts; | |
| 80 base::SplitStringDontTrim(prefix, ',', &parts); | |
| 81 if (parts.size() > 0) | |
| 82 TrimWhitespace(parts[parts.size() - 1], TRIM_LEADING, &prefix); | |
| 83 } | |
| 84 for (WebOptionElement option = options.firstItem().to<WebOptionElement>(); | |
| 85 !option.isNull(); option = options.nextItem().to<WebOptionElement>()) { | |
| 86 if (!StartsWith(option.value(), prefix, false) || | |
| 87 option.value() == prefix || | |
| 88 !element.isValidValue(option.value())) | |
| 89 continue; | |
| 90 | |
| 91 values->push_back(option.value()); | |
| 92 if (option.value() != option.label()) | |
| 93 labels->push_back(option.label()); | |
| 94 else | |
| 95 labels->push_back(base::string16()); | |
| 96 icons->push_back(base::string16()); | |
| 97 item_ids->push_back(WebAutofillClient::MenuItemIDDataListEntry); | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 // Trim the vectors before sending them to the browser process to ensure we | |
| 102 // don't send too much data through the IPC. | |
| 103 void TrimDataListsForIPC(std::vector<base::string16>* values, | |
| 104 std::vector<base::string16>* labels, | |
| 105 std::vector<base::string16>* icons, | |
| 106 std::vector<int>* unique_ids) { | |
| 107 // Limit the size of the vectors. | |
| 108 if (values->size() > kMaximumDataListSizeForAutofill) { | |
| 109 values->resize(kMaximumDataListSizeForAutofill); | |
| 110 labels->resize(kMaximumDataListSizeForAutofill); | |
| 111 icons->resize(kMaximumDataListSizeForAutofill); | |
| 112 unique_ids->resize(kMaximumDataListSizeForAutofill); | |
| 113 } | |
| 114 | |
| 115 // Limit the size of the strings in the vectors | |
| 116 for (size_t i = 0; i < values->size(); ++i) { | |
| 117 if ((*values)[i].length() > kMaximumTextSizeForAutofill) | |
| 118 (*values)[i].resize(kMaximumTextSizeForAutofill); | |
| 119 | |
| 120 if ((*labels)[i].length() > kMaximumTextSizeForAutofill) | |
| 121 (*labels)[i].resize(kMaximumTextSizeForAutofill); | |
| 122 | |
| 123 if ((*icons)[i].length() > kMaximumTextSizeForAutofill) | |
| 124 (*icons)[i].resize(kMaximumTextSizeForAutofill); | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 gfx::RectF GetScaledBoundingBox(float scale, WebInputElement* element) { | |
| 129 gfx::Rect bounding_box(element->boundsInViewportSpace()); | |
| 130 return gfx::RectF(bounding_box.x() * scale, | |
| 131 bounding_box.y() * scale, | |
| 132 bounding_box.width() * scale, | |
| 133 bounding_box.height() * scale); | |
| 134 } | |
| 135 | |
| 136 } // namespace | |
| 137 | |
| 138 namespace autofill { | |
| 139 | |
| 140 AutofillAgent::AutofillAgent(content::RenderView* render_view, | |
| 141 PasswordAutofillAgent* password_autofill_agent) | |
| 142 : content::RenderViewObserver(render_view), | |
| 143 password_autofill_agent_(password_autofill_agent), | |
| 144 autofill_query_id_(0), | |
| 145 autofill_action_(AUTOFILL_NONE), | |
| 146 topmost_frame_(NULL), | |
| 147 web_view_(render_view->GetWebView()), | |
| 148 display_warning_if_disabled_(false), | |
| 149 was_query_node_autofilled_(false), | |
| 150 has_shown_autofill_popup_for_current_edit_(false), | |
| 151 did_set_node_text_(false), | |
| 152 autocheckout_click_in_progress_(false), | |
| 153 is_autocheckout_supported_(false), | |
| 154 has_new_forms_for_browser_(false), | |
| 155 ignore_text_changes_(false), | |
| 156 weak_ptr_factory_(this) { | |
| 157 render_view->GetWebView()->setAutofillClient(this); | |
| 158 | |
| 159 // The PageClickTracker is a RenderViewObserver, and hence will be freed when | |
| 160 // the RenderView is destroyed. | |
| 161 new PageClickTracker(render_view, this); | |
| 162 } | |
| 163 | |
| 164 AutofillAgent::~AutofillAgent() {} | |
| 165 | |
| 166 bool AutofillAgent::OnMessageReceived(const IPC::Message& message) { | |
| 167 bool handled = true; | |
| 168 IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message) | |
| 169 IPC_MESSAGE_HANDLER(AutofillMsg_GetAllForms, OnGetAllForms) | |
| 170 IPC_MESSAGE_HANDLER(AutofillMsg_SuggestionsReturned, OnSuggestionsReturned) | |
| 171 IPC_MESSAGE_HANDLER(AutofillMsg_FormDataFilled, OnFormDataFilled) | |
| 172 IPC_MESSAGE_HANDLER(AutofillMsg_FieldTypePredictionsAvailable, | |
| 173 OnFieldTypePredictionsAvailable) | |
| 174 IPC_MESSAGE_HANDLER(AutofillMsg_SetAutofillActionFill, | |
| 175 OnSetAutofillActionFill) | |
| 176 IPC_MESSAGE_HANDLER(AutofillMsg_ClearForm, | |
| 177 OnClearForm) | |
| 178 IPC_MESSAGE_HANDLER(AutofillMsg_SetAutofillActionPreview, | |
| 179 OnSetAutofillActionPreview) | |
| 180 IPC_MESSAGE_HANDLER(AutofillMsg_ClearPreviewedForm, | |
| 181 OnClearPreviewedForm) | |
| 182 IPC_MESSAGE_HANDLER(AutofillMsg_SetNodeText, | |
| 183 OnSetNodeText) | |
| 184 IPC_MESSAGE_HANDLER(AutofillMsg_AcceptDataListSuggestion, | |
| 185 OnAcceptDataListSuggestion) | |
| 186 IPC_MESSAGE_HANDLER(AutofillMsg_AcceptPasswordAutofillSuggestion, | |
| 187 OnAcceptPasswordAutofillSuggestion) | |
| 188 IPC_MESSAGE_HANDLER(AutofillMsg_RequestAutocompleteResult, | |
| 189 OnRequestAutocompleteResult) | |
| 190 IPC_MESSAGE_HANDLER(AutofillMsg_FillFormsAndClick, | |
| 191 OnFillFormsAndClick) | |
| 192 IPC_MESSAGE_HANDLER(AutofillMsg_AutocheckoutSupported, | |
| 193 OnAutocheckoutSupported) | |
| 194 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 195 IPC_END_MESSAGE_MAP() | |
| 196 return handled; | |
| 197 } | |
| 198 | |
| 199 void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) { | |
| 200 // Record timestamp on document load. This is used to record overhead of | |
| 201 // Autofill feature. | |
| 202 forms_seen_timestamp_ = base::TimeTicks::Now(); | |
| 203 | |
| 204 // The document has now been fully loaded. Scan for forms to be sent up to | |
| 205 // the browser. | |
| 206 std::vector<FormData> forms; | |
| 207 bool has_more_forms = false; | |
| 208 if (!frame->parent()) { | |
| 209 topmost_frame_ = frame; | |
| 210 form_elements_.clear(); | |
| 211 has_more_forms = form_cache_.ExtractFormsAndFormElements( | |
| 212 *frame, kRequiredAutofillFields, &forms, &form_elements_); | |
| 213 } else { | |
| 214 form_cache_.ExtractForms(*frame, &forms); | |
| 215 } | |
| 216 | |
| 217 autofill::FormsSeenState state = has_more_forms ? | |
| 218 autofill::PARTIAL_FORMS_SEEN : autofill::NO_SPECIAL_FORMS_SEEN; | |
| 219 | |
| 220 // Always communicate to browser process for topmost frame. | |
| 221 if (!forms.empty() || !frame->parent()) { | |
| 222 Send(new AutofillHostMsg_FormsSeen(routing_id(), forms, | |
| 223 forms_seen_timestamp_, | |
| 224 state)); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 void AutofillAgent::DidStartProvisionalLoad(WebFrame* frame) { | |
| 229 if (!frame->parent()) { | |
| 230 is_autocheckout_supported_ = false; | |
| 231 topmost_frame_ = NULL; | |
| 232 if (click_timer_.IsRunning()) { | |
| 233 click_timer_.Stop(); | |
| 234 autocheckout_click_in_progress_ = true; | |
| 235 } | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 void AutofillAgent::DidFailProvisionalLoad(WebFrame* frame, | |
| 240 const WebKit::WebURLError& error) { | |
| 241 if (!frame->parent() && autocheckout_click_in_progress_) { | |
| 242 autocheckout_click_in_progress_ = false; | |
| 243 ClickFailed(); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 void AutofillAgent::DidCommitProvisionalLoad(WebFrame* frame, | |
| 248 bool is_new_navigation) { | |
| 249 in_flight_request_form_.reset(); | |
| 250 if (!frame->parent()) | |
| 251 autocheckout_click_in_progress_ = false; | |
| 252 } | |
| 253 | |
| 254 void AutofillAgent::FrameDetached(WebFrame* frame) { | |
| 255 form_cache_.ResetFrame(*frame); | |
| 256 if (!frame->parent()) { | |
| 257 // |frame| is about to be destroyed so we need to clear |top_most_frame_|. | |
| 258 topmost_frame_ = NULL; | |
| 259 click_timer_.Stop(); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 void AutofillAgent::WillSubmitForm(WebFrame* frame, | |
| 264 const WebFormElement& form) { | |
| 265 FormData form_data; | |
| 266 if (WebFormElementToFormData(form, | |
| 267 WebFormControlElement(), | |
| 268 REQUIRE_AUTOCOMPLETE, | |
| 269 static_cast<ExtractMask>( | |
| 270 EXTRACT_VALUE | EXTRACT_OPTION_TEXT), | |
| 271 &form_data, | |
| 272 NULL)) { | |
| 273 Send(new AutofillHostMsg_FormSubmitted(routing_id(), form_data, | |
| 274 base::TimeTicks::Now())); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 void AutofillAgent::ZoomLevelChanged() { | |
| 279 // Any time the zoom level changes, the page's content moves, so any Autofill | |
| 280 // popups should be hidden. This is only needed for the new Autofill UI | |
| 281 // because WebKit already knows to hide the old UI when this occurs. | |
| 282 HideHostAutofillUi(); | |
| 283 } | |
| 284 | |
| 285 void AutofillAgent::FocusedNodeChanged(const WebKit::WebNode& node) { | |
| 286 if (node.isNull() || !node.isElementNode()) | |
| 287 return; | |
| 288 | |
| 289 WebKit::WebElement web_element = node.toConst<WebKit::WebElement>(); | |
| 290 | |
| 291 if (!web_element.document().frame()) | |
| 292 return; | |
| 293 | |
| 294 const WebInputElement* element = toWebInputElement(&web_element); | |
| 295 | |
| 296 if (!element || !element->isEnabled() || element->isReadOnly() || | |
| 297 !element->isTextField() || element->isPasswordField()) | |
| 298 return; | |
| 299 | |
| 300 element_ = *element; | |
| 301 | |
| 302 MaybeShowAutocheckoutBubble(); | |
| 303 } | |
| 304 | |
| 305 void AutofillAgent::MaybeShowAutocheckoutBubble() { | |
| 306 if (element_.isNull() || !element_.focused()) | |
| 307 return; | |
| 308 | |
| 309 FormData form; | |
| 310 FormFieldData field; | |
| 311 // This must be called to short circuit this method if it fails. | |
| 312 if (!FindFormAndFieldForInputElement(element_, &form, &field, REQUIRE_NONE)) | |
| 313 return; | |
| 314 | |
| 315 form.ssl_status = render_view()->GetSSLStatusOfFrame( | |
| 316 element_.document().frame()); | |
| 317 | |
| 318 Send(new AutofillHostMsg_MaybeShowAutocheckoutBubble( | |
| 319 routing_id(), | |
| 320 form, | |
| 321 GetScaledBoundingBox(web_view_->pageScaleFactor(), &element_))); | |
| 322 } | |
| 323 | |
| 324 void AutofillAgent::DidChangeScrollOffset(WebKit::WebFrame*) { | |
| 325 HideAutofillUi(); | |
| 326 } | |
| 327 | |
| 328 void AutofillAgent::didRequestAutocomplete(WebKit::WebFrame* frame, | |
| 329 const WebFormElement& form) { | |
| 330 FormData form_data; | |
| 331 if (!in_flight_request_form_.isNull() || | |
| 332 !WebFormElementToFormData(form, | |
| 333 WebFormControlElement(), | |
| 334 REQUIRE_AUTOCOMPLETE, | |
| 335 EXTRACT_OPTIONS, | |
| 336 &form_data, | |
| 337 NULL)) { | |
| 338 WebFormElement(form).finishRequestAutocomplete( | |
| 339 WebFormElement::AutocompleteResultErrorDisabled); | |
| 340 return; | |
| 341 } | |
| 342 | |
| 343 // Cancel any pending Autofill requests and hide any currently showing popups. | |
| 344 ++autofill_query_id_; | |
| 345 HideAutofillUi(); | |
| 346 | |
| 347 in_flight_request_form_ = form; | |
| 348 // Add SSL Status in the formdata to let browser process alert user | |
| 349 // appropriately using browser UI. | |
| 350 form_data.ssl_status = render_view()->GetSSLStatusOfFrame(frame); | |
| 351 Send(new AutofillHostMsg_RequestAutocomplete( | |
| 352 routing_id(), | |
| 353 form_data, | |
| 354 frame->document().url())); | |
| 355 } | |
| 356 | |
| 357 void AutofillAgent::setIgnoreTextChanges(bool ignore) { | |
| 358 ignore_text_changes_ = ignore; | |
| 359 } | |
| 360 | |
| 361 void AutofillAgent::InputElementClicked(const WebInputElement& element, | |
| 362 bool was_focused, | |
| 363 bool is_focused) { | |
| 364 if (was_focused) | |
| 365 ShowSuggestions(element, true, false, true); | |
| 366 } | |
| 367 | |
| 368 void AutofillAgent::InputElementLostFocus() { | |
| 369 HideHostAutofillUi(); | |
| 370 } | |
| 371 | |
| 372 void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node, | |
| 373 const WebString& value, | |
| 374 const WebString& label, | |
| 375 int item_id, | |
| 376 unsigned index) { | |
| 377 if (password_autofill_agent_->DidAcceptAutofillSuggestion(node, value)) | |
| 378 return; | |
| 379 | |
| 380 DCHECK(node == element_); | |
| 381 | |
| 382 switch (item_id) { | |
| 383 case WebAutofillClient::MenuItemIDWarningMessage: | |
| 384 case WebAutofillClient::MenuItemIDSeparator: | |
| 385 NOTREACHED(); | |
| 386 break; | |
| 387 case WebAutofillClient::MenuItemIDAutofillOptions: | |
| 388 // User selected 'Autofill Options'. | |
| 389 Send(new AutofillHostMsg_ShowAutofillDialog(routing_id())); | |
| 390 break; | |
| 391 case WebAutofillClient::MenuItemIDClearForm: | |
| 392 // User selected 'Clear form'. | |
| 393 form_cache_.ClearFormWithElement(element_); | |
| 394 break; | |
| 395 case WebAutofillClient::MenuItemIDAutocompleteEntry: | |
| 396 case WebAutofillClient::MenuItemIDPasswordEntry: | |
| 397 // User selected an Autocomplete or password entry, so we fill directly. | |
| 398 SetNodeText(value, &element_); | |
| 399 break; | |
| 400 case WebAutofillClient::MenuItemIDDataListEntry: | |
| 401 AcceptDataListSuggestion(value); | |
| 402 break; | |
| 403 default: | |
| 404 // A positive item_id is a unique id for an autofill (vs. autocomplete) | |
| 405 // suggestion. | |
| 406 DCHECK_GT(item_id, 0); | |
| 407 // Fill the values for the whole form. | |
| 408 FillAutofillFormData(node, item_id, AUTOFILL_FILL); | |
| 409 } | |
| 410 } | |
| 411 | |
| 412 void AutofillAgent::didSelectAutofillSuggestion(const WebNode& node, | |
| 413 const WebString& value, | |
| 414 const WebString& label, | |
| 415 int item_id) { | |
| 416 if (password_autofill_agent_->DidSelectAutofillSuggestion(node)) | |
| 417 return; | |
| 418 | |
| 419 didClearAutofillSelection(node); | |
| 420 | |
| 421 if (item_id > 0) | |
| 422 FillAutofillFormData(node, item_id, AUTOFILL_PREVIEW); | |
| 423 } | |
| 424 | |
| 425 void AutofillAgent::didClearAutofillSelection(const WebNode& node) { | |
| 426 if (password_autofill_agent_->DidClearAutofillSelection(node)) | |
| 427 return; | |
| 428 | |
| 429 if (!element_.isNull() && node == element_) { | |
| 430 ClearPreviewedFormWithElement(element_, was_query_node_autofilled_); | |
| 431 } else { | |
| 432 // TODO(isherman): There seem to be rare cases where this code *is* | |
| 433 // reachable: see [ http://crbug.com/96321#c6 ]. Ideally we would | |
| 434 // understand those cases and fix the code to avoid them. However, so far I | |
| 435 // have been unable to reproduce such a case locally. If you hit this | |
| 436 // NOTREACHED(), please file a bug against me. | |
| 437 NOTREACHED(); | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 void AutofillAgent::removeAutocompleteSuggestion(const WebString& name, | |
| 442 const WebString& value) { | |
| 443 Send(new AutofillHostMsg_RemoveAutocompleteEntry(routing_id(), name, value)); | |
| 444 } | |
| 445 | |
| 446 void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) { | |
| 447 password_autofill_agent_->TextFieldDidEndEditing(element); | |
| 448 has_shown_autofill_popup_for_current_edit_ = false; | |
| 449 Send(new AutofillHostMsg_DidEndTextFieldEditing(routing_id())); | |
| 450 } | |
| 451 | |
| 452 void AutofillAgent::textFieldDidChange(const WebInputElement& element) { | |
| 453 if (ignore_text_changes_) | |
| 454 return; | |
| 455 | |
| 456 if (did_set_node_text_) { | |
| 457 did_set_node_text_ = false; | |
| 458 return; | |
| 459 } | |
| 460 | |
| 461 // We post a task for doing the Autofill as the caret position is not set | |
| 462 // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and | |
| 463 // it is needed to trigger autofill. | |
| 464 weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 465 base::MessageLoop::current()->PostTask( | |
| 466 FROM_HERE, | |
| 467 base::Bind(&AutofillAgent::TextFieldDidChangeImpl, | |
| 468 weak_ptr_factory_.GetWeakPtr(), | |
| 469 element)); | |
| 470 } | |
| 471 | |
| 472 void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) { | |
| 473 // If the element isn't focused then the changes don't matter. This check is | |
| 474 // required to properly handle IME interactions. | |
| 475 if (!element.focused()) | |
| 476 return; | |
| 477 | |
| 478 if (password_autofill_agent_->TextDidChangeInTextField(element)) { | |
| 479 element_ = element; | |
| 480 return; | |
| 481 } | |
| 482 | |
| 483 ShowSuggestions(element, false, true, false); | |
| 484 | |
| 485 FormData form; | |
| 486 FormFieldData field; | |
| 487 if (FindFormAndFieldForInputElement(element, &form, &field, REQUIRE_NONE)) { | |
| 488 Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field, | |
| 489 base::TimeTicks::Now())); | |
| 490 } | |
| 491 } | |
| 492 | |
| 493 void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element, | |
| 494 const WebKeyboardEvent& event) { | |
| 495 if (password_autofill_agent_->TextFieldHandlingKeyDown(element, event)) { | |
| 496 element_ = element; | |
| 497 return; | |
| 498 } | |
| 499 | |
| 500 if (event.windowsKeyCode == ui::VKEY_DOWN || | |
| 501 event.windowsKeyCode == ui::VKEY_UP) | |
| 502 ShowSuggestions(element, true, true, true); | |
| 503 } | |
| 504 | |
| 505 void AutofillAgent::OnSuggestionsReturned( | |
| 506 int query_id, | |
| 507 const std::vector<base::string16>& values, | |
| 508 const std::vector<base::string16>& labels, | |
| 509 const std::vector<base::string16>& icons, | |
| 510 const std::vector<int>& unique_ids) { | |
| 511 if (query_id != autofill_query_id_) | |
| 512 return; | |
| 513 | |
| 514 if (element_.isNull() || !element_.isFocusable()) | |
| 515 return; | |
| 516 | |
| 517 std::vector<base::string16> v(values); | |
| 518 std::vector<base::string16> l(labels); | |
| 519 std::vector<base::string16> i(icons); | |
| 520 std::vector<int> ids(unique_ids); | |
| 521 | |
| 522 if (!element_.autoComplete() && !v.empty()) { | |
| 523 // If autofill is disabled and we had suggestions, show a warning instead. | |
| 524 v.assign(1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)); | |
| 525 l.assign(1, base::string16()); | |
| 526 i.assign(1, base::string16()); | |
| 527 ids.assign(1, WebAutofillClient::MenuItemIDWarningMessage); | |
| 528 } else if (ids.size() > 1 && | |
| 529 ids[0] == WebAutofillClient::MenuItemIDWarningMessage) { | |
| 530 // If we received an autofill warning plus some autocomplete suggestions, | |
| 531 // remove the autofill warning. | |
| 532 v.erase(v.begin()); | |
| 533 l.erase(l.begin()); | |
| 534 i.erase(i.begin()); | |
| 535 ids.erase(ids.begin()); | |
| 536 } | |
| 537 | |
| 538 // If we were about to show a warning and we shouldn't, don't. | |
| 539 if (!display_warning_if_disabled_ && !v.empty() && | |
| 540 ids[0] == WebAutofillClient::MenuItemIDWarningMessage) { | |
| 541 v.clear(); | |
| 542 l.clear(); | |
| 543 i.clear(); | |
| 544 ids.clear(); | |
| 545 } | |
| 546 | |
| 547 // Only include "Autofill Options" special menu item if we have Autofill | |
| 548 // items, identified by |unique_ids| having at least one valid value. | |
| 549 bool has_autofill_item = false; | |
| 550 for (size_t i = 0; i < ids.size(); ++i) { | |
| 551 if (ids[i] > 0) { | |
| 552 has_autofill_item = true; | |
| 553 break; | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 if (has_autofill_item) { | |
| 558 v.push_back(base::string16()); | |
| 559 l.push_back(base::string16()); | |
| 560 i.push_back(base::string16()); | |
| 561 ids.push_back(WebAutofillClient::MenuItemIDSeparator); | |
| 562 | |
| 563 if (FormWithElementIsAutofilled(element_)) { | |
| 564 // The form has been auto-filled, so give the user the chance to clear the | |
| 565 // form. Append the 'Clear form' menu item. | |
| 566 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM)); | |
| 567 l.push_back(base::string16()); | |
| 568 i.push_back(base::string16()); | |
| 569 ids.push_back(WebAutofillClient::MenuItemIDClearForm); | |
| 570 } | |
| 571 | |
| 572 // Append the 'Chrome Autofill options' menu item; | |
| 573 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP)); | |
| 574 l.push_back(base::string16()); | |
| 575 i.push_back(base::string16()); | |
| 576 ids.push_back(WebAutofillClient::MenuItemIDAutofillOptions); | |
| 577 } | |
| 578 | |
| 579 CombineDataListEntriesAndShow(element_, v, l, i, ids, has_autofill_item); | |
| 580 } | |
| 581 | |
| 582 void AutofillAgent::CombineDataListEntriesAndShow( | |
| 583 const WebKit::WebInputElement& element, | |
| 584 const std::vector<base::string16>& values, | |
| 585 const std::vector<base::string16>& labels, | |
| 586 const std::vector<base::string16>& icons, | |
| 587 const std::vector<int>& item_ids, | |
| 588 bool has_autofill_item) { | |
| 589 std::vector<base::string16> v; | |
| 590 std::vector<base::string16> l; | |
| 591 std::vector<base::string16> i; | |
| 592 std::vector<int> ids; | |
| 593 | |
| 594 AppendDataListSuggestions(element, &v, &l, &i, &ids); | |
| 595 | |
| 596 // If there are both <datalist> items and Autofill suggestions, add a | |
| 597 // separator between them. | |
| 598 if (!v.empty() && !values.empty()) { | |
| 599 v.push_back(base::string16()); | |
| 600 l.push_back(base::string16()); | |
| 601 i.push_back(base::string16()); | |
| 602 ids.push_back(WebAutofillClient::MenuItemIDSeparator); | |
| 603 } | |
| 604 | |
| 605 // Append the Autofill suggestions. | |
| 606 v.insert(v.end(), values.begin(), values.end()); | |
| 607 l.insert(l.end(), labels.begin(), labels.end()); | |
| 608 i.insert(i.end(), icons.begin(), icons.end()); | |
| 609 ids.insert(ids.end(), item_ids.begin(), item_ids.end()); | |
| 610 | |
| 611 if (v.empty()) { | |
| 612 // No suggestions, any popup currently showing is obsolete. | |
| 613 HideAutofillUi(); | |
| 614 return; | |
| 615 } | |
| 616 | |
| 617 WebKit::WebView* web_view = render_view()->GetWebView(); | |
| 618 if (!web_view) | |
| 619 return; | |
| 620 | |
| 621 // Send to WebKit for display. | |
| 622 web_view->applyAutofillSuggestions(element, v, l, i, ids); | |
| 623 | |
| 624 Send(new AutofillHostMsg_DidShowAutofillSuggestions( | |
| 625 routing_id(), | |
| 626 has_autofill_item && !has_shown_autofill_popup_for_current_edit_)); | |
| 627 has_shown_autofill_popup_for_current_edit_ |= has_autofill_item; | |
| 628 } | |
| 629 | |
| 630 void AutofillAgent::AcceptDataListSuggestion( | |
| 631 const base::string16& suggested_value) { | |
| 632 base::string16 new_value = suggested_value; | |
| 633 // If this element takes multiple values then replace the last part with | |
| 634 // the suggestion. | |
| 635 if (element_.isMultiple() && | |
| 636 element_.formControlType() == WebString::fromUTF8("email")) { | |
| 637 std::vector<base::string16> parts; | |
| 638 | |
| 639 base::SplitStringDontTrim(element_.editingValue(), ',', &parts); | |
| 640 if (parts.size() == 0) | |
| 641 parts.push_back(base::string16()); | |
| 642 | |
| 643 base::string16 last_part = parts.back(); | |
| 644 // We want to keep just the leading whitespace. | |
| 645 for (size_t i = 0; i < last_part.size(); ++i) { | |
| 646 if (!IsWhitespace(last_part[i])) { | |
| 647 last_part = last_part.substr(0, i); | |
| 648 break; | |
| 649 } | |
| 650 } | |
| 651 last_part.append(suggested_value); | |
| 652 parts[parts.size() - 1] = last_part; | |
| 653 | |
| 654 new_value = JoinString(parts, ','); | |
| 655 } | |
| 656 SetNodeText(new_value, &element_); | |
| 657 } | |
| 658 | |
| 659 void AutofillAgent::OnFormDataFilled(int query_id, | |
| 660 const FormData& form) { | |
| 661 if (!render_view()->GetWebView() || query_id != autofill_query_id_) | |
| 662 return; | |
| 663 | |
| 664 was_query_node_autofilled_ = element_.isAutofilled(); | |
| 665 | |
| 666 switch (autofill_action_) { | |
| 667 case AUTOFILL_FILL: | |
| 668 FillForm(form, element_); | |
| 669 Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id(), | |
| 670 base::TimeTicks::Now())); | |
| 671 break; | |
| 672 case AUTOFILL_PREVIEW: | |
| 673 PreviewForm(form, element_); | |
| 674 Send(new AutofillHostMsg_DidPreviewAutofillFormData(routing_id())); | |
| 675 break; | |
| 676 default: | |
| 677 NOTREACHED(); | |
| 678 } | |
| 679 autofill_action_ = AUTOFILL_NONE; | |
| 680 } | |
| 681 | |
| 682 void AutofillAgent::OnFieldTypePredictionsAvailable( | |
| 683 const std::vector<FormDataPredictions>& forms) { | |
| 684 for (size_t i = 0; i < forms.size(); ++i) { | |
| 685 form_cache_.ShowPredictions(forms[i]); | |
| 686 } | |
| 687 } | |
| 688 | |
| 689 void AutofillAgent::OnSetAutofillActionFill() { | |
| 690 autofill_action_ = AUTOFILL_FILL; | |
| 691 } | |
| 692 | |
| 693 void AutofillAgent::OnClearForm() { | |
| 694 form_cache_.ClearFormWithElement(element_); | |
| 695 } | |
| 696 | |
| 697 void AutofillAgent::OnSetAutofillActionPreview() { | |
| 698 autofill_action_ = AUTOFILL_PREVIEW; | |
| 699 } | |
| 700 | |
| 701 void AutofillAgent::OnClearPreviewedForm() { | |
| 702 didClearAutofillSelection(element_); | |
| 703 } | |
| 704 | |
| 705 void AutofillAgent::OnSetNodeText(const base::string16& value) { | |
| 706 SetNodeText(value, &element_); | |
| 707 } | |
| 708 | |
| 709 void AutofillAgent::OnAcceptDataListSuggestion(const base::string16& value) { | |
| 710 AcceptDataListSuggestion(value); | |
| 711 } | |
| 712 | |
| 713 void AutofillAgent::OnAcceptPasswordAutofillSuggestion( | |
| 714 const base::string16& value) { | |
| 715 // We need to make sure this is handled here because the browser process | |
| 716 // skipped it handling because it believed it would be handled here. If it | |
| 717 // isn't handled here then the browser logic needs to be updated. | |
| 718 bool handled = password_autofill_agent_->DidAcceptAutofillSuggestion( | |
| 719 element_, | |
| 720 value); | |
| 721 DCHECK(handled); | |
| 722 } | |
| 723 | |
| 724 void AutofillAgent::OnGetAllForms() { | |
| 725 form_elements_.clear(); | |
| 726 | |
| 727 // Force fetch all non empty forms. | |
| 728 std::vector<FormData> forms; | |
| 729 form_cache_.ExtractFormsAndFormElements( | |
| 730 *topmost_frame_, 0, &forms, &form_elements_); | |
| 731 | |
| 732 // OnGetAllForms should only be called if AutofillAgent reported to | |
| 733 // AutofillManager that there are more forms | |
| 734 DCHECK(!forms.empty()); | |
| 735 | |
| 736 // Report to AutofillManager that all forms are being sent. | |
| 737 Send(new AutofillHostMsg_FormsSeen(routing_id(), forms, | |
| 738 forms_seen_timestamp_, | |
| 739 NO_SPECIAL_FORMS_SEEN)); | |
| 740 } | |
| 741 | |
| 742 void AutofillAgent::OnRequestAutocompleteResult( | |
| 743 WebFormElement::AutocompleteResult result, const FormData& form_data) { | |
| 744 if (in_flight_request_form_.isNull()) | |
| 745 return; | |
| 746 | |
| 747 if (result == WebFormElement::AutocompleteResultSuccess) | |
| 748 FillFormIncludingNonFocusableElements(form_data, in_flight_request_form_); | |
| 749 | |
| 750 in_flight_request_form_.finishRequestAutocomplete(result); | |
| 751 in_flight_request_form_.reset(); | |
| 752 } | |
| 753 | |
| 754 void AutofillAgent::OnFillFormsAndClick( | |
| 755 const std::vector<FormData>& forms, | |
| 756 const WebElementDescriptor& click_element_descriptor) { | |
| 757 DCHECK_EQ(forms.size(), form_elements_.size()); | |
| 758 | |
| 759 // Fill the form. | |
| 760 for (size_t i = 0; i < forms.size(); ++i) | |
| 761 FillFormIncludingNonFocusableElements(forms[i], form_elements_[i]); | |
| 762 | |
| 763 // Exit early if there is nothing to click. | |
| 764 if (click_element_descriptor.retrieval_method == WebElementDescriptor::NONE) | |
| 765 return; | |
| 766 | |
| 767 // It's possible that clicking the element to proceed in an Autocheckout | |
| 768 // flow will not actually proceed to the next step in the flow, e.g. there | |
| 769 // is a new required field that Autocheckout does not know how to fill. In | |
| 770 // order to capture this case and present the user with an error a timer is | |
| 771 // set that informs the browser of the error. |click_timer_| has to be started | |
| 772 // before clicking so it can start before DidStartProvisionalLoad started. | |
| 773 click_timer_.Start(FROM_HERE, | |
| 774 base::TimeDelta::FromSeconds(kAutocheckoutClickTimeout), | |
| 775 this, | |
| 776 &AutofillAgent::ClickFailed); | |
| 777 if (!ClickElement(topmost_frame_->document(), | |
| 778 click_element_descriptor)) { | |
| 779 click_timer_.Stop(); | |
| 780 Send(new AutofillHostMsg_ClickFailed(routing_id(), | |
| 781 MISSING_ADVANCE)); | |
| 782 } | |
| 783 } | |
| 784 | |
| 785 void AutofillAgent::OnAutocheckoutSupported() { | |
| 786 is_autocheckout_supported_ = true; | |
| 787 if (has_new_forms_for_browser_) | |
| 788 MaybeSendDynamicFormsSeen(); | |
| 789 MaybeShowAutocheckoutBubble(); | |
| 790 } | |
| 791 | |
| 792 void AutofillAgent::ClickFailed() { | |
| 793 Send(new AutofillHostMsg_ClickFailed(routing_id(), | |
| 794 CANNOT_PROCEED)); | |
| 795 } | |
| 796 | |
| 797 void AutofillAgent::ShowSuggestions(const WebInputElement& element, | |
| 798 bool autofill_on_empty_values, | |
| 799 bool requires_caret_at_end, | |
| 800 bool display_warning_if_disabled) { | |
| 801 if (!element.isEnabled() || element.isReadOnly() || !element.isTextField() || | |
| 802 element.isPasswordField() || !element.suggestedValue().isEmpty()) | |
| 803 return; | |
| 804 | |
| 805 // Don't attempt to autofill with values that are too large or if filling | |
| 806 // criteria are not met. | |
| 807 WebString value = element.editingValue(); | |
| 808 if (value.length() > kMaximumTextSizeForAutofill || | |
| 809 (!autofill_on_empty_values && value.isEmpty()) || | |
| 810 (requires_caret_at_end && | |
| 811 (element.selectionStart() != element.selectionEnd() || | |
| 812 element.selectionEnd() != static_cast<int>(value.length())))) { | |
| 813 // Any popup currently showing is obsolete. | |
| 814 HideAutofillUi(); | |
| 815 return; | |
| 816 } | |
| 817 | |
| 818 element_ = element; | |
| 819 if (password_autofill_agent_->ShowSuggestions(element)) | |
| 820 return; | |
| 821 | |
| 822 // If autocomplete is disabled at the form level, then we might want to show a | |
| 823 // warning in place of suggestions. However, if autocomplete is disabled | |
| 824 // specifically for this field, we never want to show a warning. Otherwise, | |
| 825 // we might interfere with custom popups (e.g. search suggestions) used by the | |
| 826 // website. Note that we cannot use the WebKit method element.autoComplete() | |
| 827 // as it does not allow us to distinguish the case where autocomplete is | |
| 828 // disabled for *both* the element and for the form. | |
| 829 // Also, if the field has no name, then we won't have values. | |
| 830 const base::string16 autocomplete_attribute = | |
| 831 element.getAttribute("autocomplete"); | |
| 832 if (LowerCaseEqualsASCII(autocomplete_attribute, "off") || | |
| 833 element.nameForAutofill().isEmpty()) { | |
| 834 CombineDataListEntriesAndShow(element, std::vector<base::string16>(), | |
| 835 std::vector<base::string16>(), | |
| 836 std::vector<base::string16>(), | |
| 837 std::vector<int>(), false); | |
| 838 return; | |
| 839 } | |
| 840 | |
| 841 QueryAutofillSuggestions(element, display_warning_if_disabled); | |
| 842 } | |
| 843 | |
| 844 void AutofillAgent::QueryAutofillSuggestions(const WebInputElement& element, | |
| 845 bool display_warning_if_disabled) { | |
| 846 if (!element.document().frame()) | |
| 847 return; | |
| 848 | |
| 849 static int query_counter = 0; | |
| 850 autofill_query_id_ = query_counter++; | |
| 851 display_warning_if_disabled_ = display_warning_if_disabled; | |
| 852 | |
| 853 // If autocomplete is disabled at the form level, we want to see if there | |
| 854 // would have been any suggestions were it enabled, so that we can show a | |
| 855 // warning. Otherwise, we want to ignore fields that disable autocomplete, so | |
| 856 // that the suggestions list does not include suggestions for these form | |
| 857 // fields -- see comment 1 on http://crbug.com/69914 | |
| 858 // Rather than testing the form's autocomplete enabled state, we test the | |
| 859 // element's state. The DCHECK below ensures that this is equivalent. | |
| 860 DCHECK(element.autoComplete() || !element.form().autoComplete()); | |
| 861 const RequirementsMask requirements = | |
| 862 element.autoComplete() ? REQUIRE_AUTOCOMPLETE : REQUIRE_NONE; | |
| 863 | |
| 864 FormData form; | |
| 865 FormFieldData field; | |
| 866 if (!FindFormAndFieldForInputElement(element, &form, &field, requirements)) { | |
| 867 // If we didn't find the cached form, at least let autocomplete have a shot | |
| 868 // at providing suggestions. | |
| 869 WebFormControlElementToFormField(element, EXTRACT_VALUE, &field); | |
| 870 } | |
| 871 | |
| 872 gfx::RectF bounding_box_scaled = | |
| 873 GetScaledBoundingBox(web_view_->pageScaleFactor(), &element_); | |
| 874 | |
| 875 // Find the datalist values and send them to the browser process. | |
| 876 std::vector<base::string16> data_list_values; | |
| 877 std::vector<base::string16> data_list_labels; | |
| 878 std::vector<base::string16> data_list_icons; | |
| 879 std::vector<int> data_list_unique_ids; | |
| 880 AppendDataListSuggestions(element_, | |
| 881 &data_list_values, | |
| 882 &data_list_labels, | |
| 883 &data_list_icons, | |
| 884 &data_list_unique_ids); | |
| 885 | |
| 886 TrimDataListsForIPC(&data_list_values, | |
| 887 &data_list_labels, | |
| 888 &data_list_icons, | |
| 889 &data_list_unique_ids); | |
| 890 | |
| 891 Send(new AutofillHostMsg_SetDataList(routing_id(), | |
| 892 data_list_values, | |
| 893 data_list_labels, | |
| 894 data_list_icons, | |
| 895 data_list_unique_ids)); | |
| 896 | |
| 897 // Add SSL Status in the formdata to let browser process alert user | |
| 898 // appropriately using browser UI. | |
| 899 form.ssl_status = render_view()->GetSSLStatusOfFrame( | |
| 900 element.document().frame()); | |
| 901 Send(new AutofillHostMsg_QueryFormFieldAutofill(routing_id(), | |
| 902 autofill_query_id_, | |
| 903 form, | |
| 904 field, | |
| 905 bounding_box_scaled, | |
| 906 display_warning_if_disabled)); | |
| 907 } | |
| 908 | |
| 909 void AutofillAgent::FillAutofillFormData(const WebNode& node, | |
| 910 int unique_id, | |
| 911 AutofillAction action) { | |
| 912 DCHECK_GT(unique_id, 0); | |
| 913 | |
| 914 static int query_counter = 0; | |
| 915 autofill_query_id_ = query_counter++; | |
| 916 | |
| 917 FormData form; | |
| 918 FormFieldData field; | |
| 919 if (!FindFormAndFieldForInputElement(node.toConst<WebInputElement>(), &form, | |
| 920 &field, REQUIRE_AUTOCOMPLETE)) { | |
| 921 return; | |
| 922 } | |
| 923 | |
| 924 autofill_action_ = action; | |
| 925 Send(new AutofillHostMsg_FillAutofillFormData( | |
| 926 routing_id(), autofill_query_id_, form, field, unique_id)); | |
| 927 } | |
| 928 | |
| 929 void AutofillAgent::SetNodeText(const base::string16& value, | |
| 930 WebKit::WebInputElement* node) { | |
| 931 did_set_node_text_ = true; | |
| 932 base::string16 substring = value; | |
| 933 substring = substring.substr(0, node->maxLength()); | |
| 934 | |
| 935 node->setEditingValue(substring); | |
| 936 } | |
| 937 | |
| 938 void AutofillAgent::HideAutofillUi() { | |
| 939 WebKit::WebView* web_view = render_view()->GetWebView(); | |
| 940 if (web_view) | |
| 941 web_view->hidePopups(); | |
| 942 | |
| 943 HideHostAutofillUi(); | |
| 944 } | |
| 945 | |
| 946 void AutofillAgent::HideHostAutofillUi() { | |
| 947 Send(new AutofillHostMsg_HideAutofillUi(routing_id())); | |
| 948 } | |
| 949 | |
| 950 void AutofillAgent::didAssociateFormControls( | |
| 951 const WebKit::WebVector<WebKit::WebNode>& nodes) { | |
| 952 for (size_t i = 0; i < nodes.size(); ++i) { | |
| 953 WebKit::WebNode node = nodes[i]; | |
| 954 if (node.document().frame() == topmost_frame_) { | |
| 955 forms_seen_timestamp_ = base::TimeTicks::Now(); | |
| 956 has_new_forms_for_browser_ = true; | |
| 957 break; | |
| 958 } | |
| 959 } | |
| 960 | |
| 961 if (has_new_forms_for_browser_ && is_autocheckout_supported_) | |
| 962 MaybeSendDynamicFormsSeen(); | |
| 963 } | |
| 964 | |
| 965 void AutofillAgent::MaybeSendDynamicFormsSeen() { | |
| 966 has_new_forms_for_browser_ = false; | |
| 967 form_elements_.clear(); | |
| 968 std::vector<FormData> forms; | |
| 969 // This will only be called for Autocheckout flows, so send all forms to | |
| 970 // save an IPC. | |
| 971 form_cache_.ExtractFormsAndFormElements( | |
| 972 *topmost_frame_, 0, &forms, &form_elements_); | |
| 973 autofill::FormsSeenState state = autofill::DYNAMIC_FORMS_SEEN; | |
| 974 | |
| 975 if (!forms.empty()) { | |
| 976 if (click_timer_.IsRunning()) | |
| 977 click_timer_.Stop(); | |
| 978 Send(new AutofillHostMsg_FormsSeen(routing_id(), forms, | |
| 979 forms_seen_timestamp_, | |
| 980 state)); | |
| 981 } | |
| 982 } | |
| 983 | |
| 984 } // namespace autofill | |
| OLD | NEW |