| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/renderer/autofill/password_autofill_manager.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/memory/scoped_ptr.h" | |
| 9 #include "base/message_loop.h" | |
| 10 #include "chrome/renderer/autofill/form_autofill_util.h" | |
| 11 #include "components/autofill/common/autofill_messages.h" | |
| 12 #include "components/autofill/common/form_field_data.h" | |
| 13 #include "components/autofill/common/password_form_fill_data.h" | |
| 14 #include "content/public/common/password_form.h" | |
| 15 #include "content/public/renderer/password_form_conversion_utils.h" | |
| 16 #include "content/public/renderer/render_view.h" | |
| 17 #include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h" | |
| 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" | |
| 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
| 20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" | |
| 21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h" | |
| 22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
| 23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
| 24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | |
| 25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
| 26 #include "ui/base/keycodes/keyboard_codes.h" | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 // The size above which we stop triggering autocomplete. | |
| 31 static const size_t kMaximumTextSizeForAutocomplete = 1000; | |
| 32 | |
| 33 // Maps element names to the actual elements to simplify form filling. | |
| 34 typedef std::map<string16, WebKit::WebInputElement> | |
| 35 FormInputElementMap; | |
| 36 | |
| 37 // Utility struct for form lookup and autofill. When we parse the DOM to look up | |
| 38 // a form, in addition to action and origin URL's we have to compare all | |
| 39 // necessary form elements. To avoid having to look these up again when we want | |
| 40 // to fill the form, the FindFormElements function stores the pointers | |
| 41 // in a FormElements* result, referenced to ensure they are safe to use. | |
| 42 struct FormElements { | |
| 43 WebKit::WebFormElement form_element; | |
| 44 FormInputElementMap input_elements; | |
| 45 }; | |
| 46 | |
| 47 typedef std::vector<FormElements*> FormElementsList; | |
| 48 | |
| 49 // Helper to search the given form element for the specified input elements | |
| 50 // in |data|, and add results to |result|. | |
| 51 static bool FindFormInputElements(WebKit::WebFormElement* fe, | |
| 52 const FormData& data, | |
| 53 FormElements* result) { | |
| 54 // Loop through the list of elements we need to find on the form in order to | |
| 55 // autofill it. If we don't find any one of them, abort processing this | |
| 56 // form; it can't be the right one. | |
| 57 for (size_t j = 0; j < data.fields.size(); j++) { | |
| 58 WebKit::WebVector<WebKit::WebNode> temp_elements; | |
| 59 fe->getNamedElements(data.fields[j].name, temp_elements); | |
| 60 | |
| 61 // Match the first input element, if any. | |
| 62 // |getNamedElements| may return non-input elements where the names match, | |
| 63 // so the results are filtered for input elements. | |
| 64 // If more than one match is made, then we have ambiguity (due to misuse | |
| 65 // of "name" attribute) so is it considered not found. | |
| 66 bool found_input = false; | |
| 67 for (size_t i = 0; i < temp_elements.size(); ++i) { | |
| 68 if (temp_elements[i].to<WebKit::WebElement>().hasTagName("input")) { | |
| 69 // Check for a non-unique match. | |
| 70 if (found_input) { | |
| 71 found_input = false; | |
| 72 break; | |
| 73 } | |
| 74 | |
| 75 // This element matched, add it to our temporary result. It's possible | |
| 76 // there are multiple matches, but for purposes of identifying the form | |
| 77 // one suffices and if some function needs to deal with multiple | |
| 78 // matching elements it can get at them through the FormElement*. | |
| 79 // Note: This assignment adds a reference to the InputElement. | |
| 80 result->input_elements[data.fields[j].name] = | |
| 81 temp_elements[i].to<WebKit::WebInputElement>(); | |
| 82 found_input = true; | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 // A required element was not found. This is not the right form. | |
| 87 // Make sure no input elements from a partially matched form in this | |
| 88 // iteration remain in the result set. | |
| 89 // Note: clear will remove a reference from each InputElement. | |
| 90 if (!found_input) { | |
| 91 result->input_elements.clear(); | |
| 92 return false; | |
| 93 } | |
| 94 } | |
| 95 return true; | |
| 96 } | |
| 97 | |
| 98 // Helper to locate form elements identified by |data|. | |
| 99 void FindFormElements(WebKit::WebView* view, | |
| 100 const FormData& data, | |
| 101 FormElementsList* results) { | |
| 102 DCHECK(view); | |
| 103 DCHECK(results); | |
| 104 WebKit::WebFrame* main_frame = view->mainFrame(); | |
| 105 if (!main_frame) | |
| 106 return; | |
| 107 | |
| 108 GURL::Replacements rep; | |
| 109 rep.ClearQuery(); | |
| 110 rep.ClearRef(); | |
| 111 | |
| 112 // Loop through each frame. | |
| 113 for (WebKit::WebFrame* f = main_frame; f; f = f->traverseNext(false)) { | |
| 114 WebKit::WebDocument doc = f->document(); | |
| 115 if (!doc.isHTMLDocument()) | |
| 116 continue; | |
| 117 | |
| 118 GURL full_origin(doc.url()); | |
| 119 if (data.origin != full_origin.ReplaceComponents(rep)) | |
| 120 continue; | |
| 121 | |
| 122 WebKit::WebVector<WebKit::WebFormElement> forms; | |
| 123 doc.forms(forms); | |
| 124 | |
| 125 for (size_t i = 0; i < forms.size(); ++i) { | |
| 126 WebKit::WebFormElement fe = forms[i]; | |
| 127 | |
| 128 GURL full_action(f->document().completeURL(fe.action())); | |
| 129 if (full_action.is_empty()) { | |
| 130 // The default action URL is the form's origin. | |
| 131 full_action = full_origin; | |
| 132 } | |
| 133 | |
| 134 // Action URL must match. | |
| 135 if (data.action != full_action.ReplaceComponents(rep)) | |
| 136 continue; | |
| 137 | |
| 138 scoped_ptr<FormElements> curr_elements(new FormElements); | |
| 139 if (!FindFormInputElements(&fe, data, curr_elements.get())) | |
| 140 continue; | |
| 141 | |
| 142 // We found the right element. | |
| 143 // Note: this assignment adds a reference to |fe|. | |
| 144 curr_elements->form_element = fe; | |
| 145 results->push_back(curr_elements.release()); | |
| 146 } | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 bool IsElementEditable(const WebKit::WebInputElement& element) { | |
| 151 return element.isEnabled() && !element.isReadOnly(); | |
| 152 } | |
| 153 | |
| 154 void FillForm(FormElements* fe, const FormData& data) { | |
| 155 if (!fe->form_element.autoComplete()) | |
| 156 return; | |
| 157 | |
| 158 std::map<string16, string16> data_map; | |
| 159 for (size_t i = 0; i < data.fields.size(); i++) | |
| 160 data_map[data.fields[i].name] = data.fields[i].value; | |
| 161 | |
| 162 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
| 163 it != fe->input_elements.end(); ++it) { | |
| 164 WebKit::WebInputElement element = it->second; | |
| 165 // Don't fill a form that has pre-filled values distinct from the ones we | |
| 166 // want to fill with. | |
| 167 if (!element.value().isEmpty() && element.value() != data_map[it->first]) | |
| 168 return; | |
| 169 } | |
| 170 | |
| 171 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
| 172 it != fe->input_elements.end(); ++it) { | |
| 173 WebKit::WebInputElement element = it->second; | |
| 174 if (!IsElementEditable(element)) | |
| 175 continue; // Don't fill uneditable fields. | |
| 176 | |
| 177 // TODO(tkent): Check maxlength and pattern. | |
| 178 element.setValue(data_map[it->first]); | |
| 179 element.setAutofilled(true); | |
| 180 element.dispatchFormControlChangeEvent(); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) { | |
| 185 if (element->isAutofilled() == autofilled) | |
| 186 return; | |
| 187 element->setAutofilled(autofilled); | |
| 188 // Notify any changeEvent listeners. | |
| 189 element->dispatchFormControlChangeEvent(); | |
| 190 } | |
| 191 | |
| 192 bool DoUsernamesMatch(const string16& username1, | |
| 193 const string16& username2, | |
| 194 bool exact_match) { | |
| 195 if (exact_match) | |
| 196 return username1 == username2; | |
| 197 return StartsWith(username1, username2, true); | |
| 198 } | |
| 199 | |
| 200 } // namespace | |
| 201 | |
| 202 namespace autofill { | |
| 203 | |
| 204 //////////////////////////////////////////////////////////////////////////////// | |
| 205 // PasswordAutofillManager, public: | |
| 206 | |
| 207 PasswordAutofillManager::PasswordAutofillManager( | |
| 208 content::RenderView* render_view) | |
| 209 : content::RenderViewObserver(render_view), | |
| 210 disable_popup_(false), | |
| 211 web_view_(render_view->GetWebView()), | |
| 212 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 213 } | |
| 214 | |
| 215 PasswordAutofillManager::~PasswordAutofillManager() { | |
| 216 } | |
| 217 | |
| 218 bool PasswordAutofillManager::TextFieldDidEndEditing( | |
| 219 const WebKit::WebInputElement& element) { | |
| 220 LoginToPasswordInfoMap::const_iterator iter = | |
| 221 login_to_password_info_.find(element); | |
| 222 if (iter == login_to_password_info_.end()) | |
| 223 return false; | |
| 224 | |
| 225 const PasswordFormFillData& fill_data = | |
| 226 iter->second.fill_data; | |
| 227 | |
| 228 // If wait_for_username is false, we should have filled when the text changed. | |
| 229 if (!fill_data.wait_for_username) | |
| 230 return false; | |
| 231 | |
| 232 WebKit::WebInputElement password = iter->second.password_field; | |
| 233 if (!IsElementEditable(password)) | |
| 234 return false; | |
| 235 | |
| 236 WebKit::WebInputElement username = element; // We need a non-const. | |
| 237 | |
| 238 // Do not set selection when ending an editing session, otherwise it can | |
| 239 // mess with focus. | |
| 240 FillUserNameAndPassword(&username, &password, fill_data, true, false); | |
| 241 return true; | |
| 242 } | |
| 243 | |
| 244 bool PasswordAutofillManager::TextDidChangeInTextField( | |
| 245 const WebKit::WebInputElement& element) { | |
| 246 LoginToPasswordInfoMap::const_iterator iter = | |
| 247 login_to_password_info_.find(element); | |
| 248 if (iter == login_to_password_info_.end()) | |
| 249 return false; | |
| 250 | |
| 251 // The input text is being changed, so any autofilled password is now | |
| 252 // outdated. | |
| 253 WebKit::WebInputElement username = element; // We need a non-const. | |
| 254 WebKit::WebInputElement password = iter->second.password_field; | |
| 255 SetElementAutofilled(&username, false); | |
| 256 if (password.isAutofilled()) { | |
| 257 password.setValue(string16()); | |
| 258 SetElementAutofilled(&password, false); | |
| 259 } | |
| 260 | |
| 261 // If wait_for_username is true we will fill when the username loses focus. | |
| 262 if (iter->second.fill_data.wait_for_username) | |
| 263 return false; | |
| 264 | |
| 265 if (!IsElementEditable(element) || | |
| 266 !element.isText() || | |
| 267 !element.autoComplete()) { | |
| 268 return false; | |
| 269 } | |
| 270 | |
| 271 // Don't inline autocomplete if the user is deleting, that would be confusing. | |
| 272 // But refresh the popup. Note, since this is ours, return true to signal | |
| 273 // no further processing is required. | |
| 274 if (iter->second.backspace_pressed_last) { | |
| 275 ShowSuggestionPopup(iter->second.fill_data, username); | |
| 276 return true; | |
| 277 } | |
| 278 | |
| 279 WebKit::WebString name = element.nameForAutofill(); | |
| 280 if (name.isEmpty()) | |
| 281 return false; // If the field has no name, then we won't have values. | |
| 282 | |
| 283 // Don't attempt to autofill with values that are too large. | |
| 284 if (element.value().length() > kMaximumTextSizeForAutocomplete) | |
| 285 return false; | |
| 286 | |
| 287 // The caret position should have already been updated. | |
| 288 PerformInlineAutocomplete(element, password, iter->second.fill_data); | |
| 289 return true; | |
| 290 } | |
| 291 | |
| 292 bool PasswordAutofillManager::TextFieldHandlingKeyDown( | |
| 293 const WebKit::WebInputElement& element, | |
| 294 const WebKit::WebKeyboardEvent& event) { | |
| 295 // If using the new Autofill UI that lives in the browser, it will handle | |
| 296 // keypresses before this function. This is not currently an issue but if | |
| 297 // the keys handled there or here change, this issue may appear. | |
| 298 | |
| 299 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(element); | |
| 300 if (iter == login_to_password_info_.end()) | |
| 301 return false; | |
| 302 | |
| 303 int win_key_code = event.windowsKeyCode; | |
| 304 iter->second.backspace_pressed_last = | |
| 305 (win_key_code == ui::VKEY_BACK || win_key_code == ui::VKEY_DELETE); | |
| 306 return true; | |
| 307 } | |
| 308 | |
| 309 bool PasswordAutofillManager::DidAcceptAutofillSuggestion( | |
| 310 const WebKit::WebNode& node, | |
| 311 const WebKit::WebString& value) { | |
| 312 WebKit::WebInputElement input; | |
| 313 PasswordInfo password; | |
| 314 if (!FindLoginInfo(node, &input, &password)) | |
| 315 return false; | |
| 316 | |
| 317 // Set the incoming |value| in the text field and |FillUserNameAndPassword| | |
| 318 // will do the rest. | |
| 319 input.setValue(value, true); | |
| 320 return FillUserNameAndPassword(&input, &password.password_field, | |
| 321 password.fill_data, true, true); | |
| 322 } | |
| 323 | |
| 324 bool PasswordAutofillManager::DidSelectAutofillSuggestion( | |
| 325 const WebKit::WebNode& node) { | |
| 326 WebKit::WebInputElement input; | |
| 327 PasswordInfo password; | |
| 328 return FindLoginInfo(node, &input, &password); | |
| 329 } | |
| 330 | |
| 331 bool PasswordAutofillManager::DidClearAutofillSelection( | |
| 332 const WebKit::WebNode& node) { | |
| 333 WebKit::WebInputElement input; | |
| 334 PasswordInfo password; | |
| 335 return FindLoginInfo(node, &input, &password); | |
| 336 } | |
| 337 | |
| 338 void PasswordAutofillManager::SendPasswordForms(WebKit::WebFrame* frame, | |
| 339 bool only_visible) { | |
| 340 // Make sure that this security origin is allowed to use password manager. | |
| 341 WebKit::WebSecurityOrigin origin = frame->document().securityOrigin(); | |
| 342 if (!origin.canAccessPasswordManager()) | |
| 343 return; | |
| 344 | |
| 345 WebKit::WebVector<WebKit::WebFormElement> forms; | |
| 346 frame->document().forms(forms); | |
| 347 | |
| 348 std::vector<content::PasswordForm> password_forms; | |
| 349 for (size_t i = 0; i < forms.size(); ++i) { | |
| 350 const WebKit::WebFormElement& form = forms[i]; | |
| 351 | |
| 352 // If requested, ignore non-rendered forms, e.g. those styled with | |
| 353 // display:none. | |
| 354 if (only_visible && !form.hasNonEmptyBoundingBox()) | |
| 355 continue; | |
| 356 | |
| 357 scoped_ptr<content::PasswordForm> password_form( | |
| 358 content::CreatePasswordForm(form)); | |
| 359 if (password_form.get()) | |
| 360 password_forms.push_back(*password_form); | |
| 361 } | |
| 362 | |
| 363 if (password_forms.empty() && !only_visible) { | |
| 364 // We need to send the PasswordFormsRendered message regardless of whether | |
| 365 // there are any forms visible, as this is also the code path that triggers | |
| 366 // showing the infobar. | |
| 367 return; | |
| 368 } | |
| 369 | |
| 370 if (only_visible) { | |
| 371 Send(new AutofillHostMsg_PasswordFormsRendered( | |
| 372 routing_id(), password_forms)); | |
| 373 } else { | |
| 374 Send(new AutofillHostMsg_PasswordFormsParsed(routing_id(), password_forms)); | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 bool PasswordAutofillManager::OnMessageReceived(const IPC::Message& message) { | |
| 379 bool handled = true; | |
| 380 IPC_BEGIN_MESSAGE_MAP(PasswordAutofillManager, message) | |
| 381 IPC_MESSAGE_HANDLER(AutofillMsg_FillPasswordForm, OnFillPasswordForm) | |
| 382 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 383 IPC_END_MESSAGE_MAP() | |
| 384 return handled; | |
| 385 } | |
| 386 | |
| 387 void PasswordAutofillManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) { | |
| 388 // The |frame| contents have been parsed, but not yet rendered. Let the | |
| 389 // PasswordManager know that forms are loaded, even though we can't yet tell | |
| 390 // whether they're visible. | |
| 391 SendPasswordForms(frame, false); | |
| 392 } | |
| 393 | |
| 394 void PasswordAutofillManager::DidFinishLoad(WebKit::WebFrame* frame) { | |
| 395 // The |frame| contents have been rendered. Let the PasswordManager know | |
| 396 // which of the loaded frames are actually visible to the user. This also | |
| 397 // triggers the "Save password?" infobar if the user just submitted a password | |
| 398 // form. | |
| 399 SendPasswordForms(frame, true); | |
| 400 } | |
| 401 | |
| 402 void PasswordAutofillManager::FrameDetached(WebKit::WebFrame* frame) { | |
| 403 FrameClosing(frame); | |
| 404 } | |
| 405 | |
| 406 void PasswordAutofillManager::FrameWillClose(WebKit::WebFrame* frame) { | |
| 407 FrameClosing(frame); | |
| 408 } | |
| 409 | |
| 410 | |
| 411 //////////////////////////////////////////////////////////////////////////////// | |
| 412 // PageClickListener implementation: | |
| 413 | |
| 414 bool PasswordAutofillManager::InputElementClicked( | |
| 415 const WebKit::WebInputElement& element, | |
| 416 bool was_focused, | |
| 417 bool is_focused) { | |
| 418 // TODO(jcivelli): http://crbug.com/51644 Implement behavior. | |
| 419 return false; | |
| 420 } | |
| 421 | |
| 422 bool PasswordAutofillManager::InputElementLostFocus() { | |
| 423 return false; | |
| 424 } | |
| 425 | |
| 426 void PasswordAutofillManager::OnFillPasswordForm( | |
| 427 const PasswordFormFillData& form_data, | |
| 428 bool disable_popup) { | |
| 429 disable_popup_ = disable_popup; | |
| 430 | |
| 431 FormElementsList forms; | |
| 432 // We own the FormElements* in forms. | |
| 433 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); | |
| 434 FormElementsList::iterator iter; | |
| 435 for (iter = forms.begin(); iter != forms.end(); ++iter) { | |
| 436 scoped_ptr<FormElements> form_elements(*iter); | |
| 437 | |
| 438 // If wait_for_username is true, we don't want to initially fill the form | |
| 439 // until the user types in a valid username. | |
| 440 if (!form_data.wait_for_username) | |
| 441 FillForm(form_elements.get(), form_data.basic_data); | |
| 442 | |
| 443 // Attach autocomplete listener to enable selecting alternate logins. | |
| 444 // First, get pointers to username element. | |
| 445 WebKit::WebInputElement username_element = | |
| 446 form_elements->input_elements[form_data.basic_data.fields[0].name]; | |
| 447 | |
| 448 // Get pointer to password element. (We currently only support single | |
| 449 // password forms). | |
| 450 WebKit::WebInputElement password_element = | |
| 451 form_elements->input_elements[form_data.basic_data.fields[1].name]; | |
| 452 | |
| 453 // We might have already filled this form if there are two <form> elements | |
| 454 // with identical markup. | |
| 455 if (login_to_password_info_.find(username_element) != | |
| 456 login_to_password_info_.end()) | |
| 457 continue; | |
| 458 | |
| 459 PasswordInfo password_info; | |
| 460 password_info.fill_data = form_data; | |
| 461 password_info.password_field = password_element; | |
| 462 login_to_password_info_[username_element] = password_info; | |
| 463 | |
| 464 FormData form; | |
| 465 FormFieldData field; | |
| 466 FindFormAndFieldForInputElement( | |
| 467 username_element, &form, &field, REQUIRE_NONE); | |
| 468 Send(new AutofillHostMsg_AddPasswordFormMapping( | |
| 469 routing_id(), | |
| 470 field, | |
| 471 form_data)); | |
| 472 } | |
| 473 } | |
| 474 | |
| 475 //////////////////////////////////////////////////////////////////////////////// | |
| 476 // PasswordAutofillManager, private: | |
| 477 | |
| 478 void PasswordAutofillManager::GetSuggestions( | |
| 479 const PasswordFormFillData& fill_data, | |
| 480 const string16& input, | |
| 481 std::vector<string16>* suggestions) { | |
| 482 if (StartsWith(fill_data.basic_data.fields[0].value, input, false)) | |
| 483 suggestions->push_back(fill_data.basic_data.fields[0].value); | |
| 484 | |
| 485 PasswordFormFillData::LoginCollection::const_iterator iter; | |
| 486 for (iter = fill_data.additional_logins.begin(); | |
| 487 iter != fill_data.additional_logins.end(); ++iter) { | |
| 488 if (StartsWith(iter->first, input, false)) | |
| 489 suggestions->push_back(iter->first); | |
| 490 } | |
| 491 } | |
| 492 | |
| 493 bool PasswordAutofillManager::ShowSuggestionPopup( | |
| 494 const PasswordFormFillData& fill_data, | |
| 495 const WebKit::WebInputElement& user_input) { | |
| 496 WebKit::WebFrame* frame = user_input.document().frame(); | |
| 497 if (!frame) | |
| 498 return false; | |
| 499 | |
| 500 WebKit::WebView* webview = frame->view(); | |
| 501 if (!webview) | |
| 502 return false; | |
| 503 | |
| 504 std::vector<string16> suggestions; | |
| 505 GetSuggestions(fill_data, user_input.value(), &suggestions); | |
| 506 | |
| 507 if (disable_popup_) { | |
| 508 FormData form; | |
| 509 FormFieldData field; | |
| 510 FindFormAndFieldForInputElement( | |
| 511 user_input, &form, &field, REQUIRE_NONE); | |
| 512 | |
| 513 WebKit::WebInputElement selected_element = user_input; | |
| 514 gfx::Rect bounding_box(selected_element.boundsInViewportSpace()); | |
| 515 | |
| 516 float scale = web_view_->pageScaleFactor(); | |
| 517 gfx::RectF bounding_box_scaled(bounding_box.x() * scale, | |
| 518 bounding_box.y() * scale, | |
| 519 bounding_box.width() * scale, | |
| 520 bounding_box.height() * scale); | |
| 521 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), | |
| 522 field, | |
| 523 bounding_box_scaled, | |
| 524 suggestions)); | |
| 525 return !suggestions.empty(); | |
| 526 } | |
| 527 | |
| 528 | |
| 529 if (suggestions.empty()) { | |
| 530 webview->hidePopups(); | |
| 531 return false; | |
| 532 } | |
| 533 | |
| 534 std::vector<string16> labels(suggestions.size()); | |
| 535 std::vector<string16> icons(suggestions.size()); | |
| 536 std::vector<int> ids(suggestions.size(), | |
| 537 WebKit::WebAutofillClient::MenuItemIDPasswordEntry); | |
| 538 webview->applyAutofillSuggestions( | |
| 539 user_input, suggestions, labels, icons, ids); | |
| 540 return true; | |
| 541 } | |
| 542 | |
| 543 bool PasswordAutofillManager::FillUserNameAndPassword( | |
| 544 WebKit::WebInputElement* username_element, | |
| 545 WebKit::WebInputElement* password_element, | |
| 546 const PasswordFormFillData& fill_data, | |
| 547 bool exact_username_match, | |
| 548 bool set_selection) { | |
| 549 string16 current_username = username_element->value(); | |
| 550 // username and password will contain the match found if any. | |
| 551 string16 username; | |
| 552 string16 password; | |
| 553 | |
| 554 // Look for any suitable matches to current field text. | |
| 555 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, current_username, | |
| 556 exact_username_match)) { | |
| 557 username = fill_data.basic_data.fields[0].value; | |
| 558 password = fill_data.basic_data.fields[1].value; | |
| 559 } else { | |
| 560 // Scan additional logins for a match. | |
| 561 PasswordFormFillData::LoginCollection::const_iterator iter; | |
| 562 for (iter = fill_data.additional_logins.begin(); | |
| 563 iter != fill_data.additional_logins.end(); ++iter) { | |
| 564 if (DoUsernamesMatch(iter->first, current_username, | |
| 565 exact_username_match)) { | |
| 566 username = iter->first; | |
| 567 password = iter->second; | |
| 568 break; | |
| 569 } | |
| 570 } | |
| 571 } | |
| 572 if (password.empty()) | |
| 573 return false; // No match was found. | |
| 574 | |
| 575 // Input matches the username, fill in required values. | |
| 576 username_element->setValue(username); | |
| 577 | |
| 578 if (set_selection) { | |
| 579 username_element->setSelectionRange(current_username.length(), | |
| 580 username.length()); | |
| 581 } | |
| 582 | |
| 583 SetElementAutofilled(username_element, true); | |
| 584 if (IsElementEditable(*password_element)) | |
| 585 password_element->setValue(password); | |
| 586 SetElementAutofilled(password_element, true); | |
| 587 return true; | |
| 588 } | |
| 589 | |
| 590 void PasswordAutofillManager::PerformInlineAutocomplete( | |
| 591 const WebKit::WebInputElement& username_input, | |
| 592 const WebKit::WebInputElement& password_input, | |
| 593 const PasswordFormFillData& fill_data) { | |
| 594 DCHECK(!fill_data.wait_for_username); | |
| 595 | |
| 596 // We need non-const versions of the username and password inputs. | |
| 597 WebKit::WebInputElement username = username_input; | |
| 598 WebKit::WebInputElement password = password_input; | |
| 599 | |
| 600 // Don't inline autocomplete if the caret is not at the end. | |
| 601 // TODO(jcivelli): is there a better way to test the caret location? | |
| 602 if (username.selectionStart() != username.selectionEnd() || | |
| 603 username.selectionEnd() != static_cast<int>(username.value().length())) { | |
| 604 return; | |
| 605 } | |
| 606 | |
| 607 // Show the popup with the list of available usernames. | |
| 608 ShowSuggestionPopup(fill_data, username); | |
| 609 | |
| 610 | |
| 611 #if !defined(OS_ANDROID) | |
| 612 // Fill the user and password field with the most relevant match. Android | |
| 613 // only fills in the fields after the user clicks on the suggestion popup. | |
| 614 FillUserNameAndPassword(&username, &password, fill_data, false, true); | |
| 615 #endif | |
| 616 } | |
| 617 | |
| 618 void PasswordAutofillManager::FrameClosing(const WebKit::WebFrame* frame) { | |
| 619 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); | |
| 620 iter != login_to_password_info_.end();) { | |
| 621 if (iter->first.document().frame() == frame) | |
| 622 login_to_password_info_.erase(iter++); | |
| 623 else | |
| 624 ++iter; | |
| 625 } | |
| 626 } | |
| 627 | |
| 628 bool PasswordAutofillManager::FindLoginInfo( | |
| 629 const WebKit::WebNode& node, | |
| 630 WebKit::WebInputElement* found_input, | |
| 631 PasswordInfo* found_password) { | |
| 632 if (!node.isElementNode()) | |
| 633 return false; | |
| 634 | |
| 635 WebKit::WebElement element = node.toConst<WebKit::WebElement>(); | |
| 636 if (!element.hasTagName("input")) | |
| 637 return false; | |
| 638 | |
| 639 WebKit::WebInputElement input = element.to<WebKit::WebInputElement>(); | |
| 640 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); | |
| 641 if (iter == login_to_password_info_.end()) | |
| 642 return false; | |
| 643 | |
| 644 *found_input = input; | |
| 645 *found_password = iter->second; | |
| 646 return true; | |
| 647 } | |
| 648 | |
| 649 } // namespace autofill | |
| OLD | NEW |