Home | History | Annotate | Download | only in autofill
      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 <string.h>
      6 
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "chrome/test/base/chrome_render_view_test.h"
     10 #include "components/autofill/content/common/autofill_messages.h"
     11 #include "components/autofill/content/renderer/autofill_agent.h"
     12 #include "components/autofill/content/renderer/test_password_generation_agent.h"
     13 #include "components/autofill/core/common/form_data.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 #include "third_party/WebKit/public/platform/WebString.h"
     16 #include "third_party/WebKit/public/web/WebDocument.h"
     17 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     18 #include "third_party/WebKit/public/web/WebWidget.h"
     19 
     20 using blink::WebDocument;
     21 using blink::WebElement;
     22 using blink::WebInputElement;
     23 using blink::WebNode;
     24 using blink::WebString;
     25 
     26 namespace autofill {
     27 
     28 class PasswordGenerationAgentTest : public ChromeRenderViewTest {
     29  public:
     30   PasswordGenerationAgentTest() {}
     31 
     32   virtual void TearDown() {
     33     LoadHTML("");
     34     ChromeRenderViewTest::TearDown();
     35   }
     36 
     37   void SetNotBlacklistedMessage(const char* form_str) {
     38     autofill::PasswordForm form;
     39     form.origin =
     40         GURL(base::StringPrintf("data:text/html;charset=utf-8,%s", form_str));
     41     AutofillMsg_FormNotBlacklisted msg(0, form);
     42     password_generation_->OnMessageReceived(msg);
     43   }
     44 
     45   void SetAccountCreationFormsDetectedMessage(const char* form_str) {
     46     autofill::FormData form;
     47     form.origin =
     48         GURL(base::StringPrintf("data:text/html;charset=utf-8,%s", form_str));
     49     std::vector<autofill::FormData> forms;
     50     forms.push_back(form);
     51     AutofillMsg_AccountCreationFormsDetected msg(0, forms);
     52     password_generation_->OnMessageReceived(msg);
     53   }
     54 
     55   void ExpectPasswordGenerationAvailable(const char* element_id,
     56                                          bool available) {
     57     WebDocument document = GetMainFrame()->document();
     58     WebElement element =
     59         document.getElementById(WebString::fromUTF8(element_id));
     60     ASSERT_FALSE(element.isNull());
     61     ExecuteJavaScript(
     62         base::StringPrintf("document.getElementById('%s').focus();",
     63                            element_id).c_str());
     64     if (available) {
     65       ASSERT_EQ(1u, password_generation_->messages().size());
     66       EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
     67                 password_generation_->messages()[0]->type());
     68     } else {
     69       EXPECT_EQ(0u, password_generation_->messages().size());
     70     }
     71     password_generation_->clear_messages();
     72   }
     73 
     74  private:
     75   DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgentTest);
     76 };
     77 
     78 const char kSigninFormHTML[] =
     79     "<FORM name = 'blah' action = 'http://www.random.com/'> "
     80     "  <INPUT type = 'text' id = 'username'/> "
     81     "  <INPUT type = 'password' id = 'password'/> "
     82     "  <INPUT type = 'submit' value = 'LOGIN' />"
     83     "</FORM>";
     84 
     85 const char kAccountCreationFormHTML[] =
     86     "<FORM name = 'blah' action = 'http://www.random.com/'> "
     87     "  <INPUT type = 'text' id = 'username'/> "
     88     "  <INPUT type = 'password' id = 'first_password' "
     89     "         autocomplete = 'off' size = 5/>"
     90     "  <INPUT type = 'password' id = 'second_password' size = 5/> "
     91     "  <INPUT type = 'text' id = 'address'/> "
     92     "  <INPUT type = 'submit' value = 'LOGIN' />"
     93     "</FORM>";
     94 
     95 const char kHiddenPasswordAccountCreationFormHTML[] =
     96     "<FORM name = 'blah' action = 'http://www.random.com/'> "
     97     "  <INPUT type = 'text' id = 'username'/> "
     98     "  <INPUT type = 'password' id = 'first_password'/> "
     99     "  <INPUT type = 'password' id = 'second_password' style='display:none'/> "
    100     "  <INPUT type = 'submit' value = 'LOGIN' />"
    101     "</FORM>";
    102 
    103 const char kInvalidActionAccountCreationFormHTML[] =
    104     "<FORM name = 'blah' action = 'invalid'> "
    105     "  <INPUT type = 'text' id = 'username'/> "
    106     "  <INPUT type = 'password' id = 'first_password'/> "
    107     "  <INPUT type = 'password' id = 'second_password'/> "
    108     "  <INPUT type = 'submit' value = 'LOGIN' />"
    109     "</FORM>";
    110 
    111 TEST_F(PasswordGenerationAgentTest, DetectionTest) {
    112   // Don't shown the icon for non account creation forms.
    113   LoadHTML(kSigninFormHTML);
    114   ExpectPasswordGenerationAvailable("password", false);
    115 
    116   // We don't show the decoration yet because the feature isn't enabled.
    117   LoadHTML(kAccountCreationFormHTML);
    118   ExpectPasswordGenerationAvailable("first_password", false);
    119 
    120   // Pretend like We have received message indicating site is not blacklisted,
    121   // and we have received message indicating the form is classified as
    122   // ACCOUNT_CREATION_FORM form Autofill server. We should show the icon.
    123   LoadHTML(kAccountCreationFormHTML);
    124   SetNotBlacklistedMessage(kAccountCreationFormHTML);
    125   SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
    126   ExpectPasswordGenerationAvailable("first_password", true);
    127 
    128   // This doesn't trigger because hidden password fields are ignored.
    129   LoadHTML(kHiddenPasswordAccountCreationFormHTML);
    130   SetNotBlacklistedMessage(kHiddenPasswordAccountCreationFormHTML);
    131   SetAccountCreationFormsDetectedMessage(
    132       kHiddenPasswordAccountCreationFormHTML);
    133   ExpectPasswordGenerationAvailable("first_password", false);
    134 
    135   // This doesn't trigger because the form action is invalid.
    136   LoadHTML(kInvalidActionAccountCreationFormHTML);
    137   SetNotBlacklistedMessage(kInvalidActionAccountCreationFormHTML);
    138   SetAccountCreationFormsDetectedMessage(kInvalidActionAccountCreationFormHTML);
    139   ExpectPasswordGenerationAvailable("first_password", false);
    140 }
    141 
    142 TEST_F(PasswordGenerationAgentTest, FillTest) {
    143   // Make sure that we are enabled before loading HTML.
    144   LoadHTML(kAccountCreationFormHTML);
    145 
    146   WebDocument document = GetMainFrame()->document();
    147   WebElement element =
    148       document.getElementById(WebString::fromUTF8("first_password"));
    149   ASSERT_FALSE(element.isNull());
    150   WebInputElement first_password_element = element.to<WebInputElement>();
    151   element = document.getElementById(WebString::fromUTF8("second_password"));
    152   ASSERT_FALSE(element.isNull());
    153   WebInputElement second_password_element = element.to<WebInputElement>();
    154 
    155   // Both password fields should be empty.
    156   EXPECT_TRUE(first_password_element.value().isNull());
    157   EXPECT_TRUE(second_password_element.value().isNull());
    158 
    159   base::string16 password = base::ASCIIToUTF16("random_password");
    160   AutofillMsg_GeneratedPasswordAccepted msg(0, password);
    161   password_generation_->OnMessageReceived(msg);
    162 
    163   // Password fields are filled out and set as being autofilled.
    164   EXPECT_EQ(password, first_password_element.value());
    165   EXPECT_EQ(password, second_password_element.value());
    166   EXPECT_TRUE(first_password_element.isAutofilled());
    167   EXPECT_TRUE(second_password_element.isAutofilled());
    168 
    169   // Focus moved to the next input field.
    170   // TODO(zysxqn): Change this back to the address element once Bug 90224
    171   // https://bugs.webkit.org/show_bug.cgi?id=90224 has been fixed.
    172   element = document.getElementById(WebString::fromUTF8("first_password"));
    173   ASSERT_FALSE(element.isNull());
    174   EXPECT_EQ(element, document.focusedElement());
    175 }
    176 
    177 TEST_F(PasswordGenerationAgentTest, EditingTest) {
    178   LoadHTML(kAccountCreationFormHTML);
    179   SetNotBlacklistedMessage(kAccountCreationFormHTML);
    180   SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
    181 
    182   WebDocument document = GetMainFrame()->document();
    183   WebElement element =
    184       document.getElementById(WebString::fromUTF8("first_password"));
    185   ASSERT_FALSE(element.isNull());
    186   WebInputElement first_password_element = element.to<WebInputElement>();
    187   element = document.getElementById(WebString::fromUTF8("second_password"));
    188   ASSERT_FALSE(element.isNull());
    189   WebInputElement second_password_element = element.to<WebInputElement>();
    190 
    191   base::string16 password = base::ASCIIToUTF16("random_password");
    192   AutofillMsg_GeneratedPasswordAccepted msg(0, password);
    193   password_generation_->OnMessageReceived(msg);
    194 
    195   // Passwords start out the same.
    196   EXPECT_EQ(password, first_password_element.value());
    197   EXPECT_EQ(password, second_password_element.value());
    198 
    199   // After editing the first field they are still the same.
    200   base::string16 edited_password = base::ASCIIToUTF16("edited_password");
    201   first_password_element.setValue(edited_password);
    202   // Cast to WebAutofillClient where textFieldDidChange() is public.
    203   static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
    204       first_password_element);
    205   // textFieldDidChange posts a task, so we need to wait until it's been
    206   // processed.
    207   base::MessageLoop::current()->RunUntilIdle();
    208 
    209   EXPECT_EQ(edited_password, first_password_element.value());
    210   EXPECT_EQ(edited_password, second_password_element.value());
    211 }
    212 
    213 TEST_F(PasswordGenerationAgentTest, BlacklistedTest) {
    214   // Did not receive not blacklisted message. Don't show password generation
    215   // icon.
    216   LoadHTML(kAccountCreationFormHTML);
    217   SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
    218   ExpectPasswordGenerationAvailable("first_password", false);
    219 
    220   // Receive one not blacklisted message for non account creation form. Don't
    221   // show password generation icon.
    222   LoadHTML(kAccountCreationFormHTML);
    223   SetNotBlacklistedMessage(kSigninFormHTML);
    224   SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
    225   ExpectPasswordGenerationAvailable("first_password", false);
    226 
    227   // Receive one not blackliste message for account creation form. Show password
    228   // generation icon.
    229   LoadHTML(kAccountCreationFormHTML);
    230   SetNotBlacklistedMessage(kAccountCreationFormHTML);
    231   SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
    232   ExpectPasswordGenerationAvailable("first_password", true);
    233 
    234   // Receive two not blacklisted messages, one is for account creation form and
    235   // the other is not. Show password generation icon.
    236   LoadHTML(kAccountCreationFormHTML);
    237   SetNotBlacklistedMessage(kAccountCreationFormHTML);
    238   SetNotBlacklistedMessage(kSigninFormHTML);
    239   SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
    240   ExpectPasswordGenerationAvailable("first_password", true);
    241 }
    242 
    243 TEST_F(PasswordGenerationAgentTest, AccountCreationFormsDetectedTest) {
    244   // Did not receive account creation forms detected messege. Don't show
    245   // password generation icon.
    246   LoadHTML(kAccountCreationFormHTML);
    247   SetNotBlacklistedMessage(kAccountCreationFormHTML);
    248   ExpectPasswordGenerationAvailable("first_password", false);
    249 
    250   // Receive the account creation forms detected message. Show password
    251   // generation icon.
    252   LoadHTML(kAccountCreationFormHTML);
    253   SetNotBlacklistedMessage(kAccountCreationFormHTML);
    254   SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
    255   ExpectPasswordGenerationAvailable("first_password", true);
    256 }
    257 
    258 TEST_F(PasswordGenerationAgentTest, MaximumOfferSize) {
    259   LoadHTML(kAccountCreationFormHTML);
    260   SetNotBlacklistedMessage(kAccountCreationFormHTML);
    261   SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
    262   ExpectPasswordGenerationAvailable("first_password", true);
    263 
    264   WebDocument document = GetMainFrame()->document();
    265   WebElement element =
    266       document.getElementById(WebString::fromUTF8("first_password"));
    267   ASSERT_FALSE(element.isNull());
    268   WebInputElement first_password_element = element.to<WebInputElement>();
    269 
    270   // Make a password just under maximum offer size.
    271   first_password_element.setValue(
    272       base::ASCIIToUTF16(
    273           std::string(password_generation_->kMaximumOfferSize - 1, 'a')));
    274   // Cast to WebAutofillClient where textFieldDidChange() is public.
    275   static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
    276       first_password_element);
    277   // textFieldDidChange posts a task, so we need to wait until it's been
    278   // processed.
    279   base::MessageLoop::current()->RunUntilIdle();
    280   // There should now be a message to show the UI.
    281   ASSERT_EQ(1u, password_generation_->messages().size());
    282   EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
    283             password_generation_->messages()[0]->type());
    284   password_generation_->clear_messages();
    285 
    286   // Simulate a user typing a password just over maximum offer size.
    287   first_password_element.setValue(
    288       base::ASCIIToUTF16(
    289           std::string(password_generation_->kMaximumOfferSize + 1, 'a')));
    290   // Cast to WebAutofillClient where textFieldDidChange() is public.
    291   static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
    292       first_password_element);
    293   // textFieldDidChange posts a task, so we need to wait until it's been
    294   // processed.
    295   base::MessageLoop::current()->RunUntilIdle();
    296   // There should now be a message to hide the UI.
    297   ASSERT_EQ(1u, password_generation_->messages().size());
    298   EXPECT_EQ(AutofillHostMsg_HidePasswordGenerationPopup::ID,
    299             password_generation_->messages()[0]->type());
    300   password_generation_->clear_messages();
    301 
    302   // Simulate the user deleting characters. The generation popup should be shown
    303   // again.
    304   first_password_element.setValue(
    305       base::ASCIIToUTF16(
    306           std::string(password_generation_->kMaximumOfferSize, 'a')));
    307   // Cast to WebAutofillClient where textFieldDidChange() is public.
    308   static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
    309       first_password_element);
    310   // textFieldDidChange posts a task, so we need to wait until it's been
    311   // processed.
    312   base::MessageLoop::current()->RunUntilIdle();
    313   // There should now be a message to show the UI.
    314   ASSERT_EQ(1u, password_generation_->messages().size());
    315   EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
    316             password_generation_->messages()[0]->type());
    317   password_generation_->clear_messages();
    318 
    319   // Change focus. Bubble should be hidden, but that is handled by AutofilAgent,
    320   // so no messages are sent.
    321   ExecuteJavaScript("document.getElementById('username').focus();");
    322   EXPECT_EQ(0u, password_generation_->messages().size());
    323   password_generation_->clear_messages();
    324 
    325   // Focusing the password field will bring up the generation UI again.
    326   ExecuteJavaScript("document.getElementById('first_password').focus();");
    327   EXPECT_EQ(1u, password_generation_->messages().size());
    328   EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
    329             password_generation_->messages()[0]->type());
    330   password_generation_->clear_messages();
    331 }
    332 
    333 }  // namespace autofill
    334