blob: 67f77414c332c23edff18777f38a0d3c05deb7a7 [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_form_manager.h"
#include <map>
#include <memory>
#include <set>
#include <utility>
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/metrics/metrics_hashes.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/mock_autocomplete_history_manager.h"
#include "components/autofill/core/browser/proto/server.pb.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/password_manager/core/browser/fake_form_fetcher.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_test_utils.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_form_saver.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/browser/vote_uploads_test_matchers.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_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
using autofill::FieldPropertiesFlags;
using autofill::FieldPropertiesMask;
using autofill::PasswordForm;
using autofill::ValueElementPair;
using autofill::features::kAutofillEnforceMinRequiredFieldsForHeuristics;
using autofill::features::kAutofillEnforceMinRequiredFieldsForQuery;
using autofill::features::kAutofillEnforceMinRequiredFieldsForUpload;
using base::ASCIIToUTF16;
using ::testing::_;
using ::testing::AllOf;
using ::testing::AtMost;
using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::InSequence;
using ::testing::IsEmpty;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Not;
using ::testing::Pair;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SaveArgPointee;
using ::testing::StrictMock;
using ::testing::UnorderedElementsAre;
using ::testing::WithArg;
namespace password_manager {
namespace {
// Enum that describes what button the user pressed on the save prompt.
enum SavePromptInteraction { SAVE, NEVER, NO_INTERACTION };
// Creates a form with 2 text fields and 1 password field. The first and second
// fields are username and password respectively. |observed_form| is used for
// default values.
// TODO(crbug.com/824834): The minimal case should be a smaller form.
PasswordForm CreateMinimalCrowdsourcableForm(
const PasswordForm& observed_form) {
PasswordForm form = observed_form;
form.origin = GURL("https://www.foo.com/login");
form.form_data.origin = form.origin;
autofill::FormFieldData field;
field.name = ASCIIToUTF16("email");
field.form_control_type = "text";
form.form_data.fields.push_back(field);
field.name = ASCIIToUTF16("password");
field.form_control_type = "password";
form.form_data.fields.push_back(field);
field.name = ASCIIToUTF16("petname");
field.form_control_type = "text";
form.form_data.fields.push_back(field);
form.username_element = ASCIIToUTF16("email");
form.password_element = ASCIIToUTF16("password");
return form;
}
class MockFormSaver : public StubFormSaver {
public:
MockFormSaver() = default;
~MockFormSaver() override = default;
// FormSaver:
MOCK_METHOD1(PermanentlyBlacklist, void(autofill::PasswordForm* observed));
MOCK_METHOD2(
Save,
void(const autofill::PasswordForm& pending,
const std::map<base::string16, const PasswordForm*>& best_matches));
MOCK_METHOD4(
Update,
void(const autofill::PasswordForm& pending,
const std::map<base::string16, const PasswordForm*>& best_matches,
const std::vector<autofill::PasswordForm>* credentials_to_update,
const autofill::PasswordForm* old_primary_key));
MOCK_METHOD1(PresaveGeneratedPassword,
void(const autofill::PasswordForm& generated));
MOCK_METHOD0(RemovePresavedPassword, void());
std::unique_ptr<FormSaver> Clone() override {
return std::make_unique<MockFormSaver>();
}
// Convenience downcasting method.
static MockFormSaver& Get(PasswordFormManager* form_manager) {
return *static_cast<MockFormSaver*>(form_manager->form_saver());
}
private:
DISALLOW_COPY_AND_ASSIGN(MockFormSaver);
};
class MockFormFetcher : public FakeFormFetcher {
public:
MOCK_METHOD1(AddConsumer, void(Consumer*));
MOCK_METHOD1(RemoveConsumer, void(Consumer*));
MOCK_METHOD0(Clone, std::unique_ptr<FormFetcher>());
};
MATCHER_P(UsernamePtrIs, username_value, "") {
if (!arg)
return false;
if (arg->username_value != username_value) {
*result_listener << "has username: " << arg->username_value;
return false;
}
return true;
}
MATCHER_P(PasswordsWereRevealed, revealed, "") {
return arg.passwords_were_revealed() == revealed;
}
MATCHER_P(HasPasswordAttributesVote, is_vote_expected, "") {
base::Optional<std::pair<autofill::PasswordAttribute, bool>> vote =
arg.get_password_attributes_vote_for_testing();
EXPECT_EQ(is_vote_expected, vote.has_value());
if (vote.has_value()) {
size_t reported_length = arg.get_password_length_vote_for_testing();
EXPECT_LT(0u, reported_length);
EXPECT_LE(reported_length, 5u /* actual password length */);
}
return true;
}
// Matches iff the masks in |expected_field_properties| match the mask in the
// uploaded form exactly.
MATCHER_P(UploadedFieldPropertiesMasksAre, expected_field_properties, "") {
size_t matched_count = 0;
bool conflict_found = false;
for (const auto& field : arg) {
auto expectation = expected_field_properties.find(field->name);
if (expectation == expected_field_properties.end())
continue;
matched_count++;
autofill::FieldPropertiesMask expected_mask = expectation->second;
if (field->properties_mask != expected_mask) {
*result_listener << (conflict_found ? ", " : "") << field->name
<< " field: expected mask " << expected_mask
<< ", but found " << field->properties_mask;
conflict_found = true;
}
}
if (matched_count != expected_field_properties.size()) {
*result_listener
<< (conflict_found ? ", " : "")
<< "some expectations did not correspond to an uploaded field";
conflict_found = true;
}
return !conflict_found;
}
class MockAutofillDownloadManager : public autofill::AutofillDownloadManager {
public:
MockAutofillDownloadManager(
autofill::AutofillDriver* driver,
autofill::AutofillDownloadManager::Observer* observer)
: AutofillDownloadManager(driver, observer) {}
MOCK_METHOD6(StartUploadRequest,
bool(const autofill::FormStructure&,
bool,
const autofill::ServerFieldTypeSet&,
const std::string&,
bool,
PrefService*));
private:
DISALLOW_COPY_AND_ASSIGN(MockAutofillDownloadManager);
};
class MockAutofillManager : public autofill::AutofillManager {
public:
MockAutofillManager(
autofill::AutofillDriver* driver,
autofill::AutofillClient* client,
autofill::PersonalDataManager* data_manager,
autofill::MockAutocompleteHistoryManager* autocomplete_manager)
: AutofillManager(driver, client, data_manager, autocomplete_manager) {
// This function will be called in the destructor of AutofillManager.
EXPECT_CALL(*autocomplete_manager, CancelPendingQueries(this));
}
void SetDownloadManager(autofill::AutofillDownloadManager* manager) {
set_download_manager(manager);
}
// Workaround for std::unique_ptr<> lacking a copy constructor.
bool MaybeStartVoteUploadProcess(
std::unique_ptr<FormStructure> form_structure,
bool observed_submission) override {
MaybeStartVoteUploadProcessPtr(form_structure.release(),
observed_submission);
return true;
}
MOCK_METHOD2(MaybeStartVoteUploadProcessPtr, void(FormStructure*, bool));
private:
DISALLOW_COPY_AND_ASSIGN(MockAutofillManager);
};
class MockPasswordManagerDriver : public StubPasswordManagerDriver {
public:
MockPasswordManagerDriver()
: mock_autofill_manager_(&test_autofill_driver_,
&test_autofill_client_,
&test_personal_data_manager_,
&mock_autocomplete_history_manager_) {
std::unique_ptr<TestingPrefServiceSimple> prefs(
new TestingPrefServiceSimple());
prefs->registry()->RegisterBooleanPref(
autofill::prefs::kAutofillCreditCardEnabled, true);
prefs->registry()->RegisterBooleanPref(
autofill::prefs::kAutofillProfileEnabled, true);
prefs->registry()->RegisterStringPref(
autofill::prefs::kAutofillUploadEncodingSeed, "");
test_autofill_client_.SetPrefs(std::move(prefs));
mock_autofill_download_manager_ = new MockAutofillDownloadManager(
&test_autofill_driver_, &mock_autofill_manager_);
// AutofillManager takes ownership of |mock_autofill_download_manager_|.
mock_autofill_manager_.SetDownloadManager(mock_autofill_download_manager_);
}
~MockPasswordManagerDriver() override {}
MOCK_METHOD1(FillPasswordForm, void(const autofill::PasswordFormFillData&));
MOCK_METHOD0(InformNoSavedCredentials, void());
MOCK_METHOD1(ShowInitialPasswordAccountSuggestions,
void(const autofill::PasswordFormFillData&));
MOCK_METHOD1(AllowPasswordGenerationForForm,
void(const autofill::PasswordForm&));
MockAutofillManager* mock_autofill_manager() {
return &mock_autofill_manager_;
}
MockAutofillDownloadManager* mock_autofill_download_manager() {
return mock_autofill_download_manager_;
}
private:
StrictMock<autofill::MockAutocompleteHistoryManager>
mock_autocomplete_history_manager_;
autofill::TestAutofillDriver test_autofill_driver_;
autofill::TestAutofillClient test_autofill_client_;
autofill::TestPersonalDataManager test_personal_data_manager_;
MockAutofillDownloadManager* mock_autofill_download_manager_;
NiceMock<MockAutofillManager> mock_autofill_manager_;
};
class TestPasswordManagerClient : public StubPasswordManagerClient {
public:
TestPasswordManagerClient()
: driver_(new NiceMock<MockPasswordManagerDriver>) {
prefs_ = std::make_unique<TestingPrefServiceSimple>();
prefs_->registry()->RegisterBooleanPref(prefs::kCredentialsEnableService,
true);
prefs_->registry()->RegisterStringPref(
autofill::prefs::kAutofillUploadEncodingSeed, "");
}
MOCK_CONST_METHOD0(IsIncognito, bool());
PrefService* GetPrefs() const override { return prefs_.get(); }
MockPasswordManagerDriver* mock_driver() { return driver_.get(); }
base::WeakPtr<PasswordManagerDriver> driver() { return driver_->AsWeakPtr(); }
autofill::AutofillDownloadManager* GetAutofillDownloadManager() override {
return mock_driver()->mock_autofill_download_manager();
}
void KillDriver() { driver_.reset(); }
const GURL& GetMainFrameURL() const override {
static GURL url("https://www.example.com");
return url;
}
private:
std::unique_ptr<TestingPrefServiceSimple> prefs_;
std::unique_ptr<MockPasswordManagerDriver> driver_;
};
ACTION_P(SaveToUniquePtr, scoped) {
scoped->reset(arg0);
}
} // namespace
class PasswordFormManagerTest : public testing::Test {
public:
PasswordFormManagerTest() { fake_form_fetcher_.Fetch(); }
void SetUp() override {
observed_form_.origin = GURL("http://accounts.google.com/a/LoginAuth");
observed_form_.action = GURL("http://accounts.google.com/a/Login");
observed_form_.username_element = ASCIIToUTF16("Email");
observed_form_.password_element = ASCIIToUTF16("Passwd");
observed_form_.submit_element = ASCIIToUTF16("signIn");
observed_form_.signon_realm = "http://accounts.google.com";
observed_form_.form_data.name = ASCIIToUTF16("the-form-name");
saved_match_ = observed_form_;
saved_match_.origin = GURL("http://accounts.google.com/a/ServiceLoginAuth");
saved_match_.action = GURL("http://accounts.google.com/a/ServiceLogin");
saved_match_.preferred = true;
saved_match_.username_value = ASCIIToUTF16("test@gmail.com");
saved_match_.password_value = ASCIIToUTF16("test1");
saved_match_.other_possible_usernames.push_back(ValueElementPair(
ASCIIToUTF16("test2@gmail.com"), ASCIIToUTF16("full_name")));
saved_match_.all_possible_passwords = {
{ASCIIToUTF16("password"), base::string16()},
{ASCIIToUTF16("password"), ASCIIToUTF16("Passwd")}};
autofill::FormFieldData field;
field.label = ASCIIToUTF16("Full name");
field.name = ASCIIToUTF16("full_name");
field.form_control_type = "text";
saved_match_.form_data.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("Email");
field.form_control_type = "text";
saved_match_.form_data.fields.push_back(field);
field.label = ASCIIToUTF16("password");
field.name = ASCIIToUTF16("Passwd");
field.form_control_type = "password";
saved_match_.form_data.fields.push_back(field);
psl_saved_match_ = saved_match_;
psl_saved_match_.is_public_suffix_match = true;
psl_saved_match_.origin =
GURL("http://m.accounts.google.com/a/ServiceLoginAuth");
psl_saved_match_.action = GURL("http://m.accounts.google.com/a/Login");
psl_saved_match_.signon_realm = "http://m.accounts.google.com";
password_manager_.reset(new PasswordManager(&client_));
form_manager_.reset(new PasswordFormManager(
password_manager_.get(), &client_, client_.driver(), observed_form_,
std::make_unique<NiceMock<MockFormSaver>>(), &fake_form_fetcher_));
form_manager_->Init(nullptr);
}
// Save saved_match() for observed_form() where |observed_form_data|,
// |times_used|, and |status| are used to overwrite the default values for
// observed_form(). |field_type| is the upload that we expect from saving,
// with nullptr meaning no upload expected.
void AccountCreationUploadTest(const autofill::FormData& observed_form_data,
int times_used,
PasswordForm::GenerationUploadStatus status,
const autofill::ServerFieldType* field_type) {
PasswordForm form(*observed_form());
form.form_data = observed_form_data;
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
std::make_unique<NiceMock<MockFormSaver>>(), &fetcher);
form_manager.Init(nullptr);
PasswordForm match = CreateSavedMatch(false);
match.generation_upload_status = status;
match.times_used = times_used;
PasswordForm form_to_save(form);
form_to_save.preferred = true;
form_to_save.username_element = ASCIIToUTF16("observed-username-field");
form_to_save.password_element = ASCIIToUTF16("observed-password-field");
form_to_save.username_value = match.username_value;
form_to_save.password_value = match.password_value;
fetcher.SetNonFederated({&match}, 0u);
std::string expected_login_signature;
autofill::FormStructure observed_structure(observed_form_data);
autofill::FormStructure pending_structure(saved_match()->form_data);
if (observed_structure.FormSignatureAsStr() !=
pending_structure.FormSignatureAsStr() &&
times_used == 0) {
expected_login_signature = observed_structure.FormSignatureAsStr();
}
autofill::ServerFieldTypeSet expected_available_field_types;
FieldTypeMap expected_types;
expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE;
// When we're voting for an account creation form, we should also vote
// for its username field.
bool expect_username_vote = false;
if (field_type && *field_type == autofill::ACCOUNT_CREATION_PASSWORD) {
expected_types[match.username_element] = autofill::USERNAME;
expected_available_field_types.insert(autofill::USERNAME);
expect_username_vote = true;
} else {
expected_types[match.username_element] = autofill::UNKNOWN_TYPE;
}
bool expect_generation_vote = false;
if (field_type) {
// Show the password generation popup to check that the generation vote
// would be ignored.
form_manager.SetGenerationElement(saved_match()->password_element);
form_manager.SetGenerationPopupWasShown(/*shown=*/true,
/*manually_triggered*/ true);
expect_generation_vote =
*field_type != autofill::ACCOUNT_CREATION_PASSWORD;
expected_available_field_types.insert(*field_type);
expected_types[saved_match()->password_element] = *field_type;
}
if (field_type) {
if (expect_username_vote) {
EXPECT_CALL(
*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(AllOf(SignatureIsSameAs(*saved_match()),
UploadedAutofillTypesAre(expected_types),
HasGenerationVote(expect_generation_vote),
VoteTypesAre(VoteTypeMap(
{{match.username_element,
autofill::AutofillUploadContents::
Field::CREDENTIALS_REUSED}})),
HasPasswordAttributesVote(false)),
false, expected_available_field_types,
expected_login_signature, true, nullptr));
} else {
EXPECT_CALL(
*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(AllOf(SignatureIsSameAs(*saved_match()),
UploadedAutofillTypesAre(expected_types),
HasGenerationVote(expect_generation_vote),
HasPasswordAttributesVote(false)),
false, expected_available_field_types,
expected_login_signature, true, nullptr));
}
} else {
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, _, _, _, _, _))
.Times(0);
}
if (times_used == 0) {
// First login vote.
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(SignatureIsSameAs(form_to_save), _, _, _,
_, nullptr));
}
form_manager.ProvisionallySave(form_to_save);
form_manager.Save();
Mock::VerifyAndClearExpectations(
client()->mock_driver()->mock_autofill_download_manager());
}
// Test upload votes on change password forms. |field_type| is a vote that we
// expect to be uploaded.
void ChangePasswordUploadTest(autofill::ServerFieldType field_type,
bool has_confirmation_field) {
SCOPED_TRACE(testing::Message()
<< "field_type=" << field_type
<< " has_confirmation_field=" << has_confirmation_field);
// |observed_form_| should have |form_data| in order to be uploaded.
observed_form()->form_data = saved_match()->form_data;
// Turn |observed_form_| and into change password form.
observed_form()->new_password_element = ASCIIToUTF16("NewPasswd");
autofill::FormFieldData field;
field.label = ASCIIToUTF16("NewPasswd");
field.name = ASCIIToUTF16("NewPasswd");
field.form_control_type = "password";
observed_form()->form_data.fields.push_back(field);
autofill::FormFieldData empty_field;
observed_form()->form_data.fields.push_back(empty_field);
if (has_confirmation_field) {
field.label = ASCIIToUTF16("ConfPwd");
field.name = ASCIIToUTF16("ConfPwd");
field.form_control_type = "password";
observed_form()->form_data.fields.push_back(field);
}
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
std::make_unique<NiceMock<MockFormSaver>>(), &fetcher);
form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
PasswordForm submitted_form(*observed_form());
// credentials.username_element.clear();
submitted_form.username_value = saved_match()->username_value;
submitted_form.password_value = saved_match()->password_value;
submitted_form.new_password_value = ASCIIToUTF16("test2");
if (has_confirmation_field)
submitted_form.confirmation_password_element = ASCIIToUTF16("ConfPwd");
submitted_form.preferred = true;
form_manager.ProvisionallySave(submitted_form);
// Successful login. The PasswordManager would instruct PasswordFormManager
// to update.
EXPECT_FALSE(form_manager.IsNewLogin());
EXPECT_FALSE(form_manager.IsPossibleChangePasswordFormWithoutUsername());
// By now, the PasswordFormManager should have promoted the new password
// value already to be the current password, and should no longer maintain
// any info about the new password value.
EXPECT_EQ(submitted_form.new_password_value,
form_manager.GetPendingCredentials().password_value);
EXPECT_TRUE(
form_manager.GetPendingCredentials().new_password_value.empty());
std::map<base::string16, autofill::ServerFieldType> expected_types;
expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE;
expected_types[observed_form_.username_element] = autofill::UNKNOWN_TYPE;
expected_types[observed_form_.password_element] = autofill::PASSWORD;
expected_types[observed_form_.new_password_element] = field_type;
expected_types[base::string16()] = autofill::UNKNOWN_TYPE;
autofill::ServerFieldTypeSet expected_available_field_types;
expected_available_field_types.insert(autofill::PASSWORD);
expected_available_field_types.insert(field_type);
if (has_confirmation_field) {
expected_types[submitted_form.confirmation_password_element] =
autofill::CONFIRMATION_PASSWORD;
expected_available_field_types.insert(autofill::CONFIRMATION_PASSWORD);
}
InSequence in_sequence;
EXPECT_CALL(
*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(
AllOf(SignatureIsSameAs(*observed_form()),
UploadedAutofillTypesAre(expected_types),
HasGenerationVote(false), HasPasswordAttributesVote(false)),
false, expected_available_field_types, _, true, nullptr));
if (field_type == autofill::NEW_PASSWORD) {
// An unrelated vote that the credentials were used for the first time.
EXPECT_CALL(
*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(SignatureIsSameAs(submitted_form), _, _, _, _, _));
}
switch (field_type) {
case autofill::NEW_PASSWORD:
form_manager.Save();
break;
case autofill::PROBABLY_NEW_PASSWORD:
form_manager.OnNoInteraction(true /* it is an update */);
break;
case autofill::NOT_NEW_PASSWORD:
form_manager.OnNopeUpdateClicked();
break;
default:
NOTREACHED();
}
Mock::VerifyAndClearExpectations(
client()->mock_driver()->mock_autofill_download_manager());
}
autofill::AutofillUploadContents::Field::PasswordGenerationType
GetExpectedPasswordGenerationType(bool is_manual_generation,
bool is_change_password_form,
bool has_generated_password) {
if (!has_generated_password)
return autofill::AutofillUploadContents::Field::IGNORED_GENERATION_POPUP;
if (is_manual_generation) {
if (is_change_password_form) {
return autofill::AutofillUploadContents::Field::
MANUALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM;
} else {
return autofill::AutofillUploadContents::Field::
MANUALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
}
} else {
if (is_change_password_form) {
return autofill::AutofillUploadContents::Field::
AUTOMATICALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM;
} else {
return autofill::AutofillUploadContents::Field::
AUTOMATICALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
}
}
}
// The user types username and generates password on SignUp or change password
// form. The password generation might be triggered automatically or manually.
// This function checks that correct vote is uploaded on server. The vote must
// be uploaded regardless of the user's interaction with the prompt.
void GeneratedVoteUploadTest(bool is_manual_generation,
bool is_change_password_form,
bool has_generated_password,
bool generated_password_changed,
SavePromptInteraction interaction) {
SCOPED_TRACE(testing::Message()
<< "is_manual_generation=" << is_manual_generation
<< " is_change_password_form=" << is_change_password_form
<< " has_generated_password=" << has_generated_password
<< " generated_password_changed=" << generated_password_changed
<< " interaction=" << interaction);
PasswordForm form(*observed_form());
form.form_data = saved_match()->form_data;
if (is_change_password_form) {
// Turn |form| to a change password form.
form.new_password_element = ASCIIToUTF16("NewPasswd");
autofill::FormFieldData field;
field.label = ASCIIToUTF16("password");
field.name = ASCIIToUTF16("NewPasswd");
field.form_control_type = "password";
form.form_data.fields.push_back(field);
}
// Create submitted form.
PasswordForm submitted_form(form);
submitted_form.preferred = true;
submitted_form.username_value = saved_match()->username_value;
submitted_form.password_value = saved_match()->password_value;
if (is_change_password_form) {
submitted_form.new_password_value =
saved_match()->password_value + ASCIIToUTF16("1");
submitted_form.password_value = submitted_form.new_password_value;
}
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
std::make_unique<NiceMock<MockFormSaver>>(), &fetcher);
form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
autofill::ServerFieldTypeSet expected_available_field_types;
// Don't send autofill votes if the user didn't press "Save" button.
if (interaction == SAVE)
expected_available_field_types.insert(autofill::PASSWORD);
base::string16 generation_element = is_change_password_form
? form.new_password_element
: form.password_element;
form_manager.SetGenerationElement(generation_element);
form_manager.SetGenerationPopupWasShown(true, is_manual_generation);
if (has_generated_password) {
form_manager.PresaveGeneratedPassword(submitted_form);
if (generated_password_changed) {
submitted_form.password_value += ASCIIToUTF16("2");
form_manager.PresaveGeneratedPassword(submitted_form);
}
}
// Figure out expected generation event type.
autofill::AutofillUploadContents::Field::PasswordGenerationType
expected_generation_type = GetExpectedPasswordGenerationType(
is_manual_generation, is_change_password_form,
has_generated_password);
std::map<base::string16,
autofill::AutofillUploadContents::Field::PasswordGenerationType>
expected_generation_types;
expected_generation_types[generation_element] = expected_generation_type;
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(AllOf(SignatureIsSameAs(submitted_form),
UploadedGenerationTypesAre(
expected_generation_types,
generated_password_changed)),
false, expected_available_field_types,
std::string(), true, nullptr));
base::HistogramTester histogram_tester;
form_manager.ProvisionallySave(submitted_form);
switch (interaction) {
case SAVE:
form_manager.Save();
break;
case NEVER:
form_manager.OnNeverClicked();
break;
case NO_INTERACTION:
form_manager.OnNoInteraction(false /* not an update prompt*/);
break;
}
if (has_generated_password) {
histogram_tester.ExpectUniqueSample(
"PasswordGeneration.GeneratedPasswordWasEdited",
generated_password_changed /* sample */, 1);
histogram_tester.ExpectUniqueSample(
"PasswordGeneration.IsTriggeredManually",
is_manual_generation /* sample */, 1);
}
Mock::VerifyAndClearExpectations(
client()->mock_driver()->mock_autofill_download_manager());
}
void GeneratedPasswordUkmTest(bool is_manual_generation,
bool is_change_password_form,
bool has_generated_password,
bool generated_password_changed,
SavePromptInteraction interaction) {
SCOPED_TRACE(testing::Message()
<< "is_manual_generation=" << is_manual_generation
<< " is_change_password_form=" << is_change_password_form
<< " has_generated_password=" << has_generated_password
<< " generated_password_changed=" << generated_password_changed
<< " interaction=" << interaction);
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
test_ukm_recorder.UpdateSourceURL(client()->GetUkmSourceId(),
client()->GetMainFrameURL());
PasswordForm form(*observed_form());
form.form_data = saved_match()->form_data;
if (is_change_password_form) {
// Turn |form| to a change password form.
form.new_password_element = ASCIIToUTF16("NewPasswd");
autofill::FormFieldData field;
field.label = ASCIIToUTF16("password");
field.name = ASCIIToUTF16("NewPasswd");
field.form_control_type = "password";
form.form_data.fields.push_back(field);
}
// Create submitted form.
PasswordForm submitted_form(form);
submitted_form.preferred = true;
submitted_form.username_value = saved_match()->username_value;
submitted_form.password_value = saved_match()->password_value;
if (is_change_password_form) {
submitted_form.new_password_value =
saved_match()->password_value + ASCIIToUTF16("1");
submitted_form.password_value = submitted_form.new_password_value;
}
FakeFormFetcher fetcher;
fetcher.Fetch();
auto metrics_recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>(
form.origin.SchemeIsCryptographic(), client()->GetUkmSourceId());
auto form_manager = std::make_unique<PasswordFormManager>(
password_manager(), client(), client()->driver(), form,
std::make_unique<NiceMock<MockFormSaver>>(), &fetcher);
// *Move* the metrics recorder to not hold on to a reference.
form_manager->Init(std::move(metrics_recorder));
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
autofill::ServerFieldTypeSet expected_available_field_types;
// Don't send autofill votes if the user didn't press "Save" button.
if (interaction == SAVE)
expected_available_field_types.insert(autofill::PASSWORD);
base::string16 generation_element = is_change_password_form
? form.new_password_element
: form.password_element;
form_manager->SetGenerationElement(generation_element);
form_manager->SetGenerationPopupWasShown(true, is_manual_generation);
if (has_generated_password) {
form_manager->PresaveGeneratedPassword(submitted_form);
if (generated_password_changed) {
submitted_form.password_value += ASCIIToUTF16("2");
form_manager->PresaveGeneratedPassword(submitted_form);
}
}
// Figure out expected generation event type.
autofill::AutofillUploadContents::Field::PasswordGenerationType
expected_generation_type = GetExpectedPasswordGenerationType(
is_manual_generation, is_change_password_form,
has_generated_password);
std::map<base::string16,
autofill::AutofillUploadContents::Field::PasswordGenerationType>
expected_generation_types;
expected_generation_types[generation_element] = expected_generation_type;
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(AllOf(SignatureIsSameAs(submitted_form),
UploadedGenerationTypesAre(
expected_generation_types,
generated_password_changed)),
false, expected_available_field_types,
std::string(), true, nullptr));
form_manager->ProvisionallySave(submitted_form);
switch (interaction) {
case SAVE:
form_manager->Save();
break;
case NEVER:
form_manager->OnNeverClicked();
break;
case NO_INTERACTION:
form_manager->OnNoInteraction(false /* not an update prompt*/);
break;
}
// Reset form manager to flush UKM metrics.
form_manager.reset();
auto entries = test_ukm_recorder.GetEntriesByName(
ukm::builders::PasswordForm::kEntryName);
ASSERT_EQ(1u, entries.size());
ukm::TestUkmRecorder::ExpectEntryMetric(
entries[0], ukm::builders::PasswordForm::kGeneration_PopupShownName,
is_manual_generation
? static_cast<int64_t>(
password_manager::PasswordFormMetricsRecorder::
PasswordGenerationPopupShown::kShownManually)
: static_cast<int64_t>(
password_manager::PasswordFormMetricsRecorder::
PasswordGenerationPopupShown::kShownAutomatically));
ukm::TestUkmRecorder::ExpectEntryMetric(
entries[0],
ukm::builders::PasswordForm::kGeneration_GeneratedPasswordName,
has_generated_password ? 1 : 0);
if (has_generated_password) {
ukm::TestUkmRecorder::ExpectEntryMetric(
entries[0],
ukm::builders::PasswordForm::
kGeneration_GeneratedPasswordModifiedName,
generated_password_changed ? 1 : 0);
}
Mock::VerifyAndClearExpectations(
client()->mock_driver()->mock_autofill_download_manager());
}
PasswordForm* observed_form() { return &observed_form_; }
PasswordForm* saved_match() { return &saved_match_; }
PasswordForm* psl_saved_match() { return &psl_saved_match_; }
PasswordForm CreateSavedMatch(bool blacklisted) {
PasswordForm match = saved_match_;
match.blacklisted_by_user = blacklisted;
return match;
}
TestPasswordManagerClient* client() { return &client_; }
PasswordManager* password_manager() { return password_manager_.get(); }
PasswordFormManager* form_manager() { return form_manager_.get(); }
FakeFormFetcher* fake_form_fetcher() { return &fake_form_fetcher_; }
// To spare typing for PasswordFormManager instances which need no driver.
const base::WeakPtr<PasswordManagerDriver> kNoDriver;
protected:
enum class SimulatedManagerAction { NONE, AUTOFILLED, OFFERED, OFFERED_PSL };
enum class SimulatedSubmitResult { NONE, PASSED, FAILED };
enum class SuppressedFormType { HTTPS, PSL_MATCH, SAME_ORGANIZATION_NAME };
PasswordForm CreateSuppressedForm(SuppressedFormType suppression_type,
const char* username,
const char* password,
PasswordForm::Type manual_or_generated) {
PasswordForm form = *saved_match();
switch (suppression_type) {
case SuppressedFormType::HTTPS:
form.origin = GURL("https://accounts.google.com/a/LoginAuth");
form.signon_realm = "https://accounts.google.com/";
break;
case SuppressedFormType::PSL_MATCH:
form.origin = GURL("http://other.google.com/");
form.signon_realm = "http://other.google.com/";
break;
case SuppressedFormType::SAME_ORGANIZATION_NAME:
form.origin = GURL("https://may-or-may-not-be.google.appspot.com/");
form.signon_realm = "https://may-or-may-not-be.google.appspot.com/";
break;
}
form.type = manual_or_generated;
form.username_value = ASCIIToUTF16(username);
form.password_value = ASCIIToUTF16(password);
return form;
}
void SimulateActionsOnHTTPObservedForm(
FakeFormFetcher* fetcher,
SimulatedManagerAction manager_action,
SimulatedSubmitResult submit_result,
const char* filled_username,
const char* filled_password,
const char* submitted_password = nullptr) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
std::make_unique<NiceMock<MockFormSaver>>(), fetcher);
form_manager.Init(nullptr);
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, _, _, _, _, _))
.Times(::testing::AnyNumber());
PasswordForm http_stored_form = *saved_match();
http_stored_form.username_value = base::ASCIIToUTF16(filled_username);
http_stored_form.password_value = base::ASCIIToUTF16(filled_password);
if (manager_action == SimulatedManagerAction::OFFERED_PSL)
http_stored_form.is_public_suffix_match = true;
std::vector<const PasswordForm*> matches;
if (manager_action != SimulatedManagerAction::NONE)
matches.push_back(&http_stored_form);
// Extra mile: kChoose is only recorded if there were multiple
// logins available and the preferred one was changed.
PasswordForm http_stored_form2 = http_stored_form;
if (manager_action == SimulatedManagerAction::OFFERED) {
http_stored_form.preferred = false;
http_stored_form2.username_value = ASCIIToUTF16("user-other@gmail.com");
matches.push_back(&http_stored_form2);
}
fetcher->Fetch();
fetcher->SetNonFederated(matches, 0u);
if (submit_result != SimulatedSubmitResult::NONE) {
PasswordForm submitted_form(*observed_form());
submitted_form.preferred = true;
submitted_form.username_value = base::ASCIIToUTF16(filled_username);
submitted_form.password_value =
submitted_password ? base::ASCIIToUTF16(submitted_password)
: base::ASCIIToUTF16(filled_password);
form_manager.ProvisionallySave(submitted_form);
if (submit_result == SimulatedSubmitResult::PASSED) {
form_manager.LogSubmitPassed();
form_manager.Save();
} else {
form_manager.LogSubmitFailed();
}
}
}
private:
// Necessary for callbacks, and for TestAutofillDriver.
base::test::ScopedTaskEnvironment task_environment_;
PasswordForm observed_form_;
PasswordForm saved_match_;
PasswordForm psl_saved_match_;
NiceMock<TestPasswordManagerClient> client_;
std::unique_ptr<PasswordManager> password_manager_;
// Define |fake_form_fetcher_| before |form_manager_|, because the former
// needs to outlive the latter.
FakeFormFetcher fake_form_fetcher_;
std::unique_ptr<PasswordFormManager> form_manager_;
};
class PasswordFormManagerFillOnAccountSelectTest
: public PasswordFormManagerTest {
public:
PasswordFormManagerFillOnAccountSelectTest() {
scoped_feature_list_.InitAndEnableFeature(features::kFillOnAccountSelect);
}
base::test::ScopedFeatureList scoped_feature_list_;
};
// Test provisionally saving a new login.
TEST_F(PasswordFormManagerTest, TestNewLogin) {
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
// User submits credentials for the observed form.
PasswordForm credentials = *observed_form();
credentials.username_value = saved_match()->username_value;
credentials.password_value = saved_match()->password_value;
credentials.preferred = true;
form_manager()->ProvisionallySave(credentials);
// Successful login. The PasswordManager would instruct PasswordFormManager
// to save, which should know this is a new login.
EXPECT_TRUE(form_manager()->IsNewLogin());
// Make sure the credentials that would be submitted on successful login
// are going to match the stored entry in the db.
EXPECT_EQ(observed_form()->origin.spec(),
form_manager()->GetPendingCredentials().origin.spec());
EXPECT_EQ(observed_form()->signon_realm,
form_manager()->GetPendingCredentials().signon_realm);
EXPECT_EQ(observed_form()->action,
form_manager()->GetPendingCredentials().action);
EXPECT_TRUE(form_manager()->GetPendingCredentials().preferred);
EXPECT_EQ(saved_match()->password_value,
form_manager()->GetPendingCredentials().password_value);
EXPECT_EQ(saved_match()->username_value,
form_manager()->GetPendingCredentials().username_value);
EXPECT_TRUE(
form_manager()->GetPendingCredentials().new_password_element.empty());
EXPECT_TRUE(
form_manager()->GetPendingCredentials().new_password_value.empty());
}
// Test provisionally saving a new login in presence of other saved logins.
TEST_F(PasswordFormManagerTest, TestAdditionalLogin) {
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
base::string16 new_user = ASCIIToUTF16("newuser");
base::string16 new_pass = ASCIIToUTF16("newpass");
ASSERT_NE(new_user, saved_match()->username_value);
PasswordForm new_login = *observed_form();
new_login.username_value = new_user;
new_login.password_value = new_pass;
new_login.preferred = true;
form_manager()->ProvisionallySave(new_login);
// The username value differs from the saved match, so this is a new login.
EXPECT_TRUE(form_manager()->IsNewLogin());
EXPECT_EQ(observed_form()->origin.spec(),
form_manager()->GetPendingCredentials().origin.spec());
EXPECT_EQ(observed_form()->signon_realm,
form_manager()->GetPendingCredentials().signon_realm);
EXPECT_TRUE(form_manager()->GetPendingCredentials().preferred);
EXPECT_EQ(new_pass, form_manager()->GetPendingCredentials().password_value);
EXPECT_EQ(new_user, form_manager()->GetPendingCredentials().username_value);
EXPECT_TRUE(
form_manager()->GetPendingCredentials().new_password_element.empty());
EXPECT_TRUE(
form_manager()->GetPendingCredentials().new_password_value.empty());
}
// Test blacklisting in the presence of saved results.
TEST_F(PasswordFormManagerTest, TestBlacklist) {
saved_match()->origin = observed_form()->origin;
saved_match()->action = observed_form()->action;
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
PasswordForm new_login = *observed_form();
new_login.username_value = ASCIIToUTF16("newuser");
new_login.password_value = ASCIIToUTF16("newpass");
// Pretend Chrome detected a form submission with |new_login|.
form_manager()->ProvisionallySave(new_login);
EXPECT_TRUE(form_manager()->IsNewLogin());
EXPECT_EQ(observed_form()->origin.spec(),
form_manager()->GetPendingCredentials().origin.spec());
EXPECT_EQ(observed_form()->signon_realm,
form_manager()->GetPendingCredentials().signon_realm);
const PasswordForm pending_form = form_manager()->GetPendingCredentials();
PasswordForm actual_add_form;
// Now pretend the user wants to never save passwords on this origin. Chrome
// is supposed to only request blacklisting of a single form.
EXPECT_CALL(MockFormSaver::Get(form_manager()), PermanentlyBlacklist(_))
.WillOnce(SaveArgPointee<0>(&actual_add_form));
form_manager()->PermanentlyBlacklist();
EXPECT_EQ(pending_form, form_manager()->GetPendingCredentials());
// The PasswordFormManager should have updated its knowledge of blacklisting
// without waiting for PasswordStore updates.
EXPECT_TRUE(form_manager()->IsBlacklisted());
EXPECT_THAT(form_manager()->GetBlacklistedMatches(),
UnorderedElementsAre(Pointee(actual_add_form)));
}
// Test that stored blacklisted forms are correctly evaluated for whether they
// apply to the observed form.
TEST_F(PasswordFormManagerTest, TestBlacklistMatching) {
// Doesn't apply because it is just a PSL match of the observed form.
PasswordForm blacklisted_psl = *observed_form();
blacklisted_psl.signon_realm = "http://m.accounts.google.com";
blacklisted_psl.is_public_suffix_match = true;
blacklisted_psl.blacklisted_by_user = true;
// Doesn't apply because of different PasswordForm::Scheme.
PasswordForm blacklisted_not_match = *observed_form();
blacklisted_not_match.scheme = PasswordForm::SCHEME_BASIC;
// Applies despite different element names and path.
PasswordForm blacklisted_match = *observed_form();
blacklisted_match.origin = GURL("http://accounts.google.com/a/LoginAuth1234");
blacklisted_match.username_element = ASCIIToUTF16("Element1");
blacklisted_match.password_element = ASCIIToUTF16("Element2");
blacklisted_match.submit_element = ASCIIToUTF16("Element3");
blacklisted_match.blacklisted_by_user = true;
std::vector<const PasswordForm*> matches = {&blacklisted_psl,
&blacklisted_not_match,
&blacklisted_match,
saved_match()};
fake_form_fetcher()->SetNonFederated(matches, 0u);
EXPECT_TRUE(form_manager()->IsBlacklisted());
EXPECT_THAT(form_manager()->GetBlacklistedMatches(),
ElementsAre(Pointee(blacklisted_match)));
EXPECT_EQ(1u, form_manager()->GetBestMatches().size());
EXPECT_EQ(*saved_match(), *form_manager()->GetPreferredMatch());
}
// Test that even in the presence of blacklisted matches, the non-blacklisted
// ones are still autofilled.
TEST_F(PasswordFormManagerTest, AutofillBlacklisted) {
PasswordForm saved_form = *observed_form();
saved_form.username_value = ASCIIToUTF16("user");
saved_form.password_value = ASCIIToUTF16("pass");
PasswordForm blacklisted = *observed_form();
blacklisted.blacklisted_by_user = true;
blacklisted.username_value.clear();
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
fake_form_fetcher()->SetNonFederated({&saved_form, &blacklisted}, 0u);
EXPECT_EQ(1u, form_manager()->GetBlacklistedMatches().size());
EXPECT_TRUE(form_manager()->IsBlacklisted());
EXPECT_EQ(1u, form_manager()->GetBestMatches().size());
EXPECT_TRUE(fill_data.additional_logins.empty());
}
// If PSL-matched credentials had been suggested, but the user has overwritten
// the password, the provisionally saved credentials should no longer be
// considered as PSL-matched, so that the exception for not prompting before
// saving PSL-matched credentials should no longer apply.
TEST_F(PasswordFormManagerTest,
OverriddenPSLMatchedCredentialsNotMarkedAsPSLMatched) {
// The suggestion needs to be PSL-matched.
fake_form_fetcher()->SetNonFederated({psl_saved_match()}, 0u);
// User modifies the suggested password and submits the form.
PasswordForm credentials(*observed_form());
credentials.username_value = saved_match()->username_value;
credentials.password_value =
saved_match()->password_value + ASCIIToUTF16("modify");
form_manager()->ProvisionallySave(credentials);
EXPECT_TRUE(form_manager()->IsNewLogin());
EXPECT_FALSE(form_manager()->IsPendingCredentialsPublicSuffixMatch());
}
// Test that if a PSL-matched suggestion is saved on a new origin, its metadata
// are correctly updated.
TEST_F(PasswordFormManagerTest, PSLMatchedCredentialsMetadataUpdated) {
PasswordForm psl_suggestion = *saved_match();
psl_suggestion.is_public_suffix_match = true;
fake_form_fetcher()->SetNonFederated({&psl_suggestion}, 0u);
PasswordForm submitted_form(*observed_form());
submitted_form.preferred = true;
submitted_form.username_value = saved_match()->username_value;
submitted_form.password_value = saved_match()->password_value;
form_manager()->ProvisionallySave(submitted_form);
PasswordForm expected_saved_form(submitted_form);
expected_saved_form.times_used = 1;
expected_saved_form.other_possible_usernames.clear();
expected_saved_form.form_data = saved_match()->form_data;
expected_saved_form.origin = observed_form()->origin;
expected_saved_form.is_public_suffix_match = true;
PasswordForm actual_saved_form;
autofill::ServerFieldTypeSet expected_available_field_types;
expected_available_field_types.insert(autofill::ACCOUNT_CREATION_PASSWORD);
expected_available_field_types.insert(autofill::USERNAME);
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, false, expected_available_field_types, _,
true, nullptr));
EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _))
.WillOnce(SaveArg<0>(&actual_saved_form));
form_manager()->Save();
// Can't verify time, so ignore it.
actual_saved_form.date_created = base::Time();
EXPECT_EQ(expected_saved_form, actual_saved_form);
}
// Test that when the submitted form contains a "new-password" field, then the
// password value is taken from there.
TEST_F(PasswordFormManagerTest, TestNewLoginFromNewPasswordElement) {
observed_form()->new_password_element = ASCIIToUTF16("NewPasswd");
observed_form()->username_marked_by_site = true;
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
std::make_unique<MockFormSaver>(), &fetcher);
form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
// User enters current and new credentials to the observed form.
PasswordForm credentials(*observed_form());
credentials.username_value = saved_match()->username_value;
credentials.password_value = ASCIIToUTF16("oldpassword");
credentials.new_password_value = ASCIIToUTF16("newpassword");
credentials.preferred = true;
form_manager.ProvisionallySave(credentials);
// Successful login. The PasswordManager would instruct PasswordFormManager
// to save, which should know this is a new login.
EXPECT_TRUE(form_manager.IsNewLogin());
EXPECT_EQ(credentials.origin, form_manager.GetPendingCredentials().origin);
EXPECT_EQ(credentials.signon_realm,
form_manager.GetPendingCredentials().signon_realm);
EXPECT_EQ(credentials.action, form_manager.GetPendingCredentials().action);
EXPECT_TRUE(form_manager.GetPendingCredentials().preferred);
EXPECT_EQ(credentials.username_value,
form_manager.GetPendingCredentials().username_value);
// By this point, the PasswordFormManager should have promoted the new
// password value to be the current password.
EXPECT_EQ(credentials.new_password_value,
form_manager.GetPendingCredentials().password_value);
EXPECT_EQ(credentials.new_password_element,
form_manager.GetPendingCredentials().password_element);
EXPECT_TRUE(form_manager.GetPendingCredentials().new_password_value.empty());
EXPECT_TRUE(
form_manager.GetPendingCredentials().new_password_element.empty());
}
TEST_F(PasswordFormManagerTest, TestUpdatePassword) {
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
// User submits credentials for the observed form using a username previously
// stored, but a new password. Note that the observed form may have different
// origin URL (as it does in this case) than the saved_match, but we want to
// make sure the updated password is reflected in saved_match, because that is
// what we autofilled.
base::string16 new_pass = ASCIIToUTF16("test2");
PasswordForm credentials = *observed_form();
credentials.username_value = saved_match()->username_value;
credentials.password_value = new_pass;
credentials.preferred = true;
form_manager()->ProvisionallySave(credentials);
// Successful login. The PasswordManager would instruct PasswordFormManager
// to save, and since this is an update, it should know not to save as a new
// login.
EXPECT_FALSE(form_manager()->IsNewLogin());
// Make sure the credentials that would be submitted on successful login
// are going to match the stored entry in the db. (This verifies correct
// behaviour for bug 1074420).
EXPECT_EQ(form_manager()->GetPendingCredentials().origin.spec(),
saved_match()->origin.spec());
EXPECT_EQ(form_manager()->GetPendingCredentials().signon_realm,
saved_match()->signon_realm);
EXPECT_TRUE(form_manager()->GetPendingCredentials().preferred);
EXPECT_EQ(new_pass, form_manager()->GetPendingCredentials().password_value);
}
TEST_F(PasswordFormManagerTest, TestUpdatePasswordFromNewPasswordElement) {
// Add a new password field to the test form. The PasswordFormManager should
// save the password from this field, instead of the current password field.
observed_form()->new_password_element = ASCIIToUTF16("NewPasswd");
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
std::make_unique<MockFormSaver>(), &fetcher);
form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
PasswordForm credentials(*observed_form());
credentials.username_value = saved_match()->username_value;
credentials.password_value = saved_match()->password_value;
credentials.new_password_value = ASCIIToUTF16("test2");
credentials.preferred = true;
form_manager.ProvisionallySave(credentials);
// Successful login. The PasswordManager would instruct PasswordFormManager
// to save, and since this is an update, it should know not to save as a new
// login.
EXPECT_FALSE(form_manager.IsNewLogin());
// By now, the PasswordFormManager should have promoted the new password value
// already to be the current password, and should no longer maintain any info
// about the new password.
EXPECT_EQ(credentials.new_password_value,
form_manager.GetPendingCredentials().password_value);
EXPECT_TRUE(
form_manager.GetPendingCredentials().new_password_element.empty());
EXPECT_TRUE(form_manager.GetPendingCredentials().new_password_value.empty());
// Trigger saving to exercise some special case handling for updating.
PasswordForm new_credentials;
EXPECT_CALL(MockFormSaver::Get(&form_manager), Update(_, _, _, nullptr))
.WillOnce(testing::SaveArg<0>(&new_credentials));
form_manager.Save();
// The password should be updated.
EXPECT_EQ(credentials.new_password_value, new_credentials.password_value);
EXPECT_EQ(saved_match()->username_element, new_credentials.username_element);
EXPECT_EQ(saved_match()->password_element, new_credentials.password_element);
EXPECT_EQ(saved_match()->submit_element, new_credentials.submit_element);
}
// Test that saved results are not ignored if they differ in paths for action or
// origin.
TEST_F(PasswordFormManagerTest, TestIgnoreResult_Paths) {
PasswordForm observed(*observed_form());
observed.origin = GURL("https://accounts.google.com/a/LoginAuth");
observed.action = GURL("https://accounts.google.com/a/Login");
observed.signon_realm = "https://accounts.google.com";
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), observed,
std::make_unique<MockFormSaver>(), &fetcher);
form_manager.Init(nullptr);
PasswordForm saved_form = observed;
saved_form.origin = GURL("https://accounts.google.com/a/OtherLoginAuth");
saved_form.action = GURL("https://accounts.google.com/a/OtherLogin");
fetcher.SetNonFederated({&saved_form}, 0u);
// Different paths for action / origin are okay.
EXPECT_EQ(1u, form_manager.GetBestMatches().size());
EXPECT_EQ(*form_manager.GetBestMatches().begin()->second, saved_form);
}
// Test that saved empty action URL is updated with the submitted action URL.
TEST_F(PasswordFormManagerTest, TestEmptyAction) {
saved_match()->action = GURL();
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
// User logs in with the autofilled username / password from saved_match.
PasswordForm login = *observed_form();
login.username_value = saved_match()->username_value;
login.password_value = saved_match()->password_value;
form_manager()->ProvisionallySave(login);
EXPECT_FALSE(form_manager()->IsNewLogin());
// Chrome updates the saved PasswordForm entry with the action URL of the
// observed form.
EXPECT_EQ(observed_form()->action,
form_manager()->GetPendingCredentials().action);
}
TEST_F(PasswordFormManagerTest, TestUpdateAction) {
saved_match()->action = GURL("http://accounts.google.com/a/ServiceLogin");
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
// User logs in with the autofilled username / password from saved_match.
observed_form()->action = GURL("http://accounts.google.com/a/Login");
PasswordForm login = *observed_form();
login.username_value = saved_match()->username_value;
login.password_value = saved_match()->password_value;
form_manager()->ProvisionallySave(login);
EXPECT_FALSE(form_manager()->IsNewLogin());
// The observed action URL is different from the previously saved one. Chrome
// should update the store by setting the pending credential's action URL to
// be that of the currently observed form.
EXPECT_EQ(observed_form()->action,
form_manager()->GetPendingCredentials().action);
}
TEST_F(PasswordFormManagerTest, TestDynamicAction) {
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
observed_form()->action = GURL("http://accounts.google.com/a/Login");
PasswordForm login(*observed_form());
// The submitted action URL is different from the one observed on page load.
login.action = GURL("http://www.google.com/new_action");
form_manager()->ProvisionallySave(login);
EXPECT_TRUE(form_manager()->IsNewLogin());
// Check that the provisionally saved action URL is the same as the submitted
// action URL, not the one observed on page load.
EXPECT_EQ(login.action, form_manager()->GetPendingCredentials().action);
}
// Test that if the saved match has other possible usernames stored, and the
// user chooses the main one, then the other possible usernames are dropped on
// update.
TEST_F(PasswordFormManagerTest, TestAlternateUsername_NoChange) {
EXPECT_CALL(*client()->mock_driver(), AllowPasswordGenerationForForm(_));
PasswordForm saved_form = *saved_match();
saved_form.other_possible_usernames.push_back(
ValueElementPair(ASCIIToUTF16("other_possible@gmail.com"),
ASCIIToUTF16("other_username")));
fake_form_fetcher()->SetNonFederated({&saved_form}, 0u);
// The saved match has the right username already.
PasswordForm login(*observed_form());
login.preferred = true;
login.username_value = saved_match()->username_value;
login.password_value = saved_match()->password_value;
form_manager()->ProvisionallySave(login);
EXPECT_FALSE(form_manager()->IsNewLogin());
PasswordForm saved_result;
EXPECT_CALL(MockFormSaver::Get(form_manager()), Update(_, _, _, nullptr))
.WillOnce(SaveArg<0>(&saved_result));
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, false, _, _, true, nullptr));
form_manager()->Save();
// Should be only one password stored, and should not have
// |other_possible_usernames| set anymore.
EXPECT_EQ(saved_match()->username_value, saved_result.username_value);
EXPECT_TRUE(saved_result.other_possible_usernames.empty());
}
TEST_F(PasswordFormManagerTest, TestSendNotBlacklistedMessage_NoCredentials) {
// First time sign-up attempt. Password store does not contain matching
// credentials. AllowPasswordGenerationForForm should be called to send the
// "not blacklisted" message.
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
}
TEST_F(PasswordFormManagerTest, TestSendNotBlacklistedMessage_Credentials) {
// Signing up on a previously visited site. Credentials are found in the
// password store, and are not blacklisted. AllowPasswordGenerationForForm
// should be called to send the "not blacklisted" message.
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
PasswordForm simulated_result = CreateSavedMatch(false);
fake_form_fetcher()->SetNonFederated({&simulated_result}, 0u);
}
TEST_F(PasswordFormManagerTest,
TestSendNotBlacklistedMessage_DroppedCredentials) {
// There are cases, such as when a form is made explicitly for creating a new
// password, where we may ignore saved credentials. Make sure that we still
// allow generation in that case.
PasswordForm signup_form(*observed_form());
signup_form.new_password_element = base::ASCIIToUTF16("new_password_field");
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), signup_form,
std::make_unique<MockFormSaver>(), &fetcher);
form_manager.Init(nullptr);
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
PasswordForm simulated_result = CreateSavedMatch(false);
fetcher.SetNonFederated({&simulated_result}, 0u);
}
// Test that exactly one match for each username is chosen as a best match, even
// though it is a PSL match.
TEST_F(PasswordFormManagerTest, TestBestCredentialsForEachUsernameAreIncluded) {
// Add a best scoring match. It should be in |best_matches| and chosen as a
// prefferred match.
PasswordForm best_scoring = *saved_match();
// Add a match saved on another form, it has lower score. It should not be in
// |best_matches|.
PasswordForm other_form = *saved_match();
other_form.password_element = ASCIIToUTF16("signup_password");
other_form.username_element = ASCIIToUTF16("signup_username");
// Add a match saved on another form with a different username. It should be
// in |best_matches|.
PasswordForm other_username = other_form;
const base::string16 kUsername1 =
other_username.username_value + ASCIIToUTF16("1");
other_username.username_value = kUsername1;
// Add a PSL match, it should not be in |best_matches|.
PasswordForm psl_match = *psl_saved_match();
// Add a PSL match with a different username. It should be in |best_matches|.
PasswordForm psl_match_other = psl_match;
const base::string16 kUsername2 =
psl_match_other.username_value + ASCIIToUTF16("2");
psl_match_other.username_value = kUsername2;
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
fake_form_fetcher()->SetNonFederated(
{&best_scoring, &other_form, &other_username, &psl_match,
&psl_match_other},
0u);
const std::map<base::string16, const PasswordForm*>& best_matches =
form_manager()->GetBestMatches();
EXPECT_EQ(3u, best_matches.size());
EXPECT_NE(best_matches.end(),
best_matches.find(saved_match()->username_value));
EXPECT_EQ(*saved_match(),
*best_matches.find(saved_match()->username_value)->second);
EXPECT_NE(best_matches.end(), best_matches.find(kUsername1));
EXPECT_NE(best_matches.end(), best_matches.find(kUsername2));
EXPECT_EQ(*saved_match(), *form_manager()->GetPreferredMatch());
EXPECT_EQ(2u, fill_data.additional_logins.size());
}
TEST_F(PasswordFormManagerTest, TestSanitizePossibleUsernames) {
const ValueElementPair kUsernameOther(ASCIIToUTF16("other username"),
ASCIIToUTF16("other_username_id"));
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm credentials(*observed_form());
credentials.other_possible_usernames.push_back(
ValueElementPair(ASCIIToUTF16("543-43-1234"), ASCIIToUTF16("id1")));
credentials.other_possible_usernames.push_back(
ValueElementPair(ASCIIToUTF16("378282246310005"), ASCIIToUTF16("id2")));
credentials.other_possible_usernames.push_back(kUsernameOther);
credentials.username_value = ASCIIToUTF16("test@gmail.com");
credentials.preferred = true;
form_manager()->ProvisionallySave(credentials);
PasswordForm saved_result;
EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _))
.WillOnce(SaveArg<0>(&saved_result));
form_manager()->Save();
// Possible credit card number and SSN are stripped.
EXPECT_THAT(saved_result.other_possible_usernames,
UnorderedElementsAre(kUsernameOther));
}
TEST_F(PasswordFormManagerTest, TestSanitizePossibleUsernamesDuplicates) {
const ValueElementPair kUsernameSsn(ASCIIToUTF16("511-32-9830"),
ASCIIToUTF16("ssn_id"));
const ValueElementPair kUsernameEmail(ASCIIToUTF16("test@gmail.com"),
ASCIIToUTF16("email_id"));
const ValueElementPair kUsernameDuplicate(ASCIIToUTF16("duplicate"),
ASCIIToUTF16("duplicate_id"));
const ValueElementPair kUsernameRandom(ASCIIToUTF16("random"),
ASCIIToUTF16("random_id"));
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm credentials(*observed_form());
credentials.other_possible_usernames.push_back(kUsernameSsn);
credentials.other_possible_usernames.push_back(kUsernameDuplicate);
credentials.other_possible_usernames.push_back(kUsernameDuplicate);
credentials.other_possible_usernames.push_back(kUsernameRandom);
credentials.other_possible_usernames.push_back(kUsernameEmail);
credentials.username_value = kUsernameEmail.first;
credentials.preferred = true;
form_manager()->ProvisionallySave(credentials);
PasswordForm saved_result;
EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _))
.WillOnce(SaveArg<0>(&saved_result));
form_manager()->Save();
// SSN, duplicate in |other_possible_usernames| and duplicate of
// |username_value| all removed.
EXPECT_THAT(saved_result.other_possible_usernames,
UnorderedElementsAre(kUsernameDuplicate, kUsernameRandom));
}
TEST_F(PasswordFormManagerTest, TestAllPossiblePasswords) {
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
ValueElementPair pair1 = {ASCIIToUTF16("pass1"), ASCIIToUTF16("el1")};
ValueElementPair pair2 = {ASCIIToUTF16("pass2"), ASCIIToUTF16("el2")};
ValueElementPair pair3 = {ASCIIToUTF16("pass3"), ASCIIToUTF16("el3")};
PasswordForm credentials(*observed_form());
credentials.all_possible_passwords.push_back(pair1);
credentials.all_possible_passwords.push_back(pair2);
credentials.all_possible_passwords.push_back(pair3);
form_manager()->ProvisionallySave(credentials);
EXPECT_THAT(form_manager()->GetPendingCredentials().all_possible_passwords,
UnorderedElementsAre(pair1, pair2, pair3));
}
// Test that public-suffix-matched credentials score lower than same-origin
// ones.
TEST_F(PasswordFormManagerTest, TestScoringPublicSuffixMatch) {
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
PasswordForm base_match = CreateSavedMatch(false);
base_match.origin = GURL("http://accounts.google.com/a/ServiceLoginAuth");
base_match.action = GURL("http://accounts.google.com/a/ServiceLogin");
PasswordForm psl_match = base_match;
psl_match.is_public_suffix_match = true;
// Change origin and action URLs to decrease the score.
PasswordForm same_origin_match = base_match;
psl_match.origin = GURL("http://accounts.google.com/a/ServiceLoginAuth2");
psl_match.action = GURL("http://accounts.google.com/a/ServiceLogin2");
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
fake_form_fetcher()->SetNonFederated({&psl_match, &same_origin_match}, 0u);
EXPECT_TRUE(fill_data.additional_logins.empty());
EXPECT_EQ(1u, form_manager()->GetBestMatches().size());
EXPECT_FALSE(
form_manager()->GetBestMatches().begin()->second->is_public_suffix_match);
}
TEST_F(PasswordFormManagerTest, AndroidCredentialsAreAutofilled) {
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
// Although Android-based credentials are treated similarly to PSL-matched
// credentials in some respects, they should be autofilled as opposed to be
// filled on username-select.
PasswordForm android_login;
android_login.signon_realm = "android://hash@com.google.android";
android_login.origin = GURL("android://hash@com.google.android/");
android_login.is_affiliation_based_match = true;
android_login.username_value = saved_match()->username_value;
android_login.password_value = saved_match()->password_value;
android_login.preferred = false;
android_login.times_used = 42;
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
fake_form_fetcher()->SetNonFederated({&android_login}, 0u);
EXPECT_TRUE(fill_data.additional_logins.empty());
EXPECT_FALSE(fill_data.wait_for_username);
EXPECT_EQ(1u, form_manager()->GetBestMatches().size());
// When the user submits the filled form, no copy of the credential should be
// created, instead the usage counter of the original credential should be
// incremented in-place, as if it were a regular credential for that website.
PasswordForm credential(*observed_form());
credential.username_value = android_login.username_value;
credential.password_value = android_login.password_value;
credential.preferred = true;
form_manager()->ProvisionallySave(credential);
EXPECT_FALSE(form_manager()->IsNewLogin());
PasswordForm updated_credential;
EXPECT_CALL(MockFormSaver::Get(form_manager()), Update(_, _, _, nullptr))
.WillOnce(testing::SaveArg<0>(&updated_credential));
form_manager()->Save();
EXPECT_EQ(android_login.username_value, updated_credential.username_value);
EXPECT_EQ(android_login.password_value, updated_credential.password_value);
EXPECT_EQ(android_login.times_used + 1, updated_credential.times_used);
EXPECT_TRUE(updated_credential.preferred);
EXPECT_EQ(GURL(), updated_credential.action);
EXPECT_EQ(base::string16(), updated_credential.username_element);
EXPECT_EQ(base::string16(), updated_credential.password_element);
EXPECT_EQ(base::string16(), updated_credential.submit_element);
}
// Credentials saved through Android apps should always be shown in the drop-
// down menu, unless there is a better-scoring match with the same username.
TEST_F(PasswordFormManagerTest, AndroidCredentialsAreProtected) {
const char kTestUsername1[] = "test-user@gmail.com";
const char kTestUsername2[] = "test-other-user@gmail.com";
const char kTestWebPassword[] = "web-password";
const char kTestAndroidPassword1[] = "android-password-alpha";
const char kTestAndroidPassword2[] = "android-password-beta";
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
// Suppose there is one login saved through the website, and two other coming
// from Android: the first has the same username as the web-based credential,
// so it should be suppressed, but the second has a different username, so it
// should be shown.
PasswordForm website_login = CreateSavedMatch(false);
website_login.username_value = ASCIIToUTF16(kTestUsername1);
website_login.password_value = ASCIIToUTF16(kTestWebPassword);
PasswordForm android_same;
android_same.signon_realm = "android://hash@com.google.android";
android_same.origin = GURL("android://hash@com.google.android/");
android_same.username_value = ASCIIToUTF16(kTestUsername1);
android_same.password_value = ASCIIToUTF16(kTestAndroidPassword1);
PasswordForm android_other = android_same;
android_other.username_value = ASCIIToUTF16(kTestUsername2);
android_other.password_value = ASCIIToUTF16(kTestAndroidPassword2);
std::vector<std::unique_ptr<PasswordForm>> expected_matches;
expected_matches.push_back(std::make_unique<PasswordForm>(website_login));
expected_matches.push_back(std::make_unique<PasswordForm>(android_other));
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
fake_form_fetcher()->SetNonFederated(
{&website_login, &android_same, &android_other}, 0u);
EXPECT_FALSE(fill_data.wait_for_username);
EXPECT_EQ(1u, fill_data.additional_logins.size());
std::vector<std::unique_ptr<PasswordForm>> actual_matches;
for (const auto& username_match_pair : form_manager()->GetBestMatches())
actual_matches.push_back(
std::make_unique<PasswordForm>(*username_match_pair.second));
EXPECT_THAT(actual_matches,
UnorderedPasswordFormElementsAre(&expected_matches));
}
TEST_F(PasswordFormManagerTest, InvalidActionURLsDoNotMatch) {
PasswordForm invalid_action_form(*observed_form());
invalid_action_form.action = GURL("http://");
ASSERT_FALSE(invalid_action_form.action.is_valid());
ASSERT_FALSE(invalid_action_form.action.is_empty());
// Non-empty invalid action URLs should not match other actions.
// First when the compared form has an invalid URL:
EXPECT_EQ(0, form_manager()->DoesManage(invalid_action_form, nullptr) &
PasswordFormManager::RESULT_ACTION_MATCH);
// Then when the observed form has an invalid URL:
PasswordForm valid_action_form(*observed_form());
PasswordFormManager invalid_manager(
password_manager(), client(), client()->driver(), invalid_action_form,
std::make_unique<MockFormSaver>(), fake_form_fetcher());
invalid_manager.Init(nullptr);
EXPECT_EQ(0, invalid_manager.DoesManage(valid_action_form, nullptr) &
PasswordFormManager::RESULT_ACTION_MATCH);
}
TEST_F(PasswordFormManagerTest, EmptyActionURLsDoNotMatchNonEmpty) {
PasswordForm empty_action_form(*observed_form());
empty_action_form.action = GURL();
ASSERT_FALSE(empty_action_form.action.is_valid());
ASSERT_TRUE(empty_action_form.action.is_empty());
// First when the compared form has an empty URL:
EXPECT_EQ(0, form_manager()->DoesManage(empty_action_form, nullptr) &
PasswordFormManager::RESULT_ACTION_MATCH);
// Then when the observed form has an empty URL:
PasswordForm valid_action_form(*observed_form());
PasswordFormManager empty_action_manager(
password_manager(), client(), client()->driver(), empty_action_form,
std::make_unique<MockFormSaver>(), fake_form_fetcher());
empty_action_manager.Init(nullptr);
EXPECT_EQ(0, empty_action_manager.DoesManage(valid_action_form, nullptr) &
PasswordFormManager::RESULT_ACTION_MATCH);
}
TEST_F(PasswordFormManagerTest, NonHTMLFormsDoNotMatchHTMLForms) {
ASSERT_EQ(PasswordForm::SCHEME_HTML, observed_form()->scheme);
PasswordForm non_html_form(*observed_form());
non_html_form.scheme = PasswordForm::SCHEME_DIGEST;
EXPECT_EQ(PasswordFormManager::RESULT_NO_MATCH,
form_manager()->DoesManage(non_html_form, nullptr));
// The other way round: observing a non-HTML form, don't match a HTML form.
PasswordForm html_form(*observed_form());
PasswordFormManager non_html_manager(
password_manager(), client(), kNoDriver, non_html_form,
std::make_unique<MockFormSaver>(), fake_form_fetcher());
non_html_manager.Init(nullptr);
EXPECT_EQ(PasswordFormManager::RESULT_NO_MATCH,
non_html_manager.DoesManage(html_form, nullptr));
}
TEST_F(PasswordFormManagerTest, OriginCheck_HostsMatchExactly) {
// Host part of origins must match exactly, not just by prefix.
PasswordForm form_longer_host(*observed_form());
form_longer_host.origin = GURL("http://accounts.google.com.au/a/LoginAuth");
// Check that accounts.google.com does not match accounts.google.com.au.
EXPECT_EQ(PasswordFormManager::RESULT_NO_MATCH,
form_manager()->DoesManage(form_longer_host, nullptr));
}
TEST_F(PasswordFormManagerTest, OriginCheck_MoreSecureSchemePathsMatchPrefix) {
// If the URL scheme of the observed form is HTTP, and the compared form is
// HTTPS, then the compared form can extend the path.
PasswordForm form_longer_path(*observed_form());
form_longer_path.origin = GURL("https://accounts.google.com/a/LoginAuth/sec");
EXPECT_NE(0, form_manager()->DoesManage(form_longer_path, nullptr) &
PasswordFormManager::RESULT_ORIGINS_OR_FRAMES_MATCH);
}
TEST_F(PasswordFormManagerTest,
OriginCheck_NotMoreSecureSchemePathsMatchExactly) {
// If the origin URL scheme of the compared form is not more secure than that
// of the observed form, then the paths must match exactly.
PasswordForm form_longer_path(*observed_form());
form_longer_path.origin = GURL("http://accounts.google.com/a/LoginAuth/sec");
// Check that /a/LoginAuth does not match /a/LoginAuth/more.
EXPECT_EQ(PasswordFormManager::RESULT_NO_MATCH,
form_manager()->DoesManage(form_longer_path, nullptr));
PasswordForm secure_observed_form(*observed_form());
secure_observed_form.origin = GURL("https://accounts.google.com/a/LoginAuth");
PasswordFormManager secure_manager(
password_manager(), client(), client()->driver(), secure_observed_form,
std::make_unique<MockFormSaver>(), fake_form_fetcher());
secure_manager.Init(nullptr);
// Also for HTTPS in the observed form, and HTTP in the compared form, an
// exact path match is expected.
EXPECT_EQ(PasswordFormManager::RESULT_NO_MATCH,
secure_manager.DoesManage(form_longer_path, nullptr));
// Not even upgrade to HTTPS in the compared form should help.
form_longer_path.origin = GURL("https://accounts.google.com/a/LoginAuth/sec");
EXPECT_EQ(PasswordFormManager::RESULT_NO_MATCH,
secure_manager.DoesManage(form_longer_path, nullptr));
}
TEST_F(PasswordFormManagerTest, OriginCheck_OnlyOriginsMatch) {
// Make sure DoesManage() can distinguish when only origins match.
PasswordForm same_origin_only(*observed_form());
same_origin_only.form_data.name = ASCIIToUTF16("other_name");
same_origin_only.action = GURL("https://somewhere/else");
EXPECT_EQ(PasswordFormManager::RESULT_ORIGINS_OR_FRAMES_MATCH,
form_manager()->DoesManage(same_origin_only, nullptr));
}
TEST_F(PasswordFormManagerTest, FormsMatchIfNamesMatch) {
PasswordForm other_form(*observed_form());
autofill::FormFieldData field;
field.name = ASCIIToUTF16("another-field-name");
other_form.form_data.fields.push_back(field);
other_form.action = GURL("https://somewhere/else");
// Names should match, other things may not.
EXPECT_EQ(PasswordFormManager::RESULT_FORM_NAME_MATCH,
form_manager()->DoesManage(other_form, nullptr) &
PasswordFormManager::RESULT_FORM_NAME_MATCH);
}
TEST_F(PasswordFormManagerTest, FormsMatchIfSignaturesMatch) {
PasswordForm other_form(*observed_form());
other_form.action = GURL("https://somewhere/else");
// Signatures should match, other things may not.
EXPECT_EQ(PasswordFormManager::RESULT_SIGNATURE_MATCH,
form_manager()->DoesManage(other_form, nullptr) &
PasswordFormManager::RESULT_SIGNATURE_MATCH);
}
TEST_F(PasswordFormManagerTest, FormWithEmptyActionAndNameMatchesItself) {
observed_form()->form_data.name.clear();
observed_form()->action = GURL::EmptyGURL();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher());
form_manager.Init(nullptr);
// Any form should match itself regardless of missing properties. Otherwise,
// a PasswordFormManager instance is created for the same form multiple times.
PasswordForm other_form(*observed_form());
EXPECT_EQ(PasswordFormManager::RESULT_COMPLETE_MATCH,
form_manager.DoesManage(other_form, nullptr));
}
// Test that if multiple credentials with the same username are stored, and the
// user updates the password, then all of the stored passwords get updated as
// long as they have the same password value.
TEST_F(PasswordFormManagerTest, CorrectlyUpdatePasswordsWithSameUsername) {
EXPECT_CALL(*client()->mock_driver(), AllowPasswordGenerationForForm(_));
PasswordForm first(*saved_match());
first.action = observed_form()->action;
first.password_value = ASCIIToUTF16("first");
first.preferred = true;
// The second credential has the same password value, but it has a different
// |username_element| to make a different unique key for the database
// (otherwise the two credentials could not be stored at the same time). The
// different unique key results in a slightly lower score than for |first|.
PasswordForm second(first);
second.username_element.clear();
second.preferred = false;
// The third credential has a different password value. It also has a
// different |password_element| to make a different unique key for the
// database again.
PasswordForm third(first);
third.password_element.clear();
third.password_value = ASCIIToUTF16("second");
third.preferred = false;
fake_form_fetcher()->SetNonFederated({&first, &second, &third}, 0u);
// |first| scored slightly higher.
EXPECT_EQ(ASCIIToUTF16("first"),
form_manager()->GetPreferredMatch()->password_value);
PasswordForm login(*observed_form());
login.username_value = saved_match()->username_value;
login.password_value = ASCIIToUTF16("third");
login.preferred = true;
form_manager()->ProvisionallySave(login);
EXPECT_FALSE(form_manager()->IsNewLogin());
PasswordForm saved_result;
std::vector<PasswordForm> credentials_to_update;
EXPECT_CALL(MockFormSaver::Get(form_manager()), Update(_, _, _, nullptr))
.WillOnce(testing::DoAll(SaveArg<0>(&saved_result),
SaveArgPointee<2>(&credentials_to_update)));
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, false, _, _, true, nullptr));
form_manager()->Save();
// What was |first| above should be the main credential updated.
EXPECT_EQ(ASCIIToUTF16("third"), saved_result.password_value);
EXPECT_FALSE(saved_result.password_element.empty());
EXPECT_FALSE(saved_result.username_element.empty());
// What was |second| above should be another credential updated.
ASSERT_EQ(1u, credentials_to_update.size());
EXPECT_EQ(ASCIIToUTF16("third"), credentials_to_update[0].password_value);
EXPECT_FALSE(credentials_to_update[0].password_element.empty());
EXPECT_TRUE(credentials_to_update[0].username_element.empty());
}
TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword) {
// For newly saved passwords, upload a password vote for autofill::PASSWORD.
// Don't vote for the username field yet.
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *saved_match(),
std::make_unique<NiceMock<MockFormSaver>>(), &fetcher);
form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm form_to_save(*saved_match());
form_to_save.preferred = true;
form_to_save.username_value = ASCIIToUTF16("username");
form_to_save.password_value = ASCIIToUTF16("1234");
autofill::ServerFieldTypeSet expected_available_field_types;
expected_available_field_types.insert(autofill::PASSWORD);
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, false, expected_available_field_types, _,
true, nullptr));
form_manager.ProvisionallySave(form_to_save);
form_manager.Save();
}
TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword_Blacklist) {
// Do not upload a vote if the user is blacklisting the form.
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager blacklist_form_manager(
password_manager(), client(), client()->driver(), *saved_match(),
std::make_unique<NiceMock<MockFormSaver>>(), &fetcher);
blacklist_form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
autofill::ServerFieldTypeSet expected_available_field_types;
expected_available_field_types.insert(autofill::USERNAME);
expected_available_field_types.insert(autofill::PASSWORD);
EXPECT_CALL(
*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, _, expected_available_field_types, _, true, _))
.Times(0);
blacklist_form_manager.PermanentlyBlacklist();
}
TEST_F(PasswordFormManagerTest, UploadPasswordForm) {
autofill::FormData observed_form_data;
autofill::FormFieldData field;
field.label = ASCIIToUTF16("Email:");
field.name = ASCIIToUTF16("observed-username-field");
field.form_control_type = "text";
observed_form_data.fields.push_back(field);
field.label = ASCIIToUTF16("Password:");
field.name = ASCIIToUTF16("observed-password-field");
field.form_control_type = "password";
observed_form_data.fields.push_back(field);
// Form data is different than saved form data, account creation signal should
// be sent.
autofill::ServerFieldType field_type = autofill::ACCOUNT_CREATION_PASSWORD;
AccountCreationUploadTest(observed_form_data, 0, PasswordForm::NO_SIGNAL_SENT,
&field_type);
// Non-zero times used will not upload since we only upload a positive signal
// at most once.
AccountCreationUploadTest(observed_form_data, 1, PasswordForm::NO_SIGNAL_SENT,
nullptr);
// Same form data as saved match and POSITIVE_SIGNAL_SENT means there should
// be a negative autofill ping sent.
field_type = autofill::NOT_ACCOUNT_CREATION_PASSWORD;
AccountCreationUploadTest(saved_match()->form_data, 2,
PasswordForm::POSITIVE_SIGNAL_SENT, &field_type);
// For any other GenerationUploadStatus, no autofill upload should occur
// if the observed form data matches the saved form data.
AccountCreationUploadTest(saved_match()->form_data, 3,
PasswordForm::NO_SIGNAL_SENT, nullptr);
AccountCreationUploadTest(saved_match()->form_data, 3,
PasswordForm::NEGATIVE_SIGNAL_SENT, nullptr);
}
TEST_F(PasswordFormManagerTest, CorrectlySavePasswordWithoutUsernameFields) {
EXPECT_CALL(*client()->mock_driver(), AllowPasswordGenerationForForm(_));
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm login(*observed_form());
login.username_element.clear();
login.password_value = ASCIIToUTF16("password");
login.preferred = true;
form_manager()->ProvisionallySave(login);
EXPECT_TRUE(form_manager()->IsNewLogin());
PasswordForm saved_result;
EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _))
.WillOnce(SaveArg<0>(&saved_result));
form_manager()->Save();
// Make sure that the password is updated appropriately.
EXPECT_EQ(ASCIIToUTF16("password"), saved_result.password_value);
}
TEST_F(PasswordFormManagerTest, DriverDeletedBeforeStoreDone) {
// Test graceful handling of the following situation:
// 1. A form appears in a frame, a PFM is created for that form.
// 2. The PFM asks the store for credentials for this form.
// 3. The frame (and associated driver) gets deleted.
// 4. The PFM returns the callback with credentials.
// This test checks implicitly that after step 4 the PFM does not attempt
// use-after-free of the deleted driver.
std::string example_url("http://example.com");
PasswordForm form;
form.origin = GURL(example_url);
form.signon_realm = example_url;
form.action = GURL(example_url);
form.username_element = ASCIIToUTF16("u");
form.password_element = ASCIIToUTF16("p");
form.submit_element = ASCIIToUTF16("s");
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), form,
std::make_unique<MockFormSaver>(), &fetcher);
form_manager.Init(nullptr);
// Suddenly, the frame and its driver disappear.
client()->KillDriver();
fetcher.SetNonFederated({&form}, 0u);
}
TEST_F(PasswordFormManagerTest, PreferredMatchIsUpToDate) {
// Check that GetPreferredMatch() is always a member of GetBestMatches().
PasswordForm form = *observed_form();
form.username_value = ASCIIToUTF16("username");
form.password_value = ASCIIToUTF16("password1");
form.preferred = false;
PasswordForm generated_form = form;
generated_form.type = PasswordForm::TYPE_GENERATED;
generated_form.password_value = ASCIIToUTF16("password2");
generated_form.preferred = true;
fake_form_fetcher()->SetNonFederated({&form, &generated_form}, 0u);
EXPECT_EQ(1u, form_manager()->GetBestMatches().size());
EXPECT_EQ(form_manager()->GetPreferredMatch(),
form_manager()->GetBestMatches().begin()->second);
// Make sure to access all fields of preferred_match; this way if it was
// deleted, ASAN might notice it.
PasswordForm dummy(*form_manager()->GetPreferredMatch());
}
TEST_F(PasswordFormManagerTest, PasswordToSave_NoElements) {
PasswordForm form;
EXPECT_TRUE(PasswordFormManager::PasswordToSave(form).first.empty());
}
TEST_F(PasswordFormManagerTest, PasswordToSave_NoNewElement) {
PasswordForm form;
form.password_element = base::ASCIIToUTF16("pwd");
base::string16 kValue = base::ASCIIToUTF16("val");
form.password_value = kValue;
EXPECT_EQ(kValue, PasswordFormManager::PasswordToSave(form).first);
}
TEST_F(PasswordFormManagerTest, PasswordToSave_NoOldElement) {
PasswordForm form;
form.new_password_element = base::ASCIIToUTF16("new_pwd");
base::string16 kNewValue = base::ASCIIToUTF16("new_val");
form.new_password_value = kNewValue;
EXPECT_EQ(kNewValue, PasswordFormManager::PasswordToSave(form).first);
}
TEST_F(PasswordFormManagerTest, PasswordToSave_BothButNoNewValue) {
PasswordForm form;
form.password_element = base::ASCIIToUTF16("pwd");
form.new_password_element = base::ASCIIToUTF16("new_pwd");
base::string16 kValue = base::ASCIIToUTF16("val");
form.password_value = kValue;
EXPECT_EQ(kValue, PasswordFormManager::PasswordToSave(form).first);
}
TEST_F(PasswordFormManagerTest, PasswordToSave_NewValue) {
PasswordForm form;
form.password_element = base::ASCIIToUTF16("pwd");
form.new_password_element = base::ASCIIToUTF16("new_pwd");
form.password_value = base::ASCIIToUTF16("val");
base::string16 kNewValue = base::ASCIIToUTF16("new_val");
form.new_password_value = kNewValue;
EXPECT_EQ(kNewValue, PasswordFormManager::PasswordToSave(form).first);
}
TEST_F(PasswordFormManagerTest, TestSuggestingPasswordChangeForms) {
// Suggesting password on the password change form on the previously visited
// site. Credentials are found in the password store, and are not blacklisted.
PasswordForm observed_change_password_form = *observed_form();
observed_change_password_form.new_password_element =
base::ASCIIToUTF16("new_pwd");
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager manager_creds(
password_manager(), client(), client()->driver(),
observed_change_password_form, std::make_unique<MockFormSaver>(),
&fetcher);
manager_creds.Init(nullptr);
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
PasswordForm result = CreateSavedMatch(false);
fetcher.SetNonFederated({&result}, 0u);
EXPECT_EQ(1u, manager_creds.GetBestMatches().size());
EXPECT_EQ(0u, fill_data.additional_logins.size());
EXPECT_TRUE(fill_data.wait_for_username);
}
TEST_F(PasswordFormManagerTest, TestUpdateMethod) {
// Add a new password field to the test form. The PasswordFormManager should
// save the password from this field, instead of the current password field.
observed_form()->new_password_element = ASCIIToUTF16("NewPasswd");
autofill::FormFieldData field;
field.label = ASCIIToUTF16("NewPasswd");
field.name = ASCIIToUTF16("NewPasswd");
field.form_control_type = "password";
observed_form()->form_data.fields.push_back(field);
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
std::make_unique<MockFormSaver>(), &fetcher);
form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
PasswordForm credentials(*observed_form());
credentials.username_element.clear();
credentials.password_value = saved_match()->password_value;
credentials.new_password_value = ASCIIToUTF16("test2");
credentials.preferred = true;
form_manager.ProvisionallySave(credentials);
// Successful login. The PasswordManager would instruct PasswordFormManager
// to save, and since this is an update, it should know not to save as a new
// login.
EXPECT_FALSE(form_manager.IsNewLogin());
EXPECT_FALSE(form_manager.IsPossibleChangePasswordFormWithoutUsername());
// By now, the PasswordFormManager should have promoted the new password value
// already to be the current password, and should no longer maintain any info
// about the new password value.
EXPECT_EQ(credentials.new_password_value,
form_manager.GetPendingCredentials().password_value);
EXPECT_TRUE(form_manager.GetPendingCredentials().new_password_value.empty());
// Trigger saving to exercise some special case handling during updating.
PasswordForm new_credentials;
EXPECT_CALL(MockFormSaver::Get(&form_manager), Update(_, _, _, nullptr))
.WillOnce(SaveArg<0>(&new_credentials));
form_manager.Update(*saved_match());
// The password is updated.
EXPECT_EQ(credentials.new_password_value, new_credentials.password_value);
EXPECT_EQ(saved_match()->username_element, new_credentials.username_element);
EXPECT_EQ(saved_match()->password_element, new_credentials.password_element);
EXPECT_EQ(saved_match()->submit_element, new_credentials.submit_element);
}
TEST_F(PasswordFormManagerTest, TestUpdateNoUsernameTextfieldPresent) {
// Add a new password field to the test form and insert a |username_value|
// unlikely to be a real username. The PasswordFormManager should still save
// the password from this field, instead of the current password field.
observed_form()->new_password_element = ASCIIToUTF16("NewPasswd");
autofill::FormFieldData field;
field.label = ASCIIToUTF16("NewPasswd");
field.name = ASCIIToUTF16("NewPasswd");
field.form_control_type = "password";
observed_form()->form_data.fields.push_back(field);
FakeFormFetcher fetcher;
fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
std::make_unique<MockFormSaver>(), &fetcher);
form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
PasswordForm credentials(*observed_form());
// The |username_value| contains a text that's unlikely to be real username.
credentials.username_value = ASCIIToUTF16("3");
credentials.password_value = saved_match()->password_value;
credentials.new_password_value = ASCIIToUTF16("test2");
credentials.preferred = true;
form_manager.ProvisionallySave(credentials);
// Successful login. The PasswordManager would instruct PasswordFormManager
// to save, and since this is an update, it should know not to save as a new
// login.
EXPECT_FALSE(form_manager.IsNewLogin());
EXPECT_TRUE(form_manager.IsPossibleChangePasswordFormWithoutUsername());
// By now, the PasswordFormManager should have promoted the new password value
// already to be the current password, and should no longer maintain any info
// about the new password value.
EXPECT_EQ(saved_match()->username_value,
form_manager.GetPendingCredentials().username_value);
EXPECT_EQ(credentials.new_password_value,
form_manager.GetPendingCredentials().password_value);
EXPECT_TRUE(form_manager.GetPendingCredentials().new_password_value.empty());
// Trigger saving to exercise some special case handling during updating.
PasswordForm new_credentials;
EXPECT_CALL(MockFormSaver::Get(&form_manager), Update(_, _, _, nullptr))
.WillOnce(SaveArg<0>(&new_credentials));
form_manager.Update(form_manager.GetPendingCredentials());
// The password should be updated, but the username should not.
EXPECT_EQ(saved_match()->username_value, new_credentials.username_value);
EXPECT_EQ(credentials.new_password_value, new_credentials.password_value);
EXPECT_EQ(saved_match()->username_element, new_credentials.username_element);
EXPECT_EQ(saved_match()->password_element, new_credentials.password_element);
EXPECT_TRUE(new_credentials.new_password_value.empty());
EXPECT_TRUE(new_credentials.new_password_element.empty());
EXPECT_EQ(saved_match()->submit_element, new_credentials.submit_element);
}
// Test the case when a user changes the username to the value of another field
// from the form.
TEST_F(PasswordFormManagerTest, UpdateUsername_ValueOfAnotherField) {
for (bool captured_username_is_empty : {false, true}) {
SCOPED_TRACE(testing::Message() << "captured_username_is_empty="
<< captured_username_is_empty);
PasswordForm observed(*observed_form());
// Set |FormData| to upload a username vote.
autofill::FormFieldData field;
field.name = ASCIIToUTF16("full_name");
field.form_control_type = "text";
observed.form_data.fields.push_back(field);
field.name = ASCIIToUTF16("correct_username_element");
field.form_control_type = "text";
observed.form_data.fields.push_back(field);
field.name = ASCIIToUTF16("Passwd");
field.form_control_type = "password";
observed.form_data.fields.push_back(field);
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), observed,
std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher());
form_manager.Init(nullptr);
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(),
0u);
// User enters credential in the form.
PasswordForm credential(observed);
credential.username_value = captured_username_is_empty
? base::string16()
: ASCIIToUTF16("typed_username");
credential.password_value = ASCIIToUTF16("password");
credential.other_possible_usernames.push_back(
ValueElementPair(ASCIIToUTF16("edited_username"),
ASCIIToUTF16("correct_username_element")));
form_manager.ProvisionallySave(credential);
// User edits username in a prompt.
form_manager.UpdateUsername(ASCIIToUTF16("edited_username"));
EXPECT_EQ(form_manager.GetPendingCredentials().username_value,
ASCIIToUTF16("edited_username"));
EXPECT_EQ(form_manager.GetPendingCredentials().username_element,
ASCIIToUTF16("correct_username_element"));
EXPECT_EQ(form_manager.GetPendingCredentials().password_value,
ASCIIToUTF16("password"));
EXPECT_TRUE(form_manager.IsNewLogin());
// User clicks save, the edited username is saved.
PasswordForm saved_result;
EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty()))
.WillOnce(SaveArg<0>(&saved_result));
// Expect a username edited vote.
FieldTypeMap expected_types;
expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE;
expected_types[ASCIIToUTF16("correct_username_element")] =
autofill::USERNAME;
expected_types[ASCIIToUTF16("Passwd")] = autofill::PASSWORD;
VoteTypeMap expected_vote_types = {
{ASCIIToUTF16("correct_username_element"),
autofill::AutofillUploadContents::Field::USERNAME_EDITED}};
EXPECT_CALL(
*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(
AllOf(SignatureIsSameAs(observed),
UploadedAutofillTypesAre(expected_types),
HasGenerationVote(false), VoteTypesAre(expected_vote_types)),
_, Contains(autofill::USERNAME), _, _, nullptr));
form_manager.Save();
// Check what is saved.
EXPECT_EQ(ASCIIToUTF16("edited_username"), saved_result.username_value);
EXPECT_EQ(ASCIIToUTF16("correct_username_element"),
saved_result.username_element);
EXPECT_EQ(ASCIIToUTF16("password"), saved_result.password_value);
if (captured_username_is_empty) {
EXPECT_TRUE(saved_result.other_possible_usernames.empty());
} else {
EXPECT_THAT(
saved_result.other_possible_usernames,
ElementsAre(ValueElementPair(ASCIIToUTF16("typed_username"),
observed_form()->username_element)));
}
}
}
// Test the case when a user updates the username to an already existing one.
TEST_F(PasswordFormManagerTest, UpdateUsername_ValueSavedInStore) {
for (bool captured_username_is_empty : {false, true}) {
SCOPED_TRACE(testing::Message() << "captured_username_is_empty="
<< captured_username_is_empty);
// We have an already existing credential.
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
// User enters credential in the form.
PasswordForm credential(*observed_form());
credential.username_value = captured_username_is_empty
? base::string16()
: ASCIIToUTF16("different_username");
credential.password_value = ASCIIToUTF16("different_pass");
credential.preferred = true;
form_manager()->ProvisionallySave(credential);
// User edits username in a prompt to one already existing.
form_manager()->UpdateUsername(saved_match()->username_value);
// The username in credentials is expected to be updated.
EXPECT_EQ(saved_match()->username_value,
form_manager()->GetPendingCredentials().username_value);
EXPECT_EQ(saved_match()->username_element,
form_manager()->GetPendingCredentials().username_element);
EXPECT_EQ(ASCIIToUTF16("different_pass"),
form_manager()->GetPendingCredentials().password_value);
EXPECT_FALSE(form_manager()->IsNewLogin());
// Create the expected credential to be saved.
PasswordForm expected_pending(credential);
expected_pending.origin = saved_match()->origin;
expected_pending.form_data = saved_match()->form_data;
expected_pending.times_used = 1;
expected_pending.username_value = saved_match()->username_value;
// User clicks save, edited username is saved, password updated.
EXPECT_CALL(MockFormSaver::Get(form_manager()),
Update(expected_pending,
ElementsAre(Pair(saved_match()->username_value,
Pointee(*saved_match()))),
Pointee(IsEmpty()), nullptr));
// Expect a username reusing vote.
FieldTypeMap expected_types;
expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE;
expected_types[expected_pending.username_element] = autofill::USERNAME;
expected_types[expected_pending.password_element] =
autofill::ACCOUNT_CREATION_PASSWORD;
VoteTypeMap expected_vote_types = {
{expected_pending.username_element,
autofill::AutofillUploadContents::Field::CREDENTIALS_REUSED}};
EXPECT_CALL(
*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(
AllOf(SignatureIsSameAs(expected_pending),
UploadedAutofillTypesAre(expected_types),
HasGenerationVote(false), VoteTypesAre(expected_vote_types)),
_, Contains(autofill::USERNAME), _, _, nullptr));
form_manager()->Save();
}
}
// Tests the case when the username value edited in prompt doesn't coincides
// neither with other values on the form nor with saved usernames.
TEST_F(PasswordFormManagerTest, UpdateUsername_NoMatchNeitherOnFormNorInStore) {
for (bool captured_username_is_empty : {false, true}) {
SCOPED_TRACE(testing::Message() << "captured_username_is_empty="
<< captured_username_is_empty);
PasswordForm observed(*observed_form());
// Assign any |FormData| to allow crowdsourcing of this form.
observed.form_data = saved_match()->form_data;
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), observed,
std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher());
form_manager.Init(nullptr);
// We have an already existing credential.
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
// A user enters a credential in the form.
PasswordForm credential(observed);
credential.username_value = captured_username_is_empty
? base::string16()
: ASCIIToUTF16("captured_username");
credential.password_value = ASCIIToUTF16("different_pass");
credential.preferred = true;
form_manager.ProvisionallySave(credential);
// User edits username. The username doesn't exist neither in the store nor
// on the form.
form_manager.UpdateUsername(ASCIIToUTF16("new_username"));
// As there is no match on the form, |username_element| is empty.
EXPECT_TRUE(form_manager.GetPendingCredentials().username_element.empty());
EXPECT_EQ(ASCIIToUTF16("new_username"),
form_manager.GetPendingCredentials().username_value);
EXPECT_EQ(ASCIIToUTF16("different_pass"),
form_manager.GetPendingCredentials().password_value);
EXPECT_TRUE(form_manager.IsNewLogin());
PasswordForm expected_pending(credential);
expected_pending.username_value = ASCIIToUTF16("new_username");
expected_pending.username_element = base::string16();
if (!captured_username_is_empty) {
// A non-empty captured username value should be saved to recover later if
// a user makes a mistake in username editing.
expected_pending.other_possible_usernames.push_back(ValueElementPair(
ASCIIToUTF16("captured_username"), ASCIIToUTF16("Email")));
}
// User clicks save, the edited username is saved, the password is updated,
// no username vote is uploaded.
PasswordForm actual_saved_form;
EXPECT_CALL(MockFormSaver::Get(&form_manager),
Save(_, ElementsAre(Pair(saved_match()->username_value,
Pointee(*saved_match())))))
.WillOnce(SaveArg<0>(&actual_saved_form));
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, _, Not(Contains(autofill::USERNAME)), _,
_, nullptr));
form_manager.Save();
// Can't verify |date_created |, so ignore it in form comparison.
actual_saved_form.date_created = expected_pending.date_created;
EXPECT_EQ(expected_pending, actual_saved_form);
}
}
// Tests the case when a user clears the username value in a prompt.
TEST_F(PasswordFormManagerTest, UpdateUsername_UserRemovedUsername) {
PasswordForm observed(*observed_form());
// Assign any |FormData| to allow crowdsourcing of this form.
observed.form_data = saved_match()->form_data;
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), observed,
std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher());
form_manager.Init(nullptr);
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
// The user enters credential in the form.
PasswordForm credential(observed);
credential.username_value = ASCIIToUTF16("pin_code");
credential.password_value = ASCIIToUTF16("password");
credential.other_possible_usernames.push_back(
ValueElementPair(base::string16(), ASCIIToUTF16("empty_field")));
form_manager.ProvisionallySave(credential);
// The user clears the username value in the prompt.
form_manager.UpdateUsername(base::string16());
EXPECT_TRUE(form_manager.GetPendingCredentials().username_value.empty());
EXPECT_TRUE(form_manager.GetPendingCredentials().username_element.empty());
EXPECT_EQ(form_manager.GetPendingCredentials().password_value,
ASCIIToUTF16("password"));
EXPECT_TRUE(form_manager.IsNewLogin());
// The user clicks save, empty username is saved.
PasswordForm saved_result;
EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty()))
.WillOnce(SaveArg<0>(&saved_result));
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, _, Not(Contains(autofill::USERNAME)), _, _,
nullptr));
form_manager.Save();