| Index: chrome/renderer/autofill/password_autofill_agent_browsertest.cc | 
| diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc | 
| index 01562c4c97913e6b23967874cb226a119dfc9f68..04b5bb4bc9631f41e45274bd6d55da7867f5a868 100644 | 
| --- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc | 
| +++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc | 
| @@ -2085,4 +2085,169 @@ TEST_F(PasswordAutofillAgentTest, | 
| ASSERT_FALSE(message); | 
| } | 
|  | 
| +// Tests that credential suggestions are autofilled on a password (and change | 
| +// password) forms having either ambiguous or empty name. | 
| +TEST_F(PasswordAutofillAgentTest, | 
| +       SuggestionsOnFormContainingAmbiguousOrEmptyNames) { | 
| +  const char kEmpty[] = ""; | 
| +  const char kDummyUsernameField[] = "anonymous_username"; | 
| +  const char kDummyPasswordField[] = "anonymous_password"; | 
| +  const char kFormContainsEmptyNamesHTML[] = | 
| +      "<FORM name='WithoutNameIdForm' action='http://www.bidule.com' >" | 
| +      "  <INPUT type='text' placeholder='username'/>" | 
| +      "  <INPUT type='password' placeholder='Password'/>" | 
| +      "  <INPUT type='submit' />" | 
| +      "</FORM>"; | 
| + | 
| +  const char kFormContainsAmbiguousNamesHTML[] = | 
| +      "<FORM name='AmbiguousNameIdForm' action='http://www.bidule.com' >" | 
| +      "  <INPUT type='text' id='credentials' placeholder='username' />" | 
| +      "  <INPUT type='password' id='credentials' placeholder='Password' />" | 
| +      "  <INPUT type='submit' />" | 
| +      "</FORM>"; | 
| + | 
| +  const char kChangePasswordFormContainsEmptyNamesHTML[] = | 
| +      "<FORM name='ChangePwd' action='http://www.bidule.com' >" | 
| +      "  <INPUT type='text' placeholder='username' />" | 
| +      "  <INPUT type='password' placeholder='Old Password' " | 
| +      "    autocomplete='current-password' />" | 
| +      "  <INPUT type='password' placeholder='New Password' " | 
| +      "    autocomplete='new-password' />" | 
| +      "  <INPUT type='submit' />" | 
| +      "</FORM>"; | 
| + | 
| +  const char kChangePasswordFormButNoUsername[] = | 
| +      "<FORM name='ChangePwdButNoUsername' action='http://www.bidule.com' >" | 
| +      "  <INPUT type='password' placeholder='Old Password' " | 
| +      "    autocomplete='current-password' />" | 
| +      "  <INPUT type='password' placeholder='New Password' " | 
| +      "    autocomplete='new-password' />" | 
| +      "  <INPUT type='submit' />" | 
| +      "</FORM>"; | 
| + | 
| +  const char kChangePasswordFormButNoOldPassword[] = | 
| +      "<FORM name='ChangePwdButNoOldPwd' action='http://www.bidule.com' >" | 
| +      "  <INPUT type='text' placeholder='username' />" | 
| +      "  <INPUT type='password' placeholder='New Password' " | 
| +      "    autocomplete='new-password' />" | 
| +      "  <INPUT type='password' placeholder='Retype Password' " | 
| +      "    autocomplete='new-password' />" | 
| +      "  <INPUT type='submit' />" | 
| +      "</FORM>"; | 
| + | 
| +  const char kChangePasswordFormButNoAutocompleteAttribute[] = | 
| +      "<FORM name='ChangePwdButNoAutocomplete' action='http://www.bidule.com'>" | 
| +      "  <INPUT type='text' placeholder='username' />" | 
| +      "  <INPUT type='password' placeholder='Old Password' />" | 
| +      "  <INPUT type='password' placeholder='New Password' />" | 
| +      "  <INPUT type='submit' />" | 
| +      "</FORM>"; | 
| + | 
| +  const struct { | 
| +    const char* html_form; | 
| +    bool is_possible_change_password_form; | 
| +    bool does_trigger_autocomplete_on_fill; | 
| +    const char* fill_data_username_field_name; | 
| +    const char* fill_data_password_field_name; | 
| +    const char* expected_username_suggestions; | 
| +    const char* expected_password_suggestions; | 
| +    bool expected_is_username_autofillable; | 
| +    bool expected_is_password_autofillable; | 
| +  } test_cases[] = { | 
| +      // Password form without name or id attributes specified for the input | 
| +      // fields. | 
| +      {kFormContainsEmptyNamesHTML, false, true, kDummyUsernameField, | 
| +       kDummyPasswordField, kAliceUsername, kAlicePassword, true, true}, | 
| + | 
| +      // Password form with ambiguous name or id attributes specified for the | 
| +      // input fields. | 
| +      {kFormContainsAmbiguousNamesHTML, false, true, "credentials", | 
| +       "credentials", kAliceUsername, kAlicePassword, true, true}, | 
| + | 
| +      // Change password form without name or id attributes specified for the | 
| +      // input fields and |autocomplete='current-password'| attribute for old | 
| +      // password field. | 
| +      {kChangePasswordFormContainsEmptyNamesHTML, true, true, | 
| +       kDummyUsernameField, kDummyPasswordField, kAliceUsername, kAlicePassword, | 
| +       true, true}, | 
| + | 
| +      // Change password form without username field. | 
| +      {kChangePasswordFormButNoUsername, true, true, kEmpty, | 
| +       kDummyPasswordField, kEmpty, kAlicePassword, false, true}, | 
| + | 
| +      // Change password form without name or id attributes specified for the | 
| +      // input fields and |autocomplete='new-password'| attribute for new | 
| +      // password fields. This form *do not* trigger |OnFillPasswordForm| from | 
| +      // browser. | 
| +      {kChangePasswordFormButNoOldPassword, true, false, kDummyUsernameField, | 
| +       kDummyPasswordField, kEmpty, kEmpty, false, false}, | 
| + | 
| +      // Change password form without name or id attributes specified for the | 
| +      // input fields but |autocomplete='current-password'| or | 
| +      // |autocomplete='new-password'| attributes are missing for old and new | 
| +      // password fields respectively. | 
| +      {kChangePasswordFormButNoAutocompleteAttribute, true, true, | 
| +       kDummyUsernameField, kDummyPasswordField, kAliceUsername, kAlicePassword, | 
| +       true, true}, | 
| +  }; | 
| + | 
| +  for (const auto& test_case : test_cases) { | 
| +    SCOPED_TRACE(testing::Message() << "html_form: " << test_case.html_form | 
| +                                    << ", fill_data_username_field_name: " | 
| +                                    << test_case.fill_data_username_field_name | 
| +                                    << ", fill_data_password_field_name: " | 
| +                                    << test_case.fill_data_password_field_name); | 
| + | 
| +    // Load a password form. | 
| +    LoadHTML(test_case.html_form); | 
| + | 
| +    // Get the username and password form input elelments. | 
| +    blink::WebDocument document = GetMainFrame()->document(); | 
| +    blink::WebVector<blink::WebFormElement> forms; | 
| +    document.forms(forms); | 
| +    blink::WebFormElement form_element = forms[0]; | 
| +    std::vector<blink::WebFormControlElement> control_elements = | 
| +        ExtractAutofillableElementsInForm(form_element); | 
| +    bool has_fillable_username = | 
| +        (kEmpty != test_case.fill_data_username_field_name); | 
| +    if (has_fillable_username) { | 
| +      username_element_ = control_elements[0].to<blink::WebInputElement>(); | 
| +      password_element_ = control_elements[1].to<blink::WebInputElement>(); | 
| +    } else { | 
| +      password_element_ = control_elements[0].to<blink::WebInputElement>(); | 
| +    } | 
| + | 
| +    UpdateOriginForHTML(test_case.html_form); | 
| +    if (test_case.does_trigger_autocomplete_on_fill) { | 
| +      // Prepare |fill_data_| to trigger autocomplete. | 
| +      fill_data_.is_possible_change_password_form = | 
| +          test_case.is_possible_change_password_form; | 
| +      fill_data_.username_field.name = | 
| +          ASCIIToUTF16(test_case.fill_data_username_field_name); | 
| +      fill_data_.password_field.name = | 
| +          ASCIIToUTF16(test_case.fill_data_password_field_name); | 
| +      fill_data_.additional_logins.clear(); | 
| +      fill_data_.other_possible_usernames.clear(); | 
| + | 
| +      ClearUsernameAndPasswordFields(); | 
| + | 
| +      // Simulate the browser sending back the login info, it triggers the | 
| +      // autocomplete. | 
| +      SimulateOnFillPasswordForm(fill_data_); | 
| + | 
| +      if (has_fillable_username) { | 
| +        SimulateSuggestionChoice(username_element_); | 
| +      } else { | 
| +        SimulateSuggestionChoice(password_element_); | 
| +      } | 
| + | 
| +      // The username and password should now have been autocompleted. | 
| +      CheckTextFieldsDOMState(test_case.expected_username_suggestions, | 
| +                              test_case.expected_is_username_autofillable, | 
| +                              test_case.expected_password_suggestions, | 
| +                              test_case.expected_is_password_autofillable); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| }  // namespace autofill | 
|  |