Home | History | Annotate | Download | only in autofill
      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 "base/strings/string_util.h"
      6 #include "base/strings/utf_string_conversions.h"
      7 #include "chrome/test/base/chrome_render_view_test.h"
      8 #include "components/autofill/content/renderer/autofill_agent.h"
      9 #include "components/autofill/content/renderer/password_autofill_agent.h"
     10 #include "components/autofill/core/common/autofill_messages.h"
     11 #include "components/autofill/core/common/form_data.h"
     12 #include "components/autofill/core/common/form_field_data.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 #include "third_party/WebKit/public/platform/WebString.h"
     15 #include "third_party/WebKit/public/platform/WebVector.h"
     16 #include "third_party/WebKit/public/web/WebDocument.h"
     17 #include "third_party/WebKit/public/web/WebElement.h"
     18 #include "third_party/WebKit/public/web/WebFormElement.h"
     19 #include "third_party/WebKit/public/web/WebInputElement.h"
     20 #include "third_party/WebKit/public/web/WebNode.h"
     21 #include "third_party/WebKit/public/web/WebView.h"
     22 #include "ui/base/keycodes/keyboard_codes.h"
     23 
     24 using content::PasswordForm;
     25 using WebKit::WebDocument;
     26 using WebKit::WebElement;
     27 using WebKit::WebFrame;
     28 using WebKit::WebInputElement;
     29 using WebKit::WebString;
     30 using WebKit::WebView;
     31 
     32 namespace {
     33 
     34 // The name of the username/password element in the form.
     35 const char* const kUsernameName = "username";
     36 const char* const kPasswordName = "password";
     37 
     38 const char* const kAliceUsername = "alice";
     39 const char* const kAlicePassword = "password";
     40 const char* const kBobUsername = "bob";
     41 const char* const kBobPassword = "secret";
     42 const char* const kCarolUsername = "Carol";
     43 const char* const kCarolPassword = "test";
     44 const char* const kCarolAlternateUsername = "RealCarolUsername";
     45 
     46 
     47 const char* const kFormHTML =
     48     "<FORM name='LoginTestForm' action='http://www.bidule.com'>"
     49     "  <INPUT type='text' id='username'/>"
     50     "  <INPUT type='password' id='password'/>"
     51     "  <INPUT type='submit' value='Login'/>"
     52     "</FORM>";
     53 
     54 }  // namespace
     55 
     56 namespace autofill {
     57 
     58 class PasswordAutofillAgentTest : public ChromeRenderViewTest {
     59  public:
     60   PasswordAutofillAgentTest() {
     61   }
     62 
     63   // Simulates the fill password form message being sent to the renderer.
     64   // We use that so we don't have to make RenderView::OnFillPasswordForm()
     65   // protected.
     66   void SimulateOnFillPasswordForm(
     67       const PasswordFormFillData& fill_data) {
     68     AutofillMsg_FillPasswordForm msg(0, fill_data);
     69     password_autofill_->OnMessageReceived(msg);
     70   }
     71 
     72   virtual void SetUp() {
     73     ChromeRenderViewTest::SetUp();
     74 
     75     // Add a preferred login and an additional login to the FillData.
     76     username1_ = ASCIIToUTF16(kAliceUsername);
     77     password1_ = ASCIIToUTF16(kAlicePassword);
     78     username2_ = ASCIIToUTF16(kBobUsername);
     79     password2_ = ASCIIToUTF16(kBobPassword);
     80     username3_ = ASCIIToUTF16(kCarolUsername);
     81     password3_ = ASCIIToUTF16(kCarolPassword);
     82     alternate_username3_ = ASCIIToUTF16(kCarolAlternateUsername);
     83 
     84     FormFieldData username_field;
     85     username_field.name = ASCIIToUTF16(kUsernameName);
     86     username_field.value = username1_;
     87     fill_data_.basic_data.fields.push_back(username_field);
     88 
     89     FormFieldData password_field;
     90     password_field.name = ASCIIToUTF16(kPasswordName);
     91     password_field.value = password1_;
     92     password_field.form_control_type = "password";
     93     fill_data_.basic_data.fields.push_back(password_field);
     94 
     95     PasswordAndRealm password2;
     96     password2.password = password2_;
     97     fill_data_.additional_logins[username2_] = password2;
     98     PasswordAndRealm password3;
     99     password3.password = password3_;
    100     fill_data_.additional_logins[username3_] = password3;
    101 
    102     UsernamesCollectionKey key;
    103     key.username = username3_;
    104     key.password = password3_;
    105     key.realm = "google.com";
    106     fill_data_.other_possible_usernames[key].push_back(alternate_username3_);
    107 
    108     // We need to set the origin so it matches the frame URL and the action so
    109     // it matches the form action, otherwise we won't autocomplete.
    110     std::string origin("data:text/html;charset=utf-8,");
    111     origin += kFormHTML;
    112     fill_data_.basic_data.origin = GURL(origin);
    113     fill_data_.basic_data.action = GURL("http://www.bidule.com");
    114 
    115     LoadHTML(kFormHTML);
    116 
    117     // Now retrieves the input elements so the test can access them.
    118     WebDocument document = GetMainFrame()->document();
    119     WebElement element =
    120         document.getElementById(WebString::fromUTF8(kUsernameName));
    121     ASSERT_FALSE(element.isNull());
    122     username_element_ = element.to<WebKit::WebInputElement>();
    123     element = document.getElementById(WebString::fromUTF8(kPasswordName));
    124     ASSERT_FALSE(element.isNull());
    125     password_element_ = element.to<WebKit::WebInputElement>();
    126   }
    127 
    128   virtual void TearDown() {
    129     username_element_.reset();
    130     password_element_.reset();
    131     ChromeRenderViewTest::TearDown();
    132   }
    133 
    134   void ClearUsernameAndPasswordFields() {
    135     username_element_.setValue("");
    136     username_element_.setAutofilled(false);
    137     password_element_.setValue("");
    138     password_element_.setAutofilled(false);
    139   }
    140 
    141   void SimulateUsernameChange(const std::string& username,
    142                               bool move_caret_to_end) {
    143     username_element_.setValue(WebString::fromUTF8(username));
    144     // The field must have focus or AutofillAgent will think the
    145     // change should be ignored.
    146     while (!username_element_.focused())
    147       GetMainFrame()->document().frame()->view()->advanceFocus(false);
    148     if (move_caret_to_end)
    149       username_element_.setSelectionRange(username.length(), username.length());
    150     autofill_agent_->textFieldDidChange(username_element_);
    151     // Processing is delayed because of a WebKit bug, see
    152     // PasswordAutocompleteManager::TextDidChangeInTextField() for details.
    153     base::MessageLoop::current()->RunUntilIdle();
    154   }
    155 
    156   void SimulateKeyDownEvent(const WebInputElement& element,
    157                             ui::KeyboardCode key_code) {
    158     WebKit::WebKeyboardEvent key_event;
    159     key_event.windowsKeyCode = key_code;
    160     autofill_agent_->textFieldDidReceiveKeyDown(element, key_event);
    161   }
    162 
    163   void CheckTextFieldsState(const std::string& username,
    164                             bool username_autofilled,
    165                             const std::string& password,
    166                             bool password_autofilled) {
    167     EXPECT_EQ(username,
    168               static_cast<std::string>(username_element_.value().utf8()));
    169     EXPECT_EQ(username_autofilled, username_element_.isAutofilled());
    170     EXPECT_EQ(password,
    171               static_cast<std::string>(password_element_.value().utf8()));
    172     EXPECT_EQ(password_autofilled, password_element_.isAutofilled());
    173   }
    174 
    175   void CheckUsernameSelection(int start, int end) {
    176     EXPECT_EQ(start, username_element_.selectionStart());
    177     EXPECT_EQ(end, username_element_.selectionEnd());
    178   }
    179 
    180   string16 username1_;
    181   string16 username2_;
    182   string16 username3_;
    183   string16 password1_;
    184   string16 password2_;
    185   string16 password3_;
    186   string16 alternate_username3_;
    187   PasswordFormFillData fill_data_;
    188 
    189   WebInputElement username_element_;
    190   WebInputElement password_element_;
    191 
    192  private:
    193   DISALLOW_COPY_AND_ASSIGN(PasswordAutofillAgentTest);
    194 };
    195 
    196 // Tests that the password login is autocompleted as expected when the browser
    197 // sends back the password info.
    198 TEST_F(PasswordAutofillAgentTest, InitialAutocomplete) {
    199   /*
    200    * Right now we are not sending the message to the browser because we are
    201    * loading a data URL and the security origin canAccessPasswordManager()
    202    * returns false.  May be we should mock URL loading to cirmcuvent this?
    203    TODO(jcivelli): find a way to make the security origin not deny access to the
    204                    password manager and then reenable this code.
    205 
    206   // The form has been loaded, we should have sent the browser a message about
    207   // the form.
    208   const IPC::Message* msg = render_thread_.sink().GetFirstMessageMatching(
    209       AutofillHostMsg_PasswordFormsParsed::ID);
    210   ASSERT_TRUE(msg != NULL);
    211 
    212   Tuple1<std::vector<PasswordForm> > forms;
    213   AutofillHostMsg_PasswordFormsParsed::Read(msg, &forms);
    214   ASSERT_EQ(1U, forms.a.size());
    215   PasswordForm password_form = forms.a[0];
    216   EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form.scheme);
    217   EXPECT_EQ(ASCIIToUTF16(kUsernameName), password_form.username_element);
    218   EXPECT_EQ(ASCIIToUTF16(kPasswordName), password_form.password_element);
    219   */
    220 
    221   // Simulate the browser sending back the login info, it triggers the
    222   // autocomplete.
    223   SimulateOnFillPasswordForm(fill_data_);
    224 
    225   // The username and password should have been autocompleted.
    226   CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
    227 }
    228 
    229 // Tests that we correctly fill forms having an empty 'action' attribute.
    230 TEST_F(PasswordAutofillAgentTest, InitialAutocompleteForEmptyAction) {
    231   const char kEmptyActionFormHTML[] =
    232       "<FORM name='LoginTestForm'>"
    233       "  <INPUT type='text' id='username'/>"
    234       "  <INPUT type='password' id='password'/>"
    235       "  <INPUT type='submit' value='Login'/>"
    236       "</FORM>";
    237   LoadHTML(kEmptyActionFormHTML);
    238 
    239   // Retrieve the input elements so the test can access them.
    240   WebDocument document = GetMainFrame()->document();
    241   WebElement element =
    242       document.getElementById(WebString::fromUTF8(kUsernameName));
    243   ASSERT_FALSE(element.isNull());
    244   username_element_ = element.to<WebKit::WebInputElement>();
    245   element = document.getElementById(WebString::fromUTF8(kPasswordName));
    246   ASSERT_FALSE(element.isNull());
    247   password_element_ = element.to<WebKit::WebInputElement>();
    248 
    249   // Set the expected form origin and action URLs.
    250   std::string origin("data:text/html;charset=utf-8,");
    251   origin += kEmptyActionFormHTML;
    252   fill_data_.basic_data.origin = GURL(origin);
    253   fill_data_.basic_data.action = GURL(origin);
    254 
    255   // Simulate the browser sending back the login info, it triggers the
    256   // autocomplete.
    257   SimulateOnFillPasswordForm(fill_data_);
    258 
    259   // The username and password should have been autocompleted.
    260   CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
    261 }
    262 
    263 // Tests that if a password or input element is marked as readonly, neither
    264 // field is autofilled on page load.
    265 TEST_F(PasswordAutofillAgentTest, NoInitialAutocompleteForReadOnly) {
    266   password_element_.setAttribute(WebString::fromUTF8("readonly"),
    267                                  WebString::fromUTF8("true"));
    268 
    269   // Simulate the browser sending back the login info, it triggers the
    270   // autocomplete.
    271   SimulateOnFillPasswordForm(fill_data_);
    272 
    273   CheckTextFieldsState(std::string(), false, std::string(), false);
    274 }
    275 
    276 // Tests that having a non-matching username precludes the autocomplete.
    277 TEST_F(PasswordAutofillAgentTest, NoInitialAutocompleteForFilledField) {
    278   username_element_.setValue(WebString::fromUTF8("bogus"));
    279 
    280   // Simulate the browser sending back the login info, it triggers the
    281   // autocomplete.
    282   SimulateOnFillPasswordForm(fill_data_);
    283 
    284   // Neither field should be autocompleted.
    285   CheckTextFieldsState("bogus", false, std::string(), false);
    286 }
    287 
    288 // Tests that we do not autofill username/passwords if marked as
    289 // autocomplete="off".
    290 TEST_F(PasswordAutofillAgentTest, NoInitialAutocompleteForAutocompleteOff) {
    291   username_element_.setAttribute(WebString::fromUTF8("autocomplete"),
    292                                  WebString::fromUTF8("off"));
    293 
    294   // Simulate the browser sending back the login info, it triggers the
    295   // autocomplete.
    296   SimulateOnFillPasswordForm(fill_data_);
    297 
    298   CheckTextFieldsState(std::string(), false, std::string(), false);
    299 }
    300 
    301 TEST_F(PasswordAutofillAgentTest, NoAutocompleteForTextFieldPasswords) {
    302   const char kTextFieldPasswordFormHTML[] =
    303       "<FORM name='LoginTestForm' action='http://www.bidule.com'>"
    304       "  <INPUT type='text' id='username'/>"
    305       "  <INPUT type='text' id='password'/>"
    306       "  <INPUT type='submit' value='Login'/>"
    307       "</FORM>";
    308   LoadHTML(kTextFieldPasswordFormHTML);
    309 
    310   // Retrieve the input elements so the test can access them.
    311   WebDocument document = GetMainFrame()->document();
    312   WebElement element =
    313       document.getElementById(WebString::fromUTF8(kUsernameName));
    314   ASSERT_FALSE(element.isNull());
    315   username_element_ = element.to<WebKit::WebInputElement>();
    316   element = document.getElementById(WebString::fromUTF8(kPasswordName));
    317   ASSERT_FALSE(element.isNull());
    318   password_element_ = element.to<WebKit::WebInputElement>();
    319 
    320   // Set the expected form origin URL.
    321   std::string origin("data:text/html;charset=utf-8,");
    322   origin += kTextFieldPasswordFormHTML;
    323   fill_data_.basic_data.origin = GURL(origin);
    324 
    325   SimulateOnFillPasswordForm(fill_data_);
    326 
    327   // Fields should still be empty.
    328   CheckTextFieldsState(std::string(), false, std::string(), false);
    329 }
    330 
    331 TEST_F(PasswordAutofillAgentTest, NoAutocompleteForPasswordFieldUsernames) {
    332   const char kPasswordFieldUsernameFormHTML[] =
    333       "<FORM name='LoginTestForm' action='http://www.bidule.com'>"
    334       "  <INPUT type='password' id='username'/>"
    335       "  <INPUT type='password' id='password'/>"
    336       "  <INPUT type='submit' value='Login'/>"
    337       "</FORM>";
    338   LoadHTML(kPasswordFieldUsernameFormHTML);
    339 
    340   // Retrieve the input elements so the test can access them.
    341   WebDocument document = GetMainFrame()->document();
    342   WebElement element =
    343       document.getElementById(WebString::fromUTF8(kUsernameName));
    344   ASSERT_FALSE(element.isNull());
    345   username_element_ = element.to<WebKit::WebInputElement>();
    346   element = document.getElementById(WebString::fromUTF8(kPasswordName));
    347   ASSERT_FALSE(element.isNull());
    348   password_element_ = element.to<WebKit::WebInputElement>();
    349 
    350   // Set the expected form origin URL.
    351   std::string origin("data:text/html;charset=utf-8,");
    352   origin += kPasswordFieldUsernameFormHTML;
    353   fill_data_.basic_data.origin = GURL(origin);
    354 
    355   SimulateOnFillPasswordForm(fill_data_);
    356 
    357   // Fields should still be empty.
    358   CheckTextFieldsState(std::string(), false, std::string(), false);
    359 }
    360 
    361 // Tests that having a matching username does not preclude the autocomplete.
    362 TEST_F(PasswordAutofillAgentTest, InitialAutocompleteForMatchingFilledField) {
    363   username_element_.setValue(WebString::fromUTF8(kAliceUsername));
    364 
    365   // Simulate the browser sending back the login info, it triggers the
    366   // autocomplete.
    367   SimulateOnFillPasswordForm(fill_data_);
    368 
    369   // The username and password should have been autocompleted.
    370   CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
    371 }
    372 
    373 // Tests that editing the password clears the autocompleted password field.
    374 TEST_F(PasswordAutofillAgentTest, PasswordClearOnEdit) {
    375   // Simulate the browser sending back the login info, it triggers the
    376   // autocomplete.
    377   SimulateOnFillPasswordForm(fill_data_);
    378 
    379   // Simulate the user changing the username to some unknown username.
    380   SimulateUsernameChange("alicia", true);
    381 
    382   // The password should have been cleared.
    383   CheckTextFieldsState("alicia", false, std::string(), false);
    384 }
    385 
    386 // Tests that we only autocomplete on focus lost and with a full username match
    387 // when |wait_for_username| is true.
    388 TEST_F(PasswordAutofillAgentTest, WaitUsername) {
    389   // Simulate the browser sending back the login info.
    390   fill_data_.wait_for_username = true;
    391   SimulateOnFillPasswordForm(fill_data_);
    392 
    393   // No auto-fill should have taken place.
    394   CheckTextFieldsState(std::string(), false, std::string(), false);
    395 
    396   // No autocomplete should happen when text is entered in the username.
    397   SimulateUsernameChange("a", true);
    398   CheckTextFieldsState("a", false, std::string(), false);
    399   SimulateUsernameChange("al", true);
    400   CheckTextFieldsState("al", false, std::string(), false);
    401   SimulateUsernameChange(kAliceUsername, true);
    402   CheckTextFieldsState(kAliceUsername, false, std::string(), false);
    403 
    404   // Autocomplete should happen only when the username textfield is blurred with
    405   // a full match.
    406   username_element_.setValue("a");
    407   autofill_agent_->textFieldDidEndEditing(username_element_);
    408   CheckTextFieldsState("a", false, std::string(), false);
    409   username_element_.setValue("al");
    410   autofill_agent_->textFieldDidEndEditing(username_element_);
    411   CheckTextFieldsState("al", false, std::string(), false);
    412   username_element_.setValue("alices");
    413   autofill_agent_->textFieldDidEndEditing(username_element_);
    414   CheckTextFieldsState("alices", false, std::string(), false);
    415   username_element_.setValue(ASCIIToUTF16(kAliceUsername));
    416   autofill_agent_->textFieldDidEndEditing(username_element_);
    417   CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
    418 }
    419 
    420 // Tests that inline autocompletion works properly.
    421 TEST_F(PasswordAutofillAgentTest, InlineAutocomplete) {
    422   // Simulate the browser sending back the login info.
    423   SimulateOnFillPasswordForm(fill_data_);
    424 
    425   // Clear the text fields to start fresh.
    426   ClearUsernameAndPasswordFields();
    427 
    428   // Simulate the user typing in the first letter of 'alice', a stored username.
    429   SimulateUsernameChange("a", true);
    430   // Both the username and password text fields should reflect selection of the
    431   // stored login.
    432   CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
    433   // And the selection should have been set to 'lice', the last 4 letters.
    434   CheckUsernameSelection(1, 5);
    435 
    436   // Now the user types the next letter of the same username, 'l'.
    437   SimulateUsernameChange("al", true);
    438   // Now the fields should have the same value, but the selection should have a
    439   // different start value.
    440   CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
    441   CheckUsernameSelection(2, 5);
    442 
    443   // Test that deleting does not trigger autocomplete.
    444   SimulateKeyDownEvent(username_element_, ui::VKEY_BACK);
    445   SimulateUsernameChange("alic", true);
    446   CheckTextFieldsState("alic", false, std::string(), false);
    447   CheckUsernameSelection(4, 4);  // No selection.
    448   // Reset the last pressed key to something other than backspace.
    449   SimulateKeyDownEvent(username_element_, ui::VKEY_A);
    450 
    451   // Now lets say the user goes astray from the stored username and types the
    452   // letter 'f', spelling 'alf'.  We don't know alf (that's just sad), so in
    453   // practice the username should no longer be 'alice' and the selected range
    454   // should be empty.
    455   SimulateUsernameChange("alf", true);
    456   CheckTextFieldsState("alf", false, std::string(), false);
    457   CheckUsernameSelection(3, 3);  // No selection.
    458 
    459   // Ok, so now the user removes all the text and enters the letter 'b'.
    460   SimulateUsernameChange("b", true);
    461   // The username and password fields should match the 'bob' entry.
    462   CheckTextFieldsState(kBobUsername, true, kBobPassword, true);
    463   CheckUsernameSelection(1, 3);
    464 
    465   // Then, the user again removes all the text and types an uppercase 'C'.
    466   SimulateUsernameChange("C", true);
    467   // The username and password fields should match the 'Carol' entry.
    468   CheckTextFieldsState(kCarolUsername, true, kCarolPassword, true);
    469   CheckUsernameSelection(1, 5);
    470   // The user removes all the text and types a lowercase 'c'.  We only
    471   // want case-sensitive autocompletion, so the username and the selected range
    472   // should be empty.
    473   SimulateUsernameChange("c", true);
    474   CheckTextFieldsState("c", false, std::string(), false);
    475   CheckUsernameSelection(1, 1);
    476 
    477   // Check that we complete other_possible_usernames as well.
    478   SimulateUsernameChange("R", true);
    479   CheckTextFieldsState(kCarolAlternateUsername, true, kCarolPassword, true);
    480   CheckUsernameSelection(1, 17);
    481 }
    482 
    483 // Tests that selecting an item in the suggestion drop-down no-ops.
    484 TEST_F(PasswordAutofillAgentTest, SuggestionSelect) {
    485   // Simulate the browser sending back the login info.
    486   SimulateOnFillPasswordForm(fill_data_);
    487 
    488   // Clear the text fields to start fresh.
    489   ClearUsernameAndPasswordFields();
    490 
    491   // To simulate accepting an item in the suggestion drop-down we just mimic
    492   // what the WebView does: it sets the element value then calls
    493   // didSelectAutofillSuggestion on the renderer.
    494   autofill_agent_->didSelectAutofillSuggestion(username_element_,
    495                                                ASCIIToUTF16(kAliceUsername),
    496                                                WebKit::WebString(),
    497                                                0);
    498   // Autocomplete should not have kicked in.
    499   CheckTextFieldsState(std::string(), false, std::string(), false);
    500 }
    501 
    502 }  // namespace autofill
    503