blob: 6d130194e6b0a80a680a4aa24f7f9be2fdc52b3e [file] [log] [blame]
// Copyright 2013 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 <string.h>
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#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/password_generation_agent.h"
#include "components/autofill/core/common/form_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/WebWidget.h"
using blink::WebDocument;
using blink::WebElement;
using blink::WebInputElement;
using blink::WebNode;
using blink::WebString;
namespace autofill {
class TestPasswordGenerationAgent : public PasswordGenerationAgent {
public:
explicit TestPasswordGenerationAgent(content::RenderView* view)
: PasswordGenerationAgent(view) {}
virtual ~TestPasswordGenerationAgent() {}
// Make this public
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
return PasswordGenerationAgent::OnMessageReceived(message);
}
const std::vector<IPC::Message*>& messages() {
return messages_.get();
}
void ClearMessages() {
messages_.clear();
}
protected:
virtual bool ShouldAnalyzeDocument(const blink::WebDocument& document) const
OVERRIDE {
return true;
}
virtual bool Send(IPC::Message* message) OVERRIDE {
messages_.push_back(message);
return true;
}
private:
ScopedVector<IPC::Message> messages_;
DISALLOW_COPY_AND_ASSIGN(TestPasswordGenerationAgent);
};
class PasswordGenerationAgentTest : public ChromeRenderViewTest {
public:
PasswordGenerationAgentTest() {}
virtual void SetUp() {
// We don't actually create a PasswordGenerationAgent during
// ChromeRenderViewTest::SetUp because it's behind a flag. Since we want
// to use a test manager anyway, we just create our own.
ChromeRenderViewTest::SetUp();
generation_manager_.reset(new TestPasswordGenerationAgent(view_));
}
virtual void TearDown() {
LoadHTML("");
generation_manager_.reset();
ChromeRenderViewTest::TearDown();
}
void SimulateClickOnDecoration(blink::WebInputElement* input_element) {
generation_manager_->ClearMessages();
blink::WebElement decoration =
input_element->decorationElementFor(generation_manager_.get());
decoration.simulateClick();
}
bool DecorationIsVisible(blink::WebInputElement* input_element) {
blink::WebElement decoration =
input_element->decorationElementFor(generation_manager_.get());
return decoration.hasNonEmptyBoundingBox();
}
void SetNotBlacklistedMessage(const char* form_str) {
autofill::PasswordForm form;
form.origin =
GURL(base::StringPrintf("data:text/html;charset=utf-8,%s", form_str));
AutofillMsg_FormNotBlacklisted msg(0, form);
generation_manager_->OnMessageReceived(msg);
}
void SetAccountCreationFormsDetectedMessage(const char* form_str) {
autofill::FormData form;
form.origin =
GURL(base::StringPrintf("data:text/html;charset=utf-8,%s", form_str));
std::vector<autofill::FormData> forms;
forms.push_back(form);
AutofillMsg_AccountCreationFormsDetected msg(0, forms);
generation_manager_->OnMessageReceived(msg);
}
void ExpectPasswordGenerationIconShown(const char* element_id, bool shown) {
WebDocument document = GetMainFrame()->document();
WebElement element =
document.getElementById(WebString::fromUTF8(element_id));
ASSERT_FALSE(element.isNull());
WebInputElement target_element = element.to<WebInputElement>();
if (shown) {
EXPECT_TRUE(DecorationIsVisible(&target_element));
SimulateClickOnDecoration(&target_element);
EXPECT_EQ(1u, generation_manager_->messages().size());
EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
generation_manager_->messages()[0]->type());
} else {
EXPECT_FALSE(DecorationIsVisible(&target_element));
}
}
protected:
scoped_ptr<TestPasswordGenerationAgent> generation_manager_;
private:
DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgentTest);
};
const char kSigninFormHTML[] =
"<FORM name = 'blah' action = 'http://www.random.com/'> "
" <INPUT type = 'text' id = 'username'/> "
" <INPUT type = 'password' id = 'password'/> "
" <INPUT type = 'submit' value = 'LOGIN' />"
"</FORM>";
const char kAccountCreationFormHTML[] =
"<FORM name = 'blah' action = 'http://www.random.com/'> "
" <INPUT type = 'text' id = 'username'/> "
" <INPUT type = 'password' id = 'first_password' "
" autocomplete = 'off' size = 5/>"
" <INPUT type = 'password' id = 'second_password' size = 5/> "
" <INPUT type = 'text' id = 'address'/> "
" <INPUT type = 'submit' value = 'LOGIN' />"
"</FORM>";
const char kHiddenPasswordAccountCreationFormHTML[] =
"<FORM name = 'blah' action = 'http://www.random.com/'> "
" <INPUT type = 'text' id = 'username'/> "
" <INPUT type = 'password' id = 'first_password'/> "
" <INPUT type = 'password' id = 'second_password' style='display:none'/> "
" <INPUT type = 'submit' value = 'LOGIN' />"
"</FORM>";
const char kInvalidActionAccountCreationFormHTML[] =
"<FORM name = 'blah' action = 'invalid'> "
" <INPUT type = 'text' id = 'username'/> "
" <INPUT type = 'password' id = 'first_password'/> "
" <INPUT type = 'password' id = 'second_password'/> "
" <INPUT type = 'submit' value = 'LOGIN' />"
"</FORM>";
TEST_F(PasswordGenerationAgentTest, DetectionTest) {
// Don't shown the icon for non account creation forms.
LoadHTML(kSigninFormHTML);
ExpectPasswordGenerationIconShown("password", false);
// We don't show the decoration yet because the feature isn't enabled.
LoadHTML(kAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", false);
// Pretend like We have received message indicating site is not blacklisted,
// and we have received message indicating the form is classified as
// ACCOUNT_CREATION_FORM form Autofill server. We should show the icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", true);
// This doesn't trigger because hidden password fields are ignored.
LoadHTML(kHiddenPasswordAccountCreationFormHTML);
SetNotBlacklistedMessage(kHiddenPasswordAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(
kHiddenPasswordAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", false);
// This doesn't trigger because the form action is invalid.
LoadHTML(kInvalidActionAccountCreationFormHTML);
SetNotBlacklistedMessage(kInvalidActionAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kInvalidActionAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", false);
}
TEST_F(PasswordGenerationAgentTest, FillTest) {
// Make sure that we are enabled before loading HTML.
LoadHTML(kAccountCreationFormHTML);
WebDocument document = GetMainFrame()->document();
WebElement element =
document.getElementById(WebString::fromUTF8("first_password"));
ASSERT_FALSE(element.isNull());
WebInputElement first_password_element = element.to<WebInputElement>();
element = document.getElementById(WebString::fromUTF8("second_password"));
ASSERT_FALSE(element.isNull());
WebInputElement second_password_element = element.to<WebInputElement>();
// Both password fields should be empty.
EXPECT_TRUE(first_password_element.value().isNull());
EXPECT_TRUE(second_password_element.value().isNull());
base::string16 password = ASCIIToUTF16("random_password");
AutofillMsg_GeneratedPasswordAccepted msg(0, password);
generation_manager_->OnMessageReceived(msg);
// Password fields are filled out and set as being autofilled.
EXPECT_EQ(password, first_password_element.value());
EXPECT_EQ(password, second_password_element.value());
EXPECT_TRUE(first_password_element.isAutofilled());
EXPECT_TRUE(second_password_element.isAutofilled());
// Focus moved to the next input field.
// TODO(zysxqn): Change this back to the address element once Bug 90224
// https://bugs.webkit.org/show_bug.cgi?id=90224 has been fixed.
element = document.getElementById(WebString::fromUTF8("first_password"));
ASSERT_FALSE(element.isNull());
EXPECT_EQ(element, document.focusedNode());
}
TEST_F(PasswordGenerationAgentTest, BlacklistedTest) {
// Did not receive not blacklisted message. Don't show password generation
// icon.
LoadHTML(kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", false);
// Receive one not blacklisted message for non account creation form. Don't
// show password generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kSigninFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", false);
// Receive one not blackliste message for account creation form. Show password
// generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", true);
// Receive two not blacklisted messages, one is for account creation form and
// the other is not. Show password generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kSigninFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", true);
}
TEST_F(PasswordGenerationAgentTest, AccountCreationFormsDetectedTest) {
// Did not receive account creation forms detected messege. Don't show
// password generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", false);
// Receive the account creation forms detected message. Show password
// generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
ExpectPasswordGenerationIconShown("first_password", true);
}
} // namespace autofill