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/command_line.h"
      6 #include "base/strings/stringprintf.h"
      7 #include "base/strings/utf_string_conversions.h"
      8 #include "chrome/test/base/chrome_render_view_test.h"
      9 #include "components/autofill/content/common/autofill_messages.h"
     10 #include "components/autofill/content/renderer/autofill_agent.h"
     11 #include "components/autofill/core/common/form_data.h"
     12 #include "components/autofill/core/common/form_field_data.h"
     13 #include "content/public/common/content_switches.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 #include "third_party/WebKit/public/platform/WebString.h"
     16 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     17 #include "third_party/WebKit/public/platform/WebVector.h"
     18 #include "third_party/WebKit/public/web/WebDocument.h"
     19 #include "third_party/WebKit/public/web/WebFormElement.h"
     20 #include "third_party/WebKit/public/web/WebInputElement.h"
     21 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     22 
     23 using base::ASCIIToUTF16;
     24 using blink::WebDocument;
     25 using blink::WebElement;
     26 using blink::WebFormElement;
     27 using blink::WebFrame;
     28 using blink::WebLocalFrame;
     29 using blink::WebInputElement;
     30 using blink::WebString;
     31 using blink::WebURLRequest;
     32 using blink::WebVector;
     33 
     34 namespace autofill {
     35 
     36 typedef Tuple5<int,
     37                autofill::FormData,
     38                autofill::FormFieldData,
     39                gfx::RectF,
     40                bool> AutofillQueryParam;
     41 
     42 class AutofillRendererTest : public ChromeRenderViewTest {
     43  public:
     44   AutofillRendererTest() {}
     45   virtual ~AutofillRendererTest() {}
     46 
     47  protected:
     48   virtual void SetUp() OVERRIDE {
     49     ChromeRenderViewTest::SetUp();
     50 
     51     // Don't want any delay for form state sync changes. This will still post a
     52     // message so updates will get coalesced, but as soon as we spin the message
     53     // loop, it will generate an update.
     54     SendContentStateImmediately();
     55   }
     56 
     57   void SimulateRequestAutocompleteResult(
     58       const blink::WebFormElement::AutocompleteResult& result,
     59       const base::string16& message) {
     60     AutofillMsg_RequestAutocompleteResult msg(0, result, message, FormData());
     61     static_cast<content::RenderViewObserver*>(autofill_agent_)
     62         ->OnMessageReceived(msg);
     63   }
     64 
     65  private:
     66   DISALLOW_COPY_AND_ASSIGN(AutofillRendererTest);
     67 };
     68 
     69 TEST_F(AutofillRendererTest, SendForms) {
     70   LoadHTML("<form method='POST'>"
     71            "  <input type='text' id='firstname'/>"
     72            "  <input type='text' id='middlename'/>"
     73            "  <input type='text' id='lastname' autoComplete='off'/>"
     74            "  <input type='hidden' id='email'/>"
     75            "  <select id='state'/>"
     76            "    <option>?</option>"
     77            "    <option>California</option>"
     78            "    <option>Texas</option>"
     79            "  </select>"
     80            "</form>");
     81 
     82   // Verify that "FormsSeen" sends the expected number of fields.
     83   const IPC::Message* message = render_thread_->sink().GetFirstMessageMatching(
     84       AutofillHostMsg_FormsSeen::ID);
     85   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
     86   AutofillHostMsg_FormsSeen::Param params;
     87   AutofillHostMsg_FormsSeen::Read(message, &params);
     88   std::vector<FormData> forms = params.a;
     89   ASSERT_EQ(1UL, forms.size());
     90   ASSERT_EQ(4UL, forms[0].fields.size());
     91 
     92   FormFieldData expected;
     93 
     94   expected.name = ASCIIToUTF16("firstname");
     95   expected.value = base::string16();
     96   expected.form_control_type = "text";
     97   expected.max_length = WebInputElement::defaultMaxLength();
     98   EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[0]);
     99 
    100   expected.name = ASCIIToUTF16("middlename");
    101   expected.value = base::string16();
    102   expected.form_control_type = "text";
    103   expected.max_length = WebInputElement::defaultMaxLength();
    104   EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[1]);
    105 
    106   expected.name = ASCIIToUTF16("lastname");
    107   expected.value = base::string16();
    108   expected.form_control_type = "text";
    109   expected.autocomplete_attribute = "off";
    110   expected.max_length = WebInputElement::defaultMaxLength();
    111   EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[2]);
    112   expected.autocomplete_attribute = std::string();  // reset
    113 
    114   expected.name = ASCIIToUTF16("state");
    115   expected.value = ASCIIToUTF16("?");
    116   expected.form_control_type = "select-one";
    117   expected.max_length = 0;
    118   EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[3]);
    119 
    120   render_thread_->sink().ClearMessages();
    121 
    122   // Dynamically create a new form. A new message should be sent for it, but
    123   // not for the previous form.
    124   ExecuteJavaScript(
    125       "var newForm=document.createElement('form');"
    126       "newForm.id='new_testform';"
    127       "newForm.action='http://google.com';"
    128       "newForm.method='post';"
    129       "var newFirstname=document.createElement('input');"
    130       "newFirstname.setAttribute('type', 'text');"
    131       "newFirstname.setAttribute('id', 'second_firstname');"
    132       "newFirstname.value = 'Bob';"
    133       "var newLastname=document.createElement('input');"
    134       "newLastname.setAttribute('type', 'text');"
    135       "newLastname.setAttribute('id', 'second_lastname');"
    136       "newLastname.value = 'Hope';"
    137       "var newEmail=document.createElement('input');"
    138       "newEmail.setAttribute('type', 'text');"
    139       "newEmail.setAttribute('id', 'second_email');"
    140       "newEmail.value = 'bobhope (at) example.com';"
    141       "newForm.appendChild(newFirstname);"
    142       "newForm.appendChild(newLastname);"
    143       "newForm.appendChild(newEmail);"
    144       "document.body.appendChild(newForm);");
    145   msg_loop_.RunUntilIdle();
    146 
    147   message = render_thread_->sink().GetFirstMessageMatching(
    148       AutofillHostMsg_FormsSeen::ID);
    149   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    150   AutofillHostMsg_FormsSeen::Read(message, &params);
    151   forms = params.a;
    152   ASSERT_EQ(1UL, forms.size());
    153   ASSERT_EQ(3UL, forms[0].fields.size());
    154 
    155   expected.form_control_type = "text";
    156   expected.max_length = WebInputElement::defaultMaxLength();
    157 
    158   expected.name = ASCIIToUTF16("second_firstname");
    159   expected.value = ASCIIToUTF16("Bob");
    160   EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[0]);
    161 
    162   expected.name = ASCIIToUTF16("second_lastname");
    163   expected.value = ASCIIToUTF16("Hope");
    164   EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[1]);
    165 
    166   expected.name = ASCIIToUTF16("second_email");
    167   expected.value = ASCIIToUTF16("bobhope (at) example.com");
    168   EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[2]);
    169 }
    170 
    171 TEST_F(AutofillRendererTest, EnsureNoFormSeenIfTooFewFields) {
    172   LoadHTML("<form method='POST'>"
    173            "  <input type='text' id='firstname'/>"
    174            "  <input type='text' id='middlename'/>"
    175            "</form>");
    176 
    177   // Verify that "FormsSeen" isn't sent, as there are too few fields.
    178   const IPC::Message* message = render_thread_->sink().GetFirstMessageMatching(
    179       AutofillHostMsg_FormsSeen::ID);
    180   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    181   AutofillHostMsg_FormsSeen::Param params;
    182   AutofillHostMsg_FormsSeen::Read(message, &params);
    183   const std::vector<FormData>& forms = params.a;
    184   ASSERT_EQ(0UL, forms.size());
    185 }
    186 
    187 TEST_F(AutofillRendererTest, ShowAutofillWarning) {
    188   LoadHTML("<form method='POST' autocomplete='Off'>"
    189            "  <input id='firstname' autocomplete='OFF'/>"
    190            "  <input id='middlename'/>"
    191            "  <input id='lastname'/>"
    192            "</form>");
    193 
    194   // Verify that "QueryFormFieldAutofill" isn't sent prior to a user
    195   // interaction.
    196   const IPC::Message* message0 = render_thread_->sink().GetFirstMessageMatching(
    197       AutofillHostMsg_QueryFormFieldAutofill::ID);
    198   EXPECT_EQ(static_cast<IPC::Message*>(NULL), message0);
    199 
    200   WebFrame* web_frame = GetMainFrame();
    201   WebDocument document = web_frame->document();
    202   WebInputElement firstname =
    203       document.getElementById("firstname").to<WebInputElement>();
    204   WebInputElement middlename =
    205       document.getElementById("middlename").to<WebInputElement>();
    206 
    207   // Simulate attempting to Autofill the form from the first element, which
    208   // specifies autocomplete="off".  This should still trigger an IPC which
    209   // shouldn't display warnings.
    210   static_cast<PageClickListener*>(autofill_agent_)
    211       ->FormControlElementClicked(firstname, true);
    212   const IPC::Message* message1 = render_thread_->sink().GetFirstMessageMatching(
    213       AutofillHostMsg_QueryFormFieldAutofill::ID);
    214   EXPECT_NE(static_cast<IPC::Message*>(NULL), message1);
    215 
    216   AutofillQueryParam query_param;
    217   AutofillHostMsg_QueryFormFieldAutofill::Read(message1, &query_param);
    218   EXPECT_FALSE(query_param.e);
    219   render_thread_->sink().ClearMessages();
    220 
    221   // Simulate attempting to Autofill the form from the second element, which
    222   // does not specify autocomplete="off".  This should trigger an IPC that will
    223   // show warnings, as we *do* show warnings for elements that don't themselves
    224   // set autocomplete="off", but for which the form does.
    225   static_cast<PageClickListener*>(autofill_agent_)
    226       ->FormControlElementClicked(middlename, true);
    227   const IPC::Message* message2 = render_thread_->sink().GetFirstMessageMatching(
    228       AutofillHostMsg_QueryFormFieldAutofill::ID);
    229   ASSERT_NE(static_cast<IPC::Message*>(NULL), message2);
    230 
    231   AutofillHostMsg_QueryFormFieldAutofill::Read(message2, &query_param);
    232   EXPECT_TRUE(query_param.e);
    233 }
    234 
    235 // Regression test for [ http://crbug.com/346010 ].
    236 TEST_F(AutofillRendererTest, DontCrashWhileAssociatingForms) {
    237   LoadHTML("<form id='form'>"
    238            "<foo id='foo'>"
    239            "<script id='script'>"
    240            "document.documentElement.appendChild(foo);"
    241            "newDoc = document.implementation.createDocument("
    242            "    'http://www.w3.org/1999/xhtml', 'html');"
    243            "foo.insertBefore(form, script);"
    244            "newDoc.adoptNode(foo);"
    245            "</script>");
    246 
    247   // Shouldn't crash.
    248 }
    249 
    250 class RequestAutocompleteRendererTest : public AutofillRendererTest {
    251  public:
    252   RequestAutocompleteRendererTest()
    253       : invoking_frame_(NULL), sibling_frame_(NULL) {}
    254   virtual ~RequestAutocompleteRendererTest() {}
    255 
    256  protected:
    257   virtual void SetUp() OVERRIDE {
    258     AutofillRendererTest::SetUp();
    259 
    260     // Bypass the HTTPS-only restriction to show requestAutocomplete.
    261     CommandLine* command_line = CommandLine::ForCurrentProcess();
    262     command_line->AppendSwitch(::switches::kReduceSecurityForTesting);
    263 
    264     GURL url("data:text/html;charset=utf-8,"
    265              "<form><input autocomplete=cc-number></form>");
    266     const char kDoubleIframeHtml[] = "<iframe id=subframe src='%s'></iframe>"
    267                                      "<iframe id=sibling></iframe>";
    268     LoadHTML(base::StringPrintf(kDoubleIframeHtml, url.spec().c_str()).c_str());
    269 
    270     WebElement subframe = GetMainFrame()->document().getElementById("subframe");
    271     ASSERT_FALSE(subframe.isNull());
    272     invoking_frame_ = WebLocalFrame::fromFrameOwnerElement(subframe);
    273     ASSERT_TRUE(invoking_frame());
    274     ASSERT_EQ(GetMainFrame(), invoking_frame()->parent());
    275 
    276     WebElement sibling = GetMainFrame()->document().getElementById("sibling");
    277     ASSERT_FALSE(sibling.isNull());
    278     sibling_frame_ = WebLocalFrame::fromFrameOwnerElement(sibling);
    279     ASSERT_TRUE(sibling_frame());
    280 
    281     WebVector<WebFormElement> forms;
    282     invoking_frame()->document().forms(forms);
    283     ASSERT_EQ(1U, forms.size());
    284     invoking_form_ = forms[0];
    285     ASSERT_FALSE(invoking_form().isNull());
    286 
    287     render_thread_->sink().ClearMessages();
    288 
    289     // Invoke requestAutocomplete to show the dialog.
    290     static_cast<blink::WebAutofillClient*>(autofill_agent_)
    291         ->didRequestAutocomplete(invoking_form());
    292     ASSERT_TRUE(render_thread_->sink().GetFirstMessageMatching(
    293         AutofillHostMsg_RequestAutocomplete::ID));
    294 
    295     render_thread_->sink().ClearMessages();
    296   }
    297 
    298   virtual void TearDown() OVERRIDE {
    299     invoking_form_.reset();
    300     AutofillRendererTest::TearDown();
    301   }
    302 
    303   void NavigateFrame(WebFrame* frame) {
    304     frame->loadRequest(WebURLRequest(GURL("about:blank")));
    305     ProcessPendingMessages();
    306   }
    307 
    308   const WebFormElement& invoking_form() const { return invoking_form_; }
    309   WebLocalFrame* invoking_frame() { return invoking_frame_; }
    310   WebFrame* sibling_frame() { return sibling_frame_; }
    311 
    312  private:
    313   WebFormElement invoking_form_;
    314   WebLocalFrame* invoking_frame_;
    315   WebFrame* sibling_frame_;
    316 
    317   DISALLOW_COPY_AND_ASSIGN(RequestAutocompleteRendererTest);
    318 };
    319 
    320 TEST_F(RequestAutocompleteRendererTest, SiblingNavigateIgnored) {
    321   // Pretend that a sibling frame navigated. No cancel should be sent.
    322   NavigateFrame(sibling_frame());
    323   EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
    324       AutofillHostMsg_CancelRequestAutocomplete::ID));
    325 }
    326 
    327 TEST_F(RequestAutocompleteRendererTest, SubframeNavigateCancels) {
    328   // Pretend that the invoking frame navigated. A cancel should be sent.
    329   NavigateFrame(invoking_frame());
    330   EXPECT_TRUE(render_thread_->sink().GetFirstMessageMatching(
    331       AutofillHostMsg_CancelRequestAutocomplete::ID));
    332 }
    333 
    334 TEST_F(RequestAutocompleteRendererTest, MainFrameNavigateCancels) {
    335   // Pretend that the top-level frame navigated. A cancel should be sent.
    336   NavigateFrame(GetMainFrame());
    337   EXPECT_TRUE(render_thread_->sink().GetFirstMessageMatching(
    338       AutofillHostMsg_CancelRequestAutocomplete::ID));
    339 }
    340 
    341 TEST_F(RequestAutocompleteRendererTest, NoCancelOnSubframeNavigateAfterDone) {
    342   // Pretend that the dialog was cancelled.
    343   SimulateRequestAutocompleteResult(
    344       WebFormElement::AutocompleteResultErrorCancel,
    345       base::ASCIIToUTF16("Print me to the console"));
    346 
    347   // Additional navigations should not crash nor send cancels.
    348   NavigateFrame(invoking_frame());
    349   EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
    350       AutofillHostMsg_CancelRequestAutocomplete::ID));
    351 }
    352 
    353 TEST_F(RequestAutocompleteRendererTest, NoCancelOnMainFrameNavigateAfterDone) {
    354   // Pretend that the dialog was cancelled.
    355   SimulateRequestAutocompleteResult(
    356       WebFormElement::AutocompleteResultErrorCancel,
    357       base::ASCIIToUTF16("Print me to the console"));
    358 
    359   // Additional navigations should not crash nor send cancels.
    360   NavigateFrame(GetMainFrame());
    361   EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
    362       AutofillHostMsg_CancelRequestAutocomplete::ID));
    363 }
    364 
    365 TEST_F(RequestAutocompleteRendererTest, InvokingTwiceOnlyShowsOnce) {
    366   // Attempting to show the requestAutocomplete dialog again should be ignored.
    367   static_cast<blink::WebAutofillClient*>(autofill_agent_)
    368       ->didRequestAutocomplete(invoking_form());
    369   EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
    370       AutofillHostMsg_RequestAutocomplete::ID));
    371 }
    372 
    373 }  // namespace autofill
    374