blob: 1ec04767eeafe3d14994a1092491ddaca49fddae [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 "components/password_manager/core/browser/password_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/optional.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_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_mock_time_task_runner.h"
#include "build/build_config.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/new_password_form_manager.h"
#include "components/password_manager/core/browser/password_autofill_manager.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "components/password_manager/core/browser/stub_credentials_filter.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/password_manager/core/common/password_manager_pref_names.h"
#include "components/ukm/test_ukm_recorder.h"
#include "net/cert/cert_status_flags.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using autofill::FormData;
using autofill::FormFieldData;
using autofill::PasswordForm;
using base::ASCIIToUTF16;
using base::TestMockTimeTaskRunner;
using testing::_;
using testing::AnyNumber;
using testing::IsNull;
using testing::NotNull;
using testing::Return;
using testing::ReturnRef;
using testing::SaveArg;
using testing::WithArg;
namespace password_manager {
namespace {
class MockStoreResultFilter : public StubCredentialsFilter {
public:
MOCK_CONST_METHOD1(ShouldSave, bool(const autofill::PasswordForm& form));
MOCK_CONST_METHOD1(ReportFormLoginSuccess,
void(const PasswordFormManagerInterface& form_manager));
MOCK_CONST_METHOD1(IsSyncAccountEmail, bool(const std::string&));
MOCK_CONST_METHOD1(ShouldSaveGaiaPasswordHash,
bool(const autofill::PasswordForm&));
MOCK_CONST_METHOD1(ShouldSaveEnterprisePasswordHash,
bool(const autofill::PasswordForm&));
};
class MockPasswordManagerClient : public StubPasswordManagerClient {
public:
MockPasswordManagerClient() {
ON_CALL(*this, GetStoreResultFilter()).WillByDefault(Return(&filter_));
ON_CALL(filter_, ShouldSave(_)).WillByDefault(Return(true));
ON_CALL(filter_, ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(false));
ON_CALL(filter_, ShouldSaveEnterprisePasswordHash(_))
.WillByDefault(Return(false));
ON_CALL(filter_, IsSyncAccountEmail(_)).WillByDefault(Return(false));
}
MOCK_CONST_METHOD0(IsSavingAndFillingEnabledForCurrentPage, bool());
MOCK_CONST_METHOD0(GetMainFrameCertStatus, net::CertStatus());
MOCK_CONST_METHOD0(GetPasswordStore, PasswordStore*());
// The code inside EXPECT_CALL for PromptUserToSaveOrUpdatePasswordPtr and
// ShowManualFallbackForSavingPtr owns the PasswordFormManager* argument.
MOCK_METHOD1(PromptUserToSaveOrUpdatePasswordPtr,
void(PasswordFormManagerForUI*));
MOCK_METHOD3(ShowManualFallbackForSavingPtr,
void(PasswordFormManagerForUI*, bool, bool));
MOCK_METHOD0(HideManualFallbackForSaving, void());
MOCK_METHOD1(NotifySuccessfulLoginWithExistingPassword,
void(const autofill::PasswordForm&));
MOCK_METHOD0(AutomaticPasswordSaveIndicator, void());
MOCK_CONST_METHOD0(GetPrefs, PrefService*());
MOCK_CONST_METHOD0(GetMainFrameURL, const GURL&());
MOCK_METHOD0(GetDriver, PasswordManagerDriver*());
MOCK_CONST_METHOD0(GetStoreResultFilter, const MockStoreResultFilter*());
MOCK_METHOD0(GetMetricsRecorder, PasswordManagerMetricsRecorder*());
// Workaround for std::unique_ptr<> lacking a copy constructor.
bool PromptUserToSaveOrUpdatePassword(
std::unique_ptr<PasswordFormManagerForUI> manager,
bool update_password) override {
PromptUserToSaveOrUpdatePasswordPtr(manager.release());
return false;
}
void ShowManualFallbackForSaving(
std::unique_ptr<PasswordFormManagerForUI> manager,
bool has_generated_password,
bool is_update) override {
ShowManualFallbackForSavingPtr(manager.release(), has_generated_password,
is_update);
}
void AutomaticPasswordSave(
std::unique_ptr<PasswordFormManagerForUI> manager) override {
AutomaticPasswordSaveIndicator();
}
void FilterAllResultsForSaving() {
EXPECT_CALL(filter_, ShouldSave(_)).WillRepeatedly(Return(false));
}
private:
testing::NiceMock<MockStoreResultFilter> filter_;
};
class MockPasswordManagerDriver : public StubPasswordManagerDriver {
public:
MOCK_METHOD1(FillPasswordForm, void(const autofill::PasswordFormFillData&));
MOCK_METHOD1(AutofillDataReceived,
void(const std::map<autofill::FormData,
autofill::PasswordFormFieldPredictionMap>&));
MOCK_METHOD0(GetPasswordManager, PasswordManager*());
MOCK_METHOD0(GetPasswordAutofillManager, PasswordAutofillManager*());
};
// Invokes the password store consumer with a single copy of |form|.
ACTION_P(InvokeConsumer, form) {
std::vector<std::unique_ptr<PasswordForm>> result;
result.push_back(std::make_unique<PasswordForm>(form));
arg0->OnGetPasswordStoreResults(std::move(result));
}
ACTION(InvokeEmptyConsumerWithForms) {
arg0->OnGetPasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>>());
}
ACTION_P(SaveToScopedPtr, scoped) { scoped->reset(arg0); }
ACTION(DeletePtr) {
delete arg0;
}
void SanitizeFormData(FormData* form) {
form->main_frame_origin = url::Origin();
for (FormFieldData& field : form->fields) {
field.label.clear();
field.value.clear();
field.autocomplete_attribute.clear();
field.option_values.clear();
field.option_contents.clear();
field.placeholder.clear();
field.css_classes.clear();
field.id_attribute.clear();
field.name_attribute.clear();
}
}
// Verifies that |test_ukm_recorder| recorder has a single entry called |entry|
// and returns it.
const ukm::mojom::UkmEntry* GetMetricEntry(
const ukm::TestUkmRecorder& test_ukm_recorder,
base::StringPiece entry) {
std::vector<const ukm::mojom::UkmEntry*> ukm_entries =
test_ukm_recorder.GetEntriesByName(entry);
EXPECT_EQ(1u, ukm_entries.size());
return ukm_entries[0];
}
// Verifies the expectation that |test_ukm_recorder| recorder has a single entry
// called |entry|, and that the entry contains the metric called |metric| set
// to |value|.
template <typename T>
void CheckMetricHasValue(const ukm::TestUkmRecorder& test_ukm_recorder,
base::StringPiece entry,
base::StringPiece metric,
T value) {
ukm::TestUkmRecorder::ExpectEntryMetric(
GetMetricEntry(test_ukm_recorder, entry), metric,
static_cast<int64_t>(value));
}
} // namespace
class PasswordManagerTest : public testing::Test {
public:
PasswordManagerTest()
: test_url_("https://www.example.com"),
task_runner_(new TestMockTimeTaskRunner) {}
~PasswordManagerTest() override = default;
protected:
void SetUp() override {
store_ = new testing::StrictMock<MockPasswordStore>;
EXPECT_CALL(*store_, ReportMetrics(_, _, _)).Times(AnyNumber());
EXPECT_CALL(*store_, GetLoginsForSameOrganizationName(_, _))
.Times(AnyNumber());
CHECK(store_->Init(syncer::SyncableService::StartSyncFlare(), nullptr));
ON_CALL(client_, GetPasswordStore()).WillByDefault(Return(store_.get()));
EXPECT_CALL(*store_, GetSiteStatsImpl(_)).Times(AnyNumber());
ON_CALL(client_, GetDriver()).WillByDefault(Return(&driver_));
manager_.reset(new PasswordManager(&client_));
password_autofill_manager_.reset(
new PasswordAutofillManager(client_.GetDriver(), nullptr, &client_));
EXPECT_CALL(driver_, GetPasswordManager())
.WillRepeatedly(Return(manager_.get()));
EXPECT_CALL(driver_, GetPasswordAutofillManager())
.WillRepeatedly(Return(password_autofill_manager_.get()));
ON_CALL(client_, GetMainFrameCertStatus()).WillByDefault(Return(0));
EXPECT_CALL(*store_, IsAbleToSavePasswords()).WillRepeatedly(Return(true));
ON_CALL(client_, GetMainFrameURL()).WillByDefault(ReturnRef(test_url_));
ON_CALL(client_, GetMetricsRecorder()).WillByDefault(Return(nullptr));
ON_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillByDefault(WithArg<0>(DeletePtr()));
ON_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _))
.WillByDefault(WithArg<0>(DeletePtr()));
}
void TearDown() override {
store_->ShutdownOnUIThread();
store_ = nullptr;
}
PasswordForm MakeSimpleForm() {
PasswordForm form;
form.origin = GURL("http://www.google.com/a/LoginAuth");
form.action = GURL("http://www.google.com/a/Login");
form.username_element = ASCIIToUTF16("Email");
form.password_element = ASCIIToUTF16("Passwd");
form.username_value = ASCIIToUTF16("googleuser");
form.password_value = ASCIIToUTF16("p4ssword");
form.submit_element = ASCIIToUTF16("signIn");
form.signon_realm = "http://www.google.com/";
// Fill |form.form_data|.
form.form_data.origin = form.origin;
form.form_data.action = form.action;
form.form_data.name = ASCIIToUTF16("the-form-name");
form.form_data.unique_renderer_id = 10;
autofill::FormFieldData field;
field.name = ASCIIToUTF16("Email");
field.id_attribute = field.name;
field.name_attribute = field.name;
field.value = ASCIIToUTF16("googleuser");
field.form_control_type = "text";
field.unique_renderer_id = 2;
form.form_data.fields.push_back(field);
field.name = ASCIIToUTF16("Passwd");
field.id_attribute = field.name;
field.name_attribute = field.name;
field.value = ASCIIToUTF16("p4ssword");
field.form_control_type = "password";
field.unique_renderer_id = 3;
form.form_data.fields.push_back(field);
// On iOS the unique_id member uniquely addresses this field in the DOM.
// This is an ephemeral value which is not guaranteed to be stable across
// page loads. It serves to allow a given field to be found during the
// current navigation.
// TODO(crbug.com/896689): Expand the logic/application of this to other
// platforms and/or merge this concept with |unique_renderer_id|.
#if defined(OS_IOS)
for (auto& f : form.form_data.fields) {
f.unique_id = f.id_attribute;
}
#endif
return form;
}
PasswordForm MakeSimpleGAIAForm() {
PasswordForm form = MakeSimpleForm();
form.origin = GURL("https://accounts.google.com");
form.signon_realm = form.origin.spec();
return form;
}
PasswordForm MakeGAIAChangePasswordForm() {
PasswordForm form;
form.origin = GURL("https://accounts.google.com");
form.action = GURL("http://www.google.com/a/Login");
form.username_element = ASCIIToUTF16("Email");
form.new_password_element = ASCIIToUTF16("NewPasswd");
form.username_value = ASCIIToUTF16("googleuser");
form.new_password_value = ASCIIToUTF16("n3wp4ssword");
form.submit_element = ASCIIToUTF16("changePassword");
form.signon_realm = form.origin.spec();
form.form_data.name = ASCIIToUTF16("the-form-name");
return form;
}
// Create a sign-up form that only has a new password field.
PasswordForm MakeFormWithOnlyNewPasswordField() {
PasswordForm form = MakeSimpleForm();
form.new_password_element.swap(form.password_element);
form.new_password_value.swap(form.password_value);
return form;
}
PasswordForm MakeAndroidCredential() {
PasswordForm android_form;
android_form.origin = GURL("android://hash@google.com");
android_form.signon_realm = "android://hash@google.com";
android_form.username_value = ASCIIToUTF16("google");
android_form.password_value = ASCIIToUTF16("password");
android_form.is_affiliation_based_match = true;
return android_form;
}
// Reproduction of the form present on twitter's login page.
PasswordForm MakeTwitterLoginForm() {
PasswordForm form;
form.origin = GURL("https://twitter.com/");
form.action = GURL("https://twitter.com/sessions");
form.username_element = ASCIIToUTF16("Email");
form.password_element = ASCIIToUTF16("Passwd");
form.username_value = ASCIIToUTF16("twitter");
form.password_value = ASCIIToUTF16("password");
form.submit_element = ASCIIToUTF16("signIn");
form.signon_realm = "https://twitter.com/";
return form;
}
// Reproduction of the form present on twitter's failed login page.
PasswordForm MakeTwitterFailedLoginForm() {
PasswordForm form;
form.origin = GURL("https://twitter.com/login/error?redirect_after_login");
form.action = GURL("https://twitter.com/sessions");
form.username_element = ASCIIToUTF16("EmailField");
form.password_element = ASCIIToUTF16("PasswdField");
form.username_value = ASCIIToUTF16("twitter");
form.password_value = ASCIIToUTF16("password");
form.submit_element = ASCIIToUTF16("signIn");
form.signon_realm = "https://twitter.com/";
return form;
}
PasswordForm MakeSimpleFormWithOnlyPasswordField() {
PasswordForm form(MakeSimpleForm());
form.username_element.clear();
form.username_value.clear();
return form;
}
PasswordManager* manager() { return manager_.get(); }
void OnPasswordFormSubmitted(const PasswordForm& form) {
manager()->OnPasswordFormSubmitted(&driver_, form);
}
void TurnOnNewParsingForSaving(
base::test::ScopedFeatureList* scoped_feature_list) {
scoped_feature_list->InitWithFeatures(
{features::kNewPasswordFormParsing,
features::kNewPasswordFormParsingForSaving},
{});
manager_.reset(new PasswordManager(&client_));
}
void TurnOnOnlyNewPassword(
base::test::ScopedFeatureList* scoped_feature_list) {
scoped_feature_list->InitWithFeatures(
{features::kNewPasswordFormParsing,
features::kNewPasswordFormParsingForSaving, features::kOnlyNewParser},
{});
manager_.reset(new PasswordManager(&client_));
}
const GURL test_url_;
base::test::ScopedTaskEnvironment task_environment_;
scoped_refptr<MockPasswordStore> store_;
testing::NiceMock<MockPasswordManagerClient> client_;
MockPasswordManagerDriver driver_;
std::unique_ptr<PasswordAutofillManager> password_autofill_manager_;
std::unique_ptr<PasswordManager> manager_;
scoped_refptr<TestMockTimeTaskRunner> task_runner_;
};
MATCHER_P(FormMatches, form, "") {
return form.signon_realm == arg.signon_realm && form.origin == arg.origin &&
form.action == arg.action &&
form.username_element == arg.username_element &&
form.username_value == arg.username_value &&
form.password_element == arg.password_element &&
form.password_value == arg.password_value &&
form.new_password_element == arg.new_password_element;
}
TEST_F(PasswordManagerTest, FormSubmitWithOnlyNewPasswordField) {
// Test that when a form only contains a "new password" field, the form gets
// saved and in password store, the new password value is saved as a current
// password value.
std::vector<PasswordForm> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form, as if the info bar was accepted.
PasswordForm saved_form;
EXPECT_CALL(*store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved_form));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
// The value of the new password field should have been promoted to, and saved
// to the password store as the current password.
PasswordForm expected_form(form);
expected_form.password_value.swap(expected_form.new_password_value);
expected_form.password_element.swap(expected_form.new_password_element);
EXPECT_THAT(saved_form, FormMatches(expected_form));
}
TEST_F(PasswordManagerTest, GeneratedPasswordFormSubmitEmptyStore) {
for (bool new_parsing_for_saving : {false, true}) {
SCOPED_TRACE(testing::Message()
<< "new_parsing_for_saving = " << new_parsing_for_saving);
base::test::ScopedFeatureList scoped_feature_list;
if (new_parsing_for_saving)
TurnOnNewParsingForSaving(&scoped_feature_list);
// Test that generated passwords are stored without asking the user.
std::vector<PasswordForm> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate the user generating the password and submitting the form.
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
form.password_value = form.new_password_value;
manager()->OnPresaveGeneratedPassword(&driver_, form);
OnPasswordFormSubmitted(form);
// The user should not need to confirm saving as they have already given
// consent by using the generated password. The form should be saved once
// navigation occurs. The client will be informed that automatic saving has
// occurred.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
PasswordForm form_to_save;
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _))
.WillOnce(SaveArg<0>(&form_to_save));
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator());
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_EQ(form.username_value, form_to_save.username_value);
// What was "new password" field in the submitted form, becomes the current
// password field in the form to save.
EXPECT_EQ(form.new_password_value, form_to_save.password_value);
}
}
TEST_F(PasswordManagerTest, FormSubmitNoGoodMatch) {
// When the password store already contains credentials for a given form, new
// credentials get still added, as long as they differ in username from the
// stored ones.
PasswordForm existing_different(MakeSimpleForm());
existing_different.username_value = ASCIIToUTF16("google2");
PasswordForm form(MakeSimpleForm());
std::vector<PasswordForm> observed = {form};
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _))
.WillOnce(WithArg<1>(InvokeConsumer(existing_different)));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
// We still expect an add, since we didn't have a good match.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
}
TEST_F(PasswordManagerTest, BestMatchFormToManager) {
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
std::vector<PasswordForm> observed;
// Observe the form that will be submitted.
PasswordForm form(MakeSimpleForm());
// This form is different from the on that will be submitted.
PasswordForm no_match_form(MakeSimpleForm());
no_match_form.form_data.name = ASCIIToUTF16("another-name");
no_match_form.action = GURL("http://www.google.com/somethingelse");
autofill::FormFieldData field;
field.name = ASCIIToUTF16("another-field-name");
no_match_form.form_data.fields.push_back(field);
observed.push_back(no_match_form);
observed.push_back(form);
// Simulate observing forms after navigation the page.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
// The form is modified before being submitted and does not match perfectly.
// Out of the criteria {name, action, signature}, we keep the signature the
// same and change the rest.
PasswordForm changed_form(form);
changed_form.username_element = ASCIIToUTF16("changed-name");
changed_form.action = GURL("http://www.google.com/changed-action");
OnPasswordFormSubmitted(changed_form);
EXPECT_EQ(CalculateFormSignature(form.form_data),
CalculateFormSignature(changed_form.form_data));
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Verify that PasswordFormManager to be save owns the correct pair of
// observed and submitted forms.
// TODO(https://crbug.com/831123): Implement subsequent expectation with
// PasswordFormManagerInterface and avoid casting to PasswordFormManager*.
PasswordFormManager* form_manager =
static_cast<PasswordFormManager*>(form_manager_to_save.get());
EXPECT_EQ(form.action, form_manager->observed_form().action);
EXPECT_EQ(form.form_data.name, form_manager->observed_form().form_data.name);
EXPECT_EQ(changed_form.action, form_manager->GetSubmittedForm()->action);
EXPECT_EQ(changed_form.form_data.name,
form_manager->GetSubmittedForm()->form_data.name);
}
// As long as the is a PasswordFormManager that matches the origin, we should
// not fail to match a submitted PasswordForm to a PasswordFormManager.
TEST_F(PasswordManagerTest, AnyMatchFormToManager) {
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
// Observe the form that will be submitted.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
// Simulate observing forms after navigation the page.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
// The form is modified before being submitted and does not match perfectly.
// We change all of the criteria: {name, action, signature}.
PasswordForm changed_form(form);
autofill::FormFieldData field;
field.name = ASCIIToUTF16("another-field-name");
changed_form.form_data.fields.push_back(field);
changed_form.username_element = ASCIIToUTF16("changed-name");
changed_form.action = GURL("http://www.google.com/changed-action");
OnPasswordFormSubmitted(changed_form);
EXPECT_NE(CalculateFormSignature(form.form_data),
CalculateFormSignature(changed_form.form_data));
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Verify that we matched the form to a PasswordFormManager, although with the
// worst possible match.
// TODO(https://crbug.com/831123): Implement subsequent expectation with
// PasswordFormManagerInterface and avoid casting to PasswordFormManager*.
PasswordFormManager* form_manager =
static_cast<PasswordFormManager*>(form_manager_to_save.get());
EXPECT_EQ(form.action, form_manager->observed_form().action);
EXPECT_EQ(form.form_data.name, form_manager->observed_form().form_data.name);
EXPECT_EQ(changed_form.action, form_manager->GetSubmittedForm()->action);
EXPECT_EQ(changed_form.form_data.name,
form_manager->GetSubmittedForm()->form_data.name);
}
// Tests that a credential wouldn't be saved if it is already in the store.
TEST_F(PasswordManagerTest, DontSaveAlreadySavedCredential) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
std::vector<PasswordForm> observed = {form};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(form)));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// The user is typing a credential manually. Till the credential is different
// from the saved one, the fallback should be available.
PasswordForm incomplete_match(form);
incomplete_match.password_value =
form.password_value.substr(0, form.password_value.length() - 1);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, incomplete_match);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(incomplete_match));
base::UserActionTester user_action_tester;
// The user completes typing the credential. No fallback should be available,
// because the credential is already in the store.
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true)).Times(0);
EXPECT_CALL(client_, HideManualFallbackForSaving());
manager()->ShowManualFallbackForSaving(&driver_, form);
// The user submits the form. No prompt should pop up. The credential is
// updated in background.
OnPasswordFormSubmitted(form);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, UpdateLogin(_));
observed.clear();
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_EQ(1,
user_action_tester.GetActionCount("PasswordManager_LoginPassed"));
}
// Tests that on Chrome sign-in form credentials are not saved.
TEST_F(PasswordManagerTest, DoNotSaveOnChromeSignInForm) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
form.is_gaia_with_skip_save_password_form = true;
std::vector<PasswordForm> observed = {form};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(*client_.GetStoreResultFilter(), ShouldSave(_))
.WillRepeatedly(Return(false));
// The user is typing a credential. No fallback should be available.
PasswordForm typed_credentials(form);
typed_credentials.password_value = ASCIIToUTF16("pw");
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _)).Times(0);
manager()->ShowManualFallbackForSaving(&driver_, form);
// The user submits the form. No prompt should pop up.
OnPasswordFormSubmitted(form);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
observed.clear();
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// Tests that a UKM metric "Login Passed" is sent when the submitted credentials
// are already in the store and OnPasswordFormsParsed is called multiple times.
TEST_F(PasswordManagerTest,
SubmissionMetricsIsPassedWhenDontSaveAlreadySavedCredential) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(4);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// The user submits the form.
OnPasswordFormSubmitted(form);
// Another call of OnPasswordFormsParsed happens. In production it happens
// because of some DOM updates.
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_CALL(client_, HideManualFallbackForSaving());
// The call to manual fallback with |form| equal to already saved should close
// the fallback, but it should not prevent sending metrics.
manager()->ShowManualFallbackForSaving(&driver_, form);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, UpdateLogin(_));
// Simulate successful login. Expect "Login Passed" metric.
base::UserActionTester user_action_tester;
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_EQ(1,
user_action_tester.GetActionCount("PasswordManager_LoginPassed"));
}
TEST_F(PasswordManagerTest, FormSeenThenLeftPage) {
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// No message from the renderer that a password was submitted. No
// expected calls.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_F(PasswordManagerTest, FormSubmit) {
// Test that a plain form submit results in offering to save passwords.
PasswordForm form(MakeSimpleForm());
std::vector<PasswordForm> observed = {form};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_TRUE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form, as if the info bar was accepted.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
}
TEST_F(PasswordManagerTest, FormSubmitWhenPasswordsCannotBeSaved) {
// Test that a plain form submit doesn't result in offering to save passwords.
EXPECT_CALL(*store_, IsAbleToSavePasswords()).WillOnce(Return(false));
PasswordForm form(MakeSimpleForm());
std::vector<PasswordForm> observed = {form};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_TRUE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// This test verifies a fix for http://crbug.com/236673
TEST_F(PasswordManagerTest, FormSubmitWithFormOnPreviousPage) {
PasswordForm first_form(MakeSimpleForm());
first_form.origin = GURL("http://www.nytimes.com/");
first_form.action = GURL("https://myaccount.nytimes.com/auth/login");
first_form.signon_realm = "http://www.nytimes.com/";
PasswordForm second_form(MakeSimpleForm());
second_form.origin = GURL("https://myaccount.nytimes.com/auth/login");
second_form.action = GURL("https://myaccount.nytimes.com/auth/login");
second_form.signon_realm = "https://myaccount.nytimes.com/";
// Pretend that the form is hidden on the first page.
std::vector<PasswordForm> observed;
observed.push_back(first_form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Now navigate to a second page.
manager()->DidNavigateMainFrame();
// This page contains a form with the same markup, but on a different
// URL.
observed.push_back(second_form);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Now submit this form
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(second_form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Navigation after form submit, no forms appear.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form, as if the info bar was accepted and make sure
// that the saved form matches the second form, not the first.
EXPECT_CALL(*store_, AddLogin(FormMatches(second_form)));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
}
TEST_F(PasswordManagerTest, FormSubmitInvisibleLogin) {
// Tests fix of http://crbug.com/28911: if the login form reappears on the
// subsequent page, but is invisible, it shouldn't count as a failed login.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
// Expect info bar to appear:
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// The form reappears, but is not visible in the layout:
manager()->OnPasswordFormsParsed(&driver_, observed);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
}
TEST_F(PasswordManagerTest, InitiallyInvisibleForm) {
// Make sure an invisible login form still gets autofilled.
PasswordForm form(MakeSimpleForm());
std::vector<PasswordForm> observed;
observed.push_back(form);
EXPECT_CALL(driver_, FillPasswordForm(_));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_F(PasswordManagerTest, FillPasswordsOnDisabledManager) {
// Test fix for http://crbug.com/158296: Passwords must be filled even if the
// password manager is disabled.
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(false));
std::vector<PasswordForm> observed;
observed.push_back(form);
EXPECT_CALL(driver_, FillPasswordForm(_));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
}
TEST_F(PasswordManagerTest, PasswordFormReappearance) {
// If the password form reappears after submit, PasswordManager should deduce
// that the login failed and not offer saving.
std::vector<PasswordForm> observed;
PasswordForm login_form(MakeTwitterLoginForm());
observed.push_back(login_form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(login_form);
observed.clear();
observed.push_back(MakeTwitterFailedLoginForm());
// A PasswordForm appears, and is visible in the layout:
// No expected calls to the PasswordStore...
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator()).Times(0);
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
EXPECT_CALL(*store_, UpdateLogin(_)).Times(0);
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_F(PasswordManagerTest, SyncCredentialsNotSaved) {
// Simulate loading a simple form with no existing stored password.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleGAIAForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// User should not be prompted and password should not be saved.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(*store_,
SaveGaiaPasswordHash(
"googleuser", form.password_value,
metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA));
#endif
// Prefs are needed for failure logging about sync credentials.
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
TEST_F(PasswordManagerTest, HashSavedOnGaiaFormWithSkipSavePassword) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleGAIAForm());
// Simulate that this is Gaia form that should be ignored for saving/filling.
form.is_gaia_with_skip_save_password_form = true;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), ShouldSave(_))
.WillByDefault(Return(false));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(*store_,
SaveGaiaPasswordHash(
"googleuser", form.password_value,
metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA));
OnPasswordFormSubmitted(form);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
#endif
// On a successful login with an updated password,
// CredentialsFilter::ReportFormLoginSuccess and CredentialsFilter::ShouldSave
// should be called. The argument of ShouldSave shold be the submitted form.
TEST_F(PasswordManagerTest, ReportFormLoginSuccessAndShouldSaveCalled) {
PasswordForm stored_form(MakeSimpleForm());
std::vector<PasswordForm> observed;
PasswordForm observed_form = stored_form;
// Different values of |username_element| needed to ensure that it is the
// |observed_form| and not the |stored_form| what is passed to ShouldSave.
observed_form.username_element += ASCIIToUTF16("1");
observed.push_back(observed_form);
// Simulate that |form| is already in the store, making this an update.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(stored_form)));
EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
manager()->ProvisionallySavePassword(observed_form, nullptr);
// Chrome should recognise the successful login and call
// ReportFormLoginSuccess.
EXPECT_CALL(*client_.GetStoreResultFilter(), ReportFormLoginSuccess(_));
PasswordForm submitted_form = observed_form;
submitted_form.preferred = true;
EXPECT_CALL(*client_.GetStoreResultFilter(), ShouldSave(submitted_form));
EXPECT_CALL(*store_, UpdateLogin(_));
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
// As the clone PasswordFormManager ends up saving the form, it triggers an
// update of the pending login managers, which in turn triggers new filling.
EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// When there is a sync password saved, and the user successfully uses the
// stored version of it, PasswordManager should not drop that password.
TEST_F(PasswordManagerTest, SyncCredentialsNotDroppedIfUpToDate) {
PasswordForm form(MakeSimpleGAIAForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
client_.FilterAllResultsForSaving();
std::vector<PasswordForm> observed;
observed.push_back(form);
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(*store_,
SaveGaiaPasswordHash(
"googleuser", form.password_value,
metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA));
#endif
manager()->ProvisionallySavePassword(form, nullptr);
// Chrome should not remove the sync credential, because it was successfully
// used as stored, and therefore is up to date.
EXPECT_CALL(*store_, RemoveLogin(_)).Times(0);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// While sync credentials are not saved, they are still filled to avoid users
// thinking they lost access to their accounts.
TEST_F(PasswordManagerTest, SyncCredentialsStillFilled) {
PasswordForm form(MakeSimpleForm());
// Pretend that the password store contains credentials stored for |form|.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
client_.FilterAllResultsForSaving();
// Load the page.
autofill::PasswordFormFillData form_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&form_data));
std::vector<PasswordForm> observed;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_EQ(form.password_value, form_data.password_field.value);
}
// On failed login attempts, the retry-form can have action scheme changed from
// HTTP to HTTPS (see http://crbug.com/400769). Check that such retry-form is
// considered equal to the original login form, and the attempt recognised as a
// failure.
TEST_F(PasswordManagerTest,
SeeingFormActionWithOnlyHttpHttpsChangeIsLoginFailure) {
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
PasswordForm first_form(MakeSimpleForm());
first_form.origin = GURL("http://www.xda-developers.com/");
first_form.action = GURL("http://forum.xda-developers.com/login.php");
// |second_form|'s action differs only with it's scheme i.e. *https://*.
PasswordForm second_form(first_form);
second_form.action = GURL("https://forum.xda-developers.com/login.php");
std::vector<PasswordForm> observed;
observed.push_back(first_form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(first_form);
// Simulate loading a page, which contains |second_form| instead of
// |first_form|.
observed.clear();
observed.push_back(second_form);
// Verify that no prompt to save the password is shown.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_F(PasswordManagerTest,
ShouldBlockPasswordForSameOriginButDifferentSchemeTest) {
constexpr struct {
const char* old_origin;
const char* new_origin;
bool result;
} kTestData[] = {
// Same origin and same scheme.
{"https://example.com/login", "https://example.com/login", false},
// Same host and same scheme, different port.
{"https://example.com:443/login", "https://example.com:444/login", false},
// Same host but different scheme (https to http).
{"https://example.com/login", "http://example.com/login", true},
// Same host but different scheme (http to https).
{"http://example.com/login", "https://example.com/login", false},
// Different TLD, same schemes.
{"https://example.com/login", "https://example.org/login", false},
// Different TLD, different schemes.
{"https://example.com/login", "http://example.org/login", false},
// Different subdomains, same schemes.
{"https://sub1.example.com/login", "https://sub2.example.org/login",
false},
};
PasswordForm form = MakeSimpleForm();
for (const auto& test_case : kTestData) {
SCOPED_TRACE(testing::Message("#test_case = ") << (&test_case - kTestData));
manager()->main_frame_url_ = GURL(test_case.old_origin);
form.origin = GURL(test_case.new_origin);
EXPECT_EQ(
test_case.result,
manager()->ShouldBlockPasswordForSameOriginButDifferentScheme(form));
}
}
// Tests whether two submissions to the same origin but different schemes
// result in only saving the first submission, which has a secure scheme.
TEST_F(PasswordManagerTest, AttemptedSavePasswordSameOriginInsecureScheme) {
PasswordForm secure_form(MakeSimpleForm());
secure_form.origin = GURL("https://example.com/login");
secure_form.action = GURL("https://example.com/login");
secure_form.signon_realm = secure_form.origin.spec();
PasswordForm insecure_form(MakeSimpleForm());
insecure_form.username_element += ASCIIToUTF16("1");
insecure_form.username_value = ASCIIToUTF16("compromised_user");
insecure_form.password_value = ASCIIToUTF16("C0mpr0m1s3d_P4ss");
insecure_form.origin = GURL("http://example.com/home");
insecure_form.action = GURL("http://example.com/home");
insecure_form.signon_realm = insecure_form.origin.spec();
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetMainFrameURL())
.WillRepeatedly(ReturnRef(secure_form.origin));
// Parse, render and submit the secure form.
std::vector<PasswordForm> observed = {secure_form};
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
OnPasswordFormSubmitted(secure_form);
// Make sure |PromptUserToSaveOrUpdatePassword| gets called, and the resulting
// form manager is saved.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
EXPECT_CALL(client_, GetMainFrameURL())
.WillRepeatedly(ReturnRef(insecure_form.origin));
// Parse, render and submit the insecure form.
observed = {insecure_form};
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
OnPasswordFormSubmitted(insecure_form);
// Expect no further calls to |PromptUserToSaveOrUpdatePassword| due to
// insecure origin.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
// Trigger call to |ProvisionalSavePassword| by rendering a page without
// forms.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Make sure that the form saved by the user is indeed the secure form.
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(secure_form));
}
// Create a form with both a new and current password element. Let the current
// password value be non-empty and the new password value be empty and submit
// the form. While normally saving the new password is preferred (on change
// password forms, that would be the reasonable choice), if the new password is
// empty, this is likely just a slightly misunderstood form, and Chrome should
// save the non-empty current password field.
TEST_F(PasswordManagerTest, DoNotSaveWithEmptyNewPasswordAndNonemptyPassword) {
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
ASSERT_FALSE(form.password_value.empty());
form.new_password_element = ASCIIToUTF16("new_password_element");
form.new_password_value.clear();
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the login to complete successfully.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
ASSERT_TRUE(form_manager_to_save);
EXPECT_EQ(form.password_value,
PasswordFormManager::PasswordToSave(
form_manager_to_save->GetPendingCredentials())
.first);
}
TEST_F(PasswordManagerTest, FormSubmitWithOnlyPasswordField) {
// Test to verify that on submitting the HTML password form without having
// username input filed shows password save promt and saves the password to
// store.
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
std::vector<PasswordForm> observed;
// Loads passsword form without username input field.
PasswordForm form(MakeSimpleFormWithOnlyPasswordField());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form, as if the info bar was accepted.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
}
// Test that if there are two "similar" forms in different frames, both get
// filled. This means slightly different things depending on whether the
// kNewPasswordFormParsing feature is enabled or not, so it is covered by two
// tests below.
// If kNewPasswordFormParsing is enabled, then "similar" is governed by
// NewPasswordFormManager::DoesManage, which in turn delegates to the unique
// renderer ID of the forms being the same. Note, however, that such ID is only
// unique within one renderer process. If different frames on the page are
// rendered by different processes, two unrelated forms can end up with the same
// ID. The test checks that nevertheless each of them gets assigned its own
// NewPasswordFormManager and filled as expected.
TEST_F(PasswordManagerTest, FillPasswordOnManyFrames_SameId) {
// Setting task runner is required since NewPasswordFormManager uses
// PostDelayTask for making filling.
TestMockTimeTaskRunner::ScopedContext scoped_context_(task_runner_.get());
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNewPasswordFormParsing);
// Two unrelated forms...
FormData form_data;
form_data.origin = GURL("http://www.google.com/a/LoginAuth");
form_data.action = GURL("http://www.google.com/a/Login");
form_data.fields.resize(2);
form_data.fields[0].name = ASCIIToUTF16("Email");
form_data.fields[0].value = ASCIIToUTF16("googleuser");
form_data.fields[0].unique_renderer_id = 1;
form_data.fields[0].form_control_type = "text";
form_data.fields[1].name = ASCIIToUTF16("Passwd");
form_data.fields[1].value = ASCIIToUTF16("p4ssword");
form_data.fields[1].unique_renderer_id = 2;
form_data.fields[1].form_control_type = "password";
PasswordForm first_form;
first_form.form_data = form_data;
form_data.origin = GURL("http://www.example.com/");
form_data.action = GURL("http://www.example.com/");
form_data.fields[0].name = ASCIIToUTF16("User");
form_data.fields[0].value = ASCIIToUTF16("exampleuser");
form_data.fields[0].unique_renderer_id = 3;
form_data.fields[1].name = ASCIIToUTF16("Pwd");
form_data.fields[1].value = ASCIIToUTF16("1234");
form_data.fields[1].unique_renderer_id = 4;
PasswordForm second_form;
second_form.form_data = form_data;
// Make the forms be "similar".
first_form.form_data.unique_renderer_id =
second_form.form_data.unique_renderer_id = 7654;
// The following expectation covers the calls from the old
// PasswordFormManager.
EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(PasswordForm()), _))
.Times(2);
// Observe the form in the first frame.
EXPECT_CALL(*store_,
GetLogins(PasswordStore::FormDigest(first_form.form_data), _))
.WillOnce(WithArg<1>(InvokeConsumer(first_form)));
EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->OnPasswordFormsParsed(&driver_, {first_form});
// Observe the form in the second frame.
MockPasswordManagerDriver driver_b;
EXPECT_CALL(*store_,
GetLogins(PasswordStore::FormDigest(second_form.form_data), _))
.WillOnce(WithArg<1>(InvokeConsumer(second_form)));
EXPECT_CALL(driver_b, FillPasswordForm(_));
manager()->OnPasswordFormsParsed(&driver_b, {second_form});
task_runner_->FastForwardUntilNoTasksRemain();
}
// If kNewPasswordFormParsing is disabled, "similar" is governed by
// PasswordFormManager::DoesManage and is related to actual similarity of the
// forms, including having the same signon realm (and hence origin). Should a
// page have two frames with the same origin and a form, and those two forms be
// similar, then it is important to ensure that the single governing
// PasswordFormManager knows about both PasswordManagerDriver instances and
// instructs them to fill.
TEST_F(PasswordManagerTest, FillPasswordOnManyFrames_SameForm) {
PasswordForm same_form = MakeSimpleForm();
// Observe the form in the first frame.
EXPECT_CALL(driver_, FillPasswordForm(_));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(same_form)));
manager()->OnPasswordFormsParsed(&driver_, {same_form});
// Now the form will be seen the second time, in a different frame. The driver
// for that frame should be told to fill it, but the store should not be asked
// for it again.
MockPasswordManagerDriver driver_b;
EXPECT_CALL(driver_b, FillPasswordForm(_));
EXPECT_CALL(*store_, GetLogins(_, _)).Times(0);
manager()->OnPasswordFormsParsed(&driver_b, {same_form});
}
TEST_F(PasswordManagerTest, SameDocumentNavigation) {
// Test that observing a newly submitted form shows the save password bar on
// call in page navigation.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPasswordFormSubmittedNoChecks(&driver_, form);
ASSERT_TRUE(form_manager_to_save);
// Simulate saving the form, as if the info bar was accepted.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
// The Save() call triggers updating for |pending_login_managers_|, hence the
// further GetLogins call.
EXPECT_CALL(*store_, GetLogins(_, _));
form_manager_to_save->Save();
}
TEST_F(PasswordManagerTest, SameDocumentBlacklistedSite) {
// Test that observing a newly submitted form on blacklisted site does notify
// the embedder on call in page navigation.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
// Simulate that blacklisted form stored in store.
PasswordForm blacklisted_form(form);
blacklisted_form.username_value = ASCIIToUTF16("");
blacklisted_form.blacklisted_by_user = true;
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(blacklisted_form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
// Prefs are needed for failure logging about blacklisting.
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPasswordFormSubmittedNoChecks(&driver_, form);
EXPECT_TRUE(form_manager_to_save->IsBlacklisted());
}
TEST_F(PasswordManagerTest, SavingSignupForms_NoHTMLMatch) {
// Signup forms don't require HTML attributes match in order to save.
// Verify that we prefer a better match (action + origin vs. origin).
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
PasswordForm wrong_action_form(form);
wrong_action_form.action = GURL("http://www.google.com/other/action");
observed.push_back(wrong_action_form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate either form changing or heuristics choosing other fields
// after the user has entered their information.
PasswordForm submitted_form(form);
submitted_form.new_password_element = ASCIIToUTF16("new_password");
submitted_form.new_password_value = form.password_value;
submitted_form.password_element.clear();
submitted_form.password_value.clear();
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(submitted_form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form, as if the info bar was accepted.
PasswordForm form_to_save;
EXPECT_CALL(*store_, AddLogin(_)).WillOnce(SaveArg<0>(&form_to_save));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
// PasswordManager observed two forms, and should have associate the saved one
// with the observed form with a matching action.
EXPECT_EQ(form.action, form_to_save.action);
// Password values are always saved as the current password value.
EXPECT_EQ(submitted_form.new_password_value, form_to_save.password_value);
EXPECT_EQ(submitted_form.new_password_element, form_to_save.password_element);
}
TEST_F(PasswordManagerTest, SavingSignupForms_NoActionMatch) {
// Signup forms don't require HTML attributes match in order to save.
// Verify that we prefer a better match (HTML attributes + origin vs. origin).
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
// Change the submit element so we can track which of the two forms is
// chosen as a better match.
PasswordForm wrong_submit_form(form);
wrong_submit_form.submit_element = ASCIIToUTF16("different_signin");
wrong_submit_form.new_password_element = ASCIIToUTF16("new_password");
wrong_submit_form.new_password_value = form.password_value;
wrong_submit_form.password_element.clear();
wrong_submit_form.password_value.clear();
observed.push_back(wrong_submit_form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
PasswordForm submitted_form(form);
submitted_form.action = GURL("http://www.google.com/other/action");
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(submitted_form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form, as if the info bar was accepted.
PasswordForm form_to_save;
EXPECT_CALL(*store_, AddLogin(_)).WillOnce(SaveArg<0>(&form_to_save));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
// PasswordManager observed two forms, and should have associate the saved one
// with the observed form with a matching action.
EXPECT_EQ(form.submit_element, form_to_save.submit_element);
EXPECT_EQ(submitted_form.password_value, form_to_save.password_value);
EXPECT_EQ(submitted_form.password_element, form_to_save.password_element);
EXPECT_EQ(submitted_form.username_value, form_to_save.username_value);
EXPECT_EQ(submitted_form.username_element, form_to_save.username_element);
EXPECT_TRUE(form_to_save.new_password_element.empty());
EXPECT_TRUE(form_to_save.new_password_value.empty());
}
TEST_F(PasswordManagerTest, FormSubmittedChangedWithAutofillResponse) {
// This tests verifies that if the observed forms and provisionally saved
// differ in the choice of the username, the saving still succeeds, as long as
// the changed form is marked "parsed using autofill predictions".
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate that based on autofill server username prediction, the username
// of the form changed from the default candidate("Email") to something else.
// Set the parsed_using_autofill_predictions bit to true to make sure that
// choice of username is accepted by PasswordManager, otherwise the the form
// will be rejected as not equal to the observed one. Note that during
// initial parsing we don't have autofill server predictions yet, that's why
// observed form and submitted form may be different.
form.username_element = ASCIIToUTF16("Username");
form.was_parsed_using_autofill_predictions = true;
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form, as if the info bar was accepted.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
}
TEST_F(PasswordManagerTest, FormSubmittedUnchangedNotifiesClient) {
// This tests verifies that if the observed forms and provisionally saved
// forms are the same, then successful submission notifies the client.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
autofill::PasswordForm updated_form;
autofill::PasswordForm notified_form;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, UpdateLogin(_)).WillOnce(SaveArg<0>(&updated_form));
EXPECT_CALL(client_, NotifySuccessfulLoginWithExistingPassword(_))
.WillOnce(SaveArg<0>(&notified_form));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_THAT(form, FormMatches(updated_form));
EXPECT_THAT(form, FormMatches(notified_form));
}
TEST_F(PasswordManagerTest, SaveFormFetchedAfterSubmit) {
// Test that a password is offered for saving even if the response from the
// PasswordStore comes after submit.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
// GetLogins calls remain unanswered to emulate that PasswordStore did not
// fetch a form in time before submission.
EXPECT_CALL(*store_, GetLogins(_, _));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
ASSERT_EQ(1u, manager()->pending_login_managers().size());
PasswordStoreConsumer* store_consumer = nullptr;
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
// This second call is from the new FormFetcher, which is cloned during
// ProvisionalSavePasswords.
EXPECT_CALL(*store_, GetLogins(_, _)).WillOnce(SaveArg<1>(&store_consumer));
OnPasswordFormSubmitted(form);
// Emulate fetching password form from PasswordStore after submission but
// before post-navigation load.
ASSERT_TRUE(store_consumer);
store_consumer->OnGetPasswordStoreResults(
std::vector<std::unique_ptr<PasswordForm>>());
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate saving the form, as if the info bar was accepted.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
}
TEST_F(PasswordManagerTest, PasswordGeneration_FailedSubmission) {
std::vector<PasswordForm> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
form.password_value = form.new_password_value;
manager()->OnPresaveGeneratedPassword(&driver_, form);
// Do not save generated password when the password form reappears.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator()).Times(0);
// Simulate submission failing, with the same form being visible after
// navigation.
OnPasswordFormSubmitted(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// If the user edits the generated password, but does not remove it completely,
// it should stay treated as a generated password.
TEST_F(PasswordManagerTest, PasswordGenerationPasswordEdited_FailedSubmission) {
std::vector<PasswordForm> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
form.password_value = form.new_password_value;
manager()->OnPresaveGeneratedPassword(&driver_, form);
// Simulate user editing and submitting a different password. Verify that
// the edited password is the one that is saved.
form.password_value = ASCIIToUTF16("different_password");
OnPasswordFormSubmitted(form);
// Do not save generated password when the password form reappears.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator()).Times(0);
// Simulate submission failing, with the same form being visible after
// navigation.
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// Generated password are saved even if it looks like the submit failed (the
// form reappeared). Verify that passwords which are no longer marked as
// generated will not be automatically saved.
TEST_F(PasswordManagerTest,
PasswordGenerationNoLongerGeneratedPasswordNotForceSaved_FailedSubmit) {
std::vector<PasswordForm> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
form.password_value = form.new_password_value;
manager()->OnPresaveGeneratedPassword(&driver_, form);
// Simulate user removing generated password and adding a new one.
form.password_value = ASCIIToUTF16("different_password");
EXPECT_CALL(*store_, RemoveLogin(_));
manager()->OnPasswordNoLongerGenerated(&driver_, form);
OnPasswordFormSubmitted(form);
// No infobar or prompt is shown if submission fails.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator()).Times(0);
// Simulate submission failing, with the same form being visible after
// navigation.
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// Verify that passwords which are no longer generated trigger the confirmation
// dialog when submitted.
TEST_F(PasswordManagerTest,
PasswordGenerationNoLongerGeneratedPasswordNotForceSaved) {
std::vector<PasswordForm> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
form.password_value = form.new_password_value;
manager()->OnPresaveGeneratedPassword(&driver_, form);
// Simulate user removing generated password and adding a new one.
form.password_value = ASCIIToUTF16("different_password");
EXPECT_CALL(*store_, RemoveLogin(_));
manager()->OnPasswordNoLongerGenerated(&driver_, form);
OnPasswordFormSubmitted(form);
// Verify that a normal prompt is shown instead of the force saving UI.
std::unique_ptr<PasswordFormManagerForUI> form_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_to_save)));
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator()).Times(0);
// Simulate a successful submission.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_F(PasswordManagerTest, PasswordGenerationUsernameChanged) {
std::vector<PasswordForm> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
form.password_value = form.new_password_value;
manager()->OnPresaveGeneratedPassword(&driver_, form);
// Simulate user changing the password and username, without ever completely
// deleting the password.
form.new_password_value = ASCIIToUTF16("different_password");
form.username_value = ASCIIToUTF16("new_username");
OnPasswordFormSubmitted(form);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
PasswordForm form_to_save;
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _))
.WillOnce(SaveArg<0>(&form_to_save));
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator());
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_EQ(form.username_value, form_to_save.username_value);
// What was "new password" field in the submitted form, becomes the current
// password field in the form to save.
EXPECT_EQ(form.new_password_value, form_to_save.password_value);
}
TEST_F(PasswordManagerTest, PasswordGenerationPresavePassword) {
std::vector<PasswordForm> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
base::HistogramTester histogram_tester;
// The user accepts a generated password.
form.password_value = base::ASCIIToUTF16("password");
PasswordForm sanitized_form(form);
SanitizeFormData(&sanitized_form.form_data);
EXPECT_CALL(*store_, AddLogin(sanitized_form)).WillOnce(Return());
manager()->OnPresaveGeneratedPassword(&driver_, form);
// The user updates the generated password.
PasswordForm updated_form(form);
updated_form.password_value = base::ASCIIToUTF16("password_12345");
PasswordForm sanitized_updated_form(updated_form);
SanitizeFormData(&sanitized_updated_form.form_data);
EXPECT_CALL(*store_,
UpdateLoginWithPrimaryKey(sanitized_updated_form, sanitized_form))
.WillOnce(Return());
manager()->OnPresaveGeneratedPassword(&driver_, updated_form);
histogram_tester.ExpectUniqueSample(
"PasswordManager.GeneratedFormHasNoFormManager", false, 2);
// The user removes the generated password.
EXPECT_CALL(*store_, RemoveLogin(sanitized_updated_form)).WillOnce(Return());
manager()->OnPasswordNoLongerGenerated(&driver_, updated_form);
}
TEST_F(PasswordManagerTest, PasswordGenerationPresavePassword_NoFormManager) {
// Checks that GeneratedFormHasNoFormManager metric is sent if there is no
// corresponding PasswordFormManager for the given form. It should be uncommon
// case.
std::vector<PasswordForm> observed;
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
base::HistogramTester histogram_tester;
// The user accepts a generated password.
PasswordForm form(MakeFormWithOnlyNewPasswordField());
form.password_value = base::ASCIIToUTF16("password");
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
manager()->OnPresaveGeneratedPassword(&driver_, form);
histogram_tester.ExpectUniqueSample(
"PasswordManager.GeneratedFormHasNoFormManager", true, 1);
}
TEST_F(PasswordManagerTest, PasswordGenerationPresavePasswordAndLogin) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
const bool kFalseTrue[] = {false, true};
for (bool found_matched_logins_in_store : kFalseTrue) {
SCOPED_TRACE(testing::Message("found_matched_logins_in_store = ")
<< found_matched_logins_in_store);
PasswordForm form(MakeFormWithOnlyNewPasswordField());
SanitizeFormData(&form.form_data);
std::vector<PasswordForm> observed = {form};
if (found_matched_logins_in_store) {
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
EXPECT_CALL(*store_, GetLoginsForSameOrganizationName(_, _));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
} else {
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
EXPECT_CALL(*store_, GetLoginsForSameOrganizationName(_, _));
}
std::unique_ptr<PasswordFormManagerForUI> form_manager;
if (found_matched_logins_in_store) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager)));
} else {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
}
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator())
.Times(found_matched_logins_in_store ? 0 : 1);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// The user accepts generated password and makes successful login.
form.password_value = form.new_password_value;
PasswordForm presaved_form(form);
if (found_matched_logins_in_store)
presaved_form.username_value.clear();
EXPECT_CALL(*store_, AddLogin(presaved_form)).WillOnce(Return());
manager()->OnPresaveGeneratedPassword(&driver_, form);
::testing::Mock::VerifyAndClearExpectations(store_.get());
EXPECT_CALL(*store_, IsAbleToSavePasswords()).WillRepeatedly(Return(true));
if (!found_matched_logins_in_store)
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, presaved_form));
OnPasswordFormSubmitted(form);
observed.clear();
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
::testing::Mock::VerifyAndClearExpectations(store_.get());
EXPECT_CALL(*store_, IsAbleToSavePasswords()).WillRepeatedly(Return(true));
if (found_matched_logins_in_store) {
// Credentials should be updated only when the user explicitly chooses.
ASSERT_TRUE(form_manager);
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, presaved_form));
form_manager->Update(form_manager->GetPendingCredentials());
::testing::Mock::VerifyAndClearExpectations(store_.get());
}
}
}
TEST_F(PasswordManagerTest,
PasswordGenerationNoCorrespondingPasswordFormManager) {
// Verifies that if there is no corresponding password form manager for the
// given form, new password form manager should fetch data from the password
// store. Also verifies that |SetGenerationElementAndReasonForForm| doesn't
// change |has_generated_password_| of new password form manager.
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm form(MakeFormWithOnlyNewPasswordField());
std::vector<PasswordForm> observed;
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _));
manager()->SetGenerationElementAndReasonForForm(&driver_, form,
base::string16(), false);
ASSERT_EQ(1u, manager()->pending_login_managers().size());
PasswordFormManager* form_manager =
manager()->pending_login_managers().front().get();
EXPECT_FALSE(form_manager->HasGeneratedPassword());
}
TEST_F(PasswordManagerTest, UpdateFormManagers) {
for (bool new_parsing_for_saving : {false, true}) {
SCOPED_TRACE(testing::Message()
<< "new_parsing_for_saving = " << new_parsing_for_saving);
base::test::ScopedFeatureList scoped_feature_list;
if (new_parsing_for_saving)
TurnOnNewParsingForSaving(&scoped_feature_list);
// Seeing a form should result in creating PasswordFormManager and
// NewPasswordFormManager and querying PasswordStore. Calling
// UpdateFormManagers should result in querying the store again.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, {PasswordForm()});
// When new parsing is on, both PasswordFormManager and
// NewPasswordFormManager query the password store.
size_t expected_calls_to_store = new_parsing_for_saving ? 2 : 1;
EXPECT_CALL(*store_, GetLogins(_, _)).Times(expected_calls_to_store);
manager()->UpdateFormManagers();
testing::Mock::VerifyAndClearExpectations(&store_);
}
}
TEST_F(PasswordManagerTest, DropFormManagers) {
// Interrupt the normal submit flow by DropFormManagers().
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
manager()->DropFormManagers();
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_F(PasswordManagerTest, AutofillingOfAffiliatedCredentials) {
PasswordForm android_form(MakeAndroidCredential());
PasswordForm observed_form(MakeSimpleForm());
std::vector<PasswordForm> observed_forms;
observed_forms.push_back(observed_form);
autofill::PasswordFormFillData form_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&form_data));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(android_form)));
manager()->OnPasswordFormsParsed(&driver_, observed_forms);
observed_forms.clear();
manager()->OnPasswordFormsRendered(&driver_, observed_forms, true);
EXPECT_EQ(android_form.username_value, form_data.username_field.value);
EXPECT_EQ(android_form.password_value, form_data.password_field.value);
EXPECT_FALSE(form_data.wait_for_username);
EXPECT_EQ(android_form.signon_realm, form_data.preferred_realm);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm filled_form(observed_form);
filled_form.username_value = android_form.username_value;
filled_form.password_value = android_form.password_value;
OnPasswordFormSubmitted(filled_form);
PasswordForm saved_form;
PasswordForm saved_notified_form;
EXPECT_CALL(*store_, UpdateLogin(_)).WillOnce(SaveArg<0>(&saved_form));
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(client_, NotifySuccessfulLoginWithExistingPassword(_))
.WillOnce(SaveArg<0>(&saved_notified_form));
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
observed_forms.clear();
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsParsed(&driver_, observed_forms);
manager()->OnPasswordFormsRendered(&driver_, observed_forms, true);
EXPECT_THAT(saved_form, FormMatches(android_form));
EXPECT_THAT(saved_form, FormMatches(saved_notified_form));
}
// If the manager fills a credential originally saved from an affiliated Android
// application, and the user overwrites the password, they should be prompted if
// they want to update. If so, the Android credential itself should be updated.
TEST_F(PasswordManagerTest, UpdatePasswordOfAffiliatedCredential) {
PasswordForm android_form(MakeAndroidCredential());
PasswordForm observed_form(MakeSimpleForm());
std::vector<PasswordForm> observed_forms = {observed_form};
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(android_form)));
manager()->OnPasswordFormsParsed(&driver_, observed_forms);
manager()->OnPasswordFormsRendered(&driver_, observed_forms, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm filled_form(observed_form);
filled_form.username_value = android_form.username_value;
filled_form.password_value = ASCIIToUTF16("new_password");
OnPasswordFormSubmitted(filled_form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
observed_forms.clear();
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsParsed(&driver_, observed_forms);
manager()->OnPasswordFormsRendered(&driver_, observed_forms, true);
PasswordForm saved_form;
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
EXPECT_CALL(*store_, UpdateLogin(_)).WillOnce(SaveArg<0>(&saved_form));
ASSERT_TRUE(form_manager_to_save);
form_manager_to_save->Save();
PasswordForm expected_form(android_form);
expected_form.password_value = filled_form.password_value;
EXPECT_THAT(saved_form, FormMatches(expected_form));
}
TEST_F(PasswordManagerTest, ClearedFieldsSuccessCriteria) {
// Test that a submission is considered to be successful on a change password
// form without username when fields valued are cleared.
PasswordForm form(MakeFormWithOnlyNewPasswordField());
form.username_element.clear();
form.username_value.clear();
std::vector<PasswordForm> observed = {form};
// Emulate page load.
EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
ASSERT_EQ(1u, manager()->pending_login_managers().size());
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
// Returning result from the store.
PasswordFormManager* form_manager =
manager()->pending_login_managers().front().get();
ASSERT_TRUE(form_manager);
static_cast<FormFetcherImpl*>(form_manager->GetFormFetcher())
->OnGetPasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>>());
OnPasswordFormSubmitted(form);
// JavaScript cleared field values.
observed[0].password_value.clear();
observed[0].new_password_value.clear();
// Check success of the submission.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
// Check that no sync password hash is saved when no username is available,
// because we it's not clear whether the submitted credentials are sync
// credentials.
TEST_F(PasswordManagerTest, NotSavingSyncPasswordHash_NoUsername) {
// Simulate loading a simple form with no existing stored password.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleGAIAForm());
// Simulate that no username is found.
form.username_value.clear();
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
// Simulate that this credentials which is similar to be sync credentials.
client_.FilterAllResultsForSaving();
// Check that no Gaia credential password hash is saved.
EXPECT_CALL(*store_, SaveGaiaPasswordHash(_, _, _)).Times(0);
OnPasswordFormSubmitted(form);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// Check that no sync password hash is saved when the submitted credentials are
// not qualified as sync credentials.
TEST_F(PasswordManagerTest, NotSavingSyncPasswordHash_NotSyncCredentials) {
// Simulate loading a simple form with no existing stored password.
PasswordForm form(MakeSimpleGAIAForm());
std::vector<PasswordForm> observed = {form};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
// Check that no Gaia credential password hash is saved since these
// credentials are eligible for saving.
EXPECT_CALL(*store_, SaveGaiaPasswordHash(_, _, _)).Times(0);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
OnPasswordFormSubmitted(form);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
#endif
TEST_F(PasswordManagerTest, ManualFallbackForSaving) {
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
PasswordForm stored_form = form;
stored_form.password_value = ASCIIToUTF16("old_password");
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeConsumer(stored_form)));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// The username of the stored form is the same, there should be update bubble.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, form);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
// The username of the stored form is different, there should be save bubble.
PasswordForm new_form = form;
new_form.username_value = ASCIIToUTF16("another_username");
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, new_form);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(new_form));
// Hide the manual fallback.
EXPECT_CALL(client_, HideManualFallbackForSaving());
manager()->HideManualFallbackForSaving();
// Two PasswordFormManagers instances hold references to a shared
// PasswordFormMetrics recorder. These need to be freed to flush the metrics
// into the test_ukm_recorder.
manager_.reset();
form_manager_to_save.reset();
// Verify that the last state is recorded.
CheckMetricHasValue(
test_ukm_recorder, ukm::builders::PasswordForm::kEntryName,
ukm::builders::PasswordForm::kSaving_ShowedManualFallbackForSavingName,
1);
}
// Tests that the manual fallback for saving isn't shown if there is no response
// from the password storage. When crbug.com/741537 is fixed, change this test.
TEST_F(PasswordManagerTest, ManualFallbackForSaving_SlowBackend) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
PasswordStoreConsumer* store_consumer = nullptr;
EXPECT_CALL(*store_, GetLogins(_, _)).WillOnce(SaveArg<1>(&store_consumer));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// There is no response from the store. Don't show the fallback.
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _)).Times(0);
manager()->ShowManualFallbackForSaving(&driver_, form);
// The storage responded. The fallback can be shown.
ASSERT_TRUE(store_consumer);
store_consumer->OnGetPasswordStoreResults(
std::vector<std::unique_ptr<PasswordForm>>());
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, form);
}
TEST_F(PasswordManagerTest, ManualFallbackForSaving_GeneratedPassword) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// A user accepts a password generated by Chrome. It triggers password
// presaving and showing manual fallback.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(*store_, AddLogin(_));
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, true, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPresaveGeneratedPassword(&driver_, form);
manager()->ShowManualFallbackForSaving(&driver_, form);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
// A user edits the generated password. And again it causes password presaving
// and showing manual fallback.
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _));
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, true, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPresaveGeneratedPassword(&driver_, form);
manager()->ShowManualFallbackForSaving(&driver_, form);
// A user removes the generated password. The presaved password is removed,
// the fallback is disabled.
EXPECT_CALL(*store_, RemoveLogin(_));
EXPECT_CALL(client_, HideManualFallbackForSaving());
manager()->OnPasswordNoLongerGenerated(&driver_, form);
manager()->HideManualFallbackForSaving();
}
// Tests that Autofill predictions are processed correctly. If at least one of
// these predictions can be converted to a |PasswordFormFieldPredictionMap|, the
// predictions map is updated accordingly.
TEST_F(PasswordManagerTest, ProcessAutofillPredictions) {
// Create FormData form with two fields.
autofill::FormData form;
form.origin = GURL("http://foo.com");
autofill::FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("username");
field.name = ASCIIToUTF16("username");
form.fields.push_back(field);
field.label = ASCIIToUTF16("password");
field.name = ASCIIToUTF16("password");
form.fields.push_back(field);
FormStructure form_structure(form);
std::vector<FormStructure*> forms;
forms.push_back(&form_structure);
autofill::AutofillQueryResponseContents response;
// If there are multiple predictions for the field,
// |AutofillField::overall_server_type_| will store only autofill vote, but
// not password vote. |AutofillField::server_predictions_| should store all
// predictions.
autofill::AutofillQueryResponseContents_Field* field0 = response.add_field();
field0->set_overall_type_prediction(autofill::PHONE_HOME_NUMBER);
autofill::AutofillQueryResponseContents_Field_FieldPrediction*
field_prediction0 = field0->add_predictions();
field_prediction0->set_type(autofill::PHONE_HOME_NUMBER);
autofill::AutofillQueryResponseContents_Field_FieldPrediction*
field_prediction1 = field0->add_predictions();
field_prediction1->set_type(autofill::USERNAME);
autofill::AutofillQueryResponseContents_Field* field1 = response.add_field();
field1->set_overall_type_prediction(autofill::PASSWORD);
autofill::AutofillQueryResponseContents_Field_FieldPrediction*
field_prediction2 = field1->add_predictions();
field_prediction2->set_type(autofill::PASSWORD);
autofill::AutofillQueryResponseContents_Field_FieldPrediction*
field_prediction3 = field1->add_predictions();
field_prediction3->set_type(autofill::PROBABLY_NEW_PASSWORD);
std::string response_string;
ASSERT_TRUE(response.SerializeToString(&response_string));
FormStructure::ParseQueryResponse(response_string, forms, nullptr);
// Check that Autofill predictions are converted to password related
// predictions.
std::map<autofill::FormData, autofill::PasswordFormFieldPredictionMap>
predictions;
predictions[form][form.fields[0]] = autofill::PREDICTION_USERNAME;
predictions[form][form.fields[1]] = autofill::PREDICTION_CURRENT_PASSWORD;
EXPECT_CALL(driver_, AutofillDataReceived(predictions));
manager()->ProcessAutofillPredictions(&driver_, forms);
}
// Let the PasswordManager see no password forms. As a default, it should
// suggest the last commited navigation entry to check for being enabled.
TEST_F(PasswordManagerTest, EntryToCheck_Default) {
EXPECT_EQ(PasswordManager::NavigationEntryToCheck::LAST_COMMITTED,
manager()->entry_to_check());
manager()->OnPasswordFormsParsed(nullptr, std::vector<PasswordForm>());
EXPECT_EQ(PasswordManager::NavigationEntryToCheck::LAST_COMMITTED,
manager()->entry_to_check());
}
// If the PasswordManager sees HTML password forms, it should suggest the last
// commited navigation entry to check for being enabled.
TEST_F(PasswordManagerTest, EntryToCheck_HTML) {
PasswordForm html_form;
html_form.scheme = PasswordForm::SCHEME_HTML;
html_form.origin = GURL("http://accounts.google.com/");
html_form.signon_realm = "http://accounts.google.com/";
EXPECT_CALL(*store_, GetLogins(_, _));
manager()->OnPasswordFormsParsed(nullptr, {html_form});
EXPECT_EQ(PasswordManager::NavigationEntryToCheck::LAST_COMMITTED,
manager()->entry_to_check());
}
// If the PasswordManager sees HTTP auth password forms, it should suggest the
// visible navigation entry to check for being enabled.
TEST_F(PasswordManagerTest, EntryToCheck_HTTP_auth) {
PasswordForm http_auth_form;
http_auth_form.scheme = PasswordForm::SCHEME_BASIC;
http_auth_form.origin = GURL("http://accounts.google.com/");
http_auth_form.signon_realm = "http://accounts.google.com/";
EXPECT_CALL(*store_, GetLogins(_, _));
manager()->OnPasswordFormsParsed(nullptr, {http_auth_form});
EXPECT_EQ(PasswordManager::NavigationEntryToCheck::VISIBLE,
manager()->entry_to_check());
}
// Sync password hash should be updated upon submission of change password page.
TEST_F(PasswordManagerTest, SaveSyncPasswordHashOnChangePasswordPage) {
PasswordForm form(MakeGAIAChangePasswordForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
std::vector<PasswordForm> observed;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(
*store_,
SaveGaiaPasswordHash(
"googleuser", form.new_password_value,
metrics_util::SyncPasswordHashChange::CHANGED_IN_CONTENT_AREA));
#endif
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
// Non-Sync Gaia password hash should be saved upon submission of Gaia login
// page.
TEST_F(PasswordManagerTest, SaveOtherGaiaPasswordHash) {
PasswordForm form(MakeSimpleGAIAForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
std::vector<PasswordForm> observed;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
EXPECT_CALL(
*store_,
SaveGaiaPasswordHash(
"googleuser", form.password_value,
metrics_util::SyncPasswordHashChange::NOT_SYNC_PASSWORD_CHANGE));
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// Enterprise password hash should be saved upon submission of enterprise login
// page.
TEST_F(PasswordManagerTest, SaveEnterprisePasswordHash) {
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
std::vector<PasswordForm> observed;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveEnterprisePasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(false));
EXPECT_CALL(*store_,
SaveEnterprisePasswordHash("googleuser", form.password_value));
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
#endif
// If there are no forms to parse, certificate errors should not be reported.
TEST_F(PasswordManagerTest, CertErrorReported_NoForms) {
const std::vector<PasswordForm> observed;
EXPECT_CALL(client_, GetMainFrameCertStatus())
.WillRepeatedly(Return(net::CERT_STATUS_AUTHORITY_INVALID));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
base::HistogramTester histogram_tester;
manager()->OnPasswordFormsParsed(&driver_, observed);
histogram_tester.ExpectTotalCount(
"PasswordManager.CertificateErrorsWhileSeeingForms", 0);
}
TEST_F(PasswordManagerTest, CertErrorReported) {
constexpr struct {
net::CertStatus cert_status;
metrics_util::CertificateError expected_error;
} kCases[] = {
{0, metrics_util::CertificateError::NONE},
{net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, // not an error
metrics_util::CertificateError::NONE},
{net::CERT_STATUS_COMMON_NAME_INVALID,
metrics_util::CertificateError::COMMON_NAME_INVALID},
{net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM,
metrics_util::CertificateError::WEAK_SIGNATURE_ALGORITHM},
{net::CERT_STATUS_DATE_INVALID,
metrics_util::CertificateError::DATE_INVALID},
{net::CERT_STATUS_AUTHORITY_INVALID,
metrics_util::CertificateError::AUTHORITY_INVALID},
{net::CERT_STATUS_WEAK_KEY, metrics_util::CertificateError::OTHER},
{net::CERT_STATUS_DATE_INVALID | net::CERT_STATUS_WEAK_KEY,
metrics_util::CertificateError::DATE_INVALID},
{net::CERT_STATUS_DATE_INVALID | net::CERT_STATUS_AUTHORITY_INVALID,
metrics_util::CertificateError::AUTHORITY_INVALID},
{net::CERT_STATUS_DATE_INVALID | net::CERT_STATUS_AUTHORITY_INVALID |
net::CERT_STATUS_WEAK_KEY,
metrics_util::CertificateError::AUTHORITY_INVALID},
};
const std::vector<PasswordForm> observed = {PasswordForm()};
// PasswordStore requested only once for the same form.
EXPECT_CALL(*store_, GetLogins(_, _));
for (const auto& test_case : kCases) {
SCOPED_TRACE(testing::Message("index of test_case = ")
<< (&test_case - kCases));
EXPECT_CALL(client_, GetMainFrameCertStatus())
.WillRepeatedly(Return(test_case.cert_status));
base::HistogramTester histogram_tester;
manager()->OnPasswordFormsParsed(&driver_, observed);
histogram_tester.ExpectUniqueSample(
"PasswordManager.CertificateErrorsWhileSeeingForms",
test_case.expected_error, 1);
}
}
TEST_F(PasswordManagerTest, CreatingFormManagers) {
// Add the NewPasswordFormParsing feature.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNewPasswordFormParsing);
PasswordForm form(MakeSimpleForm());
std::vector<PasswordForm> observed;
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
// Check that the form manager is created.
EXPECT_EQ(1u, manager()->form_managers().size());
EXPECT_TRUE(manager()->form_managers()[0]->DoesManage(form.form_data,
client_.GetDriver()));
// Check that receiving the same form the second time does not lead to
// creating new form manager.
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_EQ(1u, manager()->form_managers().size());
}
TEST_F(PasswordManagerTest,
ShowManualFallbacksDontChangeProvisionalSaveManager) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_THAT(manager()->provisional_save_manager(), IsNull());
manager()->ShowManualFallbackForSaving(&driver_, form);
EXPECT_THAT(manager()->provisional_save_manager(), IsNull());
// The user submits the form and a provisional save manager is set.
OnPasswordFormSubmitted(form);
EXPECT_THAT(manager()->provisional_save_manager(), NotNull());
const PasswordFormManager* last_provisional_save_manager =
manager()->provisional_save_manager();
EXPECT_CALL(client_, HideManualFallbackForSaving());
// The call to manual fallback with |form| equal to already saved should close
// the fallback.
manager()->ShowManualFallbackForSaving(&driver_, form);
EXPECT_THAT(manager()->provisional_save_manager(), NotNull());
EXPECT_EQ(last_provisional_save_manager,
manager()->provisional_save_manager());
}
// Tests that processing normal HTML form submissions works properly with the
// new parsing. For details see scheme 1 in comments before
// |form_managers_| in password_manager.h.
TEST_F(PasswordManagerTest, ProcessingNormalFormSubmission) {
for (bool only_new_parser : {false, true}) {
for (bool successful_submission : {false, true}) {
SCOPED_TRACE(testing::Message("only_new_parser = ")
<< only_new_parser
<< " successful_submission = " << successful_submission);
base::test::ScopedFeatureList scoped_feature_list;
if (only_new_parser)
TurnOnOnlyNewPassword(&scoped_feature_list);
else
TurnOnNewParsingForSaving(&scoped_feature_list);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
std::vector<PasswordForm> observed;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
if (only_new_parser)
EXPECT_TRUE(manager()->pending_login_managers().empty());
auto submitted_form = form;
submitted_form.form_data.fields[0].value = ASCIIToUTF16("username");
submitted_form.form_data.fields[1].value = ASCIIToUTF16("password1");
OnPasswordFormSubmitted(submitted_form);
EXPECT_TRUE(manager()->GetSubmittedManagerForTest());
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
// Simulate submission.
if (successful_submission) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// The form disappeared, so the submission is condered to be successful.
observed.clear();
} else {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
}
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Multiple calls of OnPasswordFormsRendered should be handled gracefully.
manager()->OnPasswordFormsRendered(&driver_, observed, true);
testing::Mock::VerifyAndClearExpectations(&client_);
}
}
}
// Tests that processing form submissions without navigations works properly
// with the new parsing. For details see scheme 2 in comments before
// |form_managers_| in password_manager.h.
TEST_F(PasswordManagerTest, ProcessingOtherSubmissionTypes) {
for (bool only_new_parser : {false, true}) {
SCOPED_TRACE(testing::Message("only_new_parser = ") << only_new_parser);
base::test::ScopedFeatureList scoped_feature_list;
if (only_new_parser)
TurnOnOnlyNewPassword(&scoped_feature_list);
else
TurnOnNewParsingForSaving(&scoped_feature_list);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
std::vector<PasswordForm> observed;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
auto submitted_form = form;
submitted_form.form_data.fields[0].value = ASCIIToUTF16("username");
submitted_form.form_data.fields[1].value = ASCIIToUTF16("strong_password");
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPasswordFormSubmittedNoChecks(&driver_, submitted_form);
EXPECT_TRUE(manager()->form_managers().empty());
testing::Mock::VerifyAndClearExpectations(&client_);
}
}
TEST_F(PasswordManagerTest, SubmittedGaiaFormWithoutVisiblePasswordField) {
// Tests that a submitted GAIA sign-in form which does not contain a visible
// password field is skipped.
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleGAIAForm());
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
form.username_value = ASCIIToUTF16("username");
form.password_value = ASCIIToUTF16("password");
form.form_data.fields[1].is_focusable = false;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
manager()->OnPasswordFormSubmittedNoChecks(&driver_, form);
}
// Tests that PasswordFormManager and NewPasswordFormManager for the same form
// have the same metrics recorder.
TEST_F(PasswordManagerTest, CheckMetricsRecorder) {
base::test::ScopedFeatureList scoped_feature_list;
TurnOnNewParsingForSaving(&scoped_feature_list);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
std::vector<PasswordForm> observed;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
const std::vector<std::unique_ptr<PasswordFormManager>>&
password_form_managers = manager()->pending_login_managers();
const std::vector<std::unique_ptr<NewPasswordFormManager>>&
new_password_form_managers = manager()->form_managers();
ASSERT_EQ(1u, password_form_managers.size());
ASSERT_EQ(1u, new_password_form_managers.size());
EXPECT_TRUE(password_form_managers[0]->GetMetricsRecorder());
EXPECT_EQ(password_form_managers[0]->GetMetricsRecorder(),
new_password_form_managers[0]->GetMetricsRecorder());
}
TEST_F(PasswordManagerTest, MetricForSchemeOfSuccessfulLogins) {
for (bool origin_is_secure : {false, true}) {
SCOPED_TRACE(testing::Message("origin_is_secure = ") << origin_is_secure);
PasswordForm form(MakeSimpleForm());
form.origin =
GURL(origin_is_secure ? "https://example.com" : "http://example.com");
std::vector<PasswordForm> observed = {form};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
observed.clear();
base::HistogramTester histogram_tester;
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
histogram_tester.ExpectUniqueSample(
"PasswordManager.SuccessfulLoginHappened", origin_is_secure, 1);
}
}
TEST_F(PasswordManagerTest, ManualFallbackForSavingNewParser) {
base::test::ScopedFeatureList scoped_feature_list;
TurnOnNewParsingForSaving(&scoped_feature_list);
NewPasswordFormManager::set_wait_for_server_predictions_for_filling(false);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
std::vector<PasswordForm> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
PasswordForm stored_form = form;
stored_form.password_value = ASCIIToUTF16("old_password");
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(stored_form)));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// The username of the stored form is the same, there should be update bubble.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, form);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
// The username of the stored form is different, there should be save bubble.
PasswordForm new_form = form;
new_form.username_value = ASCIIToUTF16("another_username");
new_form.form_data.fields[0].value = new_form.username_value;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, new_form);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(new_form));
// Hide the manual fallback.
EXPECT_CALL(client_, HideManualFallbackForSaving());
manager()->HideManualFallbackForSaving();
}
// Check that some value for the ParsingOnSavingDifference UKM metric is emitted
// on a successful login.
TEST_F(PasswordManagerTest, ParsingOnSavingMetricRecorded) {
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
base::test::ScopedFeatureList scoped_feature_list;
TurnOnNewParsingForSaving(&scoped_feature_list);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
PasswordForm form = MakeSimpleForm();
std::vector<PasswordForm> observed = {form};
manager()->OnPasswordFormsParsed(nullptr, observed);
// Provisionally save and simulate a successful landing page load to make
// manager() believe this password should be saved.
manager()->OnPasswordFormSubmitted(nullptr, form);
manager()->OnPasswordFormsRendered(nullptr, {}, true);
// Destroy |manager_| to send off UKM metrics.
manager_.reset();
EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(
GetMetricEntry(test_ukm_recorder,
ukm::builders::PasswordForm::kEntryName),
ukm::builders::PasswordForm::kParsingOnSavingDifferenceName));
}
TEST_F(PasswordManagerTest, NoSavePromptWhenPasswordManagerDisabled) {
base::test::ScopedFeatureList scoped_feature_list;
TurnOnNewParsingForSaving(&scoped_feature_list);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(false));
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, {form});
auto submitted_form = form;
submitted_form.form_data.fields[0].value = ASCIIToUTF16("username");
submitted_form.form_data.fields[1].value = ASCIIToUTF16("strong_password");
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
manager()->OnPasswordFormSubmittedNoChecks(&driver_, submitted_form);
}
// Check that when autofill predictions are received before a form is found then
// server predictions are not ignored and used for filling.
TEST_F(PasswordManagerTest, AutofillPredictionBeforeFormParsed) {
TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNewPasswordFormParsing);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
// Simulate that the form is incorrectly marked as sign-up, which means it can
// not be filled without server predictions.
form.form_data.fields[1].autocomplete_attribute = "new-password";
// Server predictions says that this is a sign-in form. Since they have higher
// priority than autocomplete attributes then the form should be filled.
FormStructure form_structure(form.form_data);
form_structure.field(1)->set_server_type(autofill::PASSWORD);
manager()->ProcessAutofillPredictions(&driver_, {&form_structure});
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
// There are 2 fills, the first when the server predictions are received, the
// second when the filling delayed task is executed. In production code a
// delayed task is not posted since receiving results from the store is
// asynchronous in contrast to test code.
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
manager()->OnPasswordFormsParsed(&driver_, {form});
task_runner_->FastForwardUntilNoTasksRemain();
}
// Checks the following scenario:
// 1. The user is typing in a password form.
// 2. Navigation happens.
// 3. The password disappeared after navigation.
// 4. A save prompt is shown.
TEST_F(PasswordManagerTest, SavingAfterUserTypingAndNavigation) {
base::test::ScopedFeatureList scoped_feature_list;
TurnOnNewParsingForSaving(&scoped_feature_list);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
manager()->OnPasswordFormsParsed(&driver_, {form});
// The user is typing as a result the saving manual fallback is shown.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, form);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
// Check that a save prompt is shown when there is no password form after
// the navigation (which suggests that the submission was successful).
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->DidNavigateMainFrame();
manager()->OnPasswordFormsRendered(&driver_, {}, true);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
}
// Check that when a form is submitted and a NewPasswordFormManager not present,
// this ends up reported in ProvisionallySaveFailure UMA and UKM.
TEST_F(PasswordManagerTest, ProvisionallySaveFailure) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
for (bool new_parsing_for_saving : {false, true}) {
SCOPED_TRACE(testing::Message()
<< "new_parsing_for_saving = " << new_parsing_for_saving);
base::test::ScopedFeatureList scoped_feature_list;
if (new_parsing_for_saving)
TurnOnNewParsingForSaving(&scoped_feature_list);
manager()->OnPasswordFormsParsed(nullptr, {});
base::HistogramTester histogram_tester;
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
auto metrics_recorder = std::make_unique<PasswordManagerMetricsRecorder>(
1234, GURL("http://example.com"));
EXPECT_CALL(client_, GetMetricsRecorder())
.WillRepeatedly(Return(metrics_recorder.get()));
PasswordForm unobserved_form = MakeSimpleForm();
manager()->OnPasswordFormSubmitted(nullptr, unobserved_form);
// 2 samples instead of just 1, because one is also reported from the
// missing (old) PasswordFormManager.
const size_t kExpectedSampleCount = new_parsing_for_saving ? 2u : 1u;
histogram_tester.ExpectUniqueSample(
"PasswordManager.ProvisionalSaveFailure",
PasswordManagerMetricsRecorder::NO_MATCHING_FORM, kExpectedSampleCount);
// Flush the UKM reports.
EXPECT_CALL(client_, GetMetricsRecorder()).WillRepeatedly(Return(nullptr));
metrics_recorder.reset();
CheckMetricHasValue(
test_ukm_recorder, ukm::builders::PageWithPassword::kEntryName,
ukm::builders::PageWithPassword::kProvisionalSaveFailureName,
PasswordManagerMetricsRecorder::NO_MATCHING_FORM);
}
}
namespace {
// A convenience helper for type conversions.
template <typename T>
base::Optional<int64_t> MetricValue(T value) {
return base::Optional<int64_t>(static_cast<int64_t>(value));
}
struct MissingFormManagerTestCase {
// Description for logging.
const char* const description = nullptr;
// Is Chrome allowed to save passwords?
enum class Saving { Enabled, Disabled } saving = Saving::Enabled;
// What signal does Chrome have for saving?
enum class Signal { Automatic, Manual, None } save_signal = Signal::Automatic;
// All the forms which are parsed at once.
std::vector<PasswordForm> parsed_forms;
// A list of forms to be processed for saving, one at a time.
std::vector<PasswordForm> processed_forms;
// The expected value of the PageWithPassword::kFormManagerAvailableName
// metric, or base::nullopt if no value should be logged.
base::Optional<int64_t> expected_metric_value;
};
} // namespace
// Test that presence of form managers in various situations is appropriately
// reported through UKM.
TEST_F(PasswordManagerTest, ReportMissingFormManager) {
const PasswordForm form = MakeSimpleForm();
PasswordForm other_form = MakeSimpleForm();
++other_form.form_data.unique_renderer_id;
other_form.signon_realm += "other";
const MissingFormManagerTestCase kTestCases[] = {
{
.description =
"A form is submitted and a NewPasswordFormManager not present.",
.parsed_forms = {},
.save_signal = MissingFormManagerTestCase::Signal::Automatic,
// .parsed_forms is empty, so the processed form below was not
// observed and has no form manager associated.
.processed_forms = {form},
.expected_metric_value =
MetricValue(PasswordManagerMetricsRecorder::FormManagerAvailable::
kMissingProvisionallySave),
},
{
.description = "Manual saving is requested and a "
"NewPasswordFormManager not present.",
.parsed_forms = {},
.save_signal = MissingFormManagerTestCase::Signal::Manual,
// .parsed_forms is empty, so the processed form below was not
// observed and has no form manager associated.
.processed_forms = {form},
.expected_metric_value =
MetricValue(PasswordManagerMetricsRecorder::FormManagerAvailable::
kMissingManual),
},
{
.description = "Manual saving is successfully requested.",
.parsed_forms = {form},
.save_signal = MissingFormManagerTestCase::Signal::Manual,
.processed_forms = {form},
.expected_metric_value = MetricValue(
PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess),
},
{
.description =
"A form is submitted and a NewPasswordFormManager present.",
.parsed_forms = {form},
.save_signal = MissingFormManagerTestCase::Signal::Automatic,
.processed_forms = {form},
.expected_metric_value = MetricValue(
PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess),
},
{
.description = "First failure, then success.",
.parsed_forms = {form},
.save_signal = MissingFormManagerTestCase::Signal::Automatic,
// Processing |other_form| first signals a failure value in the
// metric, but processing |form| after that should overwrite that with
// kSuccess.
.processed_forms = {other_form, form},
.expected_metric_value = MetricValue(
PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess),
},
{
.description = "No forms, no report.",
.parsed_forms = {},
.save_signal = MissingFormManagerTestCase::Signal::None,
.processed_forms = {},
.expected_metric_value = base::nullopt,
},
{
.description = "Not enabled, no report.",
.saving = MissingFormManagerTestCase::Saving::Disabled,
.parsed_forms = {form},
.save_signal = MissingFormManagerTestCase::Signal::Automatic,
.processed_forms = {form},
.expected_metric_value = base::nullopt,
},
};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
for (const MissingFormManagerTestCase& test_case : kTestCases) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(test_case.saving ==
MissingFormManagerTestCase::Saving::Enabled));
for (bool new_parsing_for_saving : {false, true}) {
SCOPED_TRACE(testing::Message()
<< "test case = " << test_case.description
<< ", new_parsing_for_saving = " << new_parsing_for_saving);
base::test::ScopedFeatureList scoped_feature_list;
if (new_parsing_for_saving) {
// This also resets the password manager.
TurnOnNewParsingForSaving(&scoped_feature_list);
} else {
// Reset the password manager to discard state from the previous
// test_case.
manager_.reset(new PasswordManager(&client_));
}
manager()->OnPasswordFormsParsed(nullptr, test_case.parsed_forms);
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
auto metrics_recorder = std::make_unique<PasswordManagerMetricsRecorder>(
1234, GURL("http://example.com"));
EXPECT_CALL(client_, GetMetricsRecorder())
.WillRepeatedly(Return(metrics_recorder.get()));
for (const PasswordForm& processed_form : test_case.processed_forms) {
switch (test_case.save_signal) {
case MissingFormManagerTestCase::Signal::Automatic:
manager()->OnPasswordFormSubmitted(nullptr, processed_form);
break;
case MissingFormManagerTestCase::Signal::Manual:
manager()->ShowManualFallbackForSaving(nullptr, processed_form);
break;
case MissingFormManagerTestCase::Signal::None:
break;
}
}
// Flush the UKM reports.
EXPECT_CALL(client_, GetMetricsRecorder())
.WillRepeatedly(Return(nullptr));
metrics_recorder.reset();
if (test_case.expected_metric_value) {
CheckMetricHasValue(
test_ukm_recorder, ukm::builders::PageWithPassword::kEntryName,
ukm::builders::PageWithPassword::kFormManagerAvailableName,
test_case.expected_metric_value.value());
} else {
EXPECT_FALSE(ukm::TestUkmRecorder::EntryHasMetric(
GetMetricEntry(test_ukm_recorder,
ukm::builders::PageWithPassword::kEntryName),
ukm::builders::PageWithPassword::kFormManagerAvailableName));
}
}
}
}
} // namespace password_manager