Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / autofill / content / renderer / password_autofill_agent.cc
index fd52df4..480bfc6 100644 (file)
@@ -17,7 +17,6 @@
 #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"
@@ -33,6 +32,7 @@
 #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"
 
@@ -67,10 +67,13 @@ typedef std::vector<FormElements*> FormElementsList;
 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);
 
@@ -218,6 +221,10 @@ void LogHTMLForm(SavePasswordProgressLogger* logger,
                       GURL(form.action().utf8()));
 }
 
+bool FillDataContainsUsername(const PasswordFormFillData& fill_data) {
+  return !fill_data.basic_data.fields[0].name.empty();
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -273,7 +280,7 @@ void PasswordAutofillAgent::PasswordValueGatekeeper::Reset() {
 
 void PasswordAutofillAgent::PasswordValueGatekeeper::ShowValue(
     blink::WebInputElement* element) {
-  if (!element->isNull() && !element->suggestedValue().isNull())
+  if (!element->isNull() && !element->suggestedValue().isEmpty())
     element->setValue(element->suggestedValue(), true);
 }
 
@@ -284,13 +291,18 @@ bool PasswordAutofillAgent::TextFieldDidEndEditing(
   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;
 
@@ -308,6 +320,9 @@ bool PasswordAutofillAgent::TextFieldDidEndEditing(
 
 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,
@@ -317,18 +332,25 @@ bool PasswordAutofillAgent::TextDidChangeInTextField(
     // 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()) {
@@ -349,7 +371,7 @@ bool PasswordAutofillAgent::TextDidChangeInTextField(
   // 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;
   }
 
@@ -388,21 +410,21 @@ bool PasswordAutofillAgent::FillSuggestion(
     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;
 }
@@ -412,11 +434,11 @@ bool PasswordAutofillAgent::PreviewSuggestion(
     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;
   }
 
@@ -428,9 +450,9 @@ bool PasswordAutofillAgent::PreviewSuggestion(
       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;
 }
@@ -438,11 +460,11 @@ bool PasswordAutofillAgent::PreviewSuggestion(
 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;
 }
 
@@ -713,7 +735,7 @@ void PasswordAutofillAgent::DidStartProvisionalLoad(
             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.
@@ -787,13 +809,22 @@ void PasswordAutofillAgent::OnFillPasswordForm(
     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
@@ -811,11 +842,15 @@ void PasswordAutofillAgent::OnFillPasswordForm(
     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));
   }
@@ -828,6 +863,10 @@ void PasswordAutofillAgent::OnSetLoggingState(bool active) {
 ////////////////////////////////////////////////////////////////////////////////
 // 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,
@@ -909,8 +948,9 @@ void PasswordAutofillAgent::FillFormOnPasswordRecieved(
   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
@@ -919,7 +959,8 @@ void PasswordAutofillAgent::FillFormOnPasswordRecieved(
 
   // 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);
@@ -940,7 +981,15 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
     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;
@@ -991,13 +1040,9 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
   // 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);
 
@@ -1055,10 +1100,14 @@ void PasswordAutofillAgent::PerformInlineAutocomplete(
 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();
@@ -1072,7 +1121,7 @@ void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) {
 
 bool PasswordAutofillAgent::FindLoginInfo(const blink::WebNode& node,
                                           blink::WebInputElement* found_input,
-                                          PasswordInfo* found_password) {
+                                          PasswordInfo** found_password) {
   if (!node.isElementNode())
     return false;
 
@@ -1086,7 +1135,7 @@ bool PasswordAutofillAgent::FindLoginInfo(const blink::WebNode& node,
     return false;
 
   *found_input = input;
-  *found_password = iter->second;
+  *found_password = &iter->second;
   return true;
 }