// 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 "base/strings/utf_string_conversions.h"
#include "chrome/test/base/chrome_render_view_test.h"
#include "components/autofill/content/common/autofill_messages.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 "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebInputElement.h"
using blink::WebDocument;
using blink::WebFrame;
using blink::WebInputElement;
using blink::WebString;
namespace autofill {
typedef Tuple5<int,
autofill::FormData,
autofill::FormFieldData,
gfx::RectF,
bool> AutofillQueryParam;
TEST_F(ChromeRenderViewTest, SendForms) {
// Don't want any delay for form state sync changes. This will still post a
// message so updates will get coalesced, but as soon as we spin the message
// loop, it will generate an update.
SendContentStateImmediately();
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>");
// Verify that "FormsSeen" sends the expected number of fields.
const IPC::Message* message = render_thread_->sink().GetFirstMessageMatching(
AutofillHostMsg_FormsSeen::ID);
ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
AutofillHostMsg_FormsSeen::Param params;
AutofillHostMsg_FormsSeen::Read(message, ¶ms);
const std::vector<FormData>& forms = params.a;
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]);
// Verify that |didAcceptAutofillSuggestion()| sends the expected number of
// fields.
WebFrame* web_frame = GetMainFrame();
WebDocument document = web_frame->document();
WebInputElement firstname =
document.getElementById("firstname").to<WebInputElement>();
// Make sure to query for Autofill suggestions before selecting one.
autofill_agent_->element_ = firstname;
autofill_agent_->QueryAutofillSuggestions(firstname, false, false);
// Fill the form with a suggestion that contained a label. Labeled items
// indicate Autofill as opposed to Autocomplete. We're testing this
// distinction below with the |AutofillHostMsg_FillAutofillFormData::ID|
// message.
autofill_agent_->FillAutofillFormData(
firstname,
1,
AutofillAgent::AUTOFILL_PREVIEW);
ProcessPendingMessages();
const IPC::Message* message2 =
render_thread_->sink().GetUniqueMessageMatching(
AutofillHostMsg_FillAutofillFormData::ID);
ASSERT_NE(static_cast<IPC::Message*>(NULL), message2);
AutofillHostMsg_FillAutofillFormData::Param params2;
AutofillHostMsg_FillAutofillFormData::Read(message2, ¶ms2);
const FormData& form2 = params2.b;
ASSERT_EQ(3UL, form2.fields.size());
expected.name = ASCIIToUTF16("firstname");
expected.value = base::string16();
expected.form_control_type = "text";
expected.max_length = WebInputElement::defaultMaxLength();
EXPECT_FORM_FIELD_DATA_EQUALS(expected, form2.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, form2.fields[1]);
expected.name = ASCIIToUTF16("state");
expected.value = ASCIIToUTF16("?");
expected.form_control_type = "select-one";
expected.max_length = 0;
EXPECT_FORM_FIELD_DATA_EQUALS(expected, form2.fields[2]);
}
TEST_F(ChromeRenderViewTest, EnsureNoFormSeenIfTooFewFields) {
// Don't want any delay for form state sync changes. This will still post a
// message so updates will get coalesced, but as soon as we spin the message
// loop, it will generate an update.
SendContentStateImmediately();
LoadHTML("<form method=\"POST\">"
" <input type=\"text\" id=\"firstname\"/>"
" <input type=\"text\" id=\"middlename\"/>"
"</form>");
// Verify that "FormsSeen" isn't sent, as there are too few fields.
const IPC::Message* message = render_thread_->sink().GetFirstMessageMatching(
AutofillHostMsg_FormsSeen::ID);
ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
AutofillHostMsg_FormsSeen::Param params;
AutofillHostMsg_FormsSeen::Read(message, ¶ms);
const std::vector<FormData>& forms = params.a;
ASSERT_EQ(0UL, forms.size());
}
TEST_F(ChromeRenderViewTest, ShowAutofillWarning) {
// Don't want any delay for form state sync changes. This will still post a
// message so updates will get coalesced, but as soon as we spin the message
// loop, it will generate an update.
SendContentStateImmediately();
LoadHTML("<form method=\"POST\" autocomplete=\"Off\">"
" <input id=\"firstname\" autocomplete=\"OFF\"/>"
" <input id=\"middlename\"/>"
" <input id=\"lastname\"/>"
"</form>");
// Verify that "QueryFormFieldAutofill" isn't sent prior to a user
// interaction.
const IPC::Message* message0 = render_thread_->sink().GetFirstMessageMatching(
AutofillHostMsg_QueryFormFieldAutofill::ID);
EXPECT_EQ(static_cast<IPC::Message*>(NULL), message0);
WebFrame* web_frame = GetMainFrame();
WebDocument document = web_frame->document();
WebInputElement firstname =
document.getElementById("firstname").to<WebInputElement>();
WebInputElement middlename =
document.getElementById("middlename").to<WebInputElement>();
// Simulate attempting to Autofill the form from the first element, which
// specifies autocomplete="off". This should still trigger an IPC which
// shouldn't display warnings.
autofill_agent_->InputElementClicked(firstname, true, true);
const IPC::Message* message1 = render_thread_->sink().GetFirstMessageMatching(
AutofillHostMsg_QueryFormFieldAutofill::ID);
EXPECT_NE(static_cast<IPC::Message*>(NULL), message1);
AutofillQueryParam query_param;
AutofillHostMsg_QueryFormFieldAutofill::Read(message1, &query_param);
EXPECT_FALSE(query_param.e);
render_thread_->sink().ClearMessages();
// Simulate attempting to Autofill the form from the second element, which
// does not specify autocomplete="off". This should trigger an IPC that will
// show warnings, as we *do* show warnings for elements that don't themselves
// set autocomplete="off", but for which the form does.
autofill_agent_->InputElementClicked(middlename, true, true);
const IPC::Message* message2 = render_thread_->sink().GetFirstMessageMatching(
AutofillHostMsg_QueryFormFieldAutofill::ID);
ASSERT_NE(static_cast<IPC::Message*>(NULL), message2);
AutofillHostMsg_QueryFormFieldAutofill::Read(message2, &query_param);
EXPECT_TRUE(query_param.e);
}
} // namespace autofill