| 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/password_autofill_agent.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/memory/scoped_ptr.h" | |
| 9 #include "base/message_loop.h" | |
| 10 #include "base/metrics/histogram.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 "components/autofill/renderer/form_autofill_util.h" | |
| 15 #include "content/public/common/password_form.h" | |
| 16 #include "content/public/renderer/password_form_conversion_utils.h" | |
| 17 #include "content/public/renderer/render_view.h" | |
| 18 #include "third_party/WebKit/public/platform/WebVector.h" | |
| 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" | |
| 20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
| 21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" | |
| 22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h" | |
| 23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
| 24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
| 25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | |
| 26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
| 27 #include "ui/base/keycodes/keyboard_codes.h" | |
| 28 | |
| 29 namespace autofill { | |
| 30 namespace { | |
| 31 | |
| 32 // The size above which we stop triggering autocomplete. | |
| 33 static const size_t kMaximumTextSizeForAutocomplete = 1000; | |
| 34 | |
| 35 // Maps element names to the actual elements to simplify form filling. | |
| 36 typedef std::map<base::string16, WebKit::WebInputElement> | |
| 37 FormInputElementMap; | |
| 38 | |
| 39 // Utility struct for form lookup and autofill. When we parse the DOM to look up | |
| 40 // a form, in addition to action and origin URL's we have to compare all | |
| 41 // necessary form elements. To avoid having to look these up again when we want | |
| 42 // to fill the form, the FindFormElements function stores the pointers | |
| 43 // in a FormElements* result, referenced to ensure they are safe to use. | |
| 44 struct FormElements { | |
| 45 WebKit::WebFormElement form_element; | |
| 46 FormInputElementMap input_elements; | |
| 47 }; | |
| 48 | |
| 49 typedef std::vector<FormElements*> FormElementsList; | |
| 50 | |
| 51 // Helper to search the given form element for the specified input elements | |
| 52 // in |data|, and add results to |result|. | |
| 53 static bool FindFormInputElements(WebKit::WebFormElement* fe, | |
| 54 const FormData& data, | |
| 55 FormElements* result) { | |
| 56 // Loop through the list of elements we need to find on the form in order to | |
| 57 // autofill it. If we don't find any one of them, abort processing this | |
| 58 // form; it can't be the right one. | |
| 59 for (size_t j = 0; j < data.fields.size(); j++) { | |
| 60 WebKit::WebVector<WebKit::WebNode> temp_elements; | |
| 61 fe->getNamedElements(data.fields[j].name, temp_elements); | |
| 62 | |
| 63 // Match the first input element, if any. | |
| 64 // |getNamedElements| may return non-input elements where the names match, | |
| 65 // so the results are filtered for input elements. | |
| 66 // If more than one match is made, then we have ambiguity (due to misuse | |
| 67 // of "name" attribute) so is it considered not found. | |
| 68 bool found_input = false; | |
| 69 for (size_t i = 0; i < temp_elements.size(); ++i) { | |
| 70 if (temp_elements[i].to<WebKit::WebElement>().hasTagName("input")) { | |
| 71 // Check for a non-unique match. | |
| 72 if (found_input) { | |
| 73 found_input = false; | |
| 74 break; | |
| 75 } | |
| 76 | |
| 77 // This element matched, add it to our temporary result. It's possible | |
| 78 // there are multiple matches, but for purposes of identifying the form | |
| 79 // one suffices and if some function needs to deal with multiple | |
| 80 // matching elements it can get at them through the FormElement*. | |
| 81 // Note: This assignment adds a reference to the InputElement. | |
| 82 result->input_elements[data.fields[j].name] = | |
| 83 temp_elements[i].to<WebKit::WebInputElement>(); | |
| 84 found_input = true; | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 // A required element was not found. This is not the right form. | |
| 89 // Make sure no input elements from a partially matched form in this | |
| 90 // iteration remain in the result set. | |
| 91 // Note: clear will remove a reference from each InputElement. | |
| 92 if (!found_input) { | |
| 93 result->input_elements.clear(); | |
| 94 return false; | |
| 95 } | |
| 96 } | |
| 97 return true; | |
| 98 } | |
| 99 | |
| 100 // Helper to locate form elements identified by |data|. | |
| 101 void FindFormElements(WebKit::WebView* view, | |
| 102 const FormData& data, | |
| 103 FormElementsList* results) { | |
| 104 DCHECK(view); | |
| 105 DCHECK(results); | |
| 106 WebKit::WebFrame* main_frame = view->mainFrame(); | |
| 107 if (!main_frame) | |
| 108 return; | |
| 109 | |
| 110 GURL::Replacements rep; | |
| 111 rep.ClearQuery(); | |
| 112 rep.ClearRef(); | |
| 113 | |
| 114 // Loop through each frame. | |
| 115 for (WebKit::WebFrame* f = main_frame; f; f = f->traverseNext(false)) { | |
| 116 WebKit::WebDocument doc = f->document(); | |
| 117 if (!doc.isHTMLDocument()) | |
| 118 continue; | |
| 119 | |
| 120 GURL full_origin(doc.url()); | |
| 121 if (data.origin != full_origin.ReplaceComponents(rep)) | |
| 122 continue; | |
| 123 | |
| 124 WebKit::WebVector<WebKit::WebFormElement> forms; | |
| 125 doc.forms(forms); | |
| 126 | |
| 127 for (size_t i = 0; i < forms.size(); ++i) { | |
| 128 WebKit::WebFormElement fe = forms[i]; | |
| 129 | |
| 130 GURL full_action(f->document().completeURL(fe.action())); | |
| 131 if (full_action.is_empty()) { | |
| 132 // The default action URL is the form's origin. | |
| 133 full_action = full_origin; | |
| 134 } | |
| 135 | |
| 136 // Action URL must match. | |
| 137 if (data.action != full_action.ReplaceComponents(rep)) | |
| 138 continue; | |
| 139 | |
| 140 scoped_ptr<FormElements> curr_elements(new FormElements); | |
| 141 if (!FindFormInputElements(&fe, data, curr_elements.get())) | |
| 142 continue; | |
| 143 | |
| 144 // We found the right element. | |
| 145 // Note: this assignment adds a reference to |fe|. | |
| 146 curr_elements->form_element = fe; | |
| 147 results->push_back(curr_elements.release()); | |
| 148 } | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 bool IsElementEditable(const WebKit::WebInputElement& element) { | |
| 153 return element.isEnabled() && !element.isReadOnly(); | |
| 154 } | |
| 155 | |
| 156 void FillForm(FormElements* fe, const FormData& data) { | |
| 157 if (!fe->form_element.autoComplete()) | |
| 158 return; | |
| 159 | |
| 160 std::map<base::string16, base::string16> data_map; | |
| 161 for (size_t i = 0; i < data.fields.size(); i++) | |
| 162 data_map[data.fields[i].name] = data.fields[i].value; | |
| 163 | |
| 164 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
| 165 it != fe->input_elements.end(); ++it) { | |
| 166 WebKit::WebInputElement element = it->second; | |
| 167 // Don't fill a form that has pre-filled values distinct from the ones we | |
| 168 // want to fill with. | |
| 169 if (!element.value().isEmpty() && element.value() != data_map[it->first]) | |
| 170 return; | |
| 171 } | |
| 172 | |
| 173 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
| 174 it != fe->input_elements.end(); ++it) { | |
| 175 WebKit::WebInputElement element = it->second; | |
| 176 if (!IsElementEditable(element)) | |
| 177 continue; // Don't fill uneditable fields. | |
| 178 | |
| 179 // TODO(tkent): Check maxlength and pattern. | |
| 180 element.setValue(data_map[it->first]); | |
| 181 element.setAutofilled(true); | |
| 182 element.dispatchFormControlChangeEvent(); | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) { | |
| 187 if (element->isAutofilled() == autofilled) | |
| 188 return; | |
| 189 element->setAutofilled(autofilled); | |
| 190 // Notify any changeEvent listeners. | |
| 191 element->dispatchFormControlChangeEvent(); | |
| 192 } | |
| 193 | |
| 194 bool DoUsernamesMatch(const base::string16& username1, | |
| 195 const base::string16& username2, | |
| 196 bool exact_match) { | |
| 197 if (exact_match) | |
| 198 return username1 == username2; | |
| 199 return StartsWith(username1, username2, true); | |
| 200 } | |
| 201 | |
| 202 } // namespace | |
| 203 | |
| 204 //////////////////////////////////////////////////////////////////////////////// | |
| 205 // PasswordAutofillAgent, public: | |
| 206 | |
| 207 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view) | |
| 208 : content::RenderViewObserver(render_view), | |
| 209 disable_popup_(false), | |
| 210 usernames_usage_(NOTHING_TO_AUTOFILL), | |
| 211 web_view_(render_view->GetWebView()), | |
| 212 weak_ptr_factory_(this) { | |
| 213 } | |
| 214 | |
| 215 PasswordAutofillAgent::~PasswordAutofillAgent() { | |
| 216 } | |
| 217 | |
| 218 bool PasswordAutofillAgent::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 PasswordAutofillAgent::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(base::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 PasswordAutofillAgent::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 PasswordAutofillAgent::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 PasswordAutofillAgent::DidSelectAutofillSuggestion( | |
| 325 const WebKit::WebNode& node) { | |
| 326 WebKit::WebInputElement input; | |
| 327 PasswordInfo password; | |
| 328 return FindLoginInfo(node, &input, &password); | |
| 329 } | |
| 330 | |
| 331 bool PasswordAutofillAgent::DidClearAutofillSelection( | |
| 332 const WebKit::WebNode& node) { | |
| 333 WebKit::WebInputElement input; | |
| 334 PasswordInfo password; | |
| 335 return FindLoginInfo(node, &input, &password); | |
| 336 } | |
| 337 | |
| 338 bool PasswordAutofillAgent::ShowSuggestions( | |
| 339 const WebKit::WebInputElement& element) { | |
| 340 LoginToPasswordInfoMap::const_iterator iter = | |
| 341 login_to_password_info_.find(element); | |
| 342 if (iter == login_to_password_info_.end()) | |
| 343 return false; | |
| 344 | |
| 345 return ShowSuggestionPopup(iter->second.fill_data, element); | |
| 346 } | |
| 347 | |
| 348 void PasswordAutofillAgent::SendPasswordForms(WebKit::WebFrame* frame, | |
| 349 bool only_visible) { | |
| 350 // Make sure that this security origin is allowed to use password manager. | |
| 351 WebKit::WebSecurityOrigin origin = frame->document().securityOrigin(); | |
| 352 if (!origin.canAccessPasswordManager()) | |
| 353 return; | |
| 354 | |
| 355 WebKit::WebVector<WebKit::WebFormElement> forms; | |
| 356 frame->document().forms(forms); | |
| 357 | |
| 358 std::vector<content::PasswordForm> password_forms; | |
| 359 for (size_t i = 0; i < forms.size(); ++i) { | |
| 360 const WebKit::WebFormElement& form = forms[i]; | |
| 361 | |
| 362 // If requested, ignore non-rendered forms, e.g. those styled with | |
| 363 // display:none. | |
| 364 if (only_visible && !form.hasNonEmptyBoundingBox()) | |
| 365 continue; | |
| 366 | |
| 367 scoped_ptr<content::PasswordForm> password_form( | |
| 368 content::CreatePasswordForm(form)); | |
| 369 if (password_form.get()) | |
| 370 password_forms.push_back(*password_form); | |
| 371 } | |
| 372 | |
| 373 if (password_forms.empty() && !only_visible) { | |
| 374 // We need to send the PasswordFormsRendered message regardless of whether | |
| 375 // there are any forms visible, as this is also the code path that triggers | |
| 376 // showing the infobar. | |
| 377 return; | |
| 378 } | |
| 379 | |
| 380 if (only_visible) { | |
| 381 Send(new AutofillHostMsg_PasswordFormsRendered( | |
| 382 routing_id(), password_forms)); | |
| 383 } else { | |
| 384 Send(new AutofillHostMsg_PasswordFormsParsed(routing_id(), password_forms)); | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 bool PasswordAutofillAgent::OnMessageReceived(const IPC::Message& message) { | |
| 389 bool handled = true; | |
| 390 IPC_BEGIN_MESSAGE_MAP(PasswordAutofillAgent, message) | |
| 391 IPC_MESSAGE_HANDLER(AutofillMsg_FillPasswordForm, OnFillPasswordForm) | |
| 392 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 393 IPC_END_MESSAGE_MAP() | |
| 394 return handled; | |
| 395 } | |
| 396 | |
| 397 void PasswordAutofillAgent::DidStartLoading() { | |
| 398 if (usernames_usage_ != NOTHING_TO_AUTOFILL) { | |
| 399 UMA_HISTOGRAM_ENUMERATION("PasswordManager.OtherPossibleUsernamesUsage", | |
| 400 usernames_usage_, OTHER_POSSIBLE_USERNAMES_MAX); | |
| 401 usernames_usage_ = NOTHING_TO_AUTOFILL; | |
| 402 } | |
| 403 } | |
| 404 | |
| 405 void PasswordAutofillAgent::DidFinishDocumentLoad(WebKit::WebFrame* frame) { | |
| 406 // The |frame| contents have been parsed, but not yet rendered. Let the | |
| 407 // PasswordManager know that forms are loaded, even though we can't yet tell | |
| 408 // whether they're visible. | |
| 409 SendPasswordForms(frame, false); | |
| 410 } | |
| 411 | |
| 412 void PasswordAutofillAgent::DidFinishLoad(WebKit::WebFrame* frame) { | |
| 413 // The |frame| contents have been rendered. Let the PasswordManager know | |
| 414 // which of the loaded frames are actually visible to the user. This also | |
| 415 // triggers the "Save password?" infobar if the user just submitted a password | |
| 416 // form. | |
| 417 SendPasswordForms(frame, true); | |
| 418 } | |
| 419 | |
| 420 void PasswordAutofillAgent::FrameDetached(WebKit::WebFrame* frame) { | |
| 421 FrameClosing(frame); | |
| 422 } | |
| 423 | |
| 424 void PasswordAutofillAgent::FrameWillClose(WebKit::WebFrame* frame) { | |
| 425 FrameClosing(frame); | |
| 426 } | |
| 427 | |
| 428 void PasswordAutofillAgent::OnFillPasswordForm( | |
| 429 const PasswordFormFillData& form_data, | |
| 430 bool disable_popup) { | |
| 431 disable_popup_ = disable_popup; | |
| 432 if (usernames_usage_ == NOTHING_TO_AUTOFILL) { | |
| 433 if (form_data.other_possible_usernames.size()) | |
| 434 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_PRESENT; | |
| 435 else if (usernames_usage_ == NOTHING_TO_AUTOFILL) | |
| 436 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT; | |
| 437 } | |
| 438 | |
| 439 FormElementsList forms; | |
| 440 // We own the FormElements* in forms. | |
| 441 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); | |
| 442 FormElementsList::iterator iter; | |
| 443 for (iter = forms.begin(); iter != forms.end(); ++iter) { | |
| 444 scoped_ptr<FormElements> form_elements(*iter); | |
| 445 | |
| 446 // If wait_for_username is true, we don't want to initially fill the form | |
| 447 // until the user types in a valid username. | |
| 448 if (!form_data.wait_for_username) | |
| 449 FillForm(form_elements.get(), form_data.basic_data); | |
| 450 | |
| 451 // Attach autocomplete listener to enable selecting alternate logins. | |
| 452 // First, get pointers to username element. | |
| 453 WebKit::WebInputElement username_element = | |
| 454 form_elements->input_elements[form_data.basic_data.fields[0].name]; | |
| 455 | |
| 456 // Get pointer to password element. (We currently only support single | |
| 457 // password forms). | |
| 458 WebKit::WebInputElement password_element = | |
| 459 form_elements->input_elements[form_data.basic_data.fields[1].name]; | |
| 460 | |
| 461 // We might have already filled this form if there are two <form> elements | |
| 462 // with identical markup. | |
| 463 if (login_to_password_info_.find(username_element) != | |
| 464 login_to_password_info_.end()) | |
| 465 continue; | |
| 466 | |
| 467 PasswordInfo password_info; | |
| 468 password_info.fill_data = form_data; | |
| 469 password_info.password_field = password_element; | |
| 470 login_to_password_info_[username_element] = password_info; | |
| 471 | |
| 472 FormData form; | |
| 473 FormFieldData field; | |
| 474 FindFormAndFieldForInputElement( | |
| 475 username_element, &form, &field, REQUIRE_NONE); | |
| 476 Send(new AutofillHostMsg_AddPasswordFormMapping( | |
| 477 routing_id(), | |
| 478 field, | |
| 479 form_data)); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 //////////////////////////////////////////////////////////////////////////////// | |
| 484 // PasswordAutofillAgent, private: | |
| 485 | |
| 486 void PasswordAutofillAgent::GetSuggestions( | |
| 487 const PasswordFormFillData& fill_data, | |
| 488 const base::string16& input, | |
| 489 std::vector<base::string16>* suggestions) { | |
| 490 if (StartsWith(fill_data.basic_data.fields[0].value, input, false)) | |
| 491 suggestions->push_back(fill_data.basic_data.fields[0].value); | |
| 492 | |
| 493 for (PasswordFormFillData::LoginCollection::const_iterator iter = | |
| 494 fill_data.additional_logins.begin(); | |
| 495 iter != fill_data.additional_logins.end(); ++iter) { | |
| 496 if (StartsWith(iter->first, input, false)) | |
| 497 suggestions->push_back(iter->first); | |
| 498 } | |
| 499 | |
| 500 for (PasswordFormFillData::UsernamesCollection::const_iterator iter = | |
| 501 fill_data.other_possible_usernames.begin(); | |
| 502 iter != fill_data.other_possible_usernames.end(); ++iter) { | |
| 503 for (size_t i = 0; i < iter->second.size(); ++i) { | |
| 504 if (StartsWith(iter->second[i], input, false)) { | |
| 505 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SHOWN; | |
| 506 suggestions->push_back(iter->second[i]); | |
| 507 } | |
| 508 } | |
| 509 } | |
| 510 } | |
| 511 | |
| 512 bool PasswordAutofillAgent::ShowSuggestionPopup( | |
| 513 const PasswordFormFillData& fill_data, | |
| 514 const WebKit::WebInputElement& user_input) { | |
| 515 WebKit::WebFrame* frame = user_input.document().frame(); | |
| 516 if (!frame) | |
| 517 return false; | |
| 518 | |
| 519 WebKit::WebView* webview = frame->view(); | |
| 520 if (!webview) | |
| 521 return false; | |
| 522 | |
| 523 std::vector<base::string16> suggestions; | |
| 524 GetSuggestions(fill_data, user_input.value(), &suggestions); | |
| 525 | |
| 526 if (disable_popup_) { | |
| 527 FormData form; | |
| 528 FormFieldData field; | |
| 529 FindFormAndFieldForInputElement( | |
| 530 user_input, &form, &field, REQUIRE_NONE); | |
| 531 | |
| 532 WebKit::WebInputElement selected_element = user_input; | |
| 533 gfx::Rect bounding_box(selected_element.boundsInViewportSpace()); | |
| 534 | |
| 535 float scale = web_view_->pageScaleFactor(); | |
| 536 gfx::RectF bounding_box_scaled(bounding_box.x() * scale, | |
| 537 bounding_box.y() * scale, | |
| 538 bounding_box.width() * scale, | |
| 539 bounding_box.height() * scale); | |
| 540 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), | |
| 541 field, | |
| 542 bounding_box_scaled, | |
| 543 suggestions)); | |
| 544 return !suggestions.empty(); | |
| 545 } | |
| 546 | |
| 547 | |
| 548 if (suggestions.empty()) { | |
| 549 webview->hidePopups(); | |
| 550 return false; | |
| 551 } | |
| 552 | |
| 553 std::vector<base::string16> labels(suggestions.size()); | |
| 554 std::vector<base::string16> icons(suggestions.size()); | |
| 555 std::vector<int> ids(suggestions.size(), | |
| 556 WebKit::WebAutofillClient::MenuItemIDPasswordEntry); | |
| 557 webview->applyAutofillSuggestions( | |
| 558 user_input, suggestions, labels, icons, ids); | |
| 559 return true; | |
| 560 } | |
| 561 | |
| 562 bool PasswordAutofillAgent::FillUserNameAndPassword( | |
| 563 WebKit::WebInputElement* username_element, | |
| 564 WebKit::WebInputElement* password_element, | |
| 565 const PasswordFormFillData& fill_data, | |
| 566 bool exact_username_match, | |
| 567 bool set_selection) { | |
| 568 base::string16 current_username = username_element->value(); | |
| 569 // username and password will contain the match found if any. | |
| 570 base::string16 username; | |
| 571 base::string16 password; | |
| 572 | |
| 573 // Look for any suitable matches to current field text. | |
| 574 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, current_username, | |
| 575 exact_username_match)) { | |
| 576 username = fill_data.basic_data.fields[0].value; | |
| 577 password = fill_data.basic_data.fields[1].value; | |
| 578 } else { | |
| 579 // Scan additional logins for a match. | |
| 580 PasswordFormFillData::LoginCollection::const_iterator iter; | |
| 581 for (iter = fill_data.additional_logins.begin(); | |
| 582 iter != fill_data.additional_logins.end(); ++iter) { | |
| 583 if (DoUsernamesMatch(iter->first, current_username, | |
| 584 exact_username_match)) { | |
| 585 username = iter->first; | |
| 586 password = iter->second; | |
| 587 break; | |
| 588 } | |
| 589 } | |
| 590 | |
| 591 // Check possible usernames. | |
| 592 if (username.empty() && password.empty()) { | |
| 593 for (PasswordFormFillData::UsernamesCollection::const_iterator iter = | |
| 594 fill_data.other_possible_usernames.begin(); | |
| 595 iter != fill_data.other_possible_usernames.end(); ++iter) { | |
| 596 for (size_t i = 0; i < iter->second.size(); ++i) { | |
| 597 if (DoUsernamesMatch(iter->second[i], current_username, | |
| 598 exact_username_match)) { | |
| 599 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED; | |
| 600 username = iter->second[i]; | |
| 601 password = iter->first.password; | |
| 602 break; | |
| 603 } | |
| 604 } | |
| 605 if (!username.empty() && !password.empty()) | |
| 606 break; | |
| 607 } | |
| 608 } | |
| 609 } | |
| 610 if (password.empty()) | |
| 611 return false; // No match was found. | |
| 612 | |
| 613 // Input matches the username, fill in required values. | |
| 614 username_element->setValue(username); | |
| 615 | |
| 616 if (set_selection) { | |
| 617 username_element->setSelectionRange(current_username.length(), | |
| 618 username.length()); | |
| 619 } | |
| 620 | |
| 621 SetElementAutofilled(username_element, true); | |
| 622 if (IsElementEditable(*password_element)) | |
| 623 password_element->setValue(password); | |
| 624 SetElementAutofilled(password_element, true); | |
| 625 return true; | |
| 626 } | |
| 627 | |
| 628 void PasswordAutofillAgent::PerformInlineAutocomplete( | |
| 629 const WebKit::WebInputElement& username_input, | |
| 630 const WebKit::WebInputElement& password_input, | |
| 631 const PasswordFormFillData& fill_data) { | |
| 632 DCHECK(!fill_data.wait_for_username); | |
| 633 | |
| 634 // We need non-const versions of the username and password inputs. | |
| 635 WebKit::WebInputElement username = username_input; | |
| 636 WebKit::WebInputElement password = password_input; | |
| 637 | |
| 638 // Don't inline autocomplete if the caret is not at the end. | |
| 639 // TODO(jcivelli): is there a better way to test the caret location? | |
| 640 if (username.selectionStart() != username.selectionEnd() || | |
| 641 username.selectionEnd() != static_cast<int>(username.value().length())) { | |
| 642 return; | |
| 643 } | |
| 644 | |
| 645 // Show the popup with the list of available usernames. | |
| 646 ShowSuggestionPopup(fill_data, username); | |
| 647 | |
| 648 | |
| 649 #if !defined(OS_ANDROID) | |
| 650 // Fill the user and password field with the most relevant match. Android | |
| 651 // only fills in the fields after the user clicks on the suggestion popup. | |
| 652 FillUserNameAndPassword(&username, &password, fill_data, false, true); | |
| 653 #endif | |
| 654 } | |
| 655 | |
| 656 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) { | |
| 657 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); | |
| 658 iter != login_to_password_info_.end();) { | |
| 659 if (iter->first.document().frame() == frame) | |
| 660 login_to_password_info_.erase(iter++); | |
| 661 else | |
| 662 ++iter; | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 bool PasswordAutofillAgent::FindLoginInfo(const WebKit::WebNode& node, | |
| 667 WebKit::WebInputElement* found_input, | |
| 668 PasswordInfo* found_password) { | |
| 669 if (!node.isElementNode()) | |
| 670 return false; | |
| 671 | |
| 672 WebKit::WebElement element = node.toConst<WebKit::WebElement>(); | |
| 673 if (!element.hasTagName("input")) | |
| 674 return false; | |
| 675 | |
| 676 WebKit::WebInputElement input = element.to<WebKit::WebInputElement>(); | |
| 677 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); | |
| 678 if (iter == login_to_password_info_.end()) | |
| 679 return false; | |
| 680 | |
| 681 *found_input = input; | |
| 682 *found_password = iter->second; | |
| 683 return true; | |
| 684 } | |
| 685 | |
| 686 } // namespace autofill | |
| OLD | NEW |