#include "components/autofill/core/common/password_autofill_util.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_form_fill_data.h"
-#include "content/public/common/page_transition_types.h"
#include "content/public/renderer/document_state.h"
#include "content/public/renderer/navigation_state.h"
#include "content/public/renderer/render_view.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
#include "third_party/WebKit/public/web/WebView.h"
+#include "ui/base/page_transition_types.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "url/gurl.h"
static bool FindFormInputElements(blink::WebFormElement* fe,
const FormData& data,
FormElements* result) {
+ const bool username_is_present = !data.fields[0].name.empty();
+
// Loop through the list of elements we need to find on the form in order to
// autofill it. If we don't find any one of them, abort processing this
// form; it can't be the right one.
- for (size_t j = 0; j < data.fields.size(); j++) {
+ // First field is the username, skip it if not present.
+ for (size_t j = (username_is_present ? 0 : 1); j < data.fields.size(); ++j) {
blink::WebVector<blink::WebNode> temp_elements;
fe->getNamedElements(data.fields[j].name, temp_elements);
GURL(form.action().utf8()));
}
+bool FillDataContainsUsername(const PasswordFormFillData& fill_data) {
+ return !fill_data.basic_data.fields[0].name.empty();
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
void PasswordAutofillAgent::PasswordValueGatekeeper::ShowValue(
blink::WebInputElement* element) {
- if (!element->isNull() && !element->suggestedValue().isNull())
+ if (!element->isNull() && !element->suggestedValue().isEmpty())
element->setValue(element->suggestedValue(), true);
}
if (iter == login_to_password_info_.end())
return false;
- const PasswordFormFillData& fill_data = iter->second.fill_data;
+ const PasswordInfo& password_info = iter->second;
+ // Don't let autofill overwrite an explicit change made by the user.
+ if (password_info.password_was_edited_last)
+ return false;
+
+ const PasswordFormFillData& fill_data = password_info.fill_data;
// If wait_for_username is false, we should have filled when the text changed.
if (!fill_data.wait_for_username)
return false;
- blink::WebInputElement password = iter->second.password_field;
+ blink::WebInputElement password = password_info.password_field;
if (!IsElementEditable(password))
return false;
bool PasswordAutofillAgent::TextDidChangeInTextField(
const blink::WebInputElement& element) {
+ // TODO(vabr): Get a mutable argument instead. http://crbug.com/397083
+ blink::WebInputElement mutable_element = element; // We need a non-const.
+
if (element.isPasswordField()) {
// Some login forms have event handlers that put a hash of the password into
// a hidden field and then clear the password (http://crbug.com/28910,
// password will be saved here.
ProvisionallySavePassword(
element.document().frame(), element.form(), RESTRICTION_NONE);
+
+ PasswordToLoginMap::iterator iter = password_to_username_.find(element);
+ if (iter != password_to_username_.end()) {
+ login_to_password_info_[iter->second].password_was_edited_last = true;
+ // Note that the suggested value of |mutable_element| was reset when its
+ // value changed.
+ mutable_element.setAutofilled(false);
+ }
return false;
}
- LoginToPasswordInfoMap::const_iterator iter =
- login_to_password_info_.find(element);
+ LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(element);
if (iter == login_to_password_info_.end())
return false;
// The input text is being changed, so any autofilled password is now
// outdated.
- blink::WebInputElement username = element; // We need a non-const.
- username.setAutofilled(false);
+ mutable_element.setAutofilled(false);
+ iter->second.password_was_edited_last = false;
blink::WebInputElement password = iter->second.password_field;
if (password.isAutofilled()) {
// But refresh the popup. Note, since this is ours, return true to signal
// no further processing is required.
if (iter->second.backspace_pressed_last) {
- ShowSuggestionPopup(iter->second.fill_data, username, false);
+ ShowSuggestionPopup(iter->second.fill_data, element, false);
return true;
}
const blink::WebString& username,
const blink::WebString& password) {
blink::WebInputElement username_element;
- PasswordInfo password_info;
+ PasswordInfo* password_info;
if (!FindLoginInfo(node, &username_element, &password_info) ||
!IsElementAutocompletable(username_element) ||
- !IsElementAutocompletable(password_info.password_field)) {
+ !IsElementAutocompletable(password_info->password_field)) {
return false;
}
- base::string16 current_username = username_element.value();
+ password_info->password_was_edited_last = false;
username_element.setValue(username, true);
username_element.setAutofilled(true);
username_element.setSelectionRange(username.length(), username.length());
- password_info.password_field.setValue(password, true);
- password_info.password_field.setAutofilled(true);
+ password_info->password_field.setValue(password, true);
+ password_info->password_field.setAutofilled(true);
return true;
}
const blink::WebString& username,
const blink::WebString& password) {
blink::WebInputElement username_element;
- PasswordInfo password_info;
+ PasswordInfo* password_info;
if (!FindLoginInfo(node, &username_element, &password_info) ||
!IsElementAutocompletable(username_element) ||
- !IsElementAutocompletable(password_info.password_field)) {
+ !IsElementAutocompletable(password_info->password_field)) {
return false;
}
username_selection_start_,
username_element.suggestedValue().length());
- was_password_autofilled_ = password_info.password_field.isAutofilled();
- password_info.password_field.setSuggestedValue(password);
- password_info.password_field.setAutofilled(true);
+ was_password_autofilled_ = password_info->password_field.isAutofilled();
+ password_info->password_field.setSuggestedValue(password);
+ password_info->password_field.setAutofilled(true);
return true;
}
bool PasswordAutofillAgent::DidClearAutofillSelection(
const blink::WebNode& node) {
blink::WebInputElement username_element;
- PasswordInfo password_info;
+ PasswordInfo* password_info;
if (!FindLoginInfo(node, &username_element, &password_info))
return false;
- ClearPreview(&username_element, &password_info.password_field);
+ ClearPreview(&username_element, &password_info->password_field);
return true;
}
frame->provisionalDataSource());
content::NavigationState* navigation_state =
document_state->navigation_state();
- if (content::PageTransitionIsWebTriggerable(
+ if (ui::PageTransitionIsWebTriggerable(
navigation_state->transition_type()) &&
!blink::WebUserGestureIndicator::isProcessingUserGesture()) {
// If onsubmit has been called, try and save that form.
scoped_ptr<FormElements> form_elements(*iter);
// Attach autocomplete listener to enable selecting alternate logins.
- // First, get pointers to username element.
- blink::WebInputElement username_element =
- form_elements->input_elements[form_data.basic_data.fields[0].name];
+ blink::WebInputElement username_element, password_element;
+
+ // Check whether the password form has a username input field.
+ bool form_contains_username_field = FillDataContainsUsername(form_data);
+ if (form_contains_username_field) {
+ username_element =
+ form_elements->input_elements[form_data.basic_data.fields[0].name];
+ }
+
+ // No password field, bail out.
+ if (form_data.basic_data.fields[1].name.empty())
+ break;
// Get pointer to password element. (We currently only support single
// password forms).
- blink::WebInputElement password_element =
+ password_element =
form_elements->input_elements[form_data.basic_data.fields[1].name];
// If wait_for_username is true, we don't want to initially fill the form
password_info.fill_data = form_data;
password_info.password_field = password_element;
login_to_password_info_[username_element] = password_info;
+ password_to_username_[password_element] = username_element;
FormData form;
FormFieldData field;
- FindFormAndFieldForFormControlElement(
- username_element, &form, &field, REQUIRE_NONE);
+ if (form_contains_username_field) {
+ FindFormAndFieldForFormControlElement(
+ username_element, &form, &field, REQUIRE_NONE);
+ }
+
Send(new AutofillHostMsg_AddPasswordFormMapping(
routing_id(), field, form_data));
}
////////////////////////////////////////////////////////////////////////////////
// PasswordAutofillAgent, private:
+PasswordAutofillAgent::PasswordInfo::PasswordInfo()
+ : backspace_pressed_last(false), password_was_edited_last(false) {
+}
+
void PasswordAutofillAgent::GetSuggestions(
const PasswordFormFillData& fill_data,
const base::string16& input,
if (password_element.document().frame()->parent())
return;
+ bool form_contains_username_field = FillDataContainsUsername(fill_data);
if (!ShouldIgnoreAutocompleteOffForPasswordFields() &&
- !username_element.form().autoComplete())
+ form_contains_username_field && !username_element.form().autoComplete())
return;
// If we can't modify the password, don't try to set the username
// Try to set the username to the preferred name, but only if the field
// can be set and isn't prefilled.
- if (IsElementAutocompletable(username_element) &&
+ if (form_contains_username_field &&
+ IsElementAutocompletable(username_element) &&
username_element.value().isEmpty()) {
// TODO(tkent): Check maxlength and pattern.
username_element.setValue(fill_data.basic_data.fields[0].value, true);
const PasswordFormFillData& fill_data,
bool exact_username_match,
bool set_selection) {
- base::string16 current_username = username_element->value();
+ // Don't fill username if password can't be set.
+ if (!IsElementAutocompletable(*password_element))
+ return false;
+
+ base::string16 current_username;
+ if (!username_element->isNull()) {
+ current_username = username_element->value();
+ }
+
// username and password will contain the match found if any.
base::string16 username;
base::string16 password;
// TODO(tkent): Check maxlength and pattern for both username and password
// fields.
- // Don't fill username if password can't be set.
- if (!IsElementAutocompletable(*password_element)) {
- return false;
- }
-
// Input matches the username, fill in required values.
- if (IsElementAutocompletable(*username_element)) {
+ if (!username_element->isNull() &&
+ IsElementAutocompletable(*username_element)) {
username_element->setValue(username, true);
username_element->setAutofilled(true);
void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) {
for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin();
iter != login_to_password_info_.end();) {
- if (iter->first.document().frame() == frame)
+ // There may not be a username field, so get the frame from the password
+ // field.
+ if (iter->second.password_field.document().frame() == frame) {
+ password_to_username_.erase(iter->second.password_field);
login_to_password_info_.erase(iter++);
- else
+ } else {
++iter;
+ }
}
for (FrameToPasswordFormMap::iterator iter =
provisionally_saved_forms_.begin();
bool PasswordAutofillAgent::FindLoginInfo(const blink::WebNode& node,
blink::WebInputElement* found_input,
- PasswordInfo* found_password) {
+ PasswordInfo** found_password) {
if (!node.isElementNode())
return false;
return false;
*found_input = input;
- *found_password = iter->second;
+ *found_password = &iter->second;
return true;
}