blob: e9e835fa615c3c0d1a4065a2b9785f3d586e485c [file] [log] [blame]
// Copyright (c) 2012 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/command_line.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/test/base/chrome_render_view_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/autofill/content/common/autofill_driver.mojom.h"
#include "components/autofill/content/renderer/autofill_agent.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "content/public/common/content_switches.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/service_manager/public/cpp/interface_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/platform/WebVector.h"
#include "third_party/WebKit/public/web/WebDocument.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"
#include "third_party/WebKit/public/web/WebView.h"
using base::ASCIIToUTF16;
using blink::WebDocument;
using blink::WebElement;
using blink::WebFormElement;
using blink::WebFrame;
using blink::WebLocalFrame;
using blink::WebInputElement;
using blink::WebString;
using blink::WebURLRequest;
using blink::WebVector;
namespace autofill {
namespace {
class FakeContentAutofillDriver : public mojom::AutofillDriver {
public:
FakeContentAutofillDriver() : called_field_change_(false) {}
~FakeContentAutofillDriver() override {}
void BindRequest(mojom::AutofillDriverRequest request) {
bindings_.AddBinding(this, std::move(request));
}
bool called_field_change() const { return called_field_change_; }
const std::vector<FormData>* forms() const { return forms_.get(); }
void reset_forms() { return forms_.reset(); }
private:
// mojom::AutofillDriver:
void FirstUserGestureObserved() override {}
void FormsSeen(const std::vector<FormData>& forms,
base::TimeTicks timestamp) override {
// FormsSeen() could be called multiple times and sometimes even with empty
// forms array for main frame, but we're interested in only the first time
// call.
if (!forms_)
forms_.reset(new std::vector<FormData>(forms));
}
void WillSubmitForm(const FormData& form,
base::TimeTicks timestamp) override {}
void FormSubmitted(const FormData& form) override {}
void TextFieldDidChange(const FormData& form,
const FormFieldData& field,
base::TimeTicks timestamp) override {
called_field_change_ = true;
}
void QueryFormFieldAutofill(int32_t id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override {}
void HidePopup() override {}
void FocusNoLongerOnForm() override {}
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 TextFieldDidChange() get called.
bool called_field_change_;
// Records data received via FormSeen() call.
std::unique_ptr<std::vector<FormData>> forms_;
mojo::BindingSet<mojom::AutofillDriver> bindings_;
};
} // namespace
using AutofillQueryParam =
std::tuple<int, autofill::FormData, autofill::FormFieldData, gfx::RectF>;
class AutofillRendererTest : public ChromeRenderViewTest {
public:
AutofillRendererTest() {}
~AutofillRendererTest() 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.
service_manager::InterfaceProvider* remote_interfaces =
view_->GetMainRenderFrame()->GetRemoteInterfaces();
service_manager::InterfaceProvider::TestApi test_api(remote_interfaces);
test_api.SetBinderForName(
mojom::AutofillDriver::Name_,
base::Bind(&AutofillRendererTest::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(AutofillRendererTest);
};
TEST_F(AutofillRendererTest, SendForms) {
LoadHTML("<form method='POST'>"
" <input type='text' id='firstname'/>"
" <input type='text' id='middlename'/>"
" <input type='text' id='lastname' autoComplete='off'/>"
" <input type='hidden' id='email'/>"
" <select id='state'/>"
" <option>?</option>"
" <option>California</option>"
" <option>Texas</option>"
" </select>"
"</form>");
base::RunLoop run_loop;
run_loop.RunUntilIdle();
// Verify that "FormsSeen" sends the expected number of fields.
ASSERT_TRUE(fake_driver_.forms());
std::vector<FormData> forms = *(fake_driver_.forms());
ASSERT_EQ(1UL, forms.size());
ASSERT_EQ(4UL, forms[0].fields.size());
FormFieldData expected;
expected.name = ASCIIToUTF16("firstname");
expected.value = base::string16();
expected.form_control_type = "text";
expected.max_length = WebInputElement::defaultMaxLength();
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[0]);
expected.name = ASCIIToUTF16("middlename");
expected.value = base::string16();
expected.form_control_type = "text";
expected.max_length = WebInputElement::defaultMaxLength();
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[1]);
expected.name = ASCIIToUTF16("lastname");
expected.value = base::string16();
expected.form_control_type = "text";
expected.autocomplete_attribute = "off";
expected.max_length = WebInputElement::defaultMaxLength();
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[2]);
expected.autocomplete_attribute = std::string(); // reset
expected.name = ASCIIToUTF16("state");
expected.value = ASCIIToUTF16("?");
expected.form_control_type = "select-one";
expected.max_length = 0;
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[3]);
fake_driver_.reset_forms();
// Dynamically create a new form. A new message should be sent for it, but
// not for the previous form.
ExecuteJavaScriptForTests(
"var newForm=document.createElement('form');"
"newForm.id='new_testform';"
"newForm.action='http://google.com';"
"newForm.method='post';"
"var newFirstname=document.createElement('input');"
"newFirstname.setAttribute('type', 'text');"
"newFirstname.setAttribute('id', 'second_firstname');"
"newFirstname.value = 'Bob';"
"var newLastname=document.createElement('input');"
"newLastname.setAttribute('type', 'text');"
"newLastname.setAttribute('id', 'second_lastname');"
"newLastname.value = 'Hope';"
"var newEmail=document.createElement('input');"
"newEmail.setAttribute('type', 'text');"
"newEmail.setAttribute('id', 'second_email');"
"newEmail.value = 'bobhope@example.com';"
"newForm.appendChild(newFirstname);"
"newForm.appendChild(newLastname);"
"newForm.appendChild(newEmail);"
"document.body.appendChild(newForm);");
WaitForAutofillDidAssociateFormControl();
ASSERT_TRUE(fake_driver_.forms());
forms = *(fake_driver_.forms());
ASSERT_EQ(1UL, forms.size());
ASSERT_EQ(3UL, forms[0].fields.size());
expected.form_control_type = "text";
expected.max_length = WebInputElement::defaultMaxLength();
expected.name = ASCIIToUTF16("second_firstname");
expected.value = ASCIIToUTF16("Bob");
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[0]);
expected.name = ASCIIToUTF16("second_lastname");
expected.value = ASCIIToUTF16("Hope");
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[1]);
expected.name = ASCIIToUTF16("second_email");
expected.value = ASCIIToUTF16("bobhope@example.com");
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[2]);
}
TEST_F(AutofillRendererTest, EnsureNoFormSeenIfTooFewFields) {
LoadHTML("<form method='POST'>"
" <input type='text' id='firstname'/>"
" <input type='text' id='middlename'/>"
"</form>");
base::RunLoop run_loop;
run_loop.RunUntilIdle();
// Verify that "FormsSeen" isn't sent, as there are too few fields.
ASSERT_TRUE(fake_driver_.forms());
const std::vector<FormData>& forms = *(fake_driver_.forms());
ASSERT_EQ(0UL, forms.size());
}
// Regression test for [ http://crbug.com/346010 ].
TEST_F(AutofillRendererTest, DontCrashWhileAssociatingForms) {
LoadHTML("<form id='form'>"
"<foo id='foo'>"
"<script id='script'>"
"document.documentElement.appendChild(foo);"
"newDoc = document.implementation.createDocument("
" 'http://www.w3.org/1999/xhtml', 'html');"
"foo.insertBefore(form, script);"
"newDoc.adoptNode(foo);"
"</script>");
// Shouldn't crash.
}
TEST_F(AutofillRendererTest, DynamicallyAddedUnownedFormElements) {
std::string html_data;
base::FilePath test_path = ui_test_utils::GetTestFilePath(
base::FilePath(FILE_PATH_LITERAL("autofill")),
base::FilePath(FILE_PATH_LITERAL("autofill_noform_dynamic.html")));
ASSERT_TRUE(base::ReadFileToString(test_path, &html_data));
LoadHTML(html_data.c_str());
base::RunLoop run_loop;
run_loop.RunUntilIdle();
// Verify that "FormsSeen" sends the expected number of fields.
ASSERT_TRUE(fake_driver_.forms());
std::vector<FormData> forms = *(fake_driver_.forms());
ASSERT_EQ(1UL, forms.size());
ASSERT_EQ(7UL, forms[0].fields.size());
fake_driver_.reset_forms();
ExecuteJavaScriptForTests("AddFields()");
WaitForAutofillDidAssociateFormControl();
ASSERT_TRUE(fake_driver_.forms());
forms = *(fake_driver_.forms());
ASSERT_EQ(1UL, forms.size());
ASSERT_EQ(9UL, forms[0].fields.size());
FormFieldData expected;
expected.name = ASCIIToUTF16("EMAIL_ADDRESS");
expected.value.clear();
expected.form_control_type = "text";
expected.max_length = WebInputElement::defaultMaxLength();
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[7]);
expected.name = ASCIIToUTF16("PHONE_HOME_WHOLE_NUMBER");
expected.value.clear();
expected.form_control_type = "text";
expected.max_length = WebInputElement::defaultMaxLength();
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[8]);
}
TEST_F(AutofillRendererTest, IgnoreNonUserGestureTextFieldChanges) {
LoadHTML("<form method='post'>"
" <input type='text' id='full_name'/>"
"</form>");
blink::WebInputElement full_name =
GetMainFrame()->document().getElementById("full_name")
.to<blink::WebInputElement>();
while (!full_name.focused())
GetMainFrame()->view()->advanceFocus(false);
// Not a user gesture, so no IPC message to browser.
DisableUserGestureSimulationForAutofill();
ASSERT_FALSE(fake_driver_.called_field_change());
full_name.setValue("Alice", true);
GetMainFrame()->autofillClient()->textFieldDidChange(full_name);
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(fake_driver_.called_field_change());
// A user gesture will send a message to the browser.
EnableUserGestureSimulationForAutofill();
SimulateUserInputChangeForElement(&full_name, "Alice");
ASSERT_TRUE(fake_driver_.called_field_change());
}
} // namespace autofill