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:
AutofillRendererTest()44 AutofillRendererTest() {}
~AutofillRendererTest()45 virtual ~AutofillRendererTest() {}
46
47 protected:
SetUp()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
TEST_F(AutofillRendererTest,SendForms)61 TEST_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, ¶ms);
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, ¶ms);
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
TEST_F(AutofillRendererTest,EnsureNoFormSeenIfTooFewFields)163 TEST_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, ¶ms);
175 const std::vector<FormData>& forms = params.a;
176 ASSERT_EQ(0UL, forms.size());
177 }
178
TEST_F(AutofillRendererTest,ShowAutofillWarning)179 TEST_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 ].
TEST_F(AutofillRendererTest,DontCrashWhileAssociatingForms)226 TEST_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
240 class RequestAutocompleteRendererTest : public AutofillRendererTest {
241 public:
RequestAutocompleteRendererTest()242 RequestAutocompleteRendererTest()
243 : invoking_frame_(NULL), sibling_frame_(NULL) {}
~RequestAutocompleteRendererTest()244 virtual ~RequestAutocompleteRendererTest() {}
245
246 protected:
SetUp()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
TearDown()287 virtual void TearDown() OVERRIDE {
288 invoking_form_.reset();
289 AutofillRendererTest::TearDown();
290 }
291
NavigateFrame(WebFrame * frame)292 void NavigateFrame(WebFrame* frame) {
293 frame->loadRequest(WebURLRequest(GURL("about:blank")));
294 ProcessPendingMessages();
295 }
296
invoking_form() const297 const WebFormElement& invoking_form() const { return invoking_form_; }
invoking_frame()298 WebLocalFrame* invoking_frame() { return invoking_frame_; }
sibling_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
TEST_F(RequestAutocompleteRendererTest,SiblingNavigateIgnored)309 TEST_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
TEST_F(RequestAutocompleteRendererTest,SubframeNavigateCancels)316 TEST_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
TEST_F(RequestAutocompleteRendererTest,MainFrameNavigateCancels)323 TEST_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
TEST_F(RequestAutocompleteRendererTest,NoCancelOnSubframeNavigateAfterDone)330 TEST_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
TEST_F(RequestAutocompleteRendererTest,NoCancelOnMainFrameNavigateAfterDone)343 TEST_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
TEST_F(RequestAutocompleteRendererTest,InvokingTwiceOnlyShowsOnce)356 TEST_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