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