1 // Copyright (c) 2011 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.
5 #include "base/strings/utf_string_conversions.h"
6 #include "base/time/time.h"
7 #include "chrome/test/base/chrome_render_view_test.h"
8 #include "components/autofill/content/common/autofill_messages.h"
9 #include "components/autofill/content/renderer/autofill_agent.h"
10 #include "components/autofill/core/common/form_data.h"
11 #include "content/public/test/mock_render_thread.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/WebKit/public/web/WebDocument.h"
14 #include "third_party/WebKit/public/web/WebElement.h"
15 #include "third_party/WebKit/public/web/WebFormElement.h"
16 #include "third_party/WebKit/public/web/WebInputElement.h"
17 #include "third_party/WebKit/public/web/WebLocalFrame.h"
19 using blink::WebDocument
;
20 using blink::WebElement
;
21 using blink::WebInputElement
;
22 using blink::WebString
;
24 typedef ChromeRenderViewTest FormAutocompleteTest
;
30 // Helper function to verify the form-related messages received from the
31 // renderer. The same data is expected in both messages. Depending on
32 // |expect_submitted_message|, will verify presence of FormSubmitted message.
33 void VerifyReceivedRendererMessages(content::MockRenderThread
* render_thread
,
34 const std::string
& fname
,
35 const std::string
& lname
,
36 bool expect_submitted_message
) {
37 const IPC::Message
* will_submit_message
=
38 render_thread
->sink().GetFirstMessageMatching(
39 AutofillHostMsg_WillSubmitForm::ID
);
40 const IPC::Message
* submitted_message
=
41 render_thread
->sink().GetFirstMessageMatching(
42 AutofillHostMsg_FormSubmitted::ID
);
43 ASSERT_TRUE(will_submit_message
!= NULL
);
44 ASSERT_EQ(expect_submitted_message
, submitted_message
!= NULL
);
46 // The tuple also includes a timestamp, which is ignored.
47 base::Tuple
<FormData
, base::TimeTicks
> will_submit_forms
;
48 AutofillHostMsg_WillSubmitForm::Read(will_submit_message
, &will_submit_forms
);
49 ASSERT_EQ(2U, base::get
<0>(will_submit_forms
).fields
.size());
51 FormFieldData
& will_submit_form_field
=
52 base::get
<0>(will_submit_forms
).fields
[0];
53 EXPECT_EQ(WebString("fname"), will_submit_form_field
.name
);
54 EXPECT_EQ(WebString(base::UTF8ToUTF16(fname
)), will_submit_form_field
.value
);
55 will_submit_form_field
= base::get
<0>(will_submit_forms
).fields
[1];
56 EXPECT_EQ(WebString("lname"), will_submit_form_field
.name
);
57 EXPECT_EQ(WebString(base::UTF8ToUTF16(lname
)), will_submit_form_field
.value
);
59 if (expect_submitted_message
) {
60 base::Tuple
<FormData
> submitted_forms
;
61 AutofillHostMsg_FormSubmitted::Read(submitted_message
, &submitted_forms
);
62 ASSERT_EQ(2U, base::get
<0>(submitted_forms
).fields
.size());
64 FormFieldData
& submitted_field
= base::get
<0>(submitted_forms
).fields
[0];
65 EXPECT_EQ(WebString("fname"), submitted_field
.name
);
66 EXPECT_EQ(WebString(base::UTF8ToUTF16(fname
)), submitted_field
.value
);
67 submitted_field
= base::get
<0>(submitted_forms
).fields
[1];
68 EXPECT_EQ(WebString("lname"), submitted_field
.name
);
69 EXPECT_EQ(WebString(base::UTF8ToUTF16(lname
)), submitted_field
.value
);
73 // Helper function to verify that NO form-related messages are received from the
75 void VerifyNoSubmitMessagesReceived(content::MockRenderThread
* render_thread
) {
76 // No submission messages sent.
77 const IPC::Message
* will_submit_message
=
78 render_thread
->sink().GetFirstMessageMatching(
79 AutofillHostMsg_WillSubmitForm::ID
);
80 const IPC::Message
* submitted_message
=
81 render_thread
->sink().GetFirstMessageMatching(
82 AutofillHostMsg_FormSubmitted::ID
);
83 EXPECT_EQ(NULL
, will_submit_message
);
84 EXPECT_EQ(NULL
, submitted_message
);
87 // Simulates receiving a message from the browser to fill a form.
88 void SimulateOnFillForm(content::MockRenderThread
* render_thread
,
89 autofill::AutofillAgent
* autofill_agent
,
90 blink::WebFrame
* main_frame
) {
91 WebDocument document
= main_frame
->document();
93 document
.getElementById(WebString::fromUTF8("fname"));
94 ASSERT_FALSE(element
.isNull());
96 // This call is necessary to setup the autofill agent appropriate for the
97 // user selection; simulates the menu actually popping up.
98 render_thread
->sink().ClearMessages();
99 static_cast<autofill::PageClickListener
*>(autofill_agent
)
100 ->FormControlElementClicked(element
.to
<WebInputElement
>(), false);
103 data
.name
= base::ASCIIToUTF16("name");
104 data
.origin
= GURL("origin");
105 data
.action
= GURL("blade.php");
106 data
.is_form_tag
= true; // Default value.
108 FormFieldData field_data
;
109 field_data
.name
= base::ASCIIToUTF16("fname");
110 field_data
.value
= base::ASCIIToUTF16("John");
111 field_data
.is_autofilled
= true;
112 data
.fields
.push_back(field_data
);
114 field_data
.name
= base::ASCIIToUTF16("lname");
115 field_data
.value
= base::ASCIIToUTF16("Smith");
116 field_data
.is_autofilled
= true;
117 data
.fields
.push_back(field_data
);
119 AutofillMsg_FillForm
msg(0, 0, data
);
120 static_cast<content::RenderFrameObserver
*>(autofill_agent
)
121 ->OnMessageReceived(msg
);
126 // Tests that submitting a form generates WillSubmitForm and FormSubmitted
127 // messages with the form fields.
128 TEST_F(FormAutocompleteTest
, NormalFormSubmit
) {
130 LoadHTML("<html><form id='myForm'><input name='fname' value='Rick'/>"
131 "<input name='lname' value='Deckard'/></form></html>");
134 ExecuteJavaScriptForTests("document.getElementById('myForm').submit();");
135 ProcessPendingMessages();
137 VerifyReceivedRendererMessages(render_thread_
.get(), "Rick", "Deckard",
138 true /* expect_submitted_message */);
141 // Tests that submitting a form that prevents the submit event from propagating
142 // will only send the WillSubmitForm message.
143 TEST_F(FormAutocompleteTest
, SubmitEventPrevented
) {
146 "<html><form id='myForm'><input name='fname' value='Rick'/>"
147 "<input name='lname' value='Deckard'/><input type=submit></form>"
151 ExecuteJavaScriptForTests(
152 "var form = document.forms[0];"
153 "form.onsubmit = function(event) { event.preventDefault(); };"
154 "document.querySelector('input[type=submit]').click();");
155 ProcessPendingMessages();
157 VerifyReceivedRendererMessages(render_thread_
.get(), "Rick", "Deckard",
158 false /* expect_submitted_message */);
161 // Tests that completing an Ajax request and having the form disappear will
162 // trigger submission from Autofill's point of view.
163 TEST_F(FormAutocompleteTest
, AjaxSucceeded_NoLongerVisible
) {
166 "<html><form id='myForm' action='blade.php'>"
167 "<input name='fname' id='fname' value='Bob'/>"
168 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
170 // Simulate user input so that the form is "remembered".
171 WebDocument document
= GetMainFrame()->document();
173 document
.getElementById(WebString::fromUTF8("fname"));
174 ASSERT_FALSE(element
.isNull());
175 WebInputElement fname_element
= element
.to
<WebInputElement
>();
176 SimulateUserInputChangeForElement(&fname_element
, std::string("Rick"));
178 // Simulate removing the form just before the ajax request completes.
179 ExecuteJavaScriptForTests("var element = document.getElementById('myForm');"
180 "element.parentNode.removeChild(element);");
182 // Simulate an Ajax request completing.
183 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->ajaxSucceeded();
184 ProcessPendingMessages();
186 VerifyReceivedRendererMessages(render_thread_
.get(), "Rick", "Deckard",
187 true /* expect_submitted_message */);
190 // Tests that completing an Ajax request but leaving a form visible will not
191 // trigger submission from Autofill's point of view.
192 TEST_F(FormAutocompleteTest
, AjaxSucceeded_StillVisible
) {
195 "<html><form id='myForm' action='blade.php'>"
196 "<input name='fname' id='fname' value='Bob'/>"
197 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
199 // Simulate user input so that the form is "remembered".
200 WebDocument document
= GetMainFrame()->document();
202 document
.getElementById(WebString::fromUTF8("fname"));
203 ASSERT_FALSE(element
.isNull());
204 WebInputElement fname_element
= element
.to
<WebInputElement
>();
205 SimulateUserInputChangeForElement(&fname_element
, std::string("Rick"));
207 // Simulate an Ajax request completing.
208 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->ajaxSucceeded();
209 ProcessPendingMessages();
211 // No submission messages sent.
212 VerifyNoSubmitMessagesReceived(render_thread_
.get());
215 // Tests that completing an Ajax request without any prior form interaction
216 // does not trigger form submission from Autofill's point of view.
217 TEST_F(FormAutocompleteTest
, AjaxSucceeded_NoFormInteractionInvisible
) {
220 "<html><form id='myForm' action='blade.php'>"
221 "<input name='fname' id='fname' value='Bob'/>"
222 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
224 // No form interaction.
226 // Simulate removing the form just before the ajax request completes.
227 ExecuteJavaScriptForTests("var element = document.getElementById('myForm');"
228 "element.parentNode.removeChild(element);");
230 // Simulate an Ajax request completing without prior user interaction.
231 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->ajaxSucceeded();
232 ProcessPendingMessages();
234 // No submission messages sent.
235 VerifyNoSubmitMessagesReceived(render_thread_
.get());
238 // Tests that completing an Ajax request after having autofilled a form,
239 // with the form disappearing, will trigger submission from Autofill's
241 TEST_F(FormAutocompleteTest
, AjaxSucceeded_FilledFormIsInvisible
) {
244 "<html><form id='myForm' action='blade.php'>"
245 "<input name='fname' id='fname'/>"
246 "<input name='lname'/></form></html>");
248 // Simulate filling a form using Autofill.
249 SimulateOnFillForm(render_thread_
.get(), autofill_agent_
, GetMainFrame());
251 // Simulate removing the form just before the ajax request completes.
252 ExecuteJavaScriptForTests("var element = document.getElementById('myForm');"
253 "element.parentNode.removeChild(element);");
255 // Simulate an Ajax request completing.
256 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->ajaxSucceeded();
257 ProcessPendingMessages();
259 VerifyReceivedRendererMessages(render_thread_
.get(), "John", "Smith",
260 true /* expect_submitted_message */);
263 // Tests that completing an Ajax request after having autofilled a form,
264 // without the form disappearing, will not trigger submission from Autofill's
266 TEST_F(FormAutocompleteTest
, AjaxSucceeded_FilledFormStillVisible
) {
269 "<html><form id='myForm' action='blade.php'>"
270 "<input name='fname' id='fname' value='Rick'/>"
271 "<input name='lname' value='Deckard'/></form></html>");
273 // Simulate filling a form using Autofill.
274 SimulateOnFillForm(render_thread_
.get(), autofill_agent_
, GetMainFrame());
276 // Form still visible.
278 // Simulate an Ajax request completing.
279 static_cast<blink::WebAutofillClient
*>(autofill_agent_
)->ajaxSucceeded();
280 ProcessPendingMessages();
282 // No submission messages sent.
283 VerifyNoSubmitMessagesReceived(render_thread_
.get());
286 // Tests that submitting a form that has autocomplete="off" generates
287 // WillSubmitForm and FormSubmitted messages.
288 TEST_F(FormAutocompleteTest
, AutoCompleteOffFormSubmit
) {
290 LoadHTML("<html><form id='myForm' autocomplete='off'>"
291 "<input name='fname' value='Rick'/>"
292 "<input name='lname' value='Deckard'/>"
296 ExecuteJavaScriptForTests("document.getElementById('myForm').submit();");
297 ProcessPendingMessages();
299 VerifyReceivedRendererMessages(render_thread_
.get(), "Rick", "Deckard",
300 true /* expect_submitted_message */);
303 // Tests that fields with autocomplete off are submitted.
304 TEST_F(FormAutocompleteTest
, AutoCompleteOffInputSubmit
) {
306 LoadHTML("<html><form id='myForm'>"
307 "<input name='fname' value='Rick'/>"
308 "<input name='lname' value='Deckard' autocomplete='off'/>"
312 ExecuteJavaScriptForTests("document.getElementById('myForm').submit();");
313 ProcessPendingMessages();
315 VerifyReceivedRendererMessages(render_thread_
.get(), "Rick", "Deckard",
316 true /* expect_submitted_message */);
319 // Tests that submitting a form that has been dynamically set as autocomplete
320 // off generates WillSubmitForm and FormSubmitted messages.
321 // Note: We previously did the opposite, for bug http://crbug.com/36520
322 TEST_F(FormAutocompleteTest
, DynamicAutoCompleteOffFormSubmit
) {
323 LoadHTML("<html><form id='myForm'><input name='fname' value='Rick'/>"
324 "<input name='lname' value='Deckard'/></form></html>");
327 GetMainFrame()->document().getElementById(blink::WebString("myForm"));
328 ASSERT_FALSE(element
.isNull());
329 blink::WebFormElement form
= element
.to
<blink::WebFormElement
>();
330 EXPECT_TRUE(form
.autoComplete());
332 // Dynamically mark the form as autocomplete off.
333 ExecuteJavaScriptForTests(
334 "document.getElementById('myForm')."
335 "setAttribute('autocomplete', 'off');");
336 ProcessPendingMessages();
337 EXPECT_FALSE(form
.autoComplete());
340 ExecuteJavaScriptForTests("document.getElementById('myForm').submit();");
341 ProcessPendingMessages();
343 VerifyReceivedRendererMessages(render_thread_
.get(), "Rick", "Deckard",
344 true /* expect_submitted_message */);
347 } // namespace autofill