| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <tuple> |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "chrome/test/base/chrome_render_view_test.h" |
| #include "components/autofill/content/renderer/autofill_agent.h" |
| #include "components/autofill/core/common/form_data.h" |
| #include "content/public/renderer/render_frame.h" |
| #include "content/public/renderer/render_view.h" |
| #include "mojo/public/cpp/bindings/binding_set.h" |
| #include "services/shell/public/cpp/interface_provider.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/WebKit/public/web/WebDocument.h" |
| #include "third_party/WebKit/public/web/WebElement.h" |
| #include "third_party/WebKit/public/web/WebFormElement.h" |
| #include "third_party/WebKit/public/web/WebInputElement.h" |
| #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| |
| using blink::WebDocument; |
| using blink::WebElement; |
| using blink::WebInputElement; |
| using blink::WebString; |
| |
| namespace autofill { |
| |
| namespace { |
| |
| class FakeContentAutofillDriver : public mojom::AutofillDriver { |
| public: |
| FakeContentAutofillDriver() : did_unfocus_form_(false) {} |
| |
| ~FakeContentAutofillDriver() override {} |
| |
| void BindRequest(mojom::AutofillDriverRequest request) { |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| bool did_unfocus_form() const { return did_unfocus_form_; } |
| |
| const FormData* form_will_submit() const { return form_will_submit_.get(); } |
| |
| const FormData* form_submitted() const { return form_submitted_.get(); } |
| |
| private: |
| // mojom::AutofillDriver: |
| void FirstUserGestureObserved() override {} |
| |
| void FormsSeen(const std::vector<FormData>& forms, |
| base::TimeTicks timestamp) override {} |
| |
| void WillSubmitForm(const FormData& form, |
| base::TimeTicks timestamp) override { |
| form_will_submit_.reset(new FormData(form)); |
| } |
| |
| void FormSubmitted(const FormData& form) override { |
| form_submitted_.reset(new FormData(form)); |
| } |
| |
| void TextFieldDidChange(const FormData& form, |
| const FormFieldData& field, |
| base::TimeTicks timestamp) override {} |
| |
| void QueryFormFieldAutofill(int32_t id, |
| const FormData& form, |
| const FormFieldData& field, |
| const gfx::RectF& bounding_box) override {} |
| |
| void HidePopup() override {} |
| |
| void PingAck() override {} |
| |
| void FocusNoLongerOnForm() override { did_unfocus_form_ = true; } |
| |
| void DidFillAutofillFormData(const FormData& form, |
| base::TimeTicks timestamp) override {} |
| |
| void DidPreviewAutofillFormData() override {} |
| |
| void DidEndTextFieldEditing() override {} |
| |
| void SetDataList(const std::vector<base::string16>& values, |
| const std::vector<base::string16>& labels) override {} |
| |
| // Records whether FocusNoLongerOnForm() get called. |
| bool did_unfocus_form_; |
| // Records the form data received via WillSubmitForm() call. |
| std::unique_ptr<FormData> form_will_submit_; |
| // Records the form data received via FormSubmitted() call. |
| std::unique_ptr<FormData> form_submitted_; |
| |
| mojo::BindingSet<mojom::AutofillDriver> bindings_; |
| }; |
| |
| // Helper function to verify the form-related messages received from the |
| // renderer. The same data is expected in both messages. Depending on |
| // |expect_submitted_message|, will verify presence of FormSubmitted message. |
| void VerifyReceivedRendererMessages( |
| const FakeContentAutofillDriver& fake_driver, |
| const std::string& fname, |
| const std::string& lname, |
| bool expect_submitted_message) { |
| ASSERT_TRUE(fake_driver.form_will_submit()); |
| ASSERT_EQ(expect_submitted_message, fake_driver.form_submitted() != nullptr); |
| |
| // The tuple also includes a timestamp, which is ignored. |
| const FormData& will_submit_form = *(fake_driver.form_will_submit()); |
| ASSERT_LE(2U, will_submit_form.fields.size()); |
| |
| EXPECT_EQ(WebString("fname"), will_submit_form.fields[0].name); |
| EXPECT_EQ(WebString(base::UTF8ToUTF16(fname)), |
| will_submit_form.fields[0].value); |
| EXPECT_EQ(WebString("lname"), will_submit_form.fields[1].name); |
| EXPECT_EQ(WebString(base::UTF8ToUTF16(lname)), |
| will_submit_form.fields[1].value); |
| |
| if (expect_submitted_message) { |
| const FormData& submitted_form = *(fake_driver.form_submitted()); |
| ASSERT_LE(2U, submitted_form.fields.size()); |
| |
| EXPECT_EQ(WebString("fname"), submitted_form.fields[0].name); |
| EXPECT_EQ(WebString(base::UTF8ToUTF16(fname)), |
| submitted_form.fields[0].value); |
| EXPECT_EQ(WebString("lname"), submitted_form.fields[1].name); |
| EXPECT_EQ(WebString(base::UTF8ToUTF16(lname)), |
| submitted_form.fields[1].value); |
| } |
| } |
| |
| // Helper function to verify that NO form-related messages are received from the |
| // renderer. |
| void VerifyNoSubmitMessagesReceived( |
| const FakeContentAutofillDriver& fake_driver) { |
| // No submission messages sent. |
| EXPECT_EQ(nullptr, fake_driver.form_will_submit()); |
| EXPECT_EQ(nullptr, fake_driver.form_submitted()); |
| } |
| |
| // Simulates receiving a message from the browser to fill a form. |
| void SimulateOnFillForm(autofill::AutofillAgent* autofill_agent, |
| blink::WebFrame* main_frame) { |
| WebDocument document = main_frame->document(); |
| WebElement element = |
| document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| |
| // This call is necessary to setup the autofill agent appropriate for the |
| // user selection; simulates the menu actually popping up. |
| static_cast<autofill::PageClickListener*>(autofill_agent) |
| ->FormControlElementClicked(element.to<WebInputElement>(), false); |
| |
| FormData data; |
| data.name = base::ASCIIToUTF16("name"); |
| data.origin = GURL("http://example.com/"); |
| data.action = GURL("http://example.com/blade.php"); |
| data.is_form_tag = true; // Default value. |
| |
| FormFieldData field_data; |
| field_data.name = base::ASCIIToUTF16("fname"); |
| field_data.value = base::ASCIIToUTF16("John"); |
| field_data.is_autofilled = true; |
| data.fields.push_back(field_data); |
| |
| field_data.name = base::ASCIIToUTF16("lname"); |
| field_data.value = base::ASCIIToUTF16("Smith"); |
| field_data.is_autofilled = true; |
| data.fields.push_back(field_data); |
| |
| autofill_agent->FillForm(0, data); |
| } |
| |
| } // end namespace |
| |
| class FormAutocompleteTest : public ChromeRenderViewTest { |
| public: |
| FormAutocompleteTest() {} |
| ~FormAutocompleteTest() override {} |
| |
| protected: |
| void SetUp() override { |
| ChromeRenderViewTest::SetUp(); |
| |
| // We only use the fake driver for main frame |
| // because our test cases only involve the main frame. |
| shell::InterfaceProvider* remote_interfaces = |
| view_->GetMainRenderFrame()->GetRemoteInterfaces(); |
| shell::InterfaceProvider::TestApi test_api(remote_interfaces); |
| test_api.SetBinderForName( |
| mojom::AutofillDriver::Name_, |
| base::Bind(&FormAutocompleteTest::BindAutofillDriver, |
| base::Unretained(this))); |
| } |
| |
| void BindAutofillDriver(mojo::ScopedMessagePipeHandle handle) { |
| fake_driver_.BindRequest( |
| mojo::MakeRequest<mojom::AutofillDriver>(std::move(handle))); |
| } |
| |
| FakeContentAutofillDriver fake_driver_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(FormAutocompleteTest); |
| }; |
| |
| // Tests that submitting a form generates WillSubmitForm and FormSubmitted |
| // messages with the form fields. |
| TEST_F(FormAutocompleteTest, NormalFormSubmit) { |
| // Load a form. |
| LoadHTML("<html><form id='myForm'><input name='fname' value='Rick'/>" |
| "<input name='lname' value='Deckard'/></form></html>"); |
| |
| // Submit the form. |
| ExecuteJavaScriptForTests("document.getElementById('myForm').submit();"); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| true /* expect_submitted_message */); |
| } |
| |
| // Tests that submitting a form that prevents the submit event from propagating |
| // will only send the WillSubmitForm message. |
| TEST_F(FormAutocompleteTest, SubmitEventPrevented) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm'><input name='fname' value='Rick'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form>" |
| "</html>"); |
| |
| // Submit the form. |
| ExecuteJavaScriptForTests( |
| "var form = document.forms[0];" |
| "form.onsubmit = function(event) { event.preventDefault(); };" |
| "document.querySelector('input[type=submit]').click();"); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| false /* expect_submitted_message */); |
| } |
| |
| // Tests that completing an Ajax request and having the form disappear will |
| // trigger submission from Autofill's point of view. |
| TEST_F(FormAutocompleteTest, AjaxSucceeded_NoLongerVisible) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm' action='http://example.com/blade.php'>" |
| "<input name='fname' id='fname' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form></html>"); |
| |
| // Simulate user input so that the form is "remembered". |
| WebDocument document = GetMainFrame()->document(); |
| WebElement element = document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| WebInputElement fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("Rick")); |
| |
| // Simulate removing the form just before the ajax request completes. |
| ExecuteJavaScriptForTests( |
| "var element = document.getElementById('myForm');" |
| "element.parentNode.removeChild(element);"); |
| |
| // Simulate an Ajax request completing. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| true /* expect_submitted_message */); |
| } |
| |
| // Tests that completing an Ajax request and having the form with a specific |
| // action disappear will trigger submission from Autofill's point of view, even |
| // if there is another form with the same data but different action on the page. |
| TEST_F(FormAutocompleteTest, |
| AjaxSucceeded_NoLongerVisible_DifferentActionsSameData) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm' action='http://example.com/blade.php'>" |
| "<input name='fname' id='fname' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form>" |
| "<form id='myForm2' action='http://example.com/runner.php'>" |
| "<input name='fname' id='fname2' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form></html>"); |
| |
| // Simulate user input so that the form is "remembered". |
| WebDocument document = GetMainFrame()->document(); |
| WebElement element = document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| WebInputElement fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("Rick")); |
| |
| // Simulate removing the form just before the ajax request completes. |
| ExecuteJavaScriptForTests( |
| "var element = document.getElementById('myForm');" |
| "element.parentNode.removeChild(element);"); |
| |
| // Simulate an Ajax request completing. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| true /* expect_submitted_message */); |
| } |
| |
| // Tests that completing an Ajax request and having the form with no action |
| // specified disappear will trigger submission from Autofill's point of view, |
| // even if there is still another form with no action in the page. It will |
| // compare field data within the forms. |
| // TODO(kolos) Re-enable when the implementation of IsFormVisible is on-par |
| // for these platforms. |
| #if defined(OS_MACOSX) || defined(OS_ANDROID) |
| #define MAYBE_NoLongerVisibleBothNoActions DISABLED_NoLongerVisibleBothNoActions |
| #else |
| #define MAYBE_NoLongerVisibleBothNoActions NoLongerVisibleBothNoActions |
| #endif |
| TEST_F(FormAutocompleteTest, MAYBE_NoLongerVisibleBothNoActions) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm'>" |
| "<input name='fname' id='fname' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form>" |
| "<form id='myForm2'>" |
| "<input name='fname' id='fname2' value='John'/>" |
| "<input name='lname' value='Doe'/><input type=submit></form></html>"); |
| |
| // Simulate user input so that the form is "remembered". |
| WebDocument document = GetMainFrame()->document(); |
| WebElement element = document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| WebInputElement fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("Rick")); |
| |
| // Simulate removing the form just before the ajax request completes. |
| ExecuteJavaScriptForTests( |
| "var element = document.getElementById('myForm');" |
| "element.parentNode.removeChild(element);"); |
| |
| // Simulate an Ajax request completing. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| true /* expect_submitted_message */); |
| } |
| |
| // Tests that completing an Ajax request and having the form with no action |
| // specified disappear will trigger submission from Autofill's point of view. |
| TEST_F(FormAutocompleteTest, AjaxSucceeded_NoLongerVisible_NoAction) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm'>" |
| "<input name='fname' id='fname' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form></html>"); |
| |
| // Simulate user input so that the form is "remembered". |
| WebDocument document = GetMainFrame()->document(); |
| WebElement element = |
| document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| WebInputElement fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("Rick")); |
| |
| // Simulate removing the form just before the ajax request completes. |
| ExecuteJavaScriptForTests("var element = document.getElementById('myForm');" |
| "element.parentNode.removeChild(element);"); |
| |
| // Simulate an Ajax request completing. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| true /* expect_submitted_message */); |
| } |
| |
| // Tests that completing an Ajax request but leaving a form visible will not |
| // trigger submission from Autofill's point of view. |
| TEST_F(FormAutocompleteTest, AjaxSucceeded_StillVisible) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm' action='http://example.com/blade.php'>" |
| "<input name='fname' id='fname' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form></html>"); |
| |
| // Simulate user input so that the form is "remembered". |
| WebDocument document = GetMainFrame()->document(); |
| WebElement element = |
| document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| WebInputElement fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("Rick")); |
| |
| // Simulate an Ajax request completing. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| // No submission messages sent. |
| VerifyNoSubmitMessagesReceived(fake_driver_); |
| } |
| |
| // Tests that completing an Ajax request without any prior form interaction |
| // does not trigger form submission from Autofill's point of view. |
| TEST_F(FormAutocompleteTest, AjaxSucceeded_NoFormInteractionInvisible) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm' action='http://example.com/blade.php'>" |
| "<input name='fname' id='fname' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form></html>"); |
| |
| // No form interaction. |
| |
| // Simulate removing the form just before the ajax request completes. |
| ExecuteJavaScriptForTests("var element = document.getElementById('myForm');" |
| "element.parentNode.removeChild(element);"); |
| |
| // Simulate an Ajax request completing without prior user interaction. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| // No submission messages sent. |
| VerifyNoSubmitMessagesReceived(fake_driver_); |
| } |
| |
| // Tests that completing an Ajax request after having autofilled a form, |
| // with the form disappearing, will trigger submission from Autofill's |
| // point of view. |
| TEST_F(FormAutocompleteTest, AjaxSucceeded_FilledFormIsInvisible) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm' action='http://example.com/blade.php'>" |
| "<input name='fname' id='fname'/>" |
| "<input name='lname'/></form></html>"); |
| |
| // Simulate filling a form using Autofill. |
| SimulateOnFillForm(autofill_agent_, GetMainFrame()); |
| |
| // Simulate removing the form just before the ajax request completes. |
| ExecuteJavaScriptForTests("var element = document.getElementById('myForm');" |
| "element.parentNode.removeChild(element);"); |
| |
| // Simulate an Ajax request completing. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "John", "Smith", |
| true /* expect_submitted_message */); |
| } |
| |
| // Tests that completing an Ajax request after having autofilled a form, |
| // without the form disappearing, will not trigger submission from Autofill's |
| // point of view. |
| TEST_F(FormAutocompleteTest, AjaxSucceeded_FilledFormStillVisible) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm' action='http://example.com/blade.php'>" |
| "<input name='fname' id='fname' value='Rick'/>" |
| "<input name='lname' value='Deckard'/></form></html>"); |
| |
| // Simulate filling a form using Autofill. |
| SimulateOnFillForm(autofill_agent_, GetMainFrame()); |
| |
| // Form still visible. |
| |
| // Simulate an Ajax request completing. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| // No submission messages sent. |
| VerifyNoSubmitMessagesReceived(fake_driver_); |
| } |
| |
| // Tests that completing an Ajax request without a form present will still |
| // trigger submission, if all the inputs the user has modified disappear. |
| TEST_F(FormAutocompleteTest, AjaxSucceeded_FormlessElements) { |
| // Load a "form." Note that kRequiredFieldsForUpload fields are required |
| // for the formless logic to trigger, so we add a throwaway third field. |
| LoadHTML( |
| "<head><title>Checkout</title></head>" |
| "<input type='text' name='fname' id='fname'/>" |
| "<input type='text' name='lname' value='Puckett'/>" |
| "<input type='number' name='number' value='34'/>"); |
| |
| // Simulate user input. |
| WebDocument document = GetMainFrame()->document(); |
| WebElement element = document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| WebInputElement fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("Kirby")); |
| |
| // Remove element from view. |
| ExecuteJavaScriptForTests( |
| "var element = document.getElementById('fname');" |
| "element.style.display = 'none';"); |
| |
| // Simulate AJAX request. |
| static_cast<blink::WebAutofillClient*>(autofill_agent_)->ajaxSucceeded(); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Kirby", "Puckett", |
| /* expect_submitted_message = */ true); |
| } |
| |
| // Unit test for CollectFormlessElements. |
| TEST_F(FormAutocompleteTest, CollectFormlessElements) { |
| LoadHTML( |
| "<html><title>Checkout</title></head>" |
| "<input type='text' name='text_input'/>" |
| "<input type='checkbox' name='check_input'/>" |
| "<input type='number' name='number_input'/>" |
| "<select name='select_input'/>" |
| " <option value='option_1'></option>" |
| " <option value='option_2'></option>" |
| "</select>" |
| "<form><input type='text' name='excluded'/></form>" |
| "</html>"); |
| |
| FormData result; |
| autofill_agent_->CollectFormlessElements(&result); |
| |
| // Asserting size 4 also ensures that 'excluded' field inside <form> is not |
| // collected. |
| ASSERT_EQ(4U, result.fields.size()); |
| EXPECT_EQ(WebString("text_input"), result.fields[0].name); |
| EXPECT_EQ(WebString("check_input"), result.fields[1].name); |
| EXPECT_EQ(WebString("number_input"), result.fields[2].name); |
| EXPECT_EQ(WebString("select_input"), result.fields[3].name); |
| } |
| |
| // Test that a FocusNoLongerOnForm message is sent if focus goes from an |
| // interacted form to an element outside the form. |
| TEST_F(FormAutocompleteTest, |
| InteractedFormNoLongerFocused_FocusNoLongerOnForm) { |
| // Load a form. |
| LoadHTML( |
| "<html><input type='text' id='different'/>" |
| "<form id='myForm' action='http://example.com/blade.php'>" |
| "<input name='fname' id='fname' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form></html>"); |
| |
| // Simulate user input so that the form is "remembered". |
| WebDocument document = GetMainFrame()->document(); |
| WebElement element = document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| WebInputElement fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("Rick")); |
| |
| ASSERT_FALSE(fake_driver_.did_unfocus_form()); |
| |
| // Change focus to a different node outside the form. |
| WebElement different = |
| document.getElementById(WebString::fromUTF8("different")); |
| SetFocused(different); |
| |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| |
| EXPECT_TRUE(fake_driver_.did_unfocus_form()); |
| } |
| |
| // Test that a FocusNoLongerOnForm message is sent if focus goes from one |
| // interacted form to another. |
| TEST_F(FormAutocompleteTest, InteractingInDifferentForms_FocusNoLongerOnForm) { |
| // Load a form. |
| LoadHTML( |
| "<html><form id='myForm' action='http://example.com/blade.php'>" |
| "<input name='fname' id='fname' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form>" |
| "<form id='myForm2' action='http://example.com/runner.php'>" |
| "<input name='fname' id='fname2' value='Bob'/>" |
| "<input name='lname' value='Deckard'/><input type=submit></form></html>"); |
| |
| // Simulate user input in the first form so that the form is "remembered". |
| WebDocument document = GetMainFrame()->document(); |
| WebElement element = document.getElementById(WebString::fromUTF8("fname")); |
| ASSERT_FALSE(element.isNull()); |
| WebInputElement fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("Rick")); |
| |
| ASSERT_FALSE(fake_driver_.did_unfocus_form()); |
| |
| // Simulate user input in the second form so that a "no longer focused" |
| // message is sent for the first form. |
| document = GetMainFrame()->document(); |
| element = document.getElementById(WebString::fromUTF8("fname2")); |
| ASSERT_FALSE(element.isNull()); |
| fname_element = element.to<WebInputElement>(); |
| SimulateUserInputChangeForElement(&fname_element, std::string("John")); |
| |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| |
| EXPECT_TRUE(fake_driver_.did_unfocus_form()); |
| } |
| |
| // Tests that submitting a form that has autocomplete="off" generates |
| // WillSubmitForm and FormSubmitted messages. |
| TEST_F(FormAutocompleteTest, AutoCompleteOffFormSubmit) { |
| // Load a form. |
| LoadHTML("<html><form id='myForm' autocomplete='off'>" |
| "<input name='fname' value='Rick'/>" |
| "<input name='lname' value='Deckard'/>" |
| "</form></html>"); |
| |
| // Submit the form. |
| ExecuteJavaScriptForTests("document.getElementById('myForm').submit();"); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| true /* expect_submitted_message */); |
| } |
| |
| // Tests that fields with autocomplete off are submitted. |
| TEST_F(FormAutocompleteTest, AutoCompleteOffInputSubmit) { |
| // Load a form. |
| LoadHTML("<html><form id='myForm'>" |
| "<input name='fname' value='Rick'/>" |
| "<input name='lname' value='Deckard' autocomplete='off'/>" |
| "</form></html>"); |
| |
| // Submit the form. |
| ExecuteJavaScriptForTests("document.getElementById('myForm').submit();"); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| true /* expect_submitted_message */); |
| } |
| |
| // Tests that submitting a form that has been dynamically set as autocomplete |
| // off generates WillSubmitForm and FormSubmitted messages. |
| // Note: We previously did the opposite, for bug http://crbug.com/36520 |
| TEST_F(FormAutocompleteTest, DynamicAutoCompleteOffFormSubmit) { |
| LoadHTML("<html><form id='myForm'><input name='fname' value='Rick'/>" |
| "<input name='lname' value='Deckard'/></form></html>"); |
| |
| WebElement element = |
| GetMainFrame()->document().getElementById(blink::WebString("myForm")); |
| ASSERT_FALSE(element.isNull()); |
| blink::WebFormElement form = element.to<blink::WebFormElement>(); |
| EXPECT_TRUE(form.autoComplete()); |
| |
| // Dynamically mark the form as autocomplete off. |
| ExecuteJavaScriptForTests( |
| "document.getElementById('myForm')." |
| "setAttribute('autocomplete', 'off');"); |
| ProcessPendingMessages(); |
| EXPECT_FALSE(form.autoComplete()); |
| |
| // Submit the form. |
| ExecuteJavaScriptForTests("document.getElementById('myForm').submit();"); |
| ProcessPendingMessages(); |
| |
| VerifyReceivedRendererMessages(fake_driver_, "Rick", "Deckard", |
| true /* expect_submitted_message */); |
| } |
| |
| } // namespace autofill |