blob: 0cad85ab99f8e845a995a259ea22819129102b86 [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 "components/password_manager/core/browser/password_autofill_manager.h"
#include <memory>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_task_environment.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/popup_item_ids.h"
#include "components/autofill/core/browser/suggestion_test_helpers.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/favicon/core/test/mock_favicon_service.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.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 "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/image/image_unittest_util.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
#endif
// The name of the username/password element in the form.
const char kUsernameName[] = "username";
const char kInvalidUsername[] = "no-username";
const char kPasswordName[] = "password";
const char kAliceUsername[] = "alice";
const char kAlicePassword[] = "password";
using autofill::Suggestion;
using autofill::SuggestionVectorIconsAre;
using autofill::SuggestionVectorIdsAre;
using autofill::SuggestionVectorValuesAre;
using autofill::SuggestionVectorLabelsAre;
using testing::_;
using testing::ElementsAreArray;
using testing::Return;
using UkmEntry = ukm::builders::PageWithPassword;
namespace autofill {
class AutofillPopupDelegate;
}
namespace password_manager {
namespace {
constexpr char kMainFrameUrl[] = "https://example.com/";
constexpr char kDropdownSelectedHistogram[] =
"PasswordManager.PasswordDropdownItemSelected";
constexpr char kDropdownShownHistogram[] =
"PasswordManager.PasswordDropdownShown";
class MockPasswordManagerDriver : public StubPasswordManagerDriver {
public:
MOCK_METHOD2(FillSuggestion,
void(const base::string16&, const base::string16&));
MOCK_METHOD2(PreviewSuggestion,
void(const base::string16&, const base::string16&));
MOCK_METHOD0(GetPasswordManager, PasswordManager*());
};
class TestPasswordManagerClient : public StubPasswordManagerClient {
public:
TestPasswordManagerClient() : main_frame_url_(kMainFrameUrl) {}
~TestPasswordManagerClient() override = default;
MockPasswordManagerDriver* mock_driver() { return &driver_; }
const GURL& GetMainFrameURL() const override { return main_frame_url_; }
MOCK_METHOD0(GeneratePassword, void());
MOCK_METHOD0(GetFaviconService, favicon::FaviconService*());
MOCK_METHOD1(NavigateToManagePasswordsPage,
void(password_manager::ManagePasswordsReferrer));
private:
MockPasswordManagerDriver driver_;
GURL main_frame_url_;
};
class MockAutofillClient : public autofill::TestAutofillClient {
public:
MockAutofillClient() = default;
MOCK_METHOD5(ShowAutofillPopup,
void(const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
const std::vector<Suggestion>& suggestions,
bool autoselect_first_suggestion,
base::WeakPtr<autofill::AutofillPopupDelegate> delegate));
MOCK_METHOD0(HideAutofillPopup, void());
MOCK_METHOD1(ExecuteCommand, void(int));
};
bool IsPreLollipopAndroid() {
#if defined(OS_ANDROID)
return (base::android::BuildInfo::GetInstance()->sdk_int() <
base::android::SDK_VERSION_LOLLIPOP);
#else
return false;
#endif
}
std::vector<base::string16> GetSuggestionList(
std::vector<base::string16> credentials) {
if (!IsPreLollipopAndroid()) {
credentials.push_back(
l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS));
}
return credentials;
}
std::vector<base::string16> GetIconsList(std::vector<std::string> icons) {
std::vector<base::string16> ret(icons.size());
std::transform(icons.begin(), icons.end(), ret.begin(), &base::ASCIIToUTF16);
// On older Android versions the item "Manage passwords" is absent.
if (!IsPreLollipopAndroid())
ret.push_back(base::string16());
return ret;
}
} // namespace
class PasswordAutofillManagerTest : public testing::Test {
protected:
PasswordAutofillManagerTest()
: test_username_(base::ASCIIToUTF16(kAliceUsername)),
test_password_(base::ASCIIToUTF16(kAlicePassword)) {}
void SetUp() override {
// Add a preferred login and an additional login to the FillData.
autofill::FormFieldData username_field;
username_field.name = base::ASCIIToUTF16(kUsernameName);
username_field.value = test_username_;
fill_data_.username_field = username_field;
autofill::FormFieldData password_field;
password_field.name = base::ASCIIToUTF16(kPasswordName);
password_field.value = test_password_;
fill_data_.password_field = password_field;
}
void InitializePasswordAutofillManager(
TestPasswordManagerClient* client,
autofill::AutofillClient* autofill_client) {
password_autofill_manager_.reset(new PasswordAutofillManager(
client->mock_driver(), autofill_client, client));
favicon::MockFaviconService favicon_service;
EXPECT_CALL(*client, GetFaviconService())
.WillOnce(Return(&favicon_service));
EXPECT_CALL(favicon_service,
GetFaviconImageForPageURL(fill_data_.origin, _, _));
password_autofill_manager_->OnAddPasswordFillData(fill_data_);
testing::Mock::VerifyAndClearExpectations(client);
// Suppress the warnings in the tests.
EXPECT_CALL(*client, GetFaviconService()).WillRepeatedly(Return(nullptr));
}
protected:
autofill::PasswordFormFillData& fill_data() { return fill_data_; }
std::unique_ptr<PasswordAutofillManager> password_autofill_manager_;
base::string16 test_username_;
base::string16 test_password_;
private:
autofill::PasswordFormFillData fill_data_;
// The TestAutofillDriver uses a SequencedWorkerPool which expects the
// existence of a MessageLoop.
base::test::ScopedTaskEnvironment task_environment_;
};
TEST_F(PasswordAutofillManagerTest, FillSuggestion) {
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
InitializePasswordAutofillManager(client.get(), nullptr);
EXPECT_CALL(*client->mock_driver(),
FillSuggestion(test_username_, test_password_));
EXPECT_TRUE(
password_autofill_manager_->FillSuggestionForTest(test_username_));
testing::Mock::VerifyAndClearExpectations(client->mock_driver());
EXPECT_CALL(*client->mock_driver(), FillSuggestion(_, _)).Times(0);
EXPECT_FALSE(password_autofill_manager_->FillSuggestionForTest(
base::ASCIIToUTF16(kInvalidUsername)));
password_autofill_manager_->DidNavigateMainFrame();
EXPECT_FALSE(
password_autofill_manager_->FillSuggestionForTest(test_username_));
}
TEST_F(PasswordAutofillManagerTest, PreviewSuggestion) {
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
InitializePasswordAutofillManager(client.get(), nullptr);
EXPECT_CALL(*client->mock_driver(),
PreviewSuggestion(test_username_, test_password_));
EXPECT_TRUE(
password_autofill_manager_->PreviewSuggestionForTest(test_username_));
testing::Mock::VerifyAndClearExpectations(client->mock_driver());
EXPECT_CALL(*client->mock_driver(), PreviewSuggestion(_, _)).Times(0);
EXPECT_FALSE(password_autofill_manager_->PreviewSuggestionForTest(
base::ASCIIToUTF16(kInvalidUsername)));
password_autofill_manager_->DidNavigateMainFrame();
EXPECT_FALSE(
password_autofill_manager_->PreviewSuggestionForTest(test_username_));
}
// Test that the popup is marked as visible after receiving password
// suggestions.
TEST_F(PasswordAutofillManagerTest, ExternalDelegatePasswordSuggestions) {
for (bool is_suggestion_on_password_field : {false, true}) {
SCOPED_TRACE(testing::Message() << "is_suggestion_on_password_field = "
<< is_suggestion_on_password_field);
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
data.username_field.value = test_username_;
data.password_field.value = test_password_;
data.preferred_realm = "http://foo.com/";
favicon::MockFaviconService favicon_service;
EXPECT_CALL(*client, GetFaviconService())
.WillOnce(Return(&favicon_service));
favicon_base::FaviconImageCallback callback;
EXPECT_CALL(favicon_service, GetFaviconImageForPageURL(data.origin, _, _))
.WillOnce(DoAll(testing::SaveArg<1>(&callback), Return(1)));
password_autofill_manager_->OnAddPasswordFillData(data);
// Resolve the favicon.
favicon_base::FaviconImageResult image_result;
image_result.image = gfx::test::CreateImage(16, 16);
callback.Run(image_result);
std::vector<autofill::PopupItemId> ids = {
is_suggestion_on_password_field
? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY
: autofill::POPUP_ITEM_ID_USERNAME_ENTRY};
if (!IsPreLollipopAndroid()) {
ids.push_back(autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
}
std::vector<Suggestion> suggestions;
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(
_, _, SuggestionVectorIdsAre(testing::ElementsAreArray(ids)), false,
_))
.WillOnce(testing::SaveArg<2>(&suggestions));
int show_suggestion_options =
is_suggestion_on_password_field ? autofill::IS_PASSWORD_FIELD : 0;
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::string16(), show_suggestion_options,
element_bounds);
ASSERT_GE(suggestions.size(), 1u);
EXPECT_TRUE(gfx::test::AreImagesEqual(suggestions[0].custom_icon,
image_result.image));
EXPECT_CALL(*client->mock_driver(),
FillSuggestion(test_username_, test_password_));
// Accepting a suggestion should trigger a call to hide the popup.
EXPECT_CALL(*autofill_client, HideAutofillPopup());
base::HistogramTester histograms;
password_autofill_manager_->DidAcceptSuggestion(
test_username_, is_suggestion_on_password_field
? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY
: autofill::POPUP_ITEM_ID_USERNAME_ENTRY,
1);
histograms.ExpectUniqueSample(
kDropdownSelectedHistogram,
metrics_util::PasswordDropdownSelectedOption::kPassword, 1);
}
}
// Test that OnShowPasswordSuggestions correctly matches the given FormFieldData
// to the known PasswordFormFillData, and extracts the right suggestions.
TEST_F(PasswordAutofillManagerTest, ExtractSuggestions) {
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
data.username_field.value = test_username_;
data.password_field.value = test_password_;
data.preferred_realm = "http://foo.com/";
autofill::PasswordAndRealm additional;
additional.realm = "https://foobarrealm.org";
base::string16 additional_username(base::ASCIIToUTF16("John Foo"));
data.additional_logins[additional_username] = additional;
password_autofill_manager_->OnAddPasswordFillData(data);
// First, simulate displaying suggestions matching an empty prefix. Also
// verify that both the values and labels are filled correctly. The 'value'
// should be the user name; the 'label' should be the realm.
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(
element_bounds, _,
testing::AllOf(
SuggestionVectorValuesAre(testing::UnorderedElementsAreArray(
GetSuggestionList({test_username_, additional_username}))),
SuggestionVectorLabelsAre(testing::AllOf(
testing::Contains(base::UTF8ToUTF16("foo.com")),
testing::Contains(base::UTF8ToUTF16("foobarrealm.org"))))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::string16(), 0, element_bounds);
// Now simulate displaying suggestions matching "John".
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(element_bounds, _,
SuggestionVectorValuesAre(testing::ElementsAreArray(
GetSuggestionList({additional_username}))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("John"), 0, element_bounds);
// Finally, simulate displaying all suggestions, without any prefix matching.
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(
element_bounds, _,
SuggestionVectorValuesAre(testing::ElementsAreArray(
GetSuggestionList({test_username_, additional_username}))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("xyz"), autofill::SHOW_ALL,
element_bounds);
}
// Verify that, for Android application credentials, the prettified realms of
// applications are displayed as the labels of suggestions on the UI (for
// matches of all levels of preferredness).
TEST_F(PasswordAutofillManagerTest, PrettifiedAndroidRealmsAreShownAsLabels) {
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
InitializePasswordAutofillManager(client.get(), autofill_client.get());
autofill::PasswordFormFillData data;
data.username_field.value = test_username_;
data.preferred_realm = "android://hash@com.example1.android/";
autofill::PasswordAndRealm additional;
additional.realm = "android://hash@com.example2.android/";
base::string16 additional_username(base::ASCIIToUTF16("John Foo"));
data.additional_logins[additional_username] = additional;
password_autofill_manager_->OnAddPasswordFillData(data);
EXPECT_CALL(*autofill_client,
ShowAutofillPopup(_, _,
SuggestionVectorLabelsAre(testing::AllOf(
testing::Contains(base::ASCIIToUTF16(
"android://com.example1.android/")),
testing::Contains(base::ASCIIToUTF16(
"android://com.example2.android/")))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::string16(), 0, gfx::RectF());
}
TEST_F(PasswordAutofillManagerTest, FillSuggestionPasswordField) {
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
data.username_field.value = test_username_;
data.password_field.value = test_password_;
data.preferred_realm = "http://foo.com/";
autofill::PasswordAndRealm additional;
additional.realm = "https://foobarrealm.org";
base::string16 additional_username(base::ASCIIToUTF16("John Foo"));
data.additional_logins[additional_username] = additional;
password_autofill_manager_->OnAddPasswordFillData(data);
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(element_bounds, _,
SuggestionVectorValuesAre(testing::ElementsAreArray(
GetSuggestionList({test_username_}))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, test_username_, autofill::IS_PASSWORD_FIELD,
element_bounds);
}
// Verify that typing "foo" into the username field will match usernames
// "foo.bar@example.com", "bar.foo@example.com" and "example@foo.com".
TEST_F(PasswordAutofillManagerTest, DisplaySuggestionsWithMatchingTokens) {
// Token matching is currently behind a flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
autofill::switches::kEnableSuggestionsWithSubstringMatch);
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
base::string16 username = base::ASCIIToUTF16("foo.bar@example.com");
data.username_field.value = username;
data.password_field.value = base::ASCIIToUTF16("foobar");
data.preferred_realm = "http://foo.com/";
autofill::PasswordAndRealm additional;
additional.realm = "https://foobarrealm.org";
base::string16 additional_username(base::ASCIIToUTF16("bar.foo@example.com"));
data.additional_logins[additional_username] = additional;
password_autofill_manager_->OnAddPasswordFillData(data);
EXPECT_CALL(*autofill_client,
ShowAutofillPopup(
element_bounds, _,
SuggestionVectorValuesAre(testing::UnorderedElementsAreArray(
GetSuggestionList({username, additional_username}))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo"), 0, element_bounds);
}
// Verify that typing "oo" into the username field will not match any usernames
// "foo.bar@example.com", "bar.foo@example.com" or "example@foo.com".
TEST_F(PasswordAutofillManagerTest, NoSuggestionForNonPrefixTokenMatch) {
// Token matching is currently behind a flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
autofill::switches::kEnableSuggestionsWithSubstringMatch);
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
base::string16 username = base::ASCIIToUTF16("foo.bar@example.com");
data.username_field.value = username;
data.password_field.value = base::ASCIIToUTF16("foobar");
data.preferred_realm = "http://foo.com/";
autofill::PasswordAndRealm additional;
additional.realm = "https://foobarrealm.org";
base::string16 additional_username(base::ASCIIToUTF16("bar.foo@example.com"));
data.additional_logins[additional_username] = additional;
password_autofill_manager_->OnAddPasswordFillData(data);
EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _, _)).Times(0);
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("oo"), 0, element_bounds);
}
// Verify that typing "foo@exam" into the username field will match username
// "bar.foo@example.com" even if the field contents span accross multiple
// tokens.
TEST_F(PasswordAutofillManagerTest,
MatchingContentsWithSuggestionTokenSeparator) {
// Token matching is currently behind a flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
autofill::switches::kEnableSuggestionsWithSubstringMatch);
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
base::string16 username = base::ASCIIToUTF16("foo.bar@example.com");
data.username_field.value = username;
data.password_field.value = base::ASCIIToUTF16("foobar");
data.preferred_realm = "http://foo.com/";
autofill::PasswordAndRealm additional;
additional.realm = "https://foobarrealm.org";
base::string16 additional_username(base::ASCIIToUTF16("bar.foo@example.com"));
data.additional_logins[additional_username] = additional;
password_autofill_manager_->OnAddPasswordFillData(data);
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(element_bounds, _,
SuggestionVectorValuesAre(testing::ElementsAreArray(
GetSuggestionList({additional_username}))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo@exam"), 0,
element_bounds);
}
// Verify that typing "example" into the username field will match and order
// usernames "example@foo.com", "foo.bar@example.com" and "bar.foo@example.com"
// i.e. prefix matched followed by substring matched.
TEST_F(PasswordAutofillManagerTest,
DisplaySuggestionsWithPrefixesPrecedeSubstringMatched) {
// Token matching is currently behind a flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
autofill::switches::kEnableSuggestionsWithSubstringMatch);
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
base::string16 username = base::ASCIIToUTF16("foo.bar@example.com");
data.username_field.value = username;
data.password_field.value = base::ASCIIToUTF16("foobar");
data.preferred_realm = "http://foo.com/";
autofill::PasswordAndRealm additional;
additional.realm = "https://foobarrealm.org";
base::string16 additional_username(base::ASCIIToUTF16("bar.foo@example.com"));
data.additional_logins[additional_username] = additional;
password_autofill_manager_->OnAddPasswordFillData(data);
EXPECT_CALL(*autofill_client,
ShowAutofillPopup(
element_bounds, _,
SuggestionVectorValuesAre(testing::ElementsAreArray(
GetSuggestionList({username, additional_username}))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo"), false,
element_bounds);
}
TEST_F(PasswordAutofillManagerTest, PreviewAndFillEmptyUsernameSuggestion) {
// Initialize PasswordAutofillManager with credentials without username.
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
fill_data().username_field.value.clear();
InitializePasswordAutofillManager(client.get(), autofill_client.get());
base::string16 no_username_string =
l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EMPTY_LOGIN);
// Simulate that the user clicks on a username field.
EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _, _));
gfx::RectF element_bounds;
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, base::string16(), false, element_bounds);
// Check that preview of the empty username works.
EXPECT_CALL(*client->mock_driver(),
PreviewSuggestion(base::string16(), test_password_));
password_autofill_manager_->DidSelectSuggestion(no_username_string,
0 /*not used*/);
testing::Mock::VerifyAndClearExpectations(client->mock_driver());
// Check that fill of the empty username works.
EXPECT_CALL(*client->mock_driver(),
FillSuggestion(base::string16(), test_password_));
EXPECT_CALL(*autofill_client, HideAutofillPopup());
password_autofill_manager_->DidAcceptSuggestion(
no_username_string, autofill::POPUP_ITEM_ID_PASSWORD_ENTRY, 1);
testing::Mock::VerifyAndClearExpectations(client->mock_driver());
}
// Tests that the "Manage passwords" suggestion is shown along with the password
// popup.
TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnPasswordField) {
constexpr char kShownContextHistogram[] =
"PasswordManager.ShowAllSavedPasswordsShownContext";
constexpr char kAcceptedContextHistogram[] =
"PasswordManager.ShowAllSavedPasswordsAcceptedContext";
base::HistogramTester histograms;
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
auto client = std::make_unique<TestPasswordManagerClient>();
auto autofill_client = std::make_unique<MockAutofillClient>();
auto manager =
std::make_unique<password_manager::PasswordManager>(client.get());
InitializePasswordAutofillManager(client.get(), autofill_client.get());
ON_CALL(*(client->mock_driver()), GetPasswordManager())
.WillByDefault(testing::Return(manager.get()));
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
data.username_field.value = test_username_;
data.password_field.value = test_password_;
data.origin = GURL("https://foo.test");
password_autofill_manager_->OnAddPasswordFillData(data);
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(element_bounds, _,
SuggestionVectorValuesAre(testing::ElementsAreArray(
GetSuggestionList({test_username_}))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, test_username_, autofill::IS_PASSWORD_FIELD,
element_bounds);
histograms.ExpectUniqueSample(kDropdownShownHistogram,
metrics_util::PasswordDropdownState::kStandard,
1);
if (!IsPreLollipopAndroid()) {
// Expect a sample only in the shown histogram.
histograms.ExpectUniqueSample(
kShownContextHistogram,
metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD, 1);
// Clicking at the "Show all passwords row" should trigger a call to open
// the Password Manager settings page and hide the popup.
EXPECT_CALL(
*client,
NavigateToManagePasswordsPage(
password_manager::ManagePasswordsReferrer::kPasswordDropdown));
EXPECT_CALL(*autofill_client, HideAutofillPopup());
password_autofill_manager_->DidAcceptSuggestion(
base::string16(), autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY, 0);
// Expect a sample in both the shown and accepted histogram.
histograms.ExpectUniqueSample(
kShownContextHistogram,
metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD, 1);
histograms.ExpectUniqueSample(
kAcceptedContextHistogram,
metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD, 1);
histograms.ExpectUniqueSample(
kDropdownSelectedHistogram,
metrics_util::PasswordDropdownSelectedOption::kShowAll, 1);
// Trigger UKM reporting, which happens at destruction time.
ukm::SourceId expected_source_id = client->GetUkmSourceId();
manager.reset();
autofill_client.reset();
client.reset();
const auto& entries =
test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto* entry : entries) {
EXPECT_EQ(expected_source_id, entry->source_id);
test_ukm_recorder.ExpectEntryMetric(
entry, UkmEntry::kPageLevelUserActionName,
static_cast<int64_t>(
password_manager::PasswordManagerMetricsRecorder::
PageLevelUserAction::kShowAllPasswordsWhileSomeAreSuggested));
}
} else {
EXPECT_THAT(histograms.GetAllSamples(kShownContextHistogram),
testing::IsEmpty());
EXPECT_THAT(histograms.GetAllSamples(kAcceptedContextHistogram),
testing::IsEmpty());
}
}
// Tests that the "Manage passwords" fallback shows up in non-password
// fields of login forms.
TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnNonPasswordField) {
auto client = std::make_unique<TestPasswordManagerClient>();
auto autofill_client = std::make_unique<MockAutofillClient>();
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
data.username_field.value = test_username_;
data.password_field.value = test_password_;
data.origin = GURL("https://foo.test");
password_autofill_manager_->OnAddPasswordFillData(data);
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(element_bounds, _,
SuggestionVectorValuesAre(testing::ElementsAreArray(
GetSuggestionList({test_username_}))),
false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, test_username_, 0, element_bounds);
}
TEST_F(PasswordAutofillManagerTest,
MaybeShowPasswordSuggestionsWithGenerationNoCredentials) {
auto client = std::make_unique<TestPasswordManagerClient>();
auto autofill_client = std::make_unique<MockAutofillClient>();
password_autofill_manager_.reset(new PasswordAutofillManager(
client->mock_driver(), autofill_client.get(), client.get()));
EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _, _)).Times(0);
gfx::RectF element_bounds;
EXPECT_FALSE(
password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
element_bounds, base::i18n::RIGHT_TO_LEFT));
}
TEST_F(PasswordAutofillManagerTest,
MaybeShowPasswordSuggestionsWithGenerationSomeCredentials) {
base::HistogramTester histograms;
auto client = std::make_unique<TestPasswordManagerClient>();
auto autofill_client = std::make_unique<MockAutofillClient>();
InitializePasswordAutofillManager(client.get(), autofill_client.get());
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
data.username_field.value = test_username_;
data.password_field.value = test_password_;
data.origin = GURL("https://foo.test");
favicon::MockFaviconService favicon_service;
EXPECT_CALL(*client, GetFaviconService()).WillOnce(Return(&favicon_service));
EXPECT_CALL(favicon_service, GetFaviconImageForPageURL(data.origin, _, _));
password_autofill_manager_->OnAddPasswordFillData(data);
// Bring up the drop-down with the generaion option.
base::string16 generation_string =
l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD);
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(
element_bounds, base::i18n::RIGHT_TO_LEFT,
AllOf(SuggestionVectorValuesAre(ElementsAreArray(
GetSuggestionList({test_username_, generation_string}))),
SuggestionVectorIconsAre(
ElementsAreArray(GetIconsList({"globeIcon", "keyIcon"})))),
false, _));
EXPECT_TRUE(
password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
element_bounds, base::i18n::RIGHT_TO_LEFT));
histograms.ExpectUniqueSample(
kDropdownShownHistogram,
metrics_util::PasswordDropdownState::kStandardGenerate, 1);
// Click "Generate password".
EXPECT_CALL(*client, GeneratePassword());
EXPECT_CALL(*autofill_client, HideAutofillPopup());
password_autofill_manager_->DidAcceptSuggestion(
base::string16(), autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY, 1);
histograms.ExpectUniqueSample(
kDropdownSelectedHistogram,
metrics_util::PasswordDropdownSelectedOption::kGenerate, 1);
}
} // namespace password_manager