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  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
69TEST_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@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@example.com");
168  EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[2]);
169}
170
171TEST_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
187TEST_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 ].
236TEST_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
250class 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
320TEST_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
327TEST_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
334TEST_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
341TEST_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
353TEST_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
365TEST_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