blob: 511dd4ac54957903bcb387ff31a459c134d43a88 [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 <limits>
#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_piece.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/task_environment.h"
#include "base/test/test_mock_time_task_runner.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/autofill/core/common/renderer_id.h"
#include "components/autofill/core/common/signatures.h"
#include "components/password_manager/core/browser/field_info_manager.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_check.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory.h"
#include "components/password_manager/core/browser/leak_detection/mock_leak_detection_check_factory.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/password_autofill_manager.h"
#include "components/password_manager/core/browser/password_bubble_experiment.h"
#include "components/password_manager/core/browser/password_form_manager.h"
#include "components/password_manager/core/browser/password_form_manager_for_ui.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/prefs/pref_registry.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.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 "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/test/test_network_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using autofill::FieldRendererId;
using autofill::FormData;
using autofill::FormFieldData;
using autofill::FormRendererId;
using autofill::FormStructure;
using autofill::NO_SERVER_DATA;
using autofill::NOT_USERNAME;
using autofill::PasswordFormFillData;
using autofill::ServerFieldType;
using autofill::SINGLE_USERNAME;
using base::ASCIIToUTF16;
using base::Feature;
using base::TestMockTimeTaskRunner;
using testing::_;
using testing::AnyNumber;
using testing::ByMove;
using testing::Invoke;
using testing::IsNull;
using testing::Mock;
using testing::NotNull;
using testing::Return;
using testing::ReturnRef;
using testing::SaveArg;
using testing::WithArg;
namespace password_manager {
namespace {
MATCHER_P2(FormUsernamePasswordAre, username, password, "") {
return arg.username_value == username && arg.password_value == password;
}
MATCHER_P(FormHasUniqueKey, key, "") {
return ArePasswordFormUniqueKeysEqual(arg, key);
}
MATCHER_P(FormIgnoreDate, expected, "") {
PasswordForm expected_with_date = expected;
expected_with_date.date_created = arg.date_created;
return arg == expected_with_date;
}
MATCHER_P(HasUsernameValue, expected_username, "") {
return arg.username_value == expected_username;
}
class FakeNetworkContext : public network::TestNetworkContext {
public:
FakeNetworkContext() = default;
void IsHSTSActiveForHost(const std::string& host,
IsHSTSActiveForHostCallback callback) override {
std::move(callback).Run(true);
}
};
class MockLeakDetectionCheck : public LeakDetectionCheck {
public:
MOCK_METHOD3(Start, void(const GURL&, base::string16, base::string16));
};
class MockStoreResultFilter : public StubCredentialsFilter {
public:
MOCK_CONST_METHOD1(ShouldSave, bool(const PasswordForm& form));
MOCK_CONST_METHOD1(ReportFormLoginSuccess,
void(const PasswordFormManager& form_manager));
MOCK_CONST_METHOD1(IsSyncAccountEmail, bool(const std::string&));
MOCK_CONST_METHOD1(ShouldSaveGaiaPasswordHash, bool(const PasswordForm&));
MOCK_CONST_METHOD1(ShouldSaveEnterprisePasswordHash,
bool(const 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));
ON_CALL(*this, IsNewTabPage()).WillByDefault(Return(false));
ON_CALL(*this, IsAutofillAssistantUIVisible()).WillByDefault(Return(false));
}
MOCK_METHOD(bool,
IsSavingAndFillingEnabled,
(const GURL&),
(const, override));
MOCK_METHOD(net::CertStatus, GetMainFrameCertStatus, (), (const, override));
MOCK_METHOD(void,
AutofillHttpAuth,
(const PasswordForm&, const PasswordFormManagerForUI*),
(override));
MOCK_METHOD(PasswordStore*, GetProfilePasswordStore, (), (const, override));
MOCK_METHOD(PasswordStore*, GetAccountPasswordStore, (), (const, override));
// The code inside EXPECT_CALL for PromptUserToSaveOrUpdatePasswordPtr and
// ShowManualFallbackForSavingPtr owns the PasswordFormManager* argument.
MOCK_METHOD(void,
PromptUserToSaveOrUpdatePasswordPtr,
(PasswordFormManagerForUI*));
MOCK_METHOD(void,
ShowManualFallbackForSavingPtr,
(PasswordFormManagerForUI*, bool, bool));
MOCK_METHOD(void, HideManualFallbackForSaving, (), (override));
MOCK_METHOD(void,
NotifySuccessfulLoginWithExistingPassword,
(std::unique_ptr<PasswordFormManagerForUI>),
(override));
MOCK_METHOD(void,
AutomaticPasswordSave,
(std::unique_ptr<PasswordFormManagerForUI>),
(override));
MOCK_METHOD(PrefService*, GetPrefs, (), (const, override));
MOCK_METHOD(const GURL&, GetLastCommittedURL, (), (const, override));
MOCK_METHOD(bool, IsCommittedMainFrameSecure, (), (const, override));
MOCK_METHOD(const MockStoreResultFilter*,
GetStoreResultFilter,
(),
(const, override));
MOCK_METHOD(PasswordManagerMetricsRecorder*,
GetMetricsRecorder,
(),
(override));
MOCK_METHOD(bool, IsNewTabPage, (), (const, override));
MOCK_METHOD(bool, IsAutofillAssistantUIVisible, (), (const, override));
MOCK_METHOD(SyncState, GetPasswordSyncState, (), (const, override));
MOCK_METHOD(FieldInfoManager*, GetFieldInfoManager, (), (const, override));
// 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);
}
network::mojom::NetworkContext* GetNetworkContext() const override {
return &network_context_;
}
void FilterAllResultsForSaving() {
EXPECT_CALL(filter_, ShouldSave(_)).WillRepeatedly(Return(false));
}
testing::NiceMock<MockStoreResultFilter>* filter() { return &filter_; }
private:
mutable FakeNetworkContext network_context_;
testing::NiceMock<MockStoreResultFilter> filter_;
};
class MockPasswordManagerDriver : public StubPasswordManagerDriver {
public:
MockPasswordManagerDriver() {
ON_CALL(*this, GetId()).WillByDefault(Return(0));
ON_CALL(*this, IsMainFrame()).WillByDefault(Return(true));
}
MOCK_CONST_METHOD0(GetId, int());
MOCK_METHOD1(FormEligibleForGenerationFound,
void(const autofill::PasswordFormGenerationData&));
MOCK_METHOD1(FillPasswordForm, void(const autofill::PasswordFormFillData&));
MOCK_METHOD0(GetPasswordManager, PasswordManager*());
MOCK_METHOD0(GetPasswordAutofillManager, PasswordAutofillManager*());
MOCK_CONST_METHOD0(IsMainFrame, bool());
MOCK_CONST_METHOD0(GetLastCommittedURL, const GURL&());
};
// Invokes the password store consumer with a single copy of |form|.
ACTION_P2(InvokeConsumer, store, form) {
std::vector<std::unique_ptr<PasswordForm>> result;
result.push_back(std::make_unique<PasswordForm>(form));
arg0->OnGetPasswordStoreResultsFrom(store, std::move(result));
}
ACTION_P(InvokeEmptyConsumerWithForms, store) {
arg0->OnGetPasswordStoreResultsFrom(
store, 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();
}
}
void SetFieldName(const base::string16& name, FormFieldData* field) {
#if defined(OS_IOS)
field->unique_id = name;
#else
field->name = name;
#endif
}
// 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));
}
// Sets |unique_id| in fields on iOS.
void SetUniqueIdIfNeeded(FormData* form) {
// 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->fields)
f.unique_id = f.id_attribute;
#endif
}
class MockFieldInfoManager : public FieldInfoManager {
public:
MOCK_METHOD(void,
AddFieldType,
(autofill::FormSignature,
autofill::FieldSignature,
ServerFieldType),
(override));
MOCK_METHOD(ServerFieldType,
GetFieldType,
(autofill::FormSignature, autofill::FieldSignature),
(const override));
};
} // namespace
// The test parameter controls the feature "EnablePasswordsAccountStorage".
class PasswordManagerTest : public testing::TestWithParam<bool> {
public:
PasswordManagerTest() : task_runner_(new TestMockTimeTaskRunner) {
bool enable_passwords_account_storage = GetParam();
if (enable_passwords_account_storage) {
feature_list_.InitWithFeatures(
/*enabled_features=*/{features::kPasswordReuseDetectionEnabled,
features::kEnablePasswordsAccountStorage},
/*disabled_features=*/{});
} else {
feature_list_.InitWithFeatures(
/*enabled_features=*/{features::kPasswordReuseDetectionEnabled},
/*disabled_features=*/{features::kEnablePasswordsAccountStorage});
}
}
~PasswordManagerTest() override = default;
protected:
void SetUp() override {
store_ = new MockPasswordStore;
ASSERT_TRUE(store_->Init(/*prefs=*/nullptr));
ON_CALL(client_, GetProfilePasswordStore())
.WillByDefault(Return(store_.get()));
EXPECT_CALL(*store_, GetSiteStatsImpl(_)).Times(AnyNumber());
if (base::FeatureList::IsEnabled(
features::kEnablePasswordsAccountStorage)) {
account_store_ = new MockPasswordStore;
ASSERT_TRUE(account_store_->Init(/*prefs=*/nullptr));
ON_CALL(client_, GetAccountPasswordStore())
.WillByDefault(Return(account_store_.get()));
// Most tests don't really need the account store, but it'll still get
// queried by MultiStoreFormFetcher, so it needs to return something to
// its consumers. Let the account store return empty results by default,
// so that not every test has to set this up individually. Individual
// tests that do cover the account store can still override this.
ON_CALL(*account_store_, GetLogins(_, _))
.WillByDefault(
WithArg<1>(InvokeEmptyConsumerWithForms(account_store_.get())));
}
manager_ = std::make_unique<PasswordManager>(&client_);
password_autofill_manager_ =
std::make_unique<PasswordAutofillManager>(&driver_, 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_, GetLastCommittedURL()).WillByDefault(ReturnRef(test_url_));
ON_CALL(client_, IsCommittedMainFrameSecure()).WillByDefault(Return(true));
ON_CALL(client_, GetMetricsRecorder()).WillByDefault(Return(nullptr));
ON_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillByDefault(WithArg<0>(DeletePtr()));
ON_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _))
.WillByDefault(WithArg<0>(DeletePtr()));
prefs_ = std::make_unique<TestingPrefServiceSimple>();
prefs_->registry()->RegisterBooleanPref(
prefs::kPasswordLeakDetectionEnabled, true);
prefs_->registry()->RegisterBooleanPref(::prefs::kSafeBrowsingEnabled,
true);
prefs_->registry()->RegisterBooleanPref(::prefs::kSafeBrowsingEnhanced,
true);
prefs_->registry()->RegisterTimePref(
prefs::kProfileStoreDateLastUsedForFilling, base::Time());
prefs_->registry()->RegisterTimePref(
prefs::kAccountStoreDateLastUsedForFilling, base::Time());
ON_CALL(client_, GetPrefs()).WillByDefault(Return(prefs_.get()));
// When waiting for predictions is on, it makes tests more complicated.
// Disable waiting, since most tests have nothing to do with predictions.
// All tests that test working with prediction should explicitly turn
// predictions on.
PasswordFormManager::set_wait_for_server_predictions_for_filling(false);
}
void TearDown() override {
if (account_store_) {
account_store_->ShutdownOnUIThread();
account_store_ = nullptr;
}
store_->ShutdownOnUIThread();
store_ = nullptr;
}
PasswordForm MakeSavedForm() {
PasswordForm form;
form.url = 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/";
form.in_store = PasswordForm::Store::kProfileStore;
return form;
}
PasswordForm MakeSimpleForm() {
auto form = MakeSavedForm();
form.form_data = MakeSimpleFormData();
return form;
}
FormData MakeSimpleFormData() {
FormData form_data;
form_data.url = GURL("http://www.google.com/a/LoginAuth");
form_data.action = GURL("http://www.google.com/a/Login");
form_data.name = ASCIIToUTF16("the-form-name");
form_data.unique_renderer_id = FormRendererId(10);
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 = FieldRendererId(2);
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 = FieldRendererId(3);
form_data.fields.push_back(field);
SetUniqueIdIfNeeded(&form_data);
return form_data;
}
PasswordForm MakeSimpleGAIAForm() {
PasswordForm form = MakeSimpleForm();
form.form_data.url = GURL("https://accounts.google.com");
form.url = GURL("https://accounts.google.com");
form.signon_realm = form.url.spec();
return form;
}
FormData MakeSimpleGAIAFormData() {
FormData form_data = MakeSimpleFormData();
form_data.url = GURL("https://accounts.google.com");
return form_data;
}
FormData MakeGAIAChangePasswordFormData() {
FormData form_data(MakeSimpleFormData());
form_data.fields[1].autocomplete_attribute = "new-password";
form_data.url = GURL("https://accounts.google.com");
form_data.action = GURL("http://www.google.com/a/Login");
form_data.name = ASCIIToUTF16("the-form-name");
return form_data;
}
// 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);
form.form_data.fields[1].autocomplete_attribute = "new-password";
return form;
}
// Create a sign-up FormData that only has a new password field.
FormData MakeFormDataWithOnlyNewPasswordField() {
FormData form_data = MakeSimpleFormData();
form_data.fields[1].autocomplete_attribute = "new-password";
return form_data;
}
PasswordForm MakeAndroidCredential() {
PasswordForm android_form;
android_form.url = 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;
android_form.in_store = PasswordForm::Store::kProfileStore;
return android_form;
}
PasswordForm MakeSimpleFormWithOnlyUsernameField() {
PasswordForm form;
form.url = GURL("http://www.google.com/a/LoginAuth");
form.username_element = ASCIIToUTF16("Email");
form.submit_element = ASCIIToUTF16("signIn");
form.signon_realm = "http://www.google.com/";
form.form_data.name = ASCIIToUTF16("username_only_form");
form.form_data.url = GURL("http://www.google.com/a/LoginAuth");
form.form_data.unique_renderer_id = FormRendererId(30);
FormFieldData field;
field.name = ASCIIToUTF16("Email");
field.id_attribute = field.name;
field.name_attribute = field.name;
field.form_control_type = "text";
field.unique_renderer_id = FieldRendererId(31);
form.form_data.fields.push_back(field);
return form;
}
PasswordForm MakeSimpleFormWithOnlyPasswordField() {
PasswordForm form(MakeSimpleForm());
form.username_element.clear();
form.username_value.clear();
// Remove username field in |form_data|.
form.form_data.fields.erase(form.form_data.fields.begin());
return form;
}
PasswordForm MakeSimpleCreditCardForm() {
PasswordForm form;
form.url = GURL("https://accounts.google.com");
form.signon_realm = form.url.spec();
form.username_element = ASCIIToUTF16("cc-number");
form.password_element = ASCIIToUTF16("cvc");
form.username_value = ASCIIToUTF16("1234567");
form.password_value = ASCIIToUTF16("123");
form.form_data.url = form.url;
FormFieldData field;
field.name = form.username_element;
field.id_attribute = field.name;
field.value = form.username_value;
field.form_control_type = "text";
field.unique_renderer_id = FieldRendererId(2);
field.autocomplete_attribute = "cc-name";
form.form_data.fields.push_back(field);
field.name = form.password_element;
field.id_attribute = field.name;
field.value = form.password_value;
field.form_control_type = "password";
field.unique_renderer_id = FieldRendererId(3);
field.autocomplete_attribute = "cc-number";
form.form_data.fields.push_back(field);
SetUniqueIdIfNeeded(&form.form_data);
return form;
}
PasswordManager* manager() { return manager_.get(); }
void OnPasswordFormSubmitted(const FormData& form_data) {
manager()->OnPasswordFormSubmitted(&driver_, form_data);
}
base::test::ScopedFeatureList feature_list_;
const GURL test_url_{"https://www.example.com"};
base::test::SingleThreadTaskEnvironment task_environment_;
scoped_refptr<MockPasswordStore> store_;
scoped_refptr<MockPasswordStore> account_store_;
testing::NiceMock<MockPasswordManagerClient> client_;
MockPasswordManagerDriver driver_;
std::unique_ptr<TestingPrefServiceSimple> prefs_;
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.url == arg.url &&
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_P(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<FormData> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form.form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(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);
// 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_P(PasswordManagerTest, GeneratedPasswordFormSubmitEmptyStore) {
// Test that generated passwords are stored without asking the user.
std::vector<FormData> observed;
FormData form_data(MakeFormDataWithOnlyNewPasswordField());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Simulate the user generating the password and submitting the form.
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
manager()->OnPresaveGeneratedPassword(&driver_, form_data,
form_data.fields[1].value);
OnPasswordFormSubmitted(form_data);
// 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_, AutomaticPasswordSave);
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_EQ(form_data.fields[0].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_data.fields[1].value, form_to_save.password_value);
}
#if defined(OS_IOS)
TEST_P(PasswordManagerTest, EditingGeneratedPasswordOnIOS) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
FormData form_data = MakeSimpleFormData();
base::string16 username = form_data.fields[0].value;
base::string16 generated_password =
form_data.fields[1].value + ASCIIToUTF16("1");
FieldRendererId username_element = form_data.fields[0].unique_renderer_id;
FieldRendererId generation_element = form_data.fields[1].unique_renderer_id;
// A form is found by PasswordManager.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {form_data});
// The user is generating the password. The password has to be presaved.
PasswordForm presaved_form;
EXPECT_CALL(*store_, AddLogin(FormUsernamePasswordAre(
form_data.fields[0].value, generated_password)))
.WillOnce(SaveArg<0>(&presaved_form));
manager()->PresaveGeneratedPassword(&driver_, form_data, generated_password,
generation_element);
Mock::VerifyAndClearExpectations(store_.get());
// Test when the user is changing the generated password, presaved credential
// is updated.
generated_password += ASCIIToUTF16("1");
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(
FormUsernamePasswordAre(form_data.fields[0].value,
generated_password),
FormHasUniqueKey(presaved_form)))
.WillOnce(SaveArg<0>(&presaved_form));
manager()->UpdateStateOnUserInput(&driver_, form_data.unique_renderer_id,
generation_element, generated_password);
Mock::VerifyAndClearExpectations(store_.get());
// Test when the user is changing the username, presaved credential is
// updated.
username += ASCIIToUTF16("1");
EXPECT_CALL(*store_,
UpdateLoginWithPrimaryKey(
FormUsernamePasswordAre(username, generated_password),
FormHasUniqueKey(presaved_form)));
manager()->UpdateStateOnUserInput(&driver_, form_data.unique_renderer_id,
username_element, username);
}
TEST_P(PasswordManagerTest, SavingGeneratedPasswordOnIOS) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
FormData form_data = MakeSimpleFormData();
const base::string16 username = form_data.fields[0].value;
base::string16 generated_password =
form_data.fields[1].value + ASCIIToUTF16("1");
FieldRendererId generation_element = form_data.fields[1].unique_renderer_id;
// A form is found by PasswordManager.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {form_data});
// The user is generating the password.
EXPECT_CALL(*store_, AddLogin(_));
generated_password += ASCIIToUTF16("1");
manager()->PresaveGeneratedPassword(&driver_, form_data, generated_password,
generation_element);
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _));
// Test when the user is changing the generated password.
manager()->UpdateStateOnUserInput(&driver_, form_data.unique_renderer_id,
generation_element, generated_password);
// The user is submitting the form.
form_data.fields[0].value = username;
form_data.fields[1].value = generated_password;
OnPasswordFormSubmitted(form_data);
// Test that generated passwords are stored without asking the user.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_,
UpdateLoginWithPrimaryKey(
FormUsernamePasswordAre(username, generated_password), _));
EXPECT_CALL(*store_, IsAbleToSavePasswords()).WillRepeatedly(Return(true));
EXPECT_CALL(client_, AutomaticPasswordSave);
// Now the password manager waits for the navigation to complete.
manager()->OnPasswordFormsRendered(&driver_, {}, true);
}
TEST_P(PasswordManagerTest, PasswordNoLongerGeneratedOnIOS) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
FormData form_data = MakeSimpleFormData();
const base::string16 generated_password = form_data.fields[1].value;
FieldRendererId generation_element = form_data.fields[1].unique_renderer_id;
// A form is found by PasswordManager.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {form_data});
// The user is generating the password.
PasswordForm presaved_form;
EXPECT_CALL(*store_, AddLogin(_)).WillOnce(SaveArg<0>(&presaved_form));
manager()->PresaveGeneratedPassword(&driver_, form_data, generated_password,
generation_element);
// The user is removing password. Check that it is removed from the store.
EXPECT_CALL(*store_, RemoveLogin(FormHasUniqueKey(presaved_form)));
manager()->OnPasswordNoLongerGenerated(&driver_);
}
TEST_P(PasswordManagerTest, ShowHideManualFallbackOnIOS) {
ON_CALL(client_, IsSavingAndFillingEnabled(_)).WillByDefault(Return(true));
FormData form_data = MakeSimpleFormData();
FieldRendererId password_element = form_data.fields[1].unique_renderer_id;
// A form is found by PasswordManager.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {form_data});
// Check that the saving manual fallback is shown the user typed in a password
// field.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
base::string16 typed_password = ASCIIToUTF16("password");
manager()->UpdateStateOnUserInput(&driver_, form_data.unique_renderer_id,
password_element, typed_password);
Mock::VerifyAndClearExpectations(&client_);
ASSERT_TRUE(form_manager_to_save);
EXPECT_EQ(typed_password,
form_manager_to_save->GetPendingCredentials().password_value);
// Check that the saving manual is hidden when the user cleared the password
// field value.
EXPECT_CALL(client_, HideManualFallbackForSaving());
manager()->UpdateStateOnUserInput(&driver_, form_data.unique_renderer_id,
password_element, base::string16());
}
#endif // defined(OS_IOS)
TEST_P(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<FormData> observed = {form.form_data};
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _))
.WillOnce(WithArg<1>(InvokeConsumer(store_.get(), existing_different)));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form.form_data);
// 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(true);
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();
}
// Tests that a credential wouldn't be saved if it is already in the store.
TEST_P(PasswordManagerTest, DontSaveAlreadySavedCredential) {
PasswordForm form(MakeSimpleForm());
std::vector<FormData> observed = {form.form_data};
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), 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);
incomplete_match.form_data.fields[1].value = incomplete_match.password_value;
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnInformAboutUserInput(&driver_, incomplete_match.form_data);
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()->OnInformAboutUserInput(&driver_, form.form_data);
// The user submits the form. No prompt should pop up. The credential is
// updated in background.
OnPasswordFormSubmitted(form.form_data);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, UpdateLogin(_));
observed.clear();
manager()->DidNavigateMainFrame(true);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_EQ(1,
user_action_tester.GetActionCount("PasswordManager_LoginPassed"));
}
TEST_P(PasswordManagerTest, DoNotSaveWhenUserDeletesPassword) {
PasswordForm form(MakeSimpleForm());
PasswordForm stored_form = form;
stored_form.password_value = ASCIIToUTF16("old_password");
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), stored_form)));
ON_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillByDefault(Return(true));
std::vector<FormData> observed = {form.form_data};
manager()->OnPasswordFormsParsed(&driver_, observed);
// The user is typing a credential manually, the fallback should be available.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnInformAboutUserInput(&driver_, form.form_data);
ASSERT_TRUE(form_manager_to_save);
// The user deletes the password, no manuall fallback should be shown.
PasswordForm empty_password_form(form);
empty_password_form.password_value.clear();
empty_password_form.form_data.fields[1].value.clear();
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr).Times(0);
EXPECT_CALL(client_, HideManualFallbackForSaving());
manager()->OnInformAboutUserInput(&driver_, empty_password_form.form_data);
// The user submits the form. No prompt should pop up.
OnPasswordFormSubmitted(empty_password_form.form_data);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr).Times(0);
observed.clear();
manager()->DidNavigateMainFrame(true);
manager()->OnPasswordFormsParsed(&driver_, observed);
}
// Tests that on Chrome sign-in form credentials are not saved.
TEST_P(PasswordManagerTest, DoNotSaveOnChromeSignInForm) {
FormData form_data(MakeSimpleFormData());
form_data.is_gaia_with_skip_save_password_form = true;
std::vector<FormData> observed = {form_data};
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
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.
FormData typed_credentials(form_data);
typed_credentials.fields[1].value = ASCIIToUTF16("pw");
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _)).Times(0);
manager()->OnInformAboutUserInput(&driver_, form_data);
// The user submits the form. No prompt should pop up.
OnPasswordFormSubmitted(form_data);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
observed.clear();
manager()->DidNavigateMainFrame(true);
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_P(PasswordManagerTest,
SubmissionMetricsIsPassedWhenDontSaveAlreadySavedCredential) {
std::vector<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form)));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(AnyNumber());
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// The user submits the form.
OnPasswordFormSubmitted(form.form_data);
// 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()->OnInformAboutUserInput(&driver_, form.form_data);
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_P(PasswordManagerTest, FormSeenThenLeftPage) {
std::vector<FormData> observed;
FormData form_data(MakeSimpleFormData());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
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_P(PasswordManagerTest, FormSubmit) {
// Test that a plain form submit results in offering to save passwords.
PasswordForm form(MakeSimpleForm());
std::vector<FormData> observed = {form.form_data};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_TRUE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form.form_data);
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_P(PasswordManagerTest, IsPasswordFieldDetectedOnPage) {
FormData form_data(MakeSimpleFormData());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsParsed(&driver_, {form_data});
EXPECT_TRUE(manager()->IsPasswordFieldDetectedOnPage());
manager()->DropFormManagers();
EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage());
}
TEST_P(PasswordManagerTest, FormSubmitWhenPasswordsCannotBeSaved) {
// Test that a plain form submit doesn't result in offering to save passwords.
EXPECT_CALL(*store_, IsAbleToSavePasswords()).WillOnce(Return(false));
FormData form_data(MakeSimpleFormData());
std::vector<FormData> observed = {form_data};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_TRUE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form_data);
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_P(PasswordManagerTest, FormSubmitWithFormOnPreviousPage) {
PasswordForm first_form(MakeSimpleForm());
first_form.url = GURL("http://www.nytimes.com/");
first_form.form_data.url = first_form.url;
first_form.action = GURL("https://myaccount.nytimes.com/auth/login");
first_form.form_data.action = first_form.action;
first_form.signon_realm = "http://www.nytimes.com/";
PasswordForm second_form(MakeSimpleForm());
second_form.url = GURL("https://myaccount.nytimes.com/auth/login");
second_form.form_data.url = second_form.url;
second_form.action = GURL("https://myaccount.nytimes.com/auth/login");
second_form.form_data.action = second_form.action;
second_form.signon_realm = "https://myaccount.nytimes.com/";
// Pretend that the form is hidden on the first page.
std::vector<FormData> observed;
observed.push_back(first_form.form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Now navigate to a second page.
manager()->DidNavigateMainFrame(true);
// This page contains a form with the same markup, but on a different
// URL.
observed = {second_form.form_data};
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Now submit this form
EXPECT_CALL(client_, IsSavingAndFillingEnabled(second_form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(second_form.form_data);
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_P(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<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form.form_data);
// 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_P(PasswordManagerTest, InitiallyInvisibleForm) {
// Make sure an invisible login form still gets autofilled.
PasswordForm form(MakeSimpleForm());
std::vector<FormData> observed;
observed.push_back(form.form_data);
EXPECT_CALL(driver_, FillPasswordForm(_));
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the
// old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_P(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_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(false));
std::vector<FormData> observed;
observed.push_back(form.form_data);
EXPECT_CALL(driver_, FillPasswordForm(_));
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
}
TEST_P(PasswordManagerTest, PasswordFormReappearance) {
// If the password form reappears after submit, PasswordManager should deduce
// that the login failed and not offer saving.
std::vector<FormData> observed;
FormData login_form_data(MakeSimpleFormData());
observed.push_back(login_form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(login_form_data);
observed.clear();
// Simulate form reapperance with different path in url and different renderer
// ids.
FormData failed_login_form_data = login_form_data;
failed_login_form_data.unique_renderer_id.value() += 1000;
failed_login_form_data.url =
GURL("https://accounts.google.com/login/error?redirect_after_login");
observed.push_back(failed_login_form_data);
// A PasswordForm appears, and is visible in the layout:
// No expected calls to the PasswordStore...
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(client_, AutomaticPasswordSave).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_P(PasswordManagerTest, SyncCredentialsNotSaved) {
// Simulate loading a simple form with no existing stored password.
std::vector<FormData> observed;
FormData form_data(MakeSimpleGAIAFormData());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
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);
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(*store_,
SaveGaiaPasswordHash(
"googleuser", form_data.fields[1].value,
/*is_primary_account=*/true,
metrics_util::GaiaPasswordHashChange::SAVED_IN_CONTENT_AREA));
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form_data);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_P(PasswordManagerTest, HashSavedOnGaiaFormWithSkipSavePassword) {
for (bool did_stop_loading : {false, true}) {
SCOPED_TRACE(testing::Message("did_stop_loading = ") << did_stop_loading);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
std::vector<FormData> observed;
FormData form_data(MakeSimpleGAIAFormData());
// Simulate that this is Gaia form that should be ignored for
// saving/filling.
form_data.is_gaia_with_skip_save_password_form = true;
observed.push_back(form_data);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
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(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(
*store_,
SaveGaiaPasswordHash(
"googleuser", form_data.fields[1].value,
/*is_primary_account=*/true,
metrics_util::GaiaPasswordHashChange::SAVED_IN_CONTENT_AREA));
OnPasswordFormSubmitted(form_data);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, did_stop_loading);
testing::Mock::VerifyAndClearExpectations(&client_);
testing::Mock::VerifyAndClearExpectations(&store_);
}
}
TEST_P(PasswordManagerTest,
HashSavedOnGaiaFormWithSkipSavePasswordAndToNTPNavigation) {
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
FormData form_data(MakeSimpleGAIAFormData());
// Simulate that this is Gaia form that should be ignored for
// saving/filling.
form_data.is_gaia_with_skip_save_password_form = true;
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
manager()->OnPasswordFormsParsed(&driver_, {form_data});
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_data.fields[1].value,
/*is_primary_account=*/true,
metrics_util::GaiaPasswordHashChange::SAVED_IN_CONTENT_AREA));
EXPECT_CALL(client_, IsNewTabPage()).WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form_data);
manager()->DidNavigateMainFrame(false);
}
// On a successful login with an updated password,
// CredentialsFilter::ReportFormLoginSuccess and CredentialsFilter::ShouldSave
// should be called. The argument of ShouldSave should be the submitted form.
TEST_P(PasswordManagerTest, ReportFormLoginSuccessAndShouldSaveCalled) {
PasswordForm stored_form(MakeSimpleForm());
std::vector<FormData> 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");
SetFieldName(observed_form.username_element,
&observed_form.form_data.fields[0]);
observed.push_back(observed_form.form_data);
// Simulate that |form| is already in the store, making this an update.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), 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_, IsSavingAndFillingEnabled(observed_form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(observed_form.form_data);
// Chrome should recognise the successful login and call
// ReportFormLoginSuccess.
EXPECT_CALL(*client_.GetStoreResultFilter(), ReportFormLoginSuccess(_));
PasswordForm submitted_form = observed_form;
submitted_form.date_last_used = base::Time::Now();
EXPECT_CALL(*client_.GetStoreResultFilter(),
ShouldSave(FormMatches(submitted_form)));
EXPECT_CALL(*store_, UpdateLogin(_));
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
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_P(PasswordManagerTest, SyncCredentialsNotDroppedIfUpToDate) {
PasswordForm form(MakeSimpleGAIAForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form)));
client_.FilterAllResultsForSaving();
std::vector<FormData> observed;
observed.push_back(form.form_data);
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(
*store_,
SaveGaiaPasswordHash(
"googleuser", form.password_value, /*is_primary_account=*/true,
metrics_util::GaiaPasswordHashChange::SAVED_IN_CONTENT_AREA));
manager()->OnPasswordFormSubmitted(&driver_, form.form_data);
// 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_P(PasswordManagerTest, SyncCredentialsStillFilled) {
PasswordForm form(MakeSimpleForm());
// Pretend that the password store contains credentials stored for |form|.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form)));
client_.FilterAllResultsForSaving();
// Load the page.
autofill::PasswordFormFillData form_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&form_data));
std::vector<FormData> observed;
observed.push_back(form.form_data);
manager()->OnPasswordFormsParsed(&driver_, observed);
EXPECT_EQ(form.password_value, form_data.password_field.value);
}
TEST_P(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},
};
for (const auto& test_case : kTestData) {
SCOPED_TRACE(testing::Message("#test_case = ") << (&test_case - kTestData));
manager()->submitted_form_url_ = GURL(test_case.old_origin);
GURL origin = GURL(test_case.new_origin);
EXPECT_EQ(
test_case.result,
manager()->ShouldBlockPasswordForSameOriginButDifferentScheme(origin));
}
}
// Tests whether two submissions to the same origin but different schemes
// result in only saving the first submission, which has a secure scheme.
TEST_P(PasswordManagerTest, AttemptedSavePasswordSameOriginInsecureScheme) {
PasswordForm secure_form(MakeSimpleForm());
secure_form.url = GURL("https://example.com/login");
secure_form.action = GURL("https://example.com/login");
secure_form.form_data.url = secure_form.url;
secure_form.form_data.action = secure_form.action;
secure_form.signon_realm = "https://example.com/";
PasswordForm insecure_form(MakeSimpleForm());
// If all inputs of |secure_form| and |insecure_form| are the same, then
// |insecure_form| is considered as reappearing of |secure_form| and the
// submission is considered to be failed.
insecure_form.username_element += ASCIIToUTF16("1");
FormFieldData& username_field = insecure_form.form_data.fields[0];
username_field.name = insecure_form.username_element;
insecure_form.username_value = ASCIIToUTF16("compromised_user");
username_field.value = insecure_form.username_value;
insecure_form.password_value = ASCIIToUTF16("C0mpr0m1s3d_P4ss");
FormFieldData& password_field = insecure_form.form_data.fields[1];
password_field.value = insecure_form.password_value;
insecure_form.url = GURL("http://example.com/home");
insecure_form.action = GURL("http://example.com/home");
insecure_form.form_data.url = insecure_form.url;
insecure_form.form_data.action = insecure_form.action;
insecure_form.signon_realm = "http://example.com/";
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
EXPECT_CALL(client_, IsSavingAndFillingEnabled(secure_form.url))
.WillRepeatedly(Return(true));
// Parse, render and submit the secure form.
std::vector<FormData> observed = {secure_form.form_data};
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
OnPasswordFormSubmitted(secure_form.form_data);
// 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)));
// Parse, render and submit the insecure form.
observed = {insecure_form.form_data};
EXPECT_CALL(client_, IsSavingAndFillingEnabled(insecure_form.url))
.WillRepeatedly(Return(true));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
OnPasswordFormSubmitted(insecure_form.form_data);
// 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_P(PasswordManagerTest, DoNotSaveWithEmptyNewPasswordAndNonemptyPassword) {
std::vector<FormData> observed;
FormData form_data(MakeSimpleFormData());
ASSERT_FALSE(form_data.fields[1].value.empty());
FormFieldData field;
field.name = ASCIIToUTF16("new_password_element");
field.id_attribute = field.name;
field.name_attribute = field.name;
field.form_control_type = "password";
field.unique_renderer_id = FieldRendererId(4);
form_data.fields.push_back(field);
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(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 login to complete successfully.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
ASSERT_TRUE(form_manager_to_save);
EXPECT_EQ(form_data.fields[1].value,
form_manager_to_save->GetPendingCredentials().password_value);
}
TEST_P(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<FormData> observed;
// Loads passsword form without username input field.
PasswordForm form(MakeSimpleFormWithOnlyPasswordField());
observed.push_back(form.form_data);
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(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()->DidNavigateMainFrame(true);
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
// PasswordFormManager::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
// PasswordFormManager and filled as expected.
TEST_P(PasswordManagerTest, FillPasswordOnManyFrames_SameId) {
// Setting task runner is required since PasswordFormManager uses
// PostDelayTask for making filling.
PasswordFormManager::set_wait_for_server_predictions_for_filling(true);
TestMockTimeTaskRunner::ScopedContext scoped_context_(task_runner_.get());
// Two unrelated forms...
FormData form_data;
form_data.url = 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 = FieldRendererId(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 = FieldRendererId(2);
form_data.fields[1].form_control_type = "password";
PasswordForm first_form;
first_form.form_data = form_data;
form_data.url = 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 = FieldRendererId(3);
form_data.fields[1].name = ASCIIToUTF16("Pwd");
form_data.fields[1].value = ASCIIToUTF16("1234");
form_data.fields[1].unique_renderer_id = FieldRendererId(4);
PasswordForm second_form;
second_form.form_data = form_data;
// Make the forms be "similar".
first_form.form_data.unique_renderer_id = FormRendererId(7654);
second_form.form_data.unique_renderer_id = FormRendererId(7654);
// Observe the form in the first frame.
EXPECT_CALL(*store_,
GetLogins(PasswordStore::FormDigest(first_form.form_data), _))
.WillOnce(WithArg<1>(InvokeConsumer(store_.get(), first_form)));
EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->OnPasswordFormsParsed(&driver_, {first_form.form_data});
// Observe the form in the second frame.
MockPasswordManagerDriver driver_b;
EXPECT_CALL(*store_,
GetLogins(PasswordStore::FormDigest(second_form.form_data), _))
.WillOnce(WithArg<1>(InvokeConsumer(store_.get(), second_form)));
EXPECT_CALL(driver_b, FillPasswordForm(_));
manager()->OnPasswordFormsParsed(&driver_b, {second_form.form_data});
task_runner_->FastForwardUntilNoTasksRemain();
}
TEST_P(PasswordManagerTest, SameDocumentNavigation) {
// Test that observing a newly submitted form shows the save password bar on
// call in page navigation.
std::vector<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPasswordFormSubmittedNoChecks(&driver_, form.submission_event);
ASSERT_TRUE(form_manager_to_save);
// Simulate saving the form, as if the info bar was accepted.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
form_manager_to_save->Save();
}
TEST_P(PasswordManagerTest, SameDocumentBlockedSite) {
// Test that observing a newly submitted form on blocked site does notify
// the embedder on call in page navigation.
std::vector<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
// Simulate that blocked form stored in store.
PasswordForm blocked_form(form);
blocked_form.username_value = ASCIIToUTF16("");
blocked_form.blocked_by_user = true;
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), blocked_form)));
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPasswordFormSubmittedNoChecks(&driver_, form.submission_event);
EXPECT_TRUE(form_manager_to_save->IsBlocklisted());
}
TEST_P(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<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form.form_data);
PasswordForm updated_form;
PasswordForm notified_form;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, UpdateLogin(_)).WillOnce(SaveArg<0>(&updated_form));
EXPECT_CALL(client_, NotifySuccessfulLoginWithExistingPassword(_))
.WillOnce([&notified_form](const auto& submitted_manager) {
notified_form = submitted_manager->GetPendingCredentials();
});
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->DidNavigateMainFrame(true);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_THAT(form, FormMatches(updated_form));
EXPECT_THAT(form, FormMatches(notified_form));
}
TEST_P(PasswordManagerTest, SaveFormFetchedAfterSubmit) {
// Test that a password is offered for saving even if the response from the
// PasswordStore comes after submit.
std::vector<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
// GetLogins calls remain unanswered to emulate that PasswordStore did not
// fetch a form in time before submission.
PasswordStoreConsumer* store_consumer = nullptr;
EXPECT_CALL(*store_, GetLogins(_, _)).WillOnce(SaveArg<1>(&store_consumer));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form.form_data);
// Emulate fetching password form from PasswordStore after submission but
// before post-navigation load.
ASSERT_TRUE(store_consumer);
store_consumer->OnGetPasswordStoreResultsFrom(
store_.get(), 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_P(PasswordManagerTest, PasswordGeneration_FailedSubmission) {
std::vector<FormData> observed;
FormData form_data(MakeFormDataWithOnlyNewPasswordField());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
manager()->OnPresaveGeneratedPassword(&driver_, form_data,
form_data.fields[1].value);
// 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_, AutomaticPasswordSave).Times(0);
// Simulate submission failing, with the same form being visible after
// navigation.
OnPasswordFormSubmitted(form_data);
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_P(PasswordManagerTest, PasswordGenerationPasswordEdited_FailedSubmission) {
std::vector<FormData> observed;
FormData form_data(MakeFormDataWithOnlyNewPasswordField());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
manager()->OnPresaveGeneratedPassword(&driver_, form_data,
form_data.fields[1].value);
// Simulate user editing and submitting a different password. Verify that
// the edited password is the one that is saved.
form_data.fields[1].value = ASCIIToUTF16("different_password");
OnPasswordFormSubmitted(form_data);
// 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_, AutomaticPasswordSave).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_P(PasswordManagerTest,
PasswordGenerationNoLongerGeneratedPasswordNotForceSaved_FailedSubmit) {
std::vector<FormData> observed;
FormData form_data(MakeFormDataWithOnlyNewPasswordField());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
manager()->OnPresaveGeneratedPassword(&driver_, form_data,
form_data.fields[1].value);
// Simulate user removing generated password and adding a new one.
form_data.fields[1].value = ASCIIToUTF16("different_password");
EXPECT_CALL(*store_, RemoveLogin(_));
manager()->OnPasswordNoLongerGenerated(&driver_, form_data);
OnPasswordFormSubmitted(form_data);
// No infobar or prompt is shown if submission fails.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(client_, AutomaticPasswordSave).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_P(PasswordManagerTest,
PasswordGenerationNoLongerGeneratedPasswordNotForceSaved) {
std::vector<FormData> observed;
FormData form_data(MakeFormDataWithOnlyNewPasswordField());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
manager()->OnPresaveGeneratedPassword(&driver_, form_data,
form_data.fields[1].value);
// Simulate user removing generated password and adding a new one.
form_data.fields[1].value = ASCIIToUTF16("different_password");
EXPECT_CALL(*store_, RemoveLogin(_));
manager()->OnPasswordNoLongerGenerated(&driver_, form_data);
OnPasswordFormSubmitted(form_data);
// 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_, AutomaticPasswordSave).Times(0);
// Simulate a successful submission.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_P(PasswordManagerTest, PasswordGenerationUsernameChanged) {
std::vector<FormData> observed;
FormData form_data(MakeFormDataWithOnlyNewPasswordField());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, AddLogin(_));
manager()->OnPresaveGeneratedPassword(&driver_, form_data,
form_data.fields[1].value);
// Simulate user changing the username, without ever completely
// deleting the password.
form_data.fields[0].value = ASCIIToUTF16("new_username");
OnPasswordFormSubmitted(form_data);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
PasswordForm form_to_save;
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _))
.WillOnce(SaveArg<0>(&form_to_save));
EXPECT_CALL(client_, AutomaticPasswordSave);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_EQ(form_data.fields[0].value, form_to_save.username_value);
EXPECT_EQ(form_data.fields[1].value, form_to_save.password_value);
}
TEST_P(PasswordManagerTest, PasswordGenerationPresavePassword) {
std::vector<FormData> observed;
PasswordForm form(MakeFormWithOnlyNewPasswordField());
observed.push_back(form.form_data);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
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(FormMatches(sanitized_form)));
manager()->OnPresaveGeneratedPassword(&driver_, form.form_data,
form.password_value);
// 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(FormMatches(sanitized_updated_form),
FormHasUniqueKey(sanitized_form)));
manager()->OnPresaveGeneratedPassword(&driver_, updated_form.form_data,
updated_form.password_value);
histogram_tester.ExpectUniqueSample(
"PasswordManager.GeneratedFormHasNoFormManager", false, 2);
// The user removes the generated password.
EXPECT_CALL(*store_, RemoveLogin(FormHasUniqueKey(sanitized_updated_form)));
manager()->OnPasswordNoLongerGenerated(&driver_, updated_form.form_data);
}
TEST_P(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<FormData> observed;
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
base::HistogramTester histogram_tester;
// The user accepts a generated password.
FormData form_data(MakeFormDataWithOnlyNewPasswordField());
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
manager()->OnPresaveGeneratedPassword(&driver_, form_data,
form_data.fields[1].value);
histogram_tester.ExpectUniqueSample(
"PasswordManager.GeneratedFormHasNoFormManager", true, 1);
}
TEST_P(PasswordManagerTest, PasswordGenerationPresavePasswordAndLogin) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.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());
std::vector<FormData> observed = {form.form_data};
if (found_matched_logins_in_store) {
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form)));
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
} else {
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(
WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
}
EXPECT_CALL(client_, AutomaticPasswordSave)
.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(FormMatches(presaved_form)));
manager()->OnPresaveGeneratedPassword(&driver_, form.form_data,
form.password_value);
::testing::Mock::VerifyAndClearExpectations(store_.get());
EXPECT_CALL(*store_, IsAbleToSavePasswords()).WillRepeatedly(Return(true));
if (!found_matched_logins_in_store)
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(
_, FormHasUniqueKey(presaved_form)));
OnPasswordFormSubmitted(form.form_data);
observed.clear();
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);
}
manager()->DidNavigateMainFrame(true);
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(
_, FormHasUniqueKey(presaved_form)));
form_manager->Update(form_manager->GetPendingCredentials());
::testing::Mock::VerifyAndClearExpectations(store_.get());
}
}
}
TEST_P(PasswordManagerTest, SetGenerationElementAndTypeForForm) {
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _));
manager()->OnPasswordFormsParsed(&driver_, {form.form_data});
manager()->SetGenerationElementAndTypeForForm(
&driver_, form.form_data, form.form_data.fields[1].unique_renderer_id,
autofill::password_generation::PasswordGenerationType::kAutomatic);
EXPECT_CALL(*store_, AddLogin(_));
manager()->OnPresaveGeneratedPassword(&driver_, form.form_data,
form.password_value);
const PasswordFormManager* form_manager =
manager()->form_managers().front().get();
EXPECT_TRUE(form_manager->HasGeneratedPassword());
}
TEST_P(PasswordManagerTest, UpdateFormManagers) {
// Seeing a form should result in creating PasswordFormManager and
// PasswordFormManager and querying PasswordStore. Calling
// UpdateFormManagers should result in querying the store again.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {PasswordForm().form_data});
EXPECT_CALL(*store_, GetLogins(_, _));
manager()->UpdateFormManagers();
}
TEST_P(PasswordManagerTest, AutofillingOfAffiliatedCredentials) {
PasswordForm android_form(MakeAndroidCredential());
PasswordForm observed_form(MakeSimpleForm());
std::vector<FormData> observed_forms;
observed_forms.push_back(observed_form.form_data);
autofill::PasswordFormFillData form_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&form_data));
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), 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);
// On Android Touch To Fill will prevent autofilling credentials on page load.
#if defined(OS_ANDROID)
EXPECT_TRUE(form_data.wait_for_username);
#else
EXPECT_FALSE(form_data.wait_for_username);
#endif
EXPECT_EQ(android_form.signon_realm, form_data.preferred_realm);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(observed_form.url))
.WillRepeatedly(Return(true));
PasswordForm filled_form(observed_form);
filled_form.username_value = android_form.username_value;
filled_form.form_data.fields[0].value = filled_form.username_value;
filled_form.password_value = android_form.password_value;
filled_form.form_data.fields[1].value = filled_form.password_value;
OnPasswordFormSubmitted(filled_form.form_data);
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([&saved_notified_form](const auto& submitted_manager) {
saved_notified_form = submitted_manager->GetPendingCredentials();
});
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _)).Times(0);
observed_forms.clear();
manager()->DidNavigateMainFrame(true);
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_P(PasswordManagerTest, UpdatePasswordOfAffiliatedCredential) {
PasswordForm android_form(MakeAndroidCredential());
PasswordForm observed_form(MakeSimpleForm());
std::vector<FormData> observed_forms = {observed_form.form_data};
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), android_form)));
manager()->OnPasswordFormsParsed(&driver_, observed_forms);
manager()->OnPasswordFormsRendered(&driver_, observed_forms, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(observed_form.url))
.WillRepeatedly(Return(true));
PasswordForm filled_form(observed_form);
filled_form.username_value = android_form.username_value;
filled_form.form_data.fields[0].value = filled_form.username_value;
filled_form.password_value = ASCIIToUTF16("new_password");
filled_form.form_data.fields[1].value = filled_form.password_value;
OnPasswordFormSubmitted(filled_form.form_data);
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(true);
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_P(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();
form.form_data.fields[0].value.clear();
std::vector<FormData> observed = {form.form_data};
// Emulate page load.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form.form_data);
// JavaScript cleared field values.
observed[0].fields[1].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);
}
// 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_P(PasswordManagerTest, NotSavingSyncPasswordHash_NoUsername) {
// Simulate loading a simple form with no existing stored password.
std::vector<FormData> observed;
FormData form_data(MakeSimpleGAIAFormData());
// Simulate that no username is found.
form_data.fields[0].value.clear();
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.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_data);
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_P(PasswordManagerTest, NotSavingSyncPasswordHash_NotSyncCredentials) {
// Simulate loading a simple form with no existing stored password.
FormData form_data(MakeSimpleGAIAFormData());
std::vector<FormData> observed = {form_data};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.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_data);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
TEST_P(PasswordManagerTest, ManualFallbackForSaving) {
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
std::vector<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
PasswordForm stored_form = form;
stored_form.password_value = ASCIIToUTF16("old_password");
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), 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()->OnInformAboutUserInput(&driver_, form.form_data);
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()->OnInformAboutUserInput(&driver_, new_form.form_data);
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_P(PasswordManagerTest, ManualFallbackForSaving_SlowBackend) {
std::vector<FormData> observed;
FormData form_data(MakeSimpleFormData());
observed.push_back(form_data);
PasswordStoreConsumer* store_consumer = nullptr;
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(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()->OnInformAboutUserInput(&driver_, form_data);
// The storage responded. The fallback can be shown.
ASSERT_TRUE(store_consumer);
store_consumer->OnGetPasswordStoreResultsFrom(
store_.get(), 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()->OnInformAboutUserInput(&driver_, form_data);
}
TEST_P(PasswordManagerTest, ManualFallbackForSaving_GeneratedPassword) {
std::vector<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
// TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
// the old parser is gone.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
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.form_data,
form.password_value);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
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.form_data,
form.password_value);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
// 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.form_data);
manager()->HideManualFallbackForSaving();
}
// Sync password hash should be updated upon submission of change password page.
TEST_P(PasswordManagerTest, SaveSyncPasswordHashOnChangePasswordPage) {
FormData form_data(MakeGAIAChangePasswordFormData());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
std::vector<FormData> observed;
observed.push_back(form_data);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(
*store_,
SaveGaiaPasswordHash(
"googleuser", form_data.fields[1].value,
/*is_primary_account=*/true,
metrics_util::GaiaPasswordHashChange::CHANGED_IN_CONTENT_AREA));
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form_data);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// Non-Sync Gaia password hash should be saved upon submission of Gaia login
// page.
TEST_P(PasswordManagerTest, SaveOtherGaiaPasswordHash) {
FormData form_data(MakeSimpleGAIAFormData());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
std::vector<FormData> observed;
observed.push_back(form_data);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
EXPECT_CALL(
*store_,
SaveGaiaPasswordHash(
"googleuser", form_data.fields[1].value, /*is_primary_account=*/false,
metrics_util::GaiaPasswordHashChange::SAVED_IN_CONTENT_AREA));
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form_data);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// Non-Sync Gaia password hash should be saved upon submission of change
// password page.
TEST_P(PasswordManagerTest, SaveOtherGaiaPasswordHashOnChangePasswordPage) {
FormData form_data(MakeGAIAChangePasswordFormData());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
std::vector<FormData> observed;
observed.push_back(form_data);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
.WillByDefault(Return(true));
EXPECT_CALL(
*store_,
SaveGaiaPasswordHash(
"googleuser", form_data.fields[1].value, /*is_primary_account=*/false,
metrics_util::GaiaPasswordHashChange::NOT_SYNC_PASSWORD_CHANGE));
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form_data);
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// Enterprise password hash should be saved upon submission of enterprise login
// page.
TEST_P(PasswordManagerTest, SaveEnterprisePasswordHash) {
FormData form_data(MakeSimpleFormData());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
std::vector<FormData> observed;
observed.push_back(form_data);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveEnterprisePasswordHash(_))
.WillByDefault(Return(true));
ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(false));
EXPECT_CALL(*store_, SaveEnterprisePasswordHash("googleuser",
form_data.fields[1].value));
client_.FilterAllResultsForSaving();
OnPasswordFormSubmitted(form_data);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
// If there are no forms to parse, certificate errors should not be reported.
TEST_P(PasswordManagerTest, CertErrorReported_NoForms) {
const std::vector<FormData> observed;
EXPECT_CALL(client_, GetMainFrameCertStatus())
.WillRepeatedly(Return(net::CERT_STATUS_AUTHORITY_INVALID));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
base::HistogramTester histogram_tester;
manager()->OnPasswordFormsParsed(&driver_, observed);
histogram_tester.ExpectTotalCount(
"PasswordManager.CertificateErrorsWhileSeeingForms", 0);
}
TEST_P(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<FormData> observed = {PasswordForm().form_data};
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_P(PasswordManagerTest, CreatingFormManagers) {
FormData form_data(MakeSimpleFormData());
std::vector<FormData> observed;
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
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_data, &driver_));
// 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());
}
// 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_P(PasswordManagerTest, ProcessingNormalFormSubmission) {
for (bool successful_submission : {false, true}) {
SCOPED_TRACE(testing::Message("successful_submission = ")
<< successful_submission);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
FormData form_data(MakeSimpleFormData());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
std::vector<FormData> observed;
observed.push_back(form_data);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
auto submitted_form_data = form_data;
submitted_form_data.fields[0].value = ASCIIToUTF16("username");
submitted_form_data.fields[1].value = ASCIIToUTF16("password1");
OnPasswordFormSubmitted(submitted_form_data);
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_P(PasswordManagerTest, ProcessingOtherSubmissionTypes) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
FormData form_data(MakeSimpleFormData());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
std::vector<FormData> observed;
observed.push_back(form_data);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
manager()->OnInformAboutUserInput(&driver_, form_data);
auto submitted_form_data = form_data;
submitted_form_data.fields[0].value = ASCIIToUTF16("username");
submitted_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_data.submission_event);
EXPECT_TRUE(manager()->form_managers().empty());
}
TEST_P(PasswordManagerTest, SubmittedGaiaFormWithoutVisiblePasswordField) {
// Tests that a submitted GAIA sign-in form which does not contain a visible
// password field is skipped.
std::vector<FormData> observed;
FormData form_data(MakeSimpleGAIAFormData());
observed.push_back(form_data);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
form_data.fields[0].value = ASCIIToUTF16("username");
form_data.fields[1].value = ASCIIToUTF16("password");
form_data.fields[1].is_focusable = false;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
manager()->OnPasswordFormSubmittedNoChecks(&driver_,
form_data.submission_event);
}
TEST_P(PasswordManagerTest, MetricForSchemeOfSuccessfulLogins) {
for (bool origin_is_secure : {false, true}) {
SCOPED_TRACE(testing::Message("origin_is_secure = ") << origin_is_secure);
FormData form_data(MakeSimpleFormData());
form_data.url =
GURL(origin_is_secure ? "https://example.com" : "http://example.com");
std::vector<FormData> observed = {form_data};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form_data);
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_P(PasswordManagerTest, ManualFallbackForSavingNewParser) {
PasswordFormManager::set_wait_for_server_predictions_for_filling(false);
std::vector<FormData> observed;
PasswordForm form(MakeSimpleForm());
observed.push_back(form.form_data);
PasswordForm stored_form = form;
stored_form.password_value = ASCIIToUTF16("old_password");
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), 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()->OnInformAboutUserInput(&driver_, form.form_data);
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()->OnInformAboutUserInput(&driver_, new_form.form_data);
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();
}
TEST_P(PasswordManagerTest, NoSavePromptWhenPasswordManagerDisabled) {
FormData form_data(MakeSimpleFormData());
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(false));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {form_data});
auto submitted_form_data = form_data;
submitted_form_data.fields[0].value = ASCIIToUTF16("username");
submitted_form_data.fields[1].value = ASCIIToUTF16("strong_password");
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
manager()->OnPasswordFormSubmittedNoChecks(
&driver_, submitted_form_data.submission_event);
}
TEST_P(PasswordManagerTest, NoSavePromptForNotPasswordForm) {
FormData form_data(MakeSimpleFormData());
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
// Make the form to be credit card form.
form_data.fields[1].autocomplete_attribute = "cc-csc";
manager()->OnPasswordFormsParsed(&driver_, {form_data});
auto submitted_form_data = form_data;
submitted_form_data.fields[0].value = ASCIIToUTF16("text");
submitted_form_data.fields[1].value = ASCIIToUTF16("1234");
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
manager()->OnPasswordFormSubmittedNoChecks(
&driver_, submitted_form_data.submission_event);
}
// Check that when autofill predictions are received before a form is found then
// server predictions are not ignored and used for filling.
TEST_P(PasswordManagerTest, AutofillPredictionBeforeFormParsed) {
PasswordFormManager::set_wait_for_server_predictions_for_filling(true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
// 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);
#if !defined(OS_IOS)
manager()->ProcessAutofillPredictions(&driver_, {&form_structure});
#else // On iOS predictions are propagated with nullptr driver.
manager()->ProcessAutofillPredictions(nullptr, {&form_structure});
#endif
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form)));
EXPECT_CALL(driver_, FillPasswordForm(_));
// 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";
manager()->OnPasswordFormsParsed(&driver_, {form.form_data});
}
// Check that when autofill predictions are received before a form is found then
// server predictions are not ignored and used for filling in case there are
// multiple forms on a page, including forms that have UsernameFirstFlow votes.
TEST_P(PasswordManagerTest, AutofillPredictionBeforeMultipleFormsParsed) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
PasswordFormManager::set_wait_for_server_predictions_for_filling(true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
PasswordForm form1(MakeSimpleFormWithOnlyUsernameField());
PasswordForm form2(MakeSimpleForm());
EXPECT_CALL(*store_, GetLogins)
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), form2)));
FormStructure form_structure1(form1.form_data);
form_structure1.field(0)->set_server_type(autofill::SINGLE_USERNAME);
// 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_structure2(form2.form_data);
form_structure2.field(1)->set_server_type(autofill::PASSWORD);
#if !defined(OS_IOS)
manager()->ProcessAutofillPredictions(&driver_,
{&form_structure1, &form_structure2});
// Both forms should be filled.
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
#else // On iOS predictions are propagated with nullptr driver.
manager()->ProcessAutofillPredictions(nullptr,
{&form_structure1, &form_structure2});
// Only one form should be filled, as username first flow is not supported
// yet on iOS.
EXPECT_CALL(driver_, FillPasswordForm(_));
#endif
// Simulate that the form is incorrectly marked as sign-up, which means it can
// not be filled without server predictions.
form2.form_data.fields[1].autocomplete_attribute = "new-password";
manager()->OnPasswordFormsParsed(&driver_,
{form1.form_data, form2.form_data});
}
// 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_P(PasswordManagerTest, SavingAfterUserTypingAndNavigation) {
for (bool form_may_be_submitted : {false, true}) {
SCOPED_TRACE(testing::Message()
<< "form_may_be_submitted = " << form_may_be_submitted);
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {form.form_data});
// 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()->OnInformAboutUserInput(&driver_, form.form_data);
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).
if (form_may_be_submitted) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
} else {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
}
manager()->DidNavigateMainFrame(form_may_be_submitted);
manager()->OnPasswordFormsRendered(&driver_, {}, true);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(form));
testing::Mock::VerifyAndClearExpectations(&client_);
}
}
// Check that when a form is submitted and a PasswordFormManager not present,
// this ends up reported in ProvisionallySaveFailure UMA and UKM.
TEST_P(PasswordManagerTest, ProvisionallySaveFailure) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(nullptr, {});
base::HistogramTester histogram_tester;
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
auto metrics_recorder =
std::make_unique<PasswordManagerMetricsRecorder>(1234, nullptr);
EXPECT_CALL(client_, GetMetricsRecorder())
.WillRepeatedly(Return(metrics_recorder.get()));
FormData unobserved_form_data = MakeSimpleFormData();
manager()->OnPasswordFormSubmitted(nullptr, unobserved_form_data);
histogram_tester.ExpectUniqueSample(
"PasswordManager.ProvisionalSaveFailure",
PasswordManagerMetricsRecorder::NO_MATCHING_FORM, 1);
// 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<FormData> parsed_forms_data;
// A list of forms to be processed for saving, one at a time.
std::vector<FormData> processed_form_data;
// 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_P(PasswordManagerTest, ReportMissingFormManager) {
const FormData form_data = MakeSimpleFormData();
FormData other_form_data = MakeSimpleFormData();
other_form_data.unique_renderer_id.value() += 1;
const MissingFormManagerTestCase kTestCases[] = {
{
.description =
"A form is submitted and a PasswordFormManager not present.",
.save_signal = MissingFormManagerTestCase::Signal::Automatic,
.parsed_forms_data = {},
// .parsed_forms is empty, so the processed form below was not
// observed and has no form manager associated.
.processed_form_data = {form_data},
.expected_metric_value =
MetricValue(PasswordManagerMetricsRecorder::FormManagerAvailable::
kMissingProvisionallySave),
},
{
.description = "Manual saving is requested and a "
"PasswordFormManager is created.",
.save_signal = MissingFormManagerTestCase::Signal::Manual,
.parsed_forms_data = {},
// .parsed_forms is empty, so the processed form below was not
// observed and has no form manager associated.
.processed_form_data = {form_data},
.expected_metric_value = MetricValue(
PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess),
},
{
.description = "Manual saving is successfully requested.",
.save_signal = MissingFormManagerTestCase::Signal::Manual,
.parsed_forms_data = {form_data},
.processed_form_data = {form_data},
.expected_metric_value = MetricValue(
PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess),
},
{
.description =
"A form is submitted and a PasswordFormManager present.",
.save_signal = MissingFormManagerTestCase::Signal::Automatic,
.parsed_forms_data = {form_data},
.processed_form_data = {form_data},
.expected_metric_value = MetricValue(
PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess),
},
{
.description = "First failure, then success.",
.save_signal = MissingFormManagerTestCase::Signal::Automatic,
.parsed_forms_data = {form_data},
// Processing |other_form| first signals a failure value in the
// metric, but processing |form| after that should overwrite that with
// kSuccess.
.processed_form_data = {other_form_data, form_data},
.expected_metric_value = MetricValue(
PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess),
},
{
.description = "No forms, no report.",
.save_signal = MissingFormManagerTestCase::Signal::None,
.parsed_forms_data = {},
.processed_form_data = {},
.expected_metric_value = base::nullopt,
},
{
.description = "Not enabled, no report.",
.saving = MissingFormManagerTestCase::Saving::Disabled,
.save_signal = MissingFormManagerTestCase::Signal::Automatic,
.parsed_forms_data = {form_data},
.processed_form_data = {form_data},
.expected_metric_value = base::nullopt,
},
};
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
for (const MissingFormManagerTestCase& test_case : kTestCases) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(test_case.saving ==
MissingFormManagerTestCase::Saving::Enabled));
SCOPED_TRACE(testing::Message() << "test case = " << test_case.description);
manager()->OnPasswordFormsParsed(nullptr, test_case.parsed_forms_data);
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
auto metrics_recorder =
std::make_unique<PasswordManagerMetricsRecorder>(1234, nullptr);
EXPECT_CALL(client_, GetMetricsRecorder())
.WillRepeatedly(Return(metrics_recorder.get()));
for (const FormData& form_data : test_case.processed_form_data) {
switch (test_case.save_signal) {
case MissingFormManagerTestCase::Signal::Automatic:
manager()->OnPasswordFormSubmitted(nullptr, form_data);
break;
case MissingFormManagerTestCase::Signal::Manual:
manager()->OnInformAboutUserInput(nullptr, form_data);
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));
}
}
}
// Tests that despite there a form was not seen on a page load, new
// |PasswordFormManager| is created in process of saving.
TEST_P(PasswordManagerTest, CreatePasswordFormManagerOnSaving) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
PasswordForm form(MakeSimpleForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {form.form_data});
// Simulate that JavaScript creates a new form, fills username/password and
// submits it.
auto submitted_form = form;
submitted_form.form_data.unique_renderer_id.value() += 1000;
submitted_form.username_value = ASCIIToUTF16("username1");
submitted_form.form_data.fields[0].value = submitted_form.username_value;
submitted_form.password_value = ASCIIToUTF16("password1");
submitted_form.form_data.fields[1].value = submitted_form.password_value;
OnPasswordFormSubmitted(submitted_form.form_data);
EXPECT_TRUE(manager()->GetSubmittedManagerForTest());
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// The form disappeared, so the submission is considered to be successful.
manager()->OnPasswordFormsRendered(&driver_, {}, true);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(submitted_form));
}
// Tests that no save prompt from form manager is shown when Credentials
// Management API function store is called.
TEST_P(PasswordManagerTest, NoSavePromptAfterStoreCalled) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
FormData form_data(MakeSimpleFormData());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, {form_data});
// Simulate that navigator.credentials.store function is called.
manager()->NotifyStorePasswordCalled();
OnPasswordFormSubmitted(form_data);
EXPECT_FALSE(manager()->GetSubmittedManagerForTest());
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
manager()->OnPasswordFormsRendered(&driver_, {}, true);
}
// Check that on non-password form, saving and filling fallbacks are available
// but no automatic filling and saving are available.
TEST_P(PasswordManagerTest, FillingAndSavingFallbacksOnNonPasswordForm) {
PasswordFormManager::set_wait_for_server_predictions_for_filling(false);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
PasswordForm saved_match(MakeSimpleForm());
PasswordForm credit_card_form(MakeSimpleCreditCardForm());
credit_card_form.only_for_fallback = true;
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), saved_match)));
PasswordFormFillData form_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&form_data));
manager()->OnPasswordFormsParsed(&driver_, {credit_card_form.form_data});
// Check that manual filling fallback available.
EXPECT_EQ(saved_match.username_value, form_data.username_field.value);
EXPECT_EQ(saved_match.password_value, form_data.password_field.value);
// Check that no automatic filling available.
EXPECT_TRUE(form_data.username_field.unique_renderer_id.is_null());
EXPECT_TRUE(form_data.password_field.unique_renderer_id.is_null());
// Check that saving fallback is available.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnInformAboutUserInput(&driver_, credit_card_form.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(credit_card_form));
// Check that no automatic save prompt is shown.
OnPasswordFormSubmitted(credit_card_form.form_data);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
manager()->DidNavigateMainFrame(true);
manager()->OnPasswordFormsRendered(&driver_, {}, true);
}
#if !defined(OS_IOS)
// Check that on successful login the credentials are checked for leak.
TEST_P(PasswordManagerTest, StartLeakDetection) {
auto mock_factory =
std::make_unique<testing::StrictMock<MockLeakDetectionCheckFactory>>();
MockLeakDetectionCheckFactory* weak_factory = mock_factory.get();
manager()->set_leak_factory(std::move(mock_factory));
const FormData form_data = MakeSimpleFormData();
std::vector<FormData> observed = {form_data};
EXPECT_CALL(*store_, GetLogins)
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
OnPasswordFormSubmitted(form_data);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr)
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
auto check_instance = std::make_unique<MockLeakDetectionCheck>();
EXPECT_CALL(*check_instance, Start(form_data.url, form_data.fields[0].value,
form_data.fields[1].value));
EXPECT_CALL(*weak_factory, TryCreateLeakCheck)
.WillOnce(Return(ByMove(std::move(check_instance))));
// Now the password manager waits for the navigation to complete.
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
#endif // !defined(OS_IOS)
// Check that a non-password form with SINGLE_USERNAME prediction is filled.
TEST_P(PasswordManagerTest, FillSingleUsername) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
PasswordFormManager::set_wait_for_server_predictions_for_filling(true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
PasswordForm saved_match(MakeSavedForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), saved_match)));
// Create FormData for a form with 1 text field.
FormData form_data;
constexpr FormRendererId form_id(1001);
form_data.unique_renderer_id = form_id;
form_data.url = GURL("http://example.com");
FormFieldData field;
field.form_control_type = "text";
constexpr FieldRendererId field_id(10);
field.unique_renderer_id = field_id;
form_data.fields.push_back(field);
// Set SINGLE_USERNAME predictions for the field.
FormStructure form_structure(form_data);
form_structure.field(0)->set_server_type(autofill::SINGLE_USERNAME);
#if !defined(OS_IOS)
PasswordFormFillData fill_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
manager()->ProcessAutofillPredictions(&driver_, {&form_structure});
EXPECT_EQ(form_id, fill_data.form_renderer_id);
EXPECT_EQ(saved_match.username_value, fill_data.username_field.value);
EXPECT_EQ(field_id, fill_data.username_field.unique_renderer_id);
EXPECT_EQ(saved_match.password_value, fill_data.password_field.value);
EXPECT_TRUE(fill_data.password_field.unique_renderer_id.is_null());
#else // defined(OS_IOS)
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
#endif // !defined(OS_IOS)
}
// Checks that a password form with a clear-text account creation field results
// in marking the password field as eligible for password generation.
TEST_P(PasswordManagerTest,
MarkServerPredictedClearTextPasswordFieldEligibleForGeneration) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
password_manager::features::KEnablePasswordGenerationForClearTextFields);
PasswordFormManager::set_wait_for_server_predictions_for_filling(true);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
PasswordForm saved_match(MakeSavedForm());
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), saved_match)));
// Create FormdData for a form with 1 text field.
FormData form_data;
constexpr FormRendererId form_id(1001);
form_data.unique_renderer_id = form_id;
form_data.url = GURL("http://example.com");
FormFieldData username_field;
username_field.form_control_type = "text";
constexpr FieldRendererId username_field_id(10);
username_field.unique_renderer_id = username_field_id;
form_data.fields.push_back(username_field);
FormFieldData password_field;
password_field.form_control_type = "text";
constexpr FieldRendererId password_field_id(11);
password_field.unique_renderer_id = password_field_id;
form_data.fields.push_back(password_field);
// Set ACCOUNT_CREATION_PASSWORD predictions for the field.
FormStructure form_structure(form_data);
form_structure.field(1)->set_server_type(autofill::ACCOUNT_CREATION_PASSWORD);
autofill::PasswordFormGenerationData form_generation_data;
EXPECT_CALL(driver_, FormEligibleForGenerationFound(_))
.WillOnce(SaveArg<0>(&form_generation_data));
manager()->ProcessAutofillPredictions(&driver_, {&form_structure});
EXPECT_EQ(password_field_id, form_generation_data.new_password_renderer_id);
}
// Checks that username is saved on username first flow.
TEST_P(PasswordManagerTest, UsernameFirstFlow) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
PasswordForm form(MakeSimpleFormWithOnlyPasswordField());
// Simulate the user typed a username in username form.
const base::string16 username = ASCIIToUTF16("username1");
EXPECT_CALL(driver_, GetLastCommittedURL()).WillOnce(ReturnRef(form.url));
manager()->OnUserModifiedNonPasswordField(&driver_, FieldRendererId(1001),
username /* value */);
// Simulate that a form which contains only 1 field which is password is added
// to the page.
manager()->OnPasswordFormsParsed(&driver_, {form.form_data} /* observed */);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillRepeatedly(Return(true));
// Simulate that the user typed password and submitted the password form.
const base::string16 password = ASCIIToUTF16("uniquepassword");
form.form_data.fields[0].value = password;
OnPasswordFormSubmitted(form.form_data);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Simulates successful submission.
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();
#if defined(OS_ANDROID)
// Local heuristics on Android for username first flow are not supported, so
// the username should not be taken from the username form.
EXPECT_TRUE(saved_form.username_value.empty());
#else
EXPECT_EQ(username, saved_form.username_value);
#endif // defined(OS_ANDROID)
EXPECT_EQ(password, saved_form.password_value);
}
#if !defined(OS_IOS)
// Checks that username is filled on username first flow based on server and
// local predictions.
TEST_P(PasswordManagerTest, UsernameFirstFlowFillingServerAndLocalPredictions) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(
WithArg<1>(InvokeConsumer(store_.get(), MakeSavedForm())));
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
constexpr FieldRendererId kFieldRendererId(1);
FormData non_password_form;
non_password_form.url = GURL("http://example.com");
FormFieldData field;
field.form_control_type = "text";
field.unique_renderer_id = kFieldRendererId;
non_password_form.fields.push_back(field);
MockFieldInfoManager mock_field_manager;
ON_CALL(client_, GetFieldInfoManager())
.WillByDefault(Return(&mock_field_manager));
for (auto server_type : {NO_SERVER_DATA, SINGLE_USERNAME, NOT_USERNAME}) {
for (auto local_type : {NO_SERVER_DATA, SINGLE_USERNAME, NOT_USERNAME}) {
SCOPED_TRACE(testing::Message("server_type=")
<< server_type << " "
<< "local_type=" << local_type);
manager()->OnPasswordFormsParsed(&driver_, {} /* observed */);
EXPECT_CALL(mock_field_manager, GetFieldType(_, _))
.WillOnce(Return(local_type));
// Simulate filling of different forms on each iteration.
non_password_form.unique_renderer_id.value() += 1;
non_password_form.name += ASCIIToUTF16("1"); // for iOS.
FormStructure form_structure(non_password_form);
form_structure.field(0)->set_server_type(server_type);
bool should_be_filled =
server_type == SINGLE_USERNAME || local_type == SINGLE_USERNAME;
EXPECT_CALL(driver_, FillPasswordForm(_)).Times(should_be_filled ? 1 : 0);
manager()->ProcessAutofillPredictions(&driver_, {&form_structure});
Mock::VerifyAndClearExpectations(&client_);
Mock::VerifyAndClearExpectations(&mock_field_manager);
}
}
}
#endif
TEST_P(PasswordManagerTest, FormSubmittedOnMainFrame) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
FormData form_data(MakeSimpleFormData());
// Submit |form| on a main frame.
manager()->OnPasswordFormsParsed(&driver_, {form_data} /* observed */);
manager()->OnPasswordFormSubmitted(&driver_, form_data);
// Simulate finish loading of some iframe.
MockPasswordManagerDriver iframe_driver;
EXPECT_CALL(iframe_driver, IsMainFrame()).WillRepeatedly(Return(false));
EXPECT_CALL(iframe_driver, GetId()).WillRepeatedly(Return(123));
manager()->OnPasswordFormsRendered(&iframe_driver, {} /* observed */, true);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
Mock::VerifyAndClearExpectations(&client_);
// Simulate finish loading of some iframe. Check that the prompt is shown.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_));
manager()->OnPasswordFormsRendered(&driver_, {} /* observed */,
true /* did stop loading */);
}
TEST_P(PasswordManagerTest, FormSubmittedOnIFrame) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
FormData form_data(MakeSimpleFormData());
// Submit |form| on an iframe.
MockPasswordManagerDriver iframe_driver;
ON_CALL(iframe_driver, IsMainFrame()).WillByDefault(Return(false));
ON_CALL(iframe_driver, GetId()).WillByDefault(Return(123));
manager()->OnPasswordFormsParsed(&iframe_driver, {form_data});
manager()->OnPasswordFormSubmitted(&iframe_driver, form_data);
// Simulate finish loading of another iframe.
MockPasswordManagerDriver another_iframe_driver;
EXPECT_CALL(another_iframe_driver, IsMainFrame())
.WillRepeatedly(Return(false));
EXPECT_CALL(another_iframe_driver, GetId()).WillRepeatedly(Return(456));
manager()->OnPasswordFormsRendered(&another_iframe_driver, {} /* observed */,
true);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
Mock::VerifyAndClearExpectations(&client_);
// Simulate finish loading of the submitted form iframe. Check that the prompt
// is shown.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_));
manager()->OnPasswordFormsRendered(&iframe_driver, {} /* observed */,
true /* did stop loading */);
}
TEST_P(PasswordManagerTest, FormSubmittedOnIFrameMainFrameLoaded) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
FormData form_data(MakeSimpleFormData());
// Simulate a form submission on an iframe.
MockPasswordManagerDriver iframe_driver;
ON_CALL(iframe_driver, IsMainFrame()).WillByDefault(Return(false));
ON_CALL(iframe_driver, GetId()).WillByDefault(Return(123));
manager()->OnPasswordFormsParsed(&iframe_driver, {form_data});
manager()->OnPasswordFormSubmitted(&iframe_driver, form_data);
// Simulate finish loading of the main frame. Check that the prompt is shown.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_));
manager()->OnPasswordFormsRendered(&driver_, {} /* observed */,
true /* did stop loading */);
}
TEST_P(PasswordManagerTest, NoPromptAutofillAssistantManuallyCuratedScript) {
EXPECT_CALL(client_, IsAutofillAssistantUIVisible)
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins)
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
// Check that a save prompt is not shown.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr).Times(0);
// Simulate multiple submissions.
for (size_t i = 0; i < 2; i++) {
PasswordForm form(MakeSimpleForm());
manager()->OnPasswordFormsParsed(&driver_, {form.form_data});
manager()->OnInformAboutUserInput(&driver_, form.form_data);
manager()->DidNavigateMainFrame(true /* form_may_be_submitted */);
manager()->OnPasswordFormsRendered(&driver_, {} /* observed */,
true /* did stop loading */);
}
}
// Password Manager may store a pending credential that will cause a prompt when
// Autofill Assistant has already handled the submission. This test ensures that
// Password Manager forgots the pending credential and doesn't prompt to update
// the password later (e.g., after navigation).
TEST_P(PasswordManagerTest,
NoPromptAfterAutofillAssistantManuallyCuratedScript) {
EXPECT_CALL(*store_, GetLogins)
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
for (bool set_owned_form_manager : {false, true}) {
SCOPED_TRACE(testing::Message("set_owned_form_manager = ")
<< set_owned_form_manager);
EXPECT_CALL(client_, IsSavingAndFillingEnabled)
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr).Times(0);
// Make several forms ready for saving.
PasswordForm form1(MakeFormWithOnlyNewPasswordField());
PasswordForm form2(MakeSimpleForm());
manager()->OnPasswordFormsParsed(&driver_,
{form1.form_data, form2.form_data});
manager()->OnInformAboutUserInput(&driver_, form1.form_data);
manager()->OnInformAboutUserInput(&driver_, form2.form_data);
// Simulate submission in different ways depending on whether
// |owned_submitted_form_manager_| should be set and |form_managers_|should
// be cleared OR the submitted form manager should be in |form_managers_|.
if (set_owned_form_manager)
manager()->DidNavigateMainFrame(true /* form_may_be_submitted */);
else
OnPasswordFormSubmitted(form2.form_data);
// Test that Autofill Assistant has finished a script before Password
// Manager detected a successful submission. As a script has finished,
// pending credentials have reset.
manager()->ResetPendingCredentials();
manager()->OnPasswordFormsRendered(&driver_, {} /* observed */,
true /* did stop loading */);
// No form manager is ready for saving.
EXPECT_FALSE(manager()->GetSubmittedManagerForTest());
Mock::VerifyAndClearExpectations(&client_);
EXPECT_CALL(client_, IsAutofillAssistantUIVisible).WillOnce(Return(false));
// A form reappears again and a user submits it manually. Now expect a
// prompt.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr);
EXPECT_CALL(client_, IsSavingAndFillingEnabled)
.WillRepeatedly(Return(true));
manager()->OnPasswordFormsParsed(&driver_, {form2.form_data});
OnPasswordFormSubmitted(form2.form_data);
manager()->OnPasswordFormsRendered(&driver_, {} /* observed */,
true /* did stop loading */);
Mock::VerifyAndClearExpectations(&client_);
}
}
TEST_P(PasswordManagerTest, GenerationOnChangedForm) {
const bool kIsReparsingEnabled = GetParam();
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatureState(
features::kReparseServerPredictionsFollowingFormChange,
kIsReparsingEnabled);
EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
// Create FormdData for a form with 1 password field and process it.
FormData form_data;
form_data.is_form_tag = false;
form_data.url = GURL("http://www.testwebsite.com");
FormFieldData old_password_field;
old_password_field.form_control_type = "password";
old_password_field.unique_renderer_id = FieldRendererId(0);
old_password_field.name = ASCIIToUTF16("oldpass");
form_data.fields.push_back(old_password_field);
manager()->OnPasswordFormsParsed(&driver_, {form_data});
// Form changes: new and confirmation password fields are added by the
// website's scripts.
FormFieldData new_password_field;
new_password_field.form_control_type = "password";
new_password_field.unique_renderer_id = FieldRendererId(1);
new_password_field.name = ASCIIToUTF16("newpass");
form_data.fields.push_back(new_password_field);
FormFieldData confirm_password_field;
confirm_password_field.form_control_type = "password";
confirm_password_field.unique_renderer_id = FieldRendererId(2);
confirm_password_field.name = ASCIIToUTF16("confpass");
form_data.fields.push_back(confirm_password_field);
// Server predictions may arrive before the form is parsed by PasswordManager.
FormStructure form_structure(form_data);
form_structure.field(1)->set_server_type(autofill::ACCOUNT_CREATION_PASSWORD);
form_structure.field(2)->set_server_type(autofill::CONFIRMATION_PASSWORD);
manager()->ProcessAutofillPredictions(&driver_, {&form_structure});
autofill::PasswordFormGenerationData form_generation_data;
if (kIsReparsingEnabled) {
EXPECT_CALL(driver_, FormEligibleForGenerationFound)
.WillOnce(SaveArg<0>(&form_generation_data));
// The change is discovered by PasswordManager.
manager()->OnPasswordFormsParsed(&driver_, {form_data});
EXPECT_EQ(new_password_field.unique_renderer_id,
form_generation_data.new_password_renderer_id);
} else {
EXPECT_CALL(driver_, FormEligibleForGenerationFound).Times(0);
}
}
#if !defined(OS_IOS)
TEST_P(PasswordManagerTest, SubmissionDetectedOnClearedForm) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kDetectFormSubmissionOnFormClear);
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
PasswordForm saved_match(MakeSavedForm());
EXPECT_CALL(*store_, GetLogins)
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), saved_match)));
// Create FormData for a form with 1 password field and process it.
FormData form_data;
form_data.unique_renderer_id = FormRendererId(0);
form_data.url = GURL("http://www.google.com/a/LoginAuth");
FormFieldData old_password_field;
old_password_field.form_control_type = "password";
old_password_field.unique_renderer_id = FieldRendererId(1);
old_password_field.name = ASCIIToUTF16("oldpass");
old_password_field.value = ASCIIToUTF16("oldpass");
form_data.fields.push_back(old_password_field);
FormFieldData new_password_field;
new_password_field.form_control_type = "password";
new_password_field.unique_renderer_id = FieldRendererId(2);
new_password_field.name = ASCIIToUTF16("newpass");
new_password_field.autocomplete_attribute = "new-password";
form_data.fields.push_back(new_password_field);
FormFieldData confirm_password_field;
confirm_password_field.form_control_type = "password";
confirm_password_field.unique_renderer_id = FieldRendererId(3);
confirm_password_field.name = ASCIIToUTF16("confpass");
form_data.fields.push_back(confirm_password_field);
manager()->OnPasswordFormsParsed(&driver_, {form_data});
form_data.fields[0].value = ASCIIToUTF16("oldpass");
form_data.fields[1].value = ASCIIToUTF16("newpass");
form_data.fields[2].value = ASCIIToUTF16("newpass");
manager()->OnInformAboutUserInput(&driver_, form_data);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr)
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPasswordFormCleared(&driver_, form_data);
}
// Similar test as above with fields that have empty names.
TEST_P(PasswordManagerTest, SubmissionDetectedOnClearedNamelessForm) {
constexpr base::char16 kEmptyName[] = STRING16_LITERAL("");
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kDetectFormSubmissionOnFormClear);
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
PasswordForm saved_match(MakeSavedForm());
EXPECT_CALL(*store_, GetLogins)
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), saved_match)));
FormData form_data;
form_data.unique_renderer_id = FormRendererId(0);
form_data.url = GURL("http://www.google.com/a/LoginAuth");
FormFieldData old_password_field;
old_password_field.form_control_type = "password";
old_password_field.unique_renderer_id = FieldRendererId(1);
old_password_field.name = kEmptyName;
old_password_field.value = ASCIIToUTF16("oldpass");
form_data.fields.push_back(old_password_field);
FormFieldData new_password_field;
new_password_field.form_control_type = "password";
new_password_field.unique_renderer_id = FieldRendererId(2);
new_password_field.name = kEmptyName;
new_password_field.autocomplete_attribute = "new-password";
form_data.fields.push_back(new_password_field);
manager()->OnPasswordFormsParsed(&driver_, {form_data});
form_data.fields[0].value = ASCIIToUTF16("oldpass");
form_data.fields[1].value = ASCIIToUTF16("newpass");
manager()->OnInformAboutUserInput(&driver_, form_data);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr)
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPasswordFormCleared(&driver_, form_data);
}
TEST_P(PasswordManagerTest, SubmissionDetectedOnClearedFormlessFields) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kDetectFormSubmissionOnFormClear);
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
PasswordForm saved_match(MakeSavedForm());
EXPECT_CALL(*store_, GetLogins)
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), saved_match)));
for (bool new_password_field_was_cleared : {true, false}) {
SCOPED_TRACE(testing::Message("#new password field was cleared = ")
<< new_password_field_was_cleared);
// Create FormData for a form with 1 password field and process it.
FormData form_data;
form_data.is_form_tag = false;
form_data.unique_renderer_id = FormRendererId(0);
form_data.url = GURL("http://www.google.com/a/LoginAuth");
FormFieldData old_password_field;
old_password_field.form_control_type = "password";
old_password_field.unique_renderer_id = FieldRendererId(1);
old_password_field.name = ASCIIToUTF16("oldpass");
old_password_field.value = ASCIIToUTF16("oldpass");
form_data.fields.push_back(old_password_field);
FormFieldData new_password_field;
new_password_field.form_control_type = "password";
new_password_field.unique_renderer_id = FieldRendererId(2);
new_password_field.name = ASCIIToUTF16("newpass");
new_password_field.autocomplete_attribute = "new-password";
form_data.fields.push_back(new_password_field);
FormFieldData confirm_password_field;
confirm_password_field.form_control_type = "password";
confirm_password_field.unique_renderer_id = FieldRendererId(3);
confirm_password_field.name = ASCIIToUTF16("confpass");
form_data.fields.push_back(confirm_password_field);
manager()->OnPasswordFormsParsed(&driver_, {form_data});
form_data.fields[0].value = ASCIIToUTF16("oldpass");
form_data.fields[1].value = ASCIIToUTF16("newpass");
form_data.fields[2].value = ASCIIToUTF16("newpass");
manager()->OnInformAboutUserInput(&driver_, form_data);
form_data.fields[0].value = base::string16();
form_data.fields[2].value = base::string16();
if (new_password_field_was_cleared)
form_data.fields[1].value = base::string16();
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
if (new_password_field_was_cleared) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr)
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
} else {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr).Times(0);
}
manager()->OnPasswordFormCleared(&driver_, form_data);
}
}
// Similar test as above with fields that have empty names.
TEST_P(PasswordManagerTest, SubmissionDetectedOnClearedNameAndFormlessFields) {
constexpr base::char16 kEmptyName[] = STRING16_LITERAL("");
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kDetectFormSubmissionOnFormClear);
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
PasswordForm saved_match(MakeSavedForm());
EXPECT_CALL(*store_, GetLogins)
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), saved_match)));
for (bool new_password_field_was_cleared : {true, false}) {
SCOPED_TRACE(testing::Message("#new password field was cleared = ")
<< new_password_field_was_cleared);
// Create FormData for a form with 1 password field and process it.
FormData form_data;
form_data.is_form_tag = false;
form_data.unique_renderer_id = FormRendererId(0);
form_data.url = GURL("http://www.google.com/a/LoginAuth");
FormFieldData old_password_field;
old_password_field.form_control_type = "password";
old_password_field.unique_renderer_id = FieldRendererId(1);
old_password_field.name = kEmptyName;
old_password_field.value = ASCIIToUTF16("oldpass");
form_data.fields.push_back(old_password_field);
FormFieldData new_password_field;
new_password_field.form_control_type = "password";
new_password_field.unique_renderer_id = FieldRendererId(2);
new_password_field.name = kEmptyName;
new_password_field.autocomplete_attribute = "new-password";
form_data.fields.push_back(new_password_field);
manager()->OnPasswordFormsParsed(&driver_, {form_data});
form_data.fields[0].value = ASCIIToUTF16("oldpass");
form_data.fields[1].value = ASCIIToUTF16("newpass");
manager()->OnInformAboutUserInput(&driver_, form_data);
form_data.fields[0].value.clear();
if (new_password_field_was_cleared)
form_data.fields[1].value.clear();
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
if (new_password_field_was_cleared) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr)
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
} else {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr).Times(0);
}
manager()->OnPasswordFormCleared(&driver_, form_data);
}
}
#endif // !defined(OS_IOS)
TEST_P(PasswordManagerTest, IsFormManagerPendingPasswordUpdate) {
PasswordForm form(MakeSimpleForm());
std::vector<FormData> observed = {form.form_data};
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins)
.WillOnce(WithArg<1>(InvokeConsumer(store_.get(), form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Password was not updated yet.
EXPECT_FALSE(manager()->IsFormManagerPendingPasswordUpdate());
// The user updates the password.
FormData updated_data(form.form_data);
updated_data.fields[1].value = ASCIIToUTF16("new_password");
manager()->OnInformAboutUserInput(&driver_, updated_data);
EXPECT_TRUE(manager()->IsFormManagerPendingPasswordUpdate());
// The user submits the form.
OnPasswordFormSubmitted(updated_data);
EXPECT_TRUE(manager()->GetSubmittedManagerForTest());
// OnFormManagerPendingPasswordUpdate() still returns true after submission.
EXPECT_TRUE(manager()->IsFormManagerPendingPasswordUpdate());
}
INSTANTIATE_TEST_SUITE_P(, PasswordManagerTest, testing::Bool());
} // namespace password_manager