blob: f13c77781aa4423378aae9a18d80a70bb3a44965 [file] [log] [blame]
// Copyright 2018 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 "components/password_manager/core/browser/password_form_filling.h"
#include <map>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h"
#include "build/build_config.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/password_manager/core/browser/password_form_metrics_recorder.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using autofill::PasswordForm;
using autofill::PasswordFormFillData;
using base::ASCIIToUTF16;
using testing::_;
using testing::SaveArg;
namespace password_manager {
namespace {
class MockPasswordManagerDriver : public StubPasswordManagerDriver {
public:
MockPasswordManagerDriver() {}
~MockPasswordManagerDriver() override {}
MOCK_METHOD1(FillPasswordForm, void(const PasswordFormFillData&));
MOCK_METHOD0(InformNoSavedCredentials, void());
MOCK_METHOD1(ShowInitialPasswordAccountSuggestions,
void(const PasswordFormFillData&));
MOCK_METHOD1(AllowPasswordGenerationForForm, void(const PasswordForm&));
};
class MockPasswordManagerClient : public StubPasswordManagerClient {
public:
MockPasswordManagerClient() {}
MOCK_METHOD3(PasswordWasAutofilled,
void(const std::map<base::string16, const PasswordForm*>&,
const GURL&,
const std::vector<const PasswordForm*>*));
};
} // namespace
class PasswordFormFillingTest : public testing::Test {
public:
PasswordFormFillingTest() {
observed_form_.origin = GURL("http://accounts.google.com/a/LoginAuth");
observed_form_.action = GURL("http://accounts.google.com/a/Login");
observed_form_.username_element = ASCIIToUTF16("Email");
observed_form_.password_element = ASCIIToUTF16("Passwd");
observed_form_.submit_element = ASCIIToUTF16("signIn");
observed_form_.signon_realm = "http://accounts.google.com";
observed_form_.form_data.name = ASCIIToUTF16("the-form-name");
saved_match_ = observed_form_;
saved_match_.origin = GURL("http://accounts.google.com/a/ServiceLoginAuth");
saved_match_.action = GURL("http://accounts.google.com/a/ServiceLogin");
saved_match_.preferred = true;
saved_match_.username_value = ASCIIToUTF16("test@gmail.com");
saved_match_.password_value = ASCIIToUTF16("test1");
psl_saved_match_ = saved_match_;
psl_saved_match_.is_public_suffix_match = true;
psl_saved_match_.origin =
GURL("http://m.accounts.google.com/a/ServiceLoginAuth");
psl_saved_match_.action = GURL("http://m.accounts.google.com/a/Login");
psl_saved_match_.signon_realm = "http://m.accounts.google.com";
metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
true, client_.GetUkmSourceId());
}
protected:
MockPasswordManagerDriver driver_;
MockPasswordManagerClient client_;
PasswordForm observed_form_;
PasswordForm saved_match_;
PasswordForm psl_saved_match_;
scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder_;
std::vector<const autofill::PasswordForm*> federated_matches_;
};
TEST_F(PasswordFormFillingTest, NoSavedCredentials) {
std::map<base::string16, const autofill::PasswordForm*> best_matches;
EXPECT_CALL(driver_, InformNoSavedCredentials());
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
EXPECT_CALL(driver_, ShowInitialPasswordAccountSuggestions(_)).Times(0);
LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
&client_, &driver_, false /* is_blacklisted */, observed_form_,
best_matches, federated_matches_, nullptr, metrics_recorder_.get());
EXPECT_EQ(LikelyFormFilling::kNoFilling, likely_form_filling);
}
TEST_F(PasswordFormFillingTest, Autofill) {
for (bool is_blacklisted : {false, true}) {
std::map<base::string16, const autofill::PasswordForm*> best_matches;
best_matches[saved_match_.username_value] = &saved_match_;
PasswordForm another_saved_match = saved_match_;
another_saved_match.username_value += ASCIIToUTF16("1");
another_saved_match.password_value += ASCIIToUTF16("1");
best_matches[another_saved_match.username_value] = &another_saved_match;
EXPECT_CALL(driver_, InformNoSavedCredentials()).Times(0);
PasswordFormFillData fill_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
EXPECT_CALL(driver_, ShowInitialPasswordAccountSuggestions(_)).Times(0);
EXPECT_CALL(client_, PasswordWasAutofilled(_, _, _));
LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
&client_, &driver_, is_blacklisted, observed_form_, best_matches,
federated_matches_, &saved_match_, metrics_recorder_.get());
EXPECT_EQ(LikelyFormFilling::kFillOnPageLoad, likely_form_filling);
// Check that the message to the renderer (i.e. |fill_data|) is filled
// correctly.
EXPECT_EQ(observed_form_.origin, fill_data.origin);
EXPECT_FALSE(fill_data.wait_for_username);
EXPECT_EQ(observed_form_.username_element, fill_data.username_field.name);
EXPECT_EQ(saved_match_.username_value, fill_data.username_field.value);
EXPECT_EQ(observed_form_.password_element, fill_data.password_field.name);
EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value);
// Check that information about non-preferred best matches is filled.
ASSERT_EQ(1u, fill_data.additional_logins.size());
EXPECT_EQ(another_saved_match.username_value,
fill_data.additional_logins.begin()->first);
EXPECT_EQ(another_saved_match.password_value,
fill_data.additional_logins.begin()->second.password);
// Realm is empty for non-psl match.
EXPECT_TRUE(fill_data.additional_logins.begin()->second.realm.empty());
}
}
TEST_F(PasswordFormFillingTest, TestFillOnLoadSuggestion) {
const struct {
const char* description;
bool new_password_present;
bool current_password_present;
} kTestCases[] = {
{
.description = "No new, some current",
.new_password_present = false,
.current_password_present = true,
},
{
.description = "No current, some new",
.new_password_present = true,
.current_password_present = false,
},
{
.description = "Both",
.new_password_present = true,
.current_password_present = true,
},
};
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNewPasswordFormParsing);
for (const auto& test_case : kTestCases) {
SCOPED_TRACE(test_case.description);
std::map<base::string16, const PasswordForm*> best_matches;
best_matches[saved_match_.username_value] = &saved_match_;
PasswordForm observed_form = observed_form_;
observed_form.password_element_renderer_id = 123;
if (!test_case.new_password_present)
observed_form.new_password_element = ASCIIToUTF16("New Passwd");
if (!test_case.current_password_present) {
observed_form.password_element.clear();
observed_form.password_element_renderer_id =
autofill::FormFieldData::kNotSetFormControlRendererId;
}
PasswordFormFillData fill_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
EXPECT_CALL(client_, PasswordWasAutofilled(_, _, _));
LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
&client_, &driver_, false, observed_form, best_matches,
federated_matches_, &saved_match_, metrics_recorder_.get());
// In all cases where a current password exists, fill on load should be
// permitted. Otherwise, the renderer will not fill anyway and return
// kFillOnAccountSelect.
if (test_case.current_password_present)
EXPECT_EQ(LikelyFormFilling::kFillOnPageLoad, likely_form_filling);
else
EXPECT_EQ(LikelyFormFilling::kFillOnAccountSelect, likely_form_filling);
}
}
TEST_F(PasswordFormFillingTest, AutofillPSLMatch) {
std::map<base::string16, const autofill::PasswordForm*> best_matches;
best_matches[saved_match_.username_value] = &psl_saved_match_;
EXPECT_CALL(driver_, InformNoSavedCredentials()).Times(0);
PasswordFormFillData fill_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
EXPECT_CALL(driver_, ShowInitialPasswordAccountSuggestions(_)).Times(0);
EXPECT_CALL(client_, PasswordWasAutofilled(_, _, _));
LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
&client_, &driver_, false /* is_blacklisted */, observed_form_,
best_matches, federated_matches_, &psl_saved_match_,
metrics_recorder_.get());
EXPECT_EQ(LikelyFormFilling::kFillOnAccountSelect, likely_form_filling);
// Check that the message to the renderer (i.e. |fill_data|) is filled
// correctly.
EXPECT_EQ(observed_form_.origin, fill_data.origin);
EXPECT_TRUE(fill_data.wait_for_username);
EXPECT_EQ(psl_saved_match_.signon_realm, fill_data.preferred_realm);
EXPECT_EQ(observed_form_.username_element, fill_data.username_field.name);
EXPECT_EQ(saved_match_.username_value, fill_data.username_field.value);
EXPECT_EQ(observed_form_.password_element, fill_data.password_field.name);
EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value);
}
TEST_F(PasswordFormFillingTest, FillingOnHttp) {
ASSERT_FALSE(GURL(saved_match_.signon_realm).SchemeIsCryptographic());
std::map<base::string16, const autofill::PasswordForm*> best_matches;
best_matches.emplace(saved_match_.username_value, &saved_match_);
for (bool enable_foas_http : {false, true}) {
SCOPED_TRACE(testing::Message() << "Enable FOAS on HTTP: " << std::boolalpha
<< enable_foas_http);
base::test::ScopedFeatureList features;
features.InitWithFeatureState(features::kFillOnAccountSelectHttp,
enable_foas_http);
LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
&client_, &driver_, false /* is_blacklisted */, observed_form_,
best_matches, federated_matches_, &saved_match_,
metrics_recorder_.get());
EXPECT_EQ(enable_foas_http ? LikelyFormFilling::kFillOnAccountSelect
: LikelyFormFilling::kFillOnPageLoad,
likely_form_filling);
}
}
#if defined(OS_ANDROID)
TEST_F(PasswordFormFillingTest, TouchToFill) {
std::map<base::string16, const autofill::PasswordForm*> best_matches;
best_matches.emplace(saved_match_.username_value, &saved_match_);
for (bool enable_touch_to_fill : {false, true}) {
SCOPED_TRACE(testing::Message() << "Enable Touch To Fill: "
<< std::boolalpha << enable_touch_to_fill);
base::test::ScopedFeatureList features;
features.InitWithFeatureState(autofill::features::kTouchToFillAndroid,
enable_touch_to_fill);
LikelyFormFilling likely_form_filling = SendFillInformationToRenderer(
&client_, &driver_, false /* is_blacklisted */, observed_form_,
best_matches, federated_matches_, &saved_match_,
metrics_recorder_.get());
EXPECT_EQ(enable_touch_to_fill ? LikelyFormFilling::kFillOnAccountSelect
: LikelyFormFilling::kFillOnPageLoad,
likely_form_filling);
}
}
#endif
} // namespace password_manager