1 // Copyright 2013 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/content/renderer/password_form_conversion_utils.h" 6 7 #include "components/autofill/content/renderer/form_autofill_util.h" 8 #include "components/autofill/core/common/password_form.h" 9 #include "third_party/WebKit/public/platform/WebString.h" 10 #include "third_party/WebKit/public/web/WebDocument.h" 11 #include "third_party/WebKit/public/web/WebFormControlElement.h" 12 #include "third_party/WebKit/public/web/WebInputElement.h" 13 14 using blink::WebDocument; 15 using blink::WebFormControlElement; 16 using blink::WebFormElement; 17 using blink::WebInputElement; 18 using blink::WebString; 19 using blink::WebVector; 20 21 namespace autofill { 22 namespace { 23 24 // Maximum number of password fields we will observe before throwing our 25 // hands in the air and giving up with a given form. 26 static const size_t kMaxPasswords = 3; 27 28 // Helper to determine which password is the main one, and which is 29 // an old password (e.g on a "make new password" form), if any. 30 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, 31 WebInputElement* password, 32 WebInputElement* old_password) { 33 switch (passwords.size()) { 34 case 1: 35 // Single password, easy. 36 *password = passwords[0]; 37 break; 38 case 2: 39 if (passwords[0].value() == passwords[1].value()) { 40 // Treat two identical passwords as a single password. 41 *password = passwords[0]; 42 } else { 43 // Assume first is old password, second is new (no choice but to guess). 44 *old_password = passwords[0]; 45 *password = passwords[1]; 46 } 47 break; 48 case 3: 49 if (passwords[0].value() == passwords[1].value() && 50 passwords[0].value() == passwords[2].value()) { 51 // All three passwords the same? Just treat as one and hope. 52 *password = passwords[0]; 53 } else if (passwords[0].value() == passwords[1].value()) { 54 // Two the same and one different -> old password is duplicated one. 55 *old_password = passwords[0]; 56 *password = passwords[2]; 57 } else if (passwords[1].value() == passwords[2].value()) { 58 *old_password = passwords[0]; 59 *password = passwords[1]; 60 } else { 61 // Three different passwords, or first and last match with middle 62 // different. No idea which is which, so no luck. 63 return false; 64 } 65 break; 66 default: 67 return false; 68 } 69 return true; 70 } 71 72 // Get information about a login form that encapsulated in the 73 // PasswordForm struct. 74 void GetPasswordForm(const WebFormElement& form, PasswordForm* password_form) { 75 WebInputElement latest_input_element; 76 std::vector<WebInputElement> passwords; 77 std::vector<base::string16> other_possible_usernames; 78 79 WebVector<WebFormControlElement> control_elements; 80 form.getFormControlElements(control_elements); 81 82 for (size_t i = 0; i < control_elements.size(); ++i) { 83 WebFormControlElement control_element = control_elements[i]; 84 if (control_element.isActivatedSubmit()) 85 password_form->submit_element = control_element.formControlName(); 86 87 WebInputElement* input_element = toWebInputElement(&control_element); 88 if (!input_element || !input_element->isEnabled()) 89 continue; 90 91 if ((passwords.size() < kMaxPasswords) && 92 input_element->isPasswordField()) { 93 // We assume that the username element is the input element before the 94 // first password element. 95 if (passwords.empty() && !latest_input_element.isNull()) { 96 password_form->username_element = 97 latest_input_element.nameForAutofill(); 98 password_form->username_value = latest_input_element.value(); 99 // Remove the selected username from other_possible_usernames. 100 if (!other_possible_usernames.empty() && 101 !latest_input_element.value().isEmpty()) 102 other_possible_usernames.resize(other_possible_usernames.size() - 1); 103 } 104 passwords.push_back(*input_element); 105 } 106 107 // Various input types such as text, url, email can be a username field. 108 if (input_element->isTextField() && !input_element->isPasswordField()) { 109 latest_input_element = *input_element; 110 // We ignore elements that have no value. Unlike username_element, 111 // other_possible_usernames is used only for autofill, not for form 112 // identification, and blank autofill entries are not useful. 113 if (!input_element->value().isEmpty()) 114 other_possible_usernames.push_back(input_element->value()); 115 } 116 } 117 118 // Get the document URL 119 GURL full_origin(form.document().url()); 120 121 // Calculate the canonical action URL 122 WebString action = form.action(); 123 if (action.isNull()) 124 action = WebString(""); // missing 'action' attribute implies current URL 125 GURL full_action(form.document().completeURL(action)); 126 if (!full_action.is_valid()) 127 return; 128 129 WebInputElement password; 130 WebInputElement old_password; 131 if (!LocateSpecificPasswords(passwords, &password, &old_password)) 132 return; 133 134 // We want to keep the path but strip any authentication data, as well as 135 // query and ref portions of URL, for the form action and form origin. 136 GURL::Replacements rep; 137 rep.ClearUsername(); 138 rep.ClearPassword(); 139 rep.ClearQuery(); 140 rep.ClearRef(); 141 password_form->action = full_action.ReplaceComponents(rep); 142 password_form->origin = full_origin.ReplaceComponents(rep); 143 144 rep.SetPathStr(""); 145 password_form->signon_realm = full_origin.ReplaceComponents(rep).spec(); 146 147 password_form->other_possible_usernames.swap(other_possible_usernames); 148 149 if (!password.isNull()) { 150 password_form->password_element = password.nameForAutofill(); 151 password_form->password_value = password.value(); 152 password_form->password_autocomplete_set = password.autoComplete(); 153 } 154 if (!old_password.isNull()) { 155 password_form->old_password_element = old_password.nameForAutofill(); 156 password_form->old_password_value = old_password.value(); 157 } 158 159 password_form->scheme = PasswordForm::SCHEME_HTML; 160 password_form->ssl_valid = false; 161 password_form->preferred = false; 162 password_form->blacklisted_by_user = false; 163 password_form->type = PasswordForm::TYPE_MANUAL; 164 password_form->use_additional_authentication = false; 165 } 166 167 } // namespace 168 169 scoped_ptr<PasswordForm> CreatePasswordForm(const WebFormElement& web_form) { 170 if (web_form.isNull()) 171 return scoped_ptr<PasswordForm>(); 172 173 scoped_ptr<PasswordForm> password_form(new PasswordForm()); 174 GetPasswordForm(web_form, password_form.get()); 175 176 if (!password_form->action.is_valid()) 177 return scoped_ptr<PasswordForm>(); 178 179 WebFormElementToFormData(web_form, 180 blink::WebFormControlElement(), 181 REQUIRE_NONE, 182 EXTRACT_NONE, 183 &password_form->form_data, 184 NULL /* FormFieldData */); 185 186 return password_form.Pass(); 187 } 188 189 } // namespace autofill 190