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.
4
5 #include <tuple>
6
7 #include "base/bind.h"
8 #include "base/run_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/time/time.h"
11 #include "build/build_config.h"
12 #include "chrome/test/base/chrome_render_view_test.h"
13 #include "components/autofill/content/renderer/autofill_agent.h"
14 #include "components/autofill/content/renderer/focus_test_utils.h"
15 #include "components/autofill/core/common/form_data.h"
16 #include "content/public/renderer/render_frame.h"
17 #include "content/public/renderer/render_view.h"
18 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
19 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
22 #include "third_party/blink/public/web/web_document.h"
23 #include "third_party/blink/public/web/web_element.h"
24 #include "third_party/blink/public/web/web_form_element.h"
25 #include "third_party/blink/public/web/web_input_element.h"
26 #include "third_party/blink/public/web/web_local_frame.h"
27
28 using blink::WebDocument;
29 using blink::WebElement;
30 using blink::WebInputElement;
31 using blink::WebString;
32
33 namespace autofill {
34
35 using mojom::SubmissionSource;
36
37 namespace {
38
39 class FakeContentAutofillDriver : public mojom::AutofillDriver {
40 public:
FakeContentAutofillDriver()41 FakeContentAutofillDriver() : did_unfocus_form_(false) {}
42
~FakeContentAutofillDriver()43 ~FakeContentAutofillDriver() override {}
44
BindReceiver(mojo::PendingAssociatedReceiver<mojom::AutofillDriver> receiver)45 void BindReceiver(
46 mojo::PendingAssociatedReceiver<mojom::AutofillDriver> receiver) {
47 receivers_.Add(this, std::move(receiver));
48 }
49
did_unfocus_form() const50 bool did_unfocus_form() const { return did_unfocus_form_; }
51
form_submitted() const52 const FormData* form_submitted() const { return form_submitted_.get(); }
53
known_success() const54 bool known_success() const { return known_success_; }
55
submission_source() const56 SubmissionSource submission_source() const { return submission_source_; }
57
select_control_changed() const58 const FormFieldData* select_control_changed() const {
59 return select_control_changed_.get();
60 }
61
62 private:
63 // mojom::AutofillDriver:
FormsSeen(const std::vector<FormData> & forms,base::TimeTicks timestamp)64 void FormsSeen(const std::vector<FormData>& forms,
65 base::TimeTicks timestamp) override {}
66
FormSubmitted(const FormData & form,bool known_success,SubmissionSource source)67 void FormSubmitted(const FormData& form,
68 bool known_success,
69 SubmissionSource source) override {
70 form_submitted_.reset(new FormData(form));
71 known_success_ = known_success;
72 submission_source_ = source;
73 }
74
TextFieldDidChange(const FormData & form,const FormFieldData & field,const gfx::RectF & bounding_box,base::TimeTicks timestamp)75 void TextFieldDidChange(const FormData& form,
76 const FormFieldData& field,
77 const gfx::RectF& bounding_box,
78 base::TimeTicks timestamp) override {}
79
TextFieldDidScroll(const FormData & form,const FormFieldData & field,const gfx::RectF & bounding_box)80 void TextFieldDidScroll(const FormData& form,
81 const FormFieldData& field,
82 const gfx::RectF& bounding_box) override {}
83
SelectControlDidChange(const FormData & form,const FormFieldData & field,const gfx::RectF & bounding_box)84 void SelectControlDidChange(const FormData& form,
85 const FormFieldData& field,
86 const gfx::RectF& bounding_box) override {
87 select_control_changed_ = std::make_unique<FormFieldData>(field);
88 }
89
QueryFormFieldAutofill(int32_t id,const FormData & form,const FormFieldData & field,const gfx::RectF & bounding_box,bool autoselect_first_field)90 void QueryFormFieldAutofill(int32_t id,
91 const FormData& form,
92 const FormFieldData& field,
93 const gfx::RectF& bounding_box,
94 bool autoselect_first_field) override {}
95
HidePopup()96 void HidePopup() override {}
97
FocusNoLongerOnForm()98 void FocusNoLongerOnForm() override { did_unfocus_form_ = true; }
99
FocusOnFormField(const FormData & form,const FormFieldData & field,const gfx::RectF & bounding_box)100 void FocusOnFormField(const FormData& form,
101 const FormFieldData& field,
102 const gfx::RectF& bounding_box) override {}
103
DidFillAutofillFormData(const FormData & form,base::TimeTicks timestamp)104 void DidFillAutofillFormData(const FormData& form,
105 base::TimeTicks timestamp) override {}
106
DidPreviewAutofillFormData()107 void DidPreviewAutofillFormData() override {}
108
DidEndTextFieldEditing()109 void DidEndTextFieldEditing() override {}
110
SetDataList(const std::vector<base::string16> & values,const std::vector<base::string16> & labels)111 void SetDataList(const std::vector<base::string16>& values,
112 const std::vector<base::string16>& labels) override {}
113
SelectFieldOptionsDidChange(const autofill::FormData & form)114 void SelectFieldOptionsDidChange(const autofill::FormData& form) override {}
115
116 // Records whether FocusNoLongerOnForm() get called.
117 bool did_unfocus_form_;
118
119 // Records the form data received via FormSubmitted() call.
120 std::unique_ptr<FormData> form_submitted_;
121
122 bool known_success_;
123
124 SubmissionSource submission_source_;
125
126 std::unique_ptr<FormFieldData> select_control_changed_;
127
128 mojo::AssociatedReceiverSet<mojom::AutofillDriver> receivers_;
129 };
130
131 // Helper function to verify the form-related messages received from the
132 // renderer. The same data is expected in both messages. Depending on
133 // |expect_submitted_message|, will verify presence of FormSubmitted message.
VerifyReceivedRendererMessages(const FakeContentAutofillDriver & fake_driver,const std::string & fname,const std::string & lname,bool expect_known_success,SubmissionSource expect_submission_source)134 void VerifyReceivedRendererMessages(
135 const FakeContentAutofillDriver& fake_driver,
136 const std::string& fname,
137 const std::string& lname,
138 bool expect_known_success,
139 SubmissionSource expect_submission_source) {
140 ASSERT_TRUE(fake_driver.form_submitted());
141
142 // The tuple also includes a timestamp, which is ignored.
143 const FormData& submitted_form = *(fake_driver.form_submitted());
144 ASSERT_LE(2U, submitted_form.fields.size());
145 EXPECT_EQ(base::ASCIIToUTF16("fname"), submitted_form.fields[0].name);
146 EXPECT_EQ(base::UTF8ToUTF16(fname), submitted_form.fields[0].value);
147 EXPECT_EQ(base::ASCIIToUTF16("lname"), submitted_form.fields[1].name);
148 EXPECT_EQ(expect_known_success, fake_driver.known_success());
149 EXPECT_EQ(expect_submission_source,
150 mojo::ConvertTo<SubmissionSource>(fake_driver.submission_source()));
151 }
152
VerifyReceivedAddressRendererMessages(const FakeContentAutofillDriver & fake_driver,const std::string & address,bool expect_known_success,SubmissionSource expect_submission_source)153 void VerifyReceivedAddressRendererMessages(
154 const FakeContentAutofillDriver& fake_driver,
155 const std::string& address,
156 bool expect_known_success,
157 SubmissionSource expect_submission_source) {
158 ASSERT_TRUE(fake_driver.form_submitted());
159
160 // The tuple also includes a timestamp, which is ignored.
161 const FormData& submitted_form = *(fake_driver.form_submitted());
162 ASSERT_LE(1U, submitted_form.fields.size());
163 EXPECT_EQ(base::ASCIIToUTF16("address"), submitted_form.fields[0].name);
164 EXPECT_EQ(base::UTF8ToUTF16(address), submitted_form.fields[0].value);
165 EXPECT_EQ(expect_known_success, fake_driver.known_success());
166 EXPECT_EQ(expect_submission_source,
167 mojo::ConvertTo<SubmissionSource>(fake_driver.submission_source()));
168 }
169
170 // Helper function to verify that NO form-related messages are received from the
171 // renderer.
VerifyNoSubmitMessagesReceived(const FakeContentAutofillDriver & fake_driver)172 void VerifyNoSubmitMessagesReceived(
173 const FakeContentAutofillDriver& fake_driver) {
174 // No submission messages sent.
175 EXPECT_EQ(nullptr, fake_driver.form_submitted());
176 }
177
178 // Simulates receiving a message from the browser to fill a form.
SimulateOnFillForm(autofill::AutofillAgent * autofill_agent,blink::WebLocalFrame * main_frame)179 void SimulateOnFillForm(autofill::AutofillAgent* autofill_agent,
180 blink::WebLocalFrame* main_frame) {
181 FormData data;
182 data.name = base::ASCIIToUTF16("name");
183 data.url = GURL("http://example.com/");
184 data.action = GURL("http://example.com/blade.php");
185 data.is_form_tag = true; // Default value.
186
187 FormFieldData field_data;
188 field_data.name = base::ASCIIToUTF16("fname");
189 field_data.value = base::ASCIIToUTF16("John");
190 field_data.is_autofilled = true;
191 data.fields.push_back(field_data);
192
193 field_data.name = base::ASCIIToUTF16("lname");
194 field_data.value = base::ASCIIToUTF16("Smith");
195 field_data.is_autofilled = true;
196 data.fields.push_back(field_data);
197
198 WebDocument document = main_frame->GetDocument();
199 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
200 ASSERT_FALSE(element.IsNull());
201
202 // This call is necessary to setup the autofill agent appropriate for the
203 // user selection; simulates the menu actually popping up.
204 autofill_agent->FormControlElementClicked(element.To<WebInputElement>(),
205 false);
206
207 autofill_agent->FillForm(0, data);
208 }
209
210 // Simulates receiving a message from the browser to fill a form with an
211 // additional non-autofillable field.
SimulateOnFillFormWithNonFillableFields(autofill::AutofillAgent * autofill_agent,blink::WebLocalFrame * main_frame)212 void SimulateOnFillFormWithNonFillableFields(
213 autofill::AutofillAgent* autofill_agent,
214 blink::WebLocalFrame* main_frame) {
215 FormData data;
216 data.name = base::ASCIIToUTF16("name");
217 data.url = GURL("http://example.com/");
218 data.action = GURL("http://example.com/blade.php");
219 data.is_form_tag = true; // Default value.
220
221 FormFieldData field_data;
222 field_data.name = base::ASCIIToUTF16("fname");
223 field_data.value = base::ASCIIToUTF16("John");
224 field_data.is_autofilled = true;
225 data.fields.push_back(field_data);
226
227 field_data.name = base::ASCIIToUTF16("lname");
228 field_data.value = base::ASCIIToUTF16("Smith");
229 field_data.is_autofilled = true;
230 data.fields.push_back(field_data);
231
232 // Additional non-autofillable field.
233 field_data.name = base::ASCIIToUTF16("mname");
234 field_data.value = base::ASCIIToUTF16("James");
235 field_data.is_autofilled = false;
236 data.fields.push_back(field_data);
237
238 WebDocument document = main_frame->GetDocument();
239 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
240 ASSERT_FALSE(element.IsNull());
241
242 // This call is necessary to setup the autofill agent appropriate for the
243 // user selection; simulates the menu actually popping up.
244 autofill_agent->FormControlElementClicked(element.To<WebInputElement>(),
245 false);
246
247 autofill_agent->FillForm(0, data);
248 }
249
250 } // end namespace
251
252 class FormAutocompleteTest : public ChromeRenderViewTest {
253 public:
FormAutocompleteTest()254 FormAutocompleteTest() {}
~FormAutocompleteTest()255 ~FormAutocompleteTest() override {}
256
257 protected:
SetUp()258 void SetUp() override {
259 ChromeRenderViewTest::SetUp();
260
261 // We only use the fake driver for main frame
262 // because our test cases only involve the main frame.
263 blink::AssociatedInterfaceProvider* remote_interfaces =
264 view_->GetMainRenderFrame()->GetRemoteAssociatedInterfaces();
265 remote_interfaces->OverrideBinderForTesting(
266 mojom::AutofillDriver::Name_,
267 base::BindRepeating(&FormAutocompleteTest::BindAutofillDriver,
268 base::Unretained(this)));
269
270 focus_test_utils_ = std::make_unique<test::FocusTestUtils>(
271 base::BindRepeating(&FormAutocompleteTest::ExecuteJavaScriptForTests,
272 base::Unretained(this)));
273 }
274
BindAutofillDriver(mojo::ScopedInterfaceEndpointHandle handle)275 void BindAutofillDriver(mojo::ScopedInterfaceEndpointHandle handle) {
276 fake_driver_.BindReceiver(
277 mojo::PendingAssociatedReceiver<mojom::AutofillDriver>(
278 std::move(handle)));
279 }
280
SimulateUserInput(const blink::WebString & id,const std::string & value)281 void SimulateUserInput(const blink::WebString& id, const std::string& value) {
282 WebDocument document = GetMainFrame()->GetDocument();
283 WebElement element = document.GetElementById(id);
284 ASSERT_FALSE(element.IsNull());
285 WebInputElement fname_element = element.To<WebInputElement>();
286 SimulateUserInputChangeForElement(&fname_element, value);
287 }
288
GetFocusLog()289 std::string GetFocusLog() {
290 return focus_test_utils_->GetFocusLog(GetMainFrame()->GetDocument());
291 }
292
293 FakeContentAutofillDriver fake_driver_;
294 std::unique_ptr<test::FocusTestUtils> focus_test_utils_;
295
296 private:
297 DISALLOW_COPY_AND_ASSIGN(FormAutocompleteTest);
298 };
299
300 // Tests that submitting a form generates FormSubmitted message with the form
301 // fields.
TEST_F(FormAutocompleteTest,NormalFormSubmit)302 TEST_F(FormAutocompleteTest, NormalFormSubmit) {
303 // Load a form.
304 LoadHTML(
305 "<html><form id='myForm' action='about:blank'>"
306 "<input name='fname' value='Rick'/>"
307 "<input name='lname' value='Deckard'/></form></html>");
308
309 // Submit the form.
310 ExecuteJavaScriptForTests("document.getElementById('myForm').submit();");
311 base::RunLoop().RunUntilIdle();
312
313 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
314 false /* expect_known_success */,
315 SubmissionSource::FORM_SUBMISSION);
316 }
317
318 // Tests that FormSubmitted message is generated even the submit event isn't
319 // propagated by Javascript.
TEST_F(FormAutocompleteTest,SubmitEventPrevented)320 TEST_F(FormAutocompleteTest, SubmitEventPrevented) {
321 // Load a form.
322 LoadHTML(
323 "<html><form id='myForm'><input name='fname' value='Rick'/>"
324 "<input name='lname' value='Deckard'/><input type=submit></form>"
325 "</html>");
326
327 // Submit the form.
328 ExecuteJavaScriptForTests(
329 "var form = document.forms[0];"
330 "form.onsubmit = function(event) { event.preventDefault(); };"
331 "document.querySelector('input[type=submit]').click();");
332 base::RunLoop().RunUntilIdle();
333
334 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
335 false /* expect_known_success */,
336 SubmissionSource::FORM_SUBMISSION);
337 }
338
339 // Tests that completing an Ajax request and having the form disappear will
340 // trigger submission from Autofill's point of view.
TEST_F(FormAutocompleteTest,AjaxSucceeded_NoLongerVisible)341 TEST_F(FormAutocompleteTest, AjaxSucceeded_NoLongerVisible) {
342 // Load a form.
343 LoadHTML(
344 "<html><form id='myForm' action='http://example.com/blade.php'>"
345 "<input name='fname' id='fname' value='Bob'/>"
346 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
347
348 // Simulate user input so that the form is "remembered".
349 WebDocument document = GetMainFrame()->GetDocument();
350 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
351 ASSERT_FALSE(element.IsNull());
352 WebInputElement fname_element = element.To<WebInputElement>();
353 SimulateUserInputChangeForElement(&fname_element, std::string("Rick"));
354
355 // Simulate removing the form just before the ajax request completes.
356 ExecuteJavaScriptForTests(
357 "var element = document.getElementById('myForm');"
358 "element.parentNode.removeChild(element);");
359
360 // Simulate an Ajax request completing.
361 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
362 base::RunLoop().RunUntilIdle();
363
364 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
365 true /* expect_known_success */,
366 SubmissionSource::XHR_SUCCEEDED);
367 }
368
369 // Tests that completing an Ajax request and having the form with a specific
370 // action disappear will trigger submission from Autofill's point of view, even
371 // if there is another form with the same data but different action on the page.
TEST_F(FormAutocompleteTest,AjaxSucceeded_NoLongerVisible_DifferentActionsSameData)372 TEST_F(FormAutocompleteTest,
373 AjaxSucceeded_NoLongerVisible_DifferentActionsSameData) {
374 // Load a form.
375 LoadHTML(
376 "<html><form id='myForm' action='http://example.com/blade.php'>"
377 "<input name='fname' id='fname' value='Bob'/>"
378 "<input name='lname' value='Deckard'/><input type=submit></form>"
379 "<form id='myForm2' action='http://example.com/runner.php'>"
380 "<input name='fname' id='fname2' value='Bob'/>"
381 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
382
383 // Simulate user input so that the form is "remembered".
384 WebDocument document = GetMainFrame()->GetDocument();
385 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
386 ASSERT_FALSE(element.IsNull());
387 WebInputElement fname_element = element.To<WebInputElement>();
388 SimulateUserInputChangeForElement(&fname_element, std::string("Rick"));
389
390 // Simulate removing the form just before the ajax request completes.
391 ExecuteJavaScriptForTests(
392 "var element = document.getElementById('myForm');"
393 "element.parentNode.removeChild(element);");
394
395 // Simulate an Ajax request completing.
396 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
397 base::RunLoop().RunUntilIdle();
398
399 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
400 true /* expect_known_success */,
401 SubmissionSource::XHR_SUCCEEDED);
402 }
403
404 // Tests that completing an Ajax request and having the form with no action
405 // specified disappear will trigger submission from Autofill's point of view,
406 // even if there is still another form with no action in the page. It will
407 // compare field data within the forms.
408 // TODO(kolos) Re-enable when the implementation of IsFormVisible is on-par
409 // for these platforms.
410 #if defined(OS_MACOSX)
411 #define MAYBE_NoLongerVisibleBothNoActions DISABLED_NoLongerVisibleBothNoActions
412 #else
413 #define MAYBE_NoLongerVisibleBothNoActions NoLongerVisibleBothNoActions
414 #endif
TEST_F(FormAutocompleteTest,MAYBE_NoLongerVisibleBothNoActions)415 TEST_F(FormAutocompleteTest, MAYBE_NoLongerVisibleBothNoActions) {
416 // Load a form.
417 LoadHTML(
418 "<html><form id='myForm'>"
419 "<input name='fname' id='fname' value='Bob'/>"
420 "<input name='lname' value='Deckard'/><input type=submit></form>"
421 "<form id='myForm2'>"
422 "<input name='fname' id='fname2' value='John'/>"
423 "<input name='lname' value='Doe'/><input type=submit></form></html>");
424
425 // Simulate user input so that the form is "remembered".
426 WebDocument document = GetMainFrame()->GetDocument();
427 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
428 ASSERT_FALSE(element.IsNull());
429 WebInputElement fname_element = element.To<WebInputElement>();
430 SimulateUserInputChangeForElement(&fname_element, std::string("Rick"));
431
432 // Simulate removing the form just before the ajax request completes.
433 ExecuteJavaScriptForTests(
434 "var element = document.getElementById('myForm');"
435 "element.parentNode.removeChild(element);");
436
437 // Simulate an Ajax request completing.
438 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
439 base::RunLoop().RunUntilIdle();
440
441 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
442 true /* expect_known_success */,
443 SubmissionSource::XHR_SUCCEEDED);
444 }
445
446 // Tests that completing an Ajax request and having the form with no action
447 // specified disappear will trigger submission from Autofill's point of view.
TEST_F(FormAutocompleteTest,AjaxSucceeded_NoLongerVisible_NoAction)448 TEST_F(FormAutocompleteTest, AjaxSucceeded_NoLongerVisible_NoAction) {
449 // Load a form.
450 LoadHTML(
451 "<html><form id='myForm'>"
452 "<input name='fname' id='fname' value='Bob'/>"
453 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
454
455 // Simulate user input so that the form is "remembered".
456 WebDocument document = GetMainFrame()->GetDocument();
457 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
458 ASSERT_FALSE(element.IsNull());
459 WebInputElement fname_element = element.To<WebInputElement>();
460 SimulateUserInputChangeForElement(&fname_element, std::string("Rick"));
461
462 // Simulate removing the form just before the ajax request completes.
463 ExecuteJavaScriptForTests(
464 "var element = document.getElementById('myForm');"
465 "element.parentNode.removeChild(element);");
466
467 // Simulate an Ajax request completing.
468 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
469 base::RunLoop().RunUntilIdle();
470
471 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
472 true /* expect_known_success */,
473 SubmissionSource::XHR_SUCCEEDED);
474 }
475
476 // Tests that completing an Ajax request but leaving a form visible will not
477 // trigger submission from Autofill's point of view.
TEST_F(FormAutocompleteTest,AjaxSucceeded_StillVisible)478 TEST_F(FormAutocompleteTest, AjaxSucceeded_StillVisible) {
479 // Load a form.
480 LoadHTML(
481 "<html><form id='myForm' action='http://example.com/blade.php'>"
482 "<input name='fname' id='fname' value='Bob'/>"
483 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
484
485 // Simulate user input so that the form is "remembered".
486 WebDocument document = GetMainFrame()->GetDocument();
487 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
488 ASSERT_FALSE(element.IsNull());
489 WebInputElement fname_element = element.To<WebInputElement>();
490 SimulateUserInputChangeForElement(&fname_element, std::string("Rick"));
491
492 // Simulate an Ajax request completing.
493 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
494 base::RunLoop().RunUntilIdle();
495
496 // No submission messages sent.
497 VerifyNoSubmitMessagesReceived(fake_driver_);
498 }
499
500 // Tests that completing an Ajax request without any prior form interaction
501 // does not trigger form submission from Autofill's point of view.
TEST_F(FormAutocompleteTest,AjaxSucceeded_NoFormInteractionInvisible)502 TEST_F(FormAutocompleteTest, AjaxSucceeded_NoFormInteractionInvisible) {
503 // Load a form.
504 LoadHTML(
505 "<html><form id='myForm' action='http://example.com/blade.php'>"
506 "<input name='fname' id='fname' value='Bob'/>"
507 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
508
509 // No form interaction.
510
511 // Simulate removing the form just before the ajax request completes.
512 ExecuteJavaScriptForTests(
513 "var element = document.getElementById('myForm');"
514 "element.parentNode.removeChild(element);");
515
516 // Simulate an Ajax request completing without prior user interaction.
517 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
518 base::RunLoop().RunUntilIdle();
519
520 // No submission messages sent.
521 VerifyNoSubmitMessagesReceived(fake_driver_);
522 }
523
524 // Tests that completing an Ajax request after having autofilled a form,
525 // with the form disappearing, will trigger submission from Autofill's
526 // point of view.
TEST_F(FormAutocompleteTest,AjaxSucceeded_FilledFormIsInvisible)527 TEST_F(FormAutocompleteTest, AjaxSucceeded_FilledFormIsInvisible) {
528 // Load a form.
529 LoadHTML(
530 "<html><form id='myForm' action='http://example.com/blade.php'>"
531 "<input name='fname' id='fname'/>"
532 "<input name='lname'/></form></html>");
533
534 // Simulate filling a form using Autofill.
535 SimulateOnFillForm(autofill_agent_, GetMainFrame());
536
537 // Simulate user input since ajax request doesn't fire submission message
538 // if there is no user input.
539 SimulateUserInput(WebString::FromUTF8("fname"), std::string("Rick"));
540
541 // Simulate removing the form just before the ajax request completes.
542 ExecuteJavaScriptForTests(
543 "var element = document.getElementById('myForm');"
544 "element.parentNode.removeChild(element);");
545
546 // Simulate an Ajax request completing.
547 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
548 base::RunLoop().RunUntilIdle();
549
550 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Smith",
551 true /* expect_known_success */,
552 SubmissionSource::XHR_SUCCEEDED);
553 }
554
555 // Tests that completing an Ajax request after having autofilled a form,
556 // without the form disappearing, will not trigger submission from Autofill's
557 // point of view.
TEST_F(FormAutocompleteTest,AjaxSucceeded_FilledFormStillVisible)558 TEST_F(FormAutocompleteTest, AjaxSucceeded_FilledFormStillVisible) {
559 // Load a form.
560 LoadHTML(
561 "<html><form id='myForm' action='http://example.com/blade.php'>"
562 "<input name='fname' id='fname' value='Rick'/>"
563 "<input name='lname' value='Deckard'/></form></html>");
564
565 // Simulate filling a form using Autofill.
566 SimulateOnFillForm(autofill_agent_, GetMainFrame());
567
568 // Form still visible.
569
570 // Simulate an Ajax request completing.
571 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
572 base::RunLoop().RunUntilIdle();
573
574 // No submission messages sent.
575 VerifyNoSubmitMessagesReceived(fake_driver_);
576 }
577
578 // Tests that correct focus, change and blur events are emitted during the
579 // autofilling process when there is an initial focused element in a form
580 // having non-fillable fields.
TEST_F(FormAutocompleteTest,VerifyFocusAndBlurEventsAfterAutofill)581 TEST_F(FormAutocompleteTest, VerifyFocusAndBlurEventsAfterAutofill) {
582 // Load a form.
583 LoadHTML(
584 "<html><form id='myForm'>"
585 "<label>First Name:</label><input id='fname' name='0'/><br/>"
586 "<label>Last Name:</label> <input id='lname' name='1'/><br/>"
587 "<label>Middle Name:</label><input id='mname' name='2'/><br/>"
588 "</form></html>");
589
590 focus_test_utils_->SetUpFocusLogging();
591 focus_test_utils_->FocusElement("fname");
592
593 // Simulate filling the form using Autofill.
594 SimulateOnFillFormWithNonFillableFields(autofill_agent_, GetMainFrame());
595 base::RunLoop().RunUntilIdle();
596
597 // Expected Result in order:
598 // * Change fname
599 // * Blur fname
600 // * Focus lname
601 // * Change lname
602 // * Blur lname
603 // * Focus fname
604 EXPECT_EQ(GetFocusLog(), "c0b0f1c1b1f0");
605 }
606
607 // Tests that correct focus, change and blur events are emitted during the
608 // autofilling process when there is an initial focused element.
TEST_F(FormAutocompleteTest,VerifyFocusAndBlurEventsAfterAutofillWithFocusedElement)609 TEST_F(FormAutocompleteTest,
610 VerifyFocusAndBlurEventsAfterAutofillWithFocusedElement) {
611 // Load a form.
612 LoadHTML(
613 "<html><form id='myForm'>"
614 "<label>First Name:</label><input id='fname' name='0'/><br/>"
615 "<label>Last Name:</label> <input id='lname' name='1'/><br/>"
616 "</form></html>");
617
618 focus_test_utils_->SetUpFocusLogging();
619 focus_test_utils_->FocusElement("fname");
620
621 // Simulate filling the form using Autofill.
622 SimulateOnFillForm(autofill_agent_, GetMainFrame());
623 base::RunLoop().RunUntilIdle();
624
625 // Expected Result in order:
626 // * Change fname
627 // * Blur fname
628 // * Focus lname
629 // * Change lname
630 // * Blur lname
631 // * Focus fname
632 EXPECT_EQ(GetFocusLog(), "c0b0f1c1b1f0");
633 }
634
635 // Tests that correct focus, change and blur events are emitted during the
636 // autofilling process when there is an initial focused element in a form having
637 // single field.
TEST_F(FormAutocompleteTest,VerifyFocusAndBlurEventAfterAutofillWithFocusedElementForSingleElement)638 TEST_F(FormAutocompleteTest,
639 VerifyFocusAndBlurEventAfterAutofillWithFocusedElementForSingleElement) {
640 // Load a form.
641 LoadHTML(
642 "<html><form id='myForm'>"
643 "<label>First Name:</label><input id='fname' name='0'/><br/>"
644 "</form></html>");
645
646 focus_test_utils_->SetUpFocusLogging();
647 focus_test_utils_->FocusElement("fname");
648
649 // Simulate filling the form using Autofill.
650 SimulateOnFillForm(autofill_agent_, GetMainFrame());
651 base::RunLoop().RunUntilIdle();
652
653 EXPECT_EQ(GetFocusLog(), "");
654 }
655
656 // Tests that completing an Ajax request without a form present will still
657 // trigger submission, if all the inputs the user has modified disappear.
TEST_F(FormAutocompleteTest,AjaxSucceeded_FormlessElements)658 TEST_F(FormAutocompleteTest, AjaxSucceeded_FormlessElements) {
659 // Load a "form." Note that kRequiredFieldsForUpload fields are required
660 // for the formless logic to trigger, so we add a throwaway third field.
661 LoadHTML(
662 "<head><title>Checkout</title></head>"
663 "<input type='text' name='fname' id='fname'/>"
664 "<input type='text' name='lname' value='Puckett'/>"
665 "<input type='number' name='number' value='34'/>");
666
667 // Simulate user input.
668 WebDocument document = GetMainFrame()->GetDocument();
669 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
670 ASSERT_FALSE(element.IsNull());
671 WebInputElement fname_element = element.To<WebInputElement>();
672 SimulateUserInputChangeForElement(&fname_element, std::string("Kirby"));
673
674 // Remove element from view.
675 ExecuteJavaScriptForTests(
676 "var element = document.getElementById('fname');"
677 "element.style.display = 'none';");
678
679 // Simulate AJAX request.
680 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
681 base::RunLoop().RunUntilIdle();
682
683 VerifyReceivedRendererMessages(fake_driver_, "Kirby", "Puckett",
684 true /* expect_known_success */,
685 SubmissionSource::XHR_SUCCEEDED);
686 }
687
688 // Unit test for CollectFormlessElements.
TEST_F(FormAutocompleteTest,CollectFormlessElements)689 TEST_F(FormAutocompleteTest, CollectFormlessElements) {
690 LoadHTML(
691 "<html><title>Checkout</title></head>"
692 "<input type='text' name='text_input'/>"
693 "<input type='checkbox' name='check_input'/>"
694 "<input type='number' name='number_input'/>"
695 "<select name='select_input'/>"
696 " <option value='option_1'></option>"
697 " <option value='option_2'></option>"
698 "</select>"
699 "<form><input type='text' name='excluded'/></form>"
700 "</html>");
701
702 FormData result;
703 autofill_agent_->CollectFormlessElements(&result);
704
705 // Asserting size 4 also ensures that 'excluded' field inside <form> is not
706 // collected.
707 ASSERT_EQ(4U, result.fields.size());
708 EXPECT_EQ(base::ASCIIToUTF16("text_input"), result.fields[0].name);
709 EXPECT_EQ(base::ASCIIToUTF16("check_input"), result.fields[1].name);
710 EXPECT_EQ(base::ASCIIToUTF16("number_input"), result.fields[2].name);
711 EXPECT_EQ(base::ASCIIToUTF16("select_input"), result.fields[3].name);
712 }
713
714 // Unit test for AutofillAgent::AcceptDataListSuggestion.
TEST_F(FormAutocompleteTest,AcceptDataListSuggestion)715 TEST_F(FormAutocompleteTest, AcceptDataListSuggestion) {
716 LoadHTML(
717 "<html>"
718 "<input id='empty' type='email' multiple />"
719 "<input id='multi_one' type='email' multiple value='one@example.com'/>"
720 "<input id='multi_two' type='email' multiple"
721 " value='one@example.com,two@example.com'/>"
722 "<input id='multi_trailing' type='email' multiple"
723 " value='one@example.com,two@example.com,'/>"
724 "<input id='not_multi' type='email'"
725 " value='one@example.com,two@example.com,'/>"
726 "<input id='not_email' type='text' multiple"
727 " value='one@example.com,two@example.com,'/>"
728 "</html>");
729 WebDocument document = GetMainFrame()->GetDocument();
730
731 // Each case tests a different field value with the same suggestion.
732 const base::string16 kSuggestion =
733 base::ASCIIToUTF16("suggestion@example.com");
734 struct TestCase {
735 std::string id;
736 std::string expected;
737 } cases[] = {
738 // Empty text field; expect to populate with suggestion.
739 {"empty", "suggestion@example.com"},
740 // Single entry; expect to replace with suggestion.
741 {"multi_one", "suggestion@example.com"},
742 // Two comma-separated entries; expect to replace second with suggestion.
743 {"multi_two", "one@example.com,suggestion@example.com"},
744 // Two comma-separated entries with trailing comma; expect to append
745 // suggestion.
746 {"multi_trailing",
747 "one@example.com,two@example.com,suggestion@example.com"},
748 // Do not apply this logic for a non-multiple or non-email field.
749 {"not_multi", "suggestion@example.com"},
750 {"not_email", "suggestion@example.com"},
751 };
752
753 for (const auto& c : cases) {
754 WebElement element = document.GetElementById(WebString::FromUTF8(c.id));
755 ASSERT_FALSE(element.IsNull());
756 WebInputElement* input_element = blink::ToWebInputElement(&element);
757 ASSERT_TRUE(input_element);
758 // Select this element in |autofill_agent_|.
759 autofill_agent_->FormControlElementClicked(element.To<WebInputElement>(),
760 false);
761
762 autofill_agent_->AcceptDataListSuggestion(kSuggestion);
763 EXPECT_EQ(c.expected, input_element->Value().Utf8()) << "Case id: " << c.id;
764 }
765 }
766
767 // Test that a FocusNoLongerOnForm message is sent if focus goes from an
768 // interacted form to an element outside the form.
TEST_F(FormAutocompleteTest,InteractedFormNoLongerFocused_FocusNoLongerOnForm)769 TEST_F(FormAutocompleteTest,
770 InteractedFormNoLongerFocused_FocusNoLongerOnForm) {
771 // Load a form.
772 LoadHTML(
773 "<html><input type='text' id='different'/>"
774 "<form id='myForm' action='http://example.com/blade.php'>"
775 "<input name='fname' id='fname' value='Bob'/>"
776 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
777
778 // Simulate user input so that the form is "remembered".
779 WebDocument document = GetMainFrame()->GetDocument();
780 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
781 ASSERT_FALSE(element.IsNull());
782 WebInputElement fname_element = element.To<WebInputElement>();
783 SimulateUserInputChangeForElement(&fname_element, std::string("Rick"));
784
785 ASSERT_FALSE(fake_driver_.did_unfocus_form());
786
787 // Change focus to a different node outside the form.
788 WebElement different =
789 document.GetElementById(WebString::FromUTF8("different"));
790 SetFocused(different);
791
792 base::RunLoop run_loop;
793 run_loop.RunUntilIdle();
794
795 EXPECT_TRUE(fake_driver_.did_unfocus_form());
796 }
797
798 // Test that a FocusNoLongerOnForm message is sent if focus goes from one
799 // interacted form to another.
TEST_F(FormAutocompleteTest,InteractingInDifferentForms_FocusNoLongerOnForm)800 TEST_F(FormAutocompleteTest, InteractingInDifferentForms_FocusNoLongerOnForm) {
801 // Load a form.
802 LoadHTML(
803 "<html><form id='myForm' action='http://example.com/blade.php'>"
804 "<input name='fname' id='fname' value='Bob'/>"
805 "<input name='lname' value='Deckard'/><input type=submit></form>"
806 "<form id='myForm2' action='http://example.com/runner.php'>"
807 "<input name='fname' id='fname2' value='Bob'/>"
808 "<input name='lname' value='Deckard'/><input type=submit></form></html>");
809
810 // Simulate user input in the first form so that the form is "remembered".
811 WebDocument document = GetMainFrame()->GetDocument();
812 WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
813 ASSERT_FALSE(element.IsNull());
814 WebInputElement fname_element = element.To<WebInputElement>();
815 SimulateUserInputChangeForElement(&fname_element, std::string("Rick"));
816
817 ASSERT_FALSE(fake_driver_.did_unfocus_form());
818
819 // Simulate user input in the second form so that a "no longer focused"
820 // message is sent for the first form.
821 document = GetMainFrame()->GetDocument();
822 element = document.GetElementById(WebString::FromUTF8("fname2"));
823 ASSERT_FALSE(element.IsNull());
824 fname_element = element.To<WebInputElement>();
825 SimulateUserInputChangeForElement(&fname_element, std::string("John"));
826
827 base::RunLoop run_loop;
828 run_loop.RunUntilIdle();
829
830 EXPECT_TRUE(fake_driver_.did_unfocus_form());
831 }
832
833 // Tests that submitting a form that has autocomplete="off" generates
834 // WillSubmitForm and FormSubmitted messages.
TEST_F(FormAutocompleteTest,AutoCompleteOffFormSubmit)835 TEST_F(FormAutocompleteTest, AutoCompleteOffFormSubmit) {
836 // Load a form.
837 LoadHTML(
838 "<html><form id='myForm' autocomplete='off' action='about:blank'>"
839 "<input name='fname' value='Rick'/>"
840 "<input name='lname' value='Deckard'/>"
841 "</form></html>");
842
843 // Submit the form.
844 ExecuteJavaScriptForTests("document.getElementById('myForm').submit();");
845 base::RunLoop().RunUntilIdle();
846
847 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
848 false /* expect_known_success */,
849 SubmissionSource::FORM_SUBMISSION);
850 }
851
852 // Tests that fields with autocomplete off are submitted.
TEST_F(FormAutocompleteTest,AutoCompleteOffInputSubmit)853 TEST_F(FormAutocompleteTest, AutoCompleteOffInputSubmit) {
854 // Load a form.
855 LoadHTML(
856 "<html><form id='myForm' action='about:blank'>"
857 "<input name='fname' value='Rick'/>"
858 "<input name='lname' value='Deckard' autocomplete='off'/>"
859 "</form></html>");
860
861 // Submit the form.
862 ExecuteJavaScriptForTests("document.getElementById('myForm').submit();");
863 base::RunLoop().RunUntilIdle();
864
865 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
866 false /* expect_known_success */,
867 SubmissionSource::FORM_SUBMISSION);
868 }
869
870 // Tests that submitting a form that has been dynamically set as autocomplete
871 // off generates WillSubmitForm and FormSubmitted messages.
872 // Note: We previously did the opposite, for bug http://crbug.com/36520
TEST_F(FormAutocompleteTest,DynamicAutoCompleteOffFormSubmit)873 TEST_F(FormAutocompleteTest, DynamicAutoCompleteOffFormSubmit) {
874 LoadHTML(
875 "<html><form id='myForm' action='about:blank'>"
876 "<input name='fname' value='Rick'/>"
877 "<input name='lname' value='Deckard'/></form></html>");
878
879 WebElement element =
880 GetMainFrame()->GetDocument().GetElementById(blink::WebString("myForm"));
881 ASSERT_FALSE(element.IsNull());
882 blink::WebFormElement form = element.To<blink::WebFormElement>();
883 EXPECT_TRUE(form.AutoComplete());
884
885 // Dynamically mark the form as autocomplete off.
886 ExecuteJavaScriptForTests(
887 "document.getElementById('myForm')."
888 "setAttribute('autocomplete', 'off');");
889 base::RunLoop().RunUntilIdle();
890 EXPECT_FALSE(form.AutoComplete());
891
892 // Submit the form.
893 ExecuteJavaScriptForTests("document.getElementById('myForm').submit();");
894 base::RunLoop().RunUntilIdle();
895
896 VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard",
897 false /* expect_known_success */,
898 SubmissionSource::FORM_SUBMISSION);
899 }
900
TEST_F(FormAutocompleteTest,FormSubmittedByDOMMutationAfterXHR)901 TEST_F(FormAutocompleteTest, FormSubmittedByDOMMutationAfterXHR) {
902 LoadHTML(
903 "<html>"
904 "<input type='text' id='address_field' name='address' autocomplete='on'>"
905 "</html>");
906
907 SimulateUserInput(WebString::FromUTF8("address_field"), std::string("City"));
908
909 // Simulate an Ajax request completing.
910 static_cast<blink::WebAutofillClient*>(autofill_agent_)->AjaxSucceeded();
911
912 // Hide elements to simulate successful form submission.
913 std::string hide_elements =
914 "var address = document.getElementById('address_field');"
915 "address.style = 'display:none';";
916
917 ExecuteJavaScriptForTests(hide_elements.c_str());
918 base::RunLoop().RunUntilIdle();
919
920 VerifyReceivedAddressRendererMessages(
921 fake_driver_, "City", true /* expect_known_success */,
922 SubmissionSource::DOM_MUTATION_AFTER_XHR);
923 }
924
TEST_F(FormAutocompleteTest,FormSubmittedBySameDocumentNavigation)925 TEST_F(FormAutocompleteTest, FormSubmittedBySameDocumentNavigation) {
926 LoadHTML(
927 "<html>"
928 "<input type='text' id='address_field' name='address' autocomplete='on'>"
929 "</html>");
930
931 SimulateUserInput(WebString::FromUTF8("address_field"), std::string("City"));
932
933 // Hide elements to simulate successful form submission.
934 std::string hide_elements =
935 "var address = document.getElementById('address_field');"
936 "address.style = 'display:none';";
937
938 ExecuteJavaScriptForTests(hide_elements.c_str());
939
940 // Simulate same document navigation.
941 autofill_agent_->form_tracker_for_testing()->DidCommitProvisionalLoad(
942 true /*is_same_document_navigation*/, ui::PAGE_TRANSITION_LINK);
943 base::RunLoop().RunUntilIdle();
944
945 VerifyReceivedAddressRendererMessages(
946 fake_driver_, "City", true /* expect_known_success */,
947 SubmissionSource::SAME_DOCUMENT_NAVIGATION);
948 }
949
TEST_F(FormAutocompleteTest,FormSubmittedByProbablyFormSubmitted)950 TEST_F(FormAutocompleteTest, FormSubmittedByProbablyFormSubmitted) {
951 LoadHTML(
952 "<html>"
953 "<input type='text' id='address_field' name='address' autocomplete='on'>"
954 "</html>");
955
956 SimulateUserInput(WebString::FromUTF8("address_field"), std::string("City"));
957
958 // Hide elements to simulate successful form submission.
959 std::string hide_elements =
960 "var address = document.getElementById('address_field');"
961 "address.style = 'display:none';";
962
963 ExecuteJavaScriptForTests(hide_elements.c_str());
964
965 // Simulate navigation.
966 autofill_agent_->form_tracker_for_testing()
967 ->FireProbablyFormSubmittedForTesting();
968
969 base::RunLoop().RunUntilIdle();
970
971 VerifyReceivedAddressRendererMessages(
972 fake_driver_, "City", false /* expect_known_success */,
973 SubmissionSource::PROBABLY_FORM_SUBMITTED);
974 }
975
TEST_F(FormAutocompleteTest,SelectControlChanged)976 TEST_F(FormAutocompleteTest, SelectControlChanged) {
977 LoadHTML(
978 "<html>"
979 "<form>"
980 "<select id='color'><option value='red'>red</option><option "
981 "value='blue'>blue</option></select>"
982 "</form>"
983 "</html>");
984
985 std::string change_value =
986 "var color = document.getElementById('color');"
987 "color.selectedIndex = 1;";
988
989 ExecuteJavaScriptForTests(change_value.c_str());
990 WebElement element =
991 GetMainFrame()->GetDocument().GetElementById(blink::WebString("color"));
992 static_cast<blink::WebAutofillClient*>(autofill_agent_)
993 ->SelectControlDidChange(
994 *reinterpret_cast<blink::WebFormControlElement*>(&element));
995 base::RunLoop().RunUntilIdle();
996
997 const FormFieldData* field = fake_driver_.select_control_changed();
998 ASSERT_TRUE(field);
999 EXPECT_EQ(base::ASCIIToUTF16("color"), field->name);
1000 EXPECT_EQ(base::ASCIIToUTF16("blue"), field->value);
1001 }
1002
1003 } // namespace autofill
1004