| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/password_manager/core/browser/password_form_manager.h" |
| |
| #include <memory> |
| |
| #include <string> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/task_environment.h" |
| #include "build/build_config.h" |
| #include "components/autofill/core/browser/autofill_download_manager.h" |
| #include "components/autofill/core/browser/field_types.h" |
| #include "components/autofill/core/browser/form_structure.h" |
| #include "components/autofill/core/common/form_data.h" |
| #include "components/autofill/core/common/form_field_data.h" |
| #include "components/autofill/core/common/gaia_id_hash.h" |
| #include "components/autofill/core/common/password_form_fill_data.h" |
| #include "components/autofill/core/common/password_form_generation_data.h" |
| #include "components/autofill/core/common/password_generation_util.h" |
| #include "components/autofill/core/common/renderer_id.h" |
| #include "components/autofill/core/common/signatures.h" |
| #include "components/password_manager/core/browser/fake_form_fetcher.h" |
| #include "components/password_manager/core/browser/field_info_manager.h" |
| #include "components/password_manager/core/browser/multi_store_password_save_manager.h" |
| #include "components/password_manager/core/browser/password_form.h" |
| #include "components/password_manager/core/browser/password_form_manager_for_ui.h" |
| #include "components/password_manager/core/browser/password_manager_metrics_util.h" |
| #include "components/password_manager/core/browser/password_manager_util.h" |
| #include "components/password_manager/core/browser/password_save_manager_impl.h" |
| #include "components/password_manager/core/browser/password_store.h" |
| #include "components/password_manager/core/browser/possible_username_data.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.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/signin/public/identity_manager/identity_test_environment.h" |
| #include "components/signin/public/identity_manager/identity_test_utils.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using autofill::AutofillUploadContents; |
| using autofill::FieldPropertiesFlags; |
| using autofill::FieldRendererId; |
| using autofill::FormData; |
| using autofill::FormFieldData; |
| using autofill::FormRendererId; |
| using autofill::FormSignature; |
| using autofill::FormStructure; |
| using autofill::GaiaIdHash; |
| using autofill::NOT_USERNAME; |
| using autofill::PasswordFormFillData; |
| using autofill::PasswordFormGenerationData; |
| using autofill::ServerFieldType; |
| using autofill::SINGLE_USERNAME; |
| using autofill::UNKNOWN_TYPE; |
| using autofill::password_generation::PasswordGenerationType; |
| using base::ASCIIToUTF16; |
| using testing::_; |
| using testing::AllOf; |
| using testing::Contains; |
| using testing::DoAll; |
| using testing::ElementsAre; |
| using testing::IsEmpty; |
| using testing::Mock; |
| using testing::NiceMock; |
| using testing::Pointee; |
| using testing::Return; |
| using testing::ReturnRef; |
| using testing::SaveArg; |
| using testing::SaveArgPointee; |
| using testing::UnorderedElementsAre; |
| |
| namespace password_manager { |
| |
| namespace { |
| |
| // Indices of username and password fields in the observed form. |
| const int kUsernameFieldIndex = 1; |
| const int kPasswordFieldIndex = 2; |
| |
| MATCHER_P(FormHasUniqueKey, key, "") { |
| return ArePasswordFormUniqueKeysEqual(arg, key); |
| } |
| |
| MATCHER_P(FormHasUsernameValue, username_value, "") { |
| return arg.username_value == username_value; |
| } |
| |
| MATCHER_P(FormHasPassword, password_value, "") { |
| if (arg.new_password_value.empty()) |
| return arg.password_value == password_value; |
| return arg.new_password_value == password_value; |
| } |
| |
| MATCHER_P(FormDataPointeeEqualTo, form_data, "") { |
| return autofill::FormDataEqualForTesting(*arg, form_data); |
| } |
| |
| class MockPasswordManagerDriver : public StubPasswordManagerDriver { |
| public: |
| MOCK_METHOD1(FillPasswordForm, void(const PasswordFormFillData&)); |
| MOCK_METHOD1(AllowPasswordGenerationForForm, void(const PasswordForm&)); |
| MOCK_METHOD1(FormEligibleForGenerationFound, |
| void(const autofill::PasswordFormGenerationData&)); |
| }; |
| |
| class MockAutofillDownloadManager : public autofill::AutofillDownloadManager { |
| public: |
| MockAutofillDownloadManager() |
| : AutofillDownloadManager(nullptr, &fake_observer) {} |
| MockAutofillDownloadManager(const MockAutofillDownloadManager&) = delete; |
| MockAutofillDownloadManager& operator=(const MockAutofillDownloadManager&) = |
| delete; |
| |
| MOCK_METHOD6(StartUploadRequest, |
| bool(const FormStructure&, |
| bool, |
| const autofill::ServerFieldTypeSet&, |
| const std::string&, |
| bool, |
| PrefService*)); |
| |
| private: |
| class StubObserver : public AutofillDownloadManager::Observer { |
| void OnLoadedServerPredictions( |
| std::string response, |
| const std::vector<autofill::FormSignature>& form_signatures) override {} |
| }; |
| |
| StubObserver fake_observer; |
| }; |
| |
| class MockPasswordManagerClient : public StubPasswordManagerClient { |
| public: |
| MOCK_METHOD(bool, IsIncognito, (), (const, override)); |
| MOCK_METHOD(autofill::AutofillDownloadManager*, |
| GetAutofillDownloadManager, |
| (), |
| (override)); |
| MOCK_METHOD(void, UpdateFormManagers, (), (override)); |
| MOCK_METHOD(void, |
| AutofillHttpAuth, |
| (const PasswordForm&, const PasswordFormManagerForUI*), |
| (override)); |
| MOCK_METHOD(SyncState, GetPasswordSyncState, (), (const, override)); |
| MOCK_METHOD(bool, IsCommittedMainFrameSecure, (), (const, override)); |
| MOCK_METHOD(FieldInfoManager*, GetFieldInfoManager, (), (const, override)); |
| MOCK_METHOD(signin::IdentityManager*, GetIdentityManager, (), (override)); |
| MOCK_METHOD(PrefService*, GetPrefs, (), (const, override)); |
| }; |
| |
| void CheckPendingCredentials(const PasswordForm& expected, |
| const PasswordForm& actual) { |
| EXPECT_EQ(expected.signon_realm, actual.signon_realm); |
| EXPECT_EQ(expected.url, actual.url); |
| EXPECT_EQ(expected.action, actual.action); |
| EXPECT_EQ(expected.username_value, actual.username_value); |
| EXPECT_EQ(expected.password_value, actual.password_value); |
| EXPECT_EQ(expected.username_element, actual.username_element); |
| EXPECT_EQ(expected.password_element, actual.password_element); |
| EXPECT_EQ(expected.blocked_by_user, actual.blocked_by_user); |
| FormData::IdentityComparator less; |
| EXPECT_FALSE(less(expected.form_data, actual.form_data)); |
| EXPECT_FALSE(less(actual.form_data, expected.form_data)); |
| } |
| |
| struct ExpectedGenerationUKM { |
| base::Optional<int64_t> generation_popup_shown; |
| int64_t has_generated_password; |
| base::Optional<int64_t> generated_password_modified; |
| }; |
| |
| // Check that UKM |metric_name| in |entry| is equal to |expected|. |expected| == |
| // null means that no metric recording is expected. |
| void CheckMetric(const int64_t* expected, |
| const ukm::mojom::UkmEntry* entry, |
| const char* metric_name) { |
| SCOPED_TRACE(testing::Message("Checking UKM metric ") << metric_name); |
| |
| const int64_t* actual = |
| ukm::TestUkmRecorder::GetEntryMetric(entry, metric_name); |
| |
| ASSERT_EQ(!!expected, !!actual); |
| if (expected) |
| EXPECT_EQ(*expected, *actual); |
| } |
| |
| // Check that |recorder| records metrics |expected_metrics|. |
| void CheckPasswordGenerationUKM(const ukm::TestAutoSetUkmRecorder& recorder, |
| const ExpectedGenerationUKM& expected_metrics) { |
| auto entries = |
| recorder.GetEntriesByName(ukm::builders::PasswordForm::kEntryName); |
| ASSERT_EQ(1u, entries.size()); |
| const int64_t* expected_popup_shown = nullptr; |
| if (expected_metrics.generation_popup_shown) |
| expected_popup_shown = &expected_metrics.generation_popup_shown.value(); |
| CheckMetric(expected_popup_shown, entries[0], |
| ukm::builders::PasswordForm::kGeneration_PopupShownName); |
| |
| CheckMetric(&expected_metrics.has_generated_password, entries[0], |
| ukm::builders::PasswordForm::kGeneration_GeneratedPasswordName); |
| |
| const int64_t* expected_password_modified = nullptr; |
| if (expected_metrics.generated_password_modified) |
| expected_password_modified = |
| &expected_metrics.generated_password_modified.value(); |
| CheckMetric( |
| expected_password_modified, entries[0], |
| ukm::builders::PasswordForm::kGeneration_GeneratedPasswordModifiedName); |
| } |
| |
| // Create predictions for |form| using field predictions |field_predictions|. |
| std::map<FormSignature, FormPredictions> CreatePredictions( |
| const FormData& form, |
| std::vector<std::pair<int, ServerFieldType>> field_predictions) { |
| FormPredictions predictions; |
| for (const auto& index_prediction : field_predictions) { |
| autofill::FieldRendererId renderer_id = |
| form.fields[index_prediction.first].unique_renderer_id; |
| ServerFieldType server_type = index_prediction.second; |
| predictions.fields.emplace_back(); |
| |
| predictions.fields.back().renderer_id = renderer_id; |
| predictions.fields.back().type = server_type; |
| } |
| FormSignature form_signature = CalculateFormSignature(form); |
| return {{form_signature, predictions}}; |
| } |
| |
| class MockFormSaver : public StubFormSaver { |
| public: |
| MockFormSaver() = default; |
| MockFormSaver(const MockFormSaver&) = delete; |
| MockFormSaver& operator=(const MockFormSaver&) = delete; |
| ~MockFormSaver() override = default; |
| |
| // FormSaver: |
| MOCK_METHOD1(Blocklist, PasswordForm(PasswordStore::FormDigest)); |
| MOCK_METHOD3(Save, |
| void(PasswordForm pending, |
| const std::vector<const PasswordForm*>& matches, |
| const base::string16& old_password)); |
| MOCK_METHOD3(Update, |
| void(PasswordForm pending, |
| const std::vector<const PasswordForm*>& matches, |
| const base::string16& old_password)); |
| MOCK_METHOD4(UpdateReplace, |
| void(PasswordForm pending, |
| const std::vector<const PasswordForm*>& matches, |
| const base::string16& old_password, |
| const PasswordForm& old_unique_key)); |
| MOCK_METHOD1(Remove, void(const PasswordForm&)); |
| |
| 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()); |
| } |
| }; |
| |
| class MockFieldInfoManager : public FieldInfoManager { |
| public: |
| MOCK_METHOD(void, |
| AddFieldType, |
| (autofill::FormSignature, |
| autofill::FieldSignature, |
| ServerFieldType), |
| (override)); |
| MOCK_METHOD(ServerFieldType, |
| GetFieldType, |
| (autofill::FormSignature, autofill::FieldSignature), |
| (const override)); |
| }; |
| |
| class PasswordFormManagerTest : public testing::Test, |
| public testing::WithParamInterface<bool> { |
| public: |
| PasswordFormManagerTest() { |
| pref_service_.registry()->RegisterTimePref( |
| prefs::kProfileStoreDateLastUsedForFilling, base::Time()); |
| pref_service_.registry()->RegisterTimePref( |
| prefs::kAccountStoreDateLastUsedForFilling, base::Time()); |
| |
| form_manager_->set_wait_for_server_predictions_for_filling(true); |
| |
| GURL origin = GURL("https://accounts.google.com/a/ServiceLoginAuth"); |
| GURL action = GURL("https://accounts.google.com/a/ServiceLogin"); |
| GURL psl_origin = GURL("https://myaccounts.google.com/a/ServiceLoginAuth"); |
| GURL psl_action = GURL("https://myaccounts.google.com/a/ServiceLogin"); |
| |
| observed_form_.url = origin; |
| observed_form_.action = action; |
| observed_form_.name = ASCIIToUTF16("sign-in"); |
| observed_form_.unique_renderer_id = FormRendererId(0); |
| observed_form_.is_form_tag = true; |
| |
| observed_form_only_password_fields_ = observed_form_; |
| |
| FormFieldData field; |
| field.name = ASCIIToUTF16("firstname"); |
| field.id_attribute = field.name; |
| field.name_attribute = field.name; |
| field.form_control_type = "text"; |
| field.unique_renderer_id = autofill::FieldRendererId(1); |
| observed_form_.fields.push_back(field); |
| |
| field.name = ASCIIToUTF16("username"); |
| field.id_attribute = field.name; |
| field.name_attribute = field.name; |
| field.form_control_type = "text"; |
| field.unique_renderer_id = autofill::FieldRendererId(2); |
| observed_form_.fields.push_back(field); |
| |
| non_password_form_ = observed_form_; |
| |
| field.name = ASCIIToUTF16("password"); |
| field.id_attribute = field.name; |
| field.name_attribute = field.name; |
| field.form_control_type = "password"; |
| field.unique_renderer_id = autofill::FieldRendererId(3); |
| observed_form_.fields.push_back(field); |
| observed_form_only_password_fields_.fields.push_back(field); |
| |
| field.name = ASCIIToUTF16("password2"); |
| field.id_attribute = field.name; |
| field.name_attribute = field.name; |
| field.form_control_type = "password"; |
| field.unique_renderer_id = autofill::FieldRendererId(4); |
| observed_form_only_password_fields_.fields.push_back(field); |
| |
| // On iOS the unique_id member uniquely addresses this field in the DOM. |
| // This is an ephemeral value which is not guaranteed to be stable across |
| // page loads. It serves to allow a given field to be found during the |
| // current navigation. |
| // TODO(crbug.com/896689): Expand the logic/application of this to other |
| // platforms and/or merge this concept with |unique_renderer_id|. |
| #if defined(OS_IOS) |
| for (auto& f : observed_form_.fields) { |
| f.unique_id = f.id_attribute; |
| } |
| for (auto& f : observed_form_only_password_fields_.fields) { |
| f.unique_id = f.id_attribute; |
| } |
| #endif |
| |
| submitted_form_ = observed_form_; |
| submitted_form_.fields[kUsernameFieldIndex].value = ASCIIToUTF16("user1"); |
| submitted_form_.fields[kPasswordFieldIndex].value = ASCIIToUTF16("secret1"); |
| |
| submitted_non_password_form_ = non_password_form_; |
| submitted_non_password_form_.fields[kUsernameFieldIndex].value = |
| ASCIIToUTF16("user1"); |
| |
| saved_match_.url = origin; |
| saved_match_.action = action; |
| saved_match_.signon_realm = "https://accounts.google.com/"; |
| saved_match_.username_value = ASCIIToUTF16("test@gmail.com"); |
| saved_match_.username_element = ASCIIToUTF16("field1"); |
| saved_match_.password_value = ASCIIToUTF16("test1"); |
| saved_match_.password_element = ASCIIToUTF16("field2"); |
| saved_match_.is_public_suffix_match = false; |
| saved_match_.scheme = PasswordForm::Scheme::kHtml; |
| saved_match_.in_store = PasswordForm::Store::kProfileStore; |
| |
| psl_saved_match_ = saved_match_; |
| psl_saved_match_.url = psl_origin; |
| psl_saved_match_.action = psl_action; |
| psl_saved_match_.signon_realm = "https://myaccounts.google.com/"; |
| psl_saved_match_.is_public_suffix_match = true; |
| |
| parsed_observed_form_ = saved_match_; |
| parsed_observed_form_.form_data = observed_form_; |
| parsed_observed_form_.username_element = |
| observed_form_.fields[kUsernameFieldIndex].name; |
| parsed_observed_form_.password_element = |
| observed_form_.fields[kPasswordFieldIndex].name; |
| |
| parsed_submitted_form_ = parsed_observed_form_; |
| parsed_submitted_form_.form_data = submitted_form_; |
| parsed_submitted_form_.username_value = |
| submitted_form_.fields[kUsernameFieldIndex].value; |
| parsed_submitted_form_.password_value = |
| submitted_form_.fields[kPasswordFieldIndex].value; |
| |
| EXPECT_CALL(client_, GetAutofillDownloadManager()) |
| .WillRepeatedly(Return(&mock_autofill_download_manager_)); |
| ON_CALL(client_, GetPrefs()).WillByDefault(Return(&pref_service_)); |
| ON_CALL(client_, IsCommittedMainFrameSecure()).WillByDefault(Return(true)); |
| ON_CALL(*client_.GetPasswordFeatureManager(), |
| ShouldShowAccountStorageBubbleUi) |
| .WillByDefault(Return(true)); |
| ON_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(_, _, _, _, _, _)) |
| .WillByDefault(Return(true)); |
| ON_CALL(*client_.GetPasswordFeatureManager(), GetDefaultPasswordStore) |
| .WillByDefault(Return(PasswordForm::Store::kProfileStore)); |
| |
| fetcher_ = std::make_unique<FakeFormFetcher>(); |
| fetcher_->Fetch(); |
| } |
| |
| void SetUp() override { CreateFormManager(observed_form_); } |
| |
| protected: |
| MockAutofillDownloadManager mock_autofill_download_manager_; |
| FormData observed_form_; |
| FormData submitted_form_; |
| FormData observed_form_only_password_fields_; |
| FormData non_password_form_; |
| FormData submitted_non_password_form_; |
| PasswordForm saved_match_; |
| PasswordForm psl_saved_match_; |
| PasswordForm parsed_observed_form_; |
| PasswordForm parsed_submitted_form_; |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| signin::IdentityTestEnvironment identity_test_env_; |
| TestingPrefServiceSimple pref_service_; |
| MockPasswordManagerClient client_; |
| MockPasswordManagerDriver driver_; |
| |
| // Define |fetcher_| before |form_manager_|, because the former needs to |
| // outlive the latter. |
| std::unique_ptr<FakeFormFetcher> fetcher_; |
| std::unique_ptr<PasswordFormManager> form_manager_; |
| |
| // Creates PasswordFormManager and sets it to |form_manager_|. Along the |
| // way a new |fetcher_| is created. |
| virtual void CreateFormManager(const FormData& observed_form) { |
| auto password_save_manager = |
| GetParam() ? std::make_unique<MultiStorePasswordSaveManager>( |
| /*account_form_saver=*/std::make_unique< |
| NiceMock<MockFormSaver>>(), |
| /*account_form_saver=*/std::make_unique< |
| NiceMock<MockFormSaver>>()) |
| : std::make_unique<PasswordSaveManagerImpl>( |
| std::make_unique<NiceMock<MockFormSaver>>()); |
| |
| form_manager_ = std::make_unique<PasswordFormManager>( |
| &client_, driver_.AsWeakPtr(), observed_form, fetcher_.get(), |
| std::move(password_save_manager), nullptr); |
| } |
| |
| // Creates PasswordFormManager and sets it to |form_manager_| for |
| // |base_auth_observed_form|. Along the way a new |fetcher_| is created. |
| virtual void CreateFormManagerForNonWebForm( |
| const PasswordForm& base_auth_observed_form) { |
| auto password_save_manager = |
| GetParam() ? std::make_unique<MultiStorePasswordSaveManager>( |
| /*account_form_saver=*/std::make_unique< |
| NiceMock<MockFormSaver>>(), |
| /*account_form_saver=*/std::make_unique< |
| NiceMock<MockFormSaver>>()) |
| : std::make_unique<PasswordSaveManagerImpl>( |
| std::make_unique<NiceMock<MockFormSaver>>()); |
| fetcher_->set_scheme( |
| PasswordStore::FormDigest(base_auth_observed_form).scheme); |
| form_manager_ = std::make_unique<PasswordFormManager>( |
| &client_, PasswordStore::FormDigest(base_auth_observed_form), |
| fetcher_.get(), std::move(password_save_manager)); |
| } |
| |
| void SetNonFederatedAndNotifyFetchCompleted( |
| const std::vector<const PasswordForm*>& non_federated) { |
| fetcher_->SetNonFederated(non_federated); |
| fetcher_->NotifyFetchCompleted(); |
| } |
| }; |
| |
| TEST_P(PasswordFormManagerTest, DoesManage) { |
| EXPECT_TRUE(form_manager_->DoesManage(observed_form_, &driver_)); |
| // Forms on other drivers are not considered managed. |
| MockPasswordManagerDriver another_driver; |
| EXPECT_FALSE(form_manager_->DoesManage(observed_form_, &another_driver)); |
| FormData another_form = observed_form_; |
| another_form.is_form_tag = false; |
| EXPECT_FALSE(form_manager_->DoesManage(another_form, &driver_)); |
| |
| // Unique_renderer_id is the form identifier. |
| another_form = observed_form_; |
| another_form.unique_renderer_id.value() += 1; |
| EXPECT_FALSE(form_manager_->DoesManage(another_form, &driver_)); |
| } |
| |
| TEST_P(PasswordFormManagerTest, DoesManageNoFormTag) { |
| observed_form_.is_form_tag = false; |
| CreateFormManager(observed_form_); |
| |
| FormData another_form = observed_form_; |
| // Simulate that new input was added by JavaScript. |
| another_form.fields.emplace_back(); |
| EXPECT_TRUE(form_manager_->DoesManage(another_form, &driver_)); |
| // Forms on other drivers are not considered managed. |
| EXPECT_FALSE(form_manager_->DoesManage(another_form, nullptr)); |
| } |
| |
| TEST_P(PasswordFormManagerTest, Autofill) { |
| CreateFormManager(observed_form_); |
| EXPECT_CALL(driver_, FormEligibleForGenerationFound(_)).Times(0); |
| PasswordFormFillData fill_data; |
| EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data)); |
| CreateFormManager(observed_form_); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| |
| EXPECT_EQ(observed_form_.url, fill_data.url); |
| |
| // On Android Touch To Fill will prevent autofilling credentials on page load. |
| #if defined(OS_ANDROID) |
| EXPECT_TRUE(fill_data.wait_for_username); |
| #else |
| EXPECT_FALSE(fill_data.wait_for_username); |
| #endif |
| |
| EXPECT_EQ(observed_form_.fields[1].name, fill_data.username_field.name); |
| EXPECT_EQ(saved_match_.username_value, fill_data.username_field.value); |
| EXPECT_EQ(observed_form_.fields[2].name, fill_data.password_field.name); |
| EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, AutofillNotMoreThan5Times) { |
| EXPECT_CALL(driver_, FillPasswordForm(_)); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| for (size_t i = 0; i < PasswordFormManager::kMaxTimesAutofill - 1; ++i) { |
| EXPECT_CALL(driver_, FillPasswordForm(_)); |
| form_manager_->Fill(); |
| Mock::VerifyAndClearExpectations(&driver_); |
| } |
| |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| form_manager_->Fill(); |
| } |
| |
| // PasswordFormManager should always send fill data to renderer, even for |
| // sign-up forms (no "current-password" field, i.e., no password field to fill |
| // into). However, for sign-up forms, no particular password field should be |
| // identified for filling. That way, Chrome won't disturb the user by filling |
| // the sign-up form, but will be able to offer a manual fallback for filling if |
| // the form was misclassified. |
| TEST_P(PasswordFormManagerTest, AutofillSignUpForm) { |
| // Make |observed_form_| to be sign-up form. |
| observed_form_.fields.back().autocomplete_attribute = "new-password"; |
| |
| PasswordFormFillData fill_data; |
| EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data)); |
| |
| PasswordFormGenerationData generation_data; |
| EXPECT_CALL(driver_, FormEligibleForGenerationFound(_)) |
| .WillOnce(SaveArg<0>(&generation_data)); |
| |
| CreateFormManager(observed_form_); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| EXPECT_TRUE(fill_data.password_field.unique_renderer_id.is_null()); |
| EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value); |
| #if defined(OS_IOS) |
| EXPECT_EQ(observed_form_.unique_renderer_id, |
| generation_data.form_renderer_id); |
| #else |
| EXPECT_EQ(observed_form_.fields.back().unique_renderer_id, |
| generation_data.new_password_renderer_id); |
| EXPECT_TRUE(generation_data.confirmation_password_renderer_id.is_null()); |
| #endif |
| } |
| |
| // Check that generation signal is sent the the renderer when new password |
| // fields are marked with autocomplete attribute. |
| TEST_P(PasswordFormManagerTest, GenerationOnNewAndConfirmPasswordFields) { |
| // Make |observed_form_| to be sign-up form. |
| observed_form_.fields.back().autocomplete_attribute = "new-password"; |
| const autofill::FieldRendererId new_password_render_id = |
| observed_form_.fields.back().unique_renderer_id; |
| // Add a confirmation field. |
| FormFieldData field; |
| const autofill::FieldRendererId confirm_password_render_id( |
| new_password_render_id.value() + 1); |
| field.unique_renderer_id = confirm_password_render_id; |
| field.form_control_type = "password"; |
| field.autocomplete_attribute = "new-password"; |
| observed_form_.fields.push_back(field); |
| |
| PasswordFormGenerationData generation_data; |
| EXPECT_CALL(driver_, FormEligibleForGenerationFound(_)) |
| .WillOnce(SaveArg<0>(&generation_data)); |
| |
| CreateFormManager(observed_form_); |
| fetcher_->NotifyFetchCompleted(); |
| |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| #if defined(OS_IOS) |
| EXPECT_EQ(observed_form_.unique_renderer_id, |
| generation_data.form_renderer_id); |
| #else |
| EXPECT_EQ(new_password_render_id, generation_data.new_password_renderer_id); |
| EXPECT_EQ(confirm_password_render_id, |
| generation_data.confirmation_password_renderer_id); |
| #endif |
| } |
| |
| TEST_P(PasswordFormManagerTest, AutofillWithBlocklistedMatch) { |
| PasswordFormFillData fill_data; |
| EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data)); |
| fetcher_->SetNonFederated({&saved_match_}); |
| fetcher_->SetBlocklisted(true); |
| fetcher_->NotifyFetchCompleted(); |
| |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| |
| EXPECT_EQ(observed_form_.url, fill_data.url); |
| EXPECT_EQ(saved_match_.username_value, fill_data.username_field.value); |
| EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, SetSubmitted) { |
| EXPECT_FALSE(form_manager_->is_submitted()); |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| EXPECT_TRUE(form_manager_->is_submitted()); |
| |
| FormData another_form = submitted_form_; |
| another_form.name += ASCIIToUTF16("1"); |
| #if !defined(OS_IOS) |
| // |another_form| is managed because the same |unique_renderer_id| as |
| // |observed_form_|. |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(another_form, &driver_, nullptr)); |
| EXPECT_TRUE(form_manager_->is_submitted()); |
| #endif |
| } |
| |
| TEST_P(PasswordFormManagerTest, SetSubmittedMultipleTimes) { |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| EXPECT_TRUE(form_manager_->is_submitted()); |
| |
| // Make the submitted form to be invalid password form. |
| submitted_form_.fields.clear(); |
| |
| EXPECT_FALSE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| EXPECT_FALSE(form_manager_->is_submitted()); |
| EXPECT_FALSE(form_manager_->GetSubmittedForm()); |
| EXPECT_EQ(PasswordForm(), form_manager_->GetPendingCredentials()); |
| } |
| |
| TEST_P(PasswordFormManagerTest, ResetState) { |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| EXPECT_TRUE(form_manager_->is_submitted()); |
| EXPECT_TRUE(form_manager_->GetSubmittedForm()); |
| EXPECT_NE(PasswordForm(), form_manager_->GetPendingCredentials()); |
| |
| form_manager_->ResetState(); |
| |
| EXPECT_FALSE(form_manager_->is_submitted()); |
| EXPECT_FALSE(form_manager_->GetSubmittedForm()); |
| EXPECT_EQ(PasswordForm(), form_manager_->GetPendingCredentials()); |
| } |
| |
| // Tests that when PasswordFormManager receives saved matches it waits for |
| // server predictions and fills on receiving them. |
| TEST_P(PasswordFormManagerTest, ServerPredictionsWithinDelay) { |
| // Expects no filling on save matches receiving. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| std::map<FormSignature, FormPredictions> predictions = CreatePredictions( |
| observed_form_, {std::make_pair(2, autofill::PASSWORD)}); |
| |
| // Expect filling without delay on receiving server predictions. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1); |
| form_manager_->ProcessServerPredictions(predictions); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| // Expect no filling on receiving predictions again. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| form_manager_->ProcessServerPredictions(predictions); |
| } |
| |
| // Tests that PasswordFormManager fills after some delay even without |
| // server predictions. |
| TEST_P(PasswordFormManagerTest, ServerPredictionsAfterDelay) { |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| // Expect filling after passing filling delay. |
| |
| // Simulate passing filling delay. |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| std::map<FormSignature, FormPredictions> predictions = CreatePredictions( |
| observed_form_, {std::make_pair(2, autofill::PASSWORD)}); |
| |
| // Expect filling on receiving server predictions because it was less than |
| // kMaxTimesAutofill attempts to fill. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1); |
| form_manager_->ProcessServerPredictions(predictions); |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| } |
| |
| // Tests that filling happens immediately if server predictions are received |
| // before saved matches. |
| TEST_P(PasswordFormManagerTest, ServerPredictionsBeforeFetcher) { |
| // Expect no filling after receiving saved matches from |fetcher_|, since |
| // |form_manager| is waiting for server-side predictions. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| CreateFormManager(observed_form_); |
| |
| std::map<FormSignature, FormPredictions> predictions = CreatePredictions( |
| observed_form_, {std::make_pair(2, autofill::PASSWORD)}); |
| form_manager_->ProcessServerPredictions(predictions); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| // Expect filling without delay on receiving server predictions. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| } |
| |
| // Tests creating pending credentials when the password store is empty. |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsEmptyStore) { |
| fetcher_->NotifyFetchCompleted(); |
| |
| const base::Time kNow = base::Time::Now(); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| |
| const PasswordForm& pending_credentials = |
| form_manager_->GetPendingCredentials(); |
| CheckPendingCredentials(parsed_submitted_form_, pending_credentials); |
| EXPECT_GE(pending_credentials.date_last_used, kNow); |
| } |
| |
| // Tests creating pending credentials when fetch completed |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsWhenFetchCompleted) { |
| form_manager_->set_wait_for_server_predictions_for_filling(false); |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| SetNonFederatedAndNotifyFetchCompleted({&parsed_submitted_form_}); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| } |
| |
| // Tests creating pending credentials when new credentials are submitted and the |
| // store has another credentials saved. |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsNewCredentials) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| CheckPendingCredentials(parsed_submitted_form_, |
| form_manager_->GetPendingCredentials()); |
| } |
| |
| // Tests that when submitted credentials are equal to already saved one then |
| // pending credentials equal to saved match. |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsAlreadySaved) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| submitted_form_.fields[kPasswordFieldIndex].value = |
| saved_match_.password_value; |
| |
| // Tests that depending on whether we fill on page load or account select that |
| // correct user action is recorded. Fill on account select is simulated by |
| // pretending we are in incognito mode. |
| for (bool is_incognito : {false, true}) { |
| EXPECT_CALL(client_, IsIncognito).WillOnce(Return(is_incognito)); |
| form_manager_->Fill(); |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| CheckPendingCredentials(/* expected */ saved_match_, |
| form_manager_->GetPendingCredentials()); |
| } |
| } |
| |
| // Tests that when submitted credentials are equal to already saved PSL |
| // credentials. |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsPSLMatchSaved) { |
| PasswordForm expected = saved_match_; |
| |
| saved_match_.url = GURL("https://m.accounts.google.com/auth"); |
| saved_match_.signon_realm = "https://m.accounts.google.com/"; |
| saved_match_.is_public_suffix_match = true; |
| |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| submitted_form_.fields[kPasswordFieldIndex].value = |
| saved_match_.password_value; |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| } |
| |
| // Tests creating pending credentials when new credentials are different only in |
| // password with already saved one. |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsPasswordOverridden) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| PasswordForm expected = saved_match_; |
| expected.password_value += ASCIIToUTF16("1"); |
| |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| submitted_form_.fields[kPasswordFieldIndex].value = expected.password_value; |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| } |
| |
| // Tests that when submitted credentials are equal to already saved one then |
| // pending credentials equal to saved match. |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsUpdate) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| submitted_form.fields[1].value = ASCIIToUTF16("verystrongpassword"); |
| |
| PasswordForm expected = saved_match_; |
| expected.password_value = ASCIIToUTF16("verystrongpassword"); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| } |
| |
| // Tests creating pending credentials when a change password form is submitted |
| // and there are multiple saved forms. |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsUpdateMultipleSaved) { |
| PasswordForm another_saved_match = saved_match_; |
| another_saved_match.username_value += ASCIIToUTF16("1"); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_, &another_saved_match}); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| submitted_form.fields[1].value = ASCIIToUTF16("verystrongpassword"); |
| |
| PasswordForm expected = saved_match_; |
| expected.password_value = ASCIIToUTF16("verystrongpassword"); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| } |
| |
| // Tests creating pending credentials when the password field has an empty name. |
| TEST_P(PasswordFormManagerTest, CreatePendingCredentialsEmptyName) { |
| fetcher_->NotifyFetchCompleted(); |
| |
| FormData anonymous_signup = observed_form_; |
| // There is an anonymous password field. |
| anonymous_signup.fields[2].name.clear(); |
| anonymous_signup.fields[2].value = ASCIIToUTF16("a password"); |
| // Mark the password field as new-password. |
| std::map<FormSignature, FormPredictions> predictions = CreatePredictions( |
| observed_form_, {std::make_pair(2, autofill::ACCOUNT_CREATION_PASSWORD)}); |
| |
| form_manager_->ProcessServerPredictions(predictions); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(anonymous_signup, &driver_, nullptr)); |
| EXPECT_EQ(ASCIIToUTF16("a password"), |
| form_manager_->GetPendingCredentials().password_value); |
| } |
| |
| // Tests that there is no crash even when the observed form is a not password |
| // form and the submitted form is password form. |
| TEST_P(PasswordFormManagerTest, NoCrashOnNonPasswordForm) { |
| FormData form_without_password_fields = observed_form_; |
| // Remove the password field. |
| form_without_password_fields.fields.resize(kPasswordFieldIndex); |
| CreateFormManager(form_without_password_fields); |
| fetcher_->NotifyFetchCompleted(); |
| |
| FormData submitted_form = observed_form_; |
| submitted_form.fields[kUsernameFieldIndex].value = ASCIIToUTF16("username"); |
| submitted_form.fields[kPasswordFieldIndex].value = ASCIIToUTF16("password"); |
| |
| // Expect no crash. |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr); |
| } |
| |
| TEST_P(PasswordFormManagerTest, IsEqualToSubmittedForm) { |
| fetcher_->NotifyFetchCompleted(); |
| |
| FormData submitted_form = observed_form_; |
| submitted_form.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| submitted_form.fields[kPasswordFieldIndex].value = |
| saved_match_.password_value; |
| |
| // No submitted form yet. |
| EXPECT_FALSE(form_manager_->IsEqualToSubmittedForm(submitted_form)); |
| |
| ASSERT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| |
| observed_form_.unique_renderer_id.value() += 10; |
| observed_form_.fields.clear(); |
| |
| EXPECT_TRUE(form_manager_->IsEqualToSubmittedForm(observed_form_)); |
| |
| observed_form_.action = GURL("https://example.com"); |
| EXPECT_FALSE(form_manager_->IsEqualToSubmittedForm(observed_form_)); |
| } |
| |
| // Tests that when credentials with a new username (i.e. not saved yet) is |
| // successfully submitted, then they are saved correctly. |
| TEST_P(PasswordFormManagerTest, SaveNewCredentials) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| FormData submitted_form = observed_form_; |
| base::string16 new_username = saved_match_.username_value + ASCIIToUTF16("1"); |
| base::string16 new_password = saved_match_.password_value + ASCIIToUTF16("1"); |
| submitted_form.fields[kUsernameFieldIndex].value = new_username; |
| submitted_form.fields[kPasswordFieldIndex].value = new_password; |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm saved_form; |
| std::vector<const PasswordForm*> best_matches; |
| EXPECT_CALL(form_saver, Save(_, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&saved_form), SaveArg<1>(&best_matches))); |
| EXPECT_CALL(client_, UpdateFormManagers()); |
| |
| form_manager_->Save(); |
| |
| std::string expected_signon_realm = submitted_form.url.GetOrigin().spec(); |
| EXPECT_EQ(submitted_form.url, saved_form.url); |
| EXPECT_EQ(expected_signon_realm, saved_form.signon_realm); |
| EXPECT_EQ(new_username, saved_form.username_value); |
| EXPECT_EQ(new_password, saved_form.password_value); |
| |
| EXPECT_EQ(submitted_form.fields[kUsernameFieldIndex].name, |
| saved_form.username_element); |
| EXPECT_EQ(submitted_form.fields[kPasswordFieldIndex].name, |
| saved_form.password_element); |
| EXPECT_EQ(std::vector<const PasswordForm*>{&saved_match_}, best_matches); |
| |
| // Check UKM metrics. |
| form_manager_.reset(); |
| ExpectedGenerationUKM expected_metrics = { |
| {} /* shown manually */, |
| 0 /* password generated */, |
| {} /* generated password is not modified */}; |
| |
| CheckPasswordGenerationUKM(test_ukm_recorder, expected_metrics); |
| } |
| |
| // Check that if there is saved PSL matched credentials with the same |
| // username/password as in submitted form, then the saved form is the same |
| // already saved only with origin and signon_realm from the submitted form. |
| TEST_P(PasswordFormManagerTest, SavePSLToAlreadySaved) { |
| SetNonFederatedAndNotifyFetchCompleted({&psl_saved_match_}); |
| |
| FormData submitted_form = observed_form_; |
| // Change |
| submitted_form.fields[kUsernameFieldIndex].value = |
| psl_saved_match_.username_value; |
| submitted_form.fields[kPasswordFieldIndex].value = |
| psl_saved_match_.password_value; |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| EXPECT_TRUE(form_manager_->IsPendingCredentialsPublicSuffixMatch()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm saved_form; |
| std::vector<const PasswordForm*> best_matches; |
| EXPECT_CALL(form_saver, Save(_, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&saved_form), SaveArg<1>(&best_matches))); |
| |
| form_manager_->Save(); |
| |
| EXPECT_EQ(submitted_form.url, saved_form.url); |
| EXPECT_EQ(GetSignonRealm(submitted_form.url), saved_form.signon_realm); |
| EXPECT_EQ(saved_form.username_value, psl_saved_match_.username_value); |
| EXPECT_EQ(saved_form.password_value, psl_saved_match_.password_value); |
| EXPECT_EQ(saved_form.username_element, psl_saved_match_.username_element); |
| EXPECT_EQ(saved_form.password_element, psl_saved_match_.password_element); |
| |
| EXPECT_EQ(std::vector<const PasswordForm*>{&psl_saved_match_}, best_matches); |
| } |
| |
| // Tests that when credentials with already saved username but with a new |
| // password are submitted, then the saved password is updated. |
| TEST_P(PasswordFormManagerTest, OverridePassword) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| FormData submitted_form = observed_form_; |
| base::string16 username = saved_match_.username_value; |
| base::string16 new_password = saved_match_.password_value + ASCIIToUTF16("1"); |
| submitted_form.fields[kUsernameFieldIndex].value = username; |
| submitted_form.fields[kPasswordFieldIndex].value = new_password; |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_TRUE(form_manager_->IsPasswordUpdate()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm updated_form; |
| EXPECT_CALL(form_saver, Update(_, ElementsAre(Pointee(saved_match_)), |
| saved_match_.password_value)) |
| .WillOnce(SaveArg<0>(&updated_form)); |
| |
| form_manager_->Save(); |
| |
| EXPECT_TRUE(ArePasswordFormUniqueKeysEqual(saved_match_, updated_form)); |
| EXPECT_EQ(new_password, updated_form.password_value); |
| } |
| |
| // Tests that when the user changes password on a change password form then the |
| // saved password is updated. |
| TEST_P(PasswordFormManagerTest, UpdatePasswordOnChangePasswordForm) { |
| CreateFormManager(observed_form_only_password_fields_); |
| PasswordForm not_best_saved_match = saved_match_; |
| PasswordForm saved_match_another_username = saved_match_; |
| saved_match_another_username.username_value += ASCIIToUTF16("1"); |
| |
| SetNonFederatedAndNotifyFetchCompleted( |
| {&saved_match_, ¬_best_saved_match, &saved_match_another_username}); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = saved_match_.password_value; |
| base::string16 new_password = saved_match_.password_value + ASCIIToUTF16("1"); |
| submitted_form.fields[1].value = new_password; |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_TRUE(form_manager_->IsPasswordUpdate()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm updated_form; |
| EXPECT_CALL(form_saver, |
| Update(_, |
| UnorderedElementsAre( |
| Pointee(saved_match_), Pointee(not_best_saved_match), |
| Pointee(saved_match_another_username)), |
| saved_match_.password_value)) |
| .WillOnce(SaveArg<0>(&updated_form)); |
| |
| form_manager_->Save(); |
| |
| EXPECT_TRUE(ArePasswordFormUniqueKeysEqual(saved_match_, updated_form)); |
| EXPECT_EQ(new_password, updated_form.password_value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, VotesUploadingOnPasswordUpdate) { |
| for (auto expected_vote : |
| {autofill::NEW_PASSWORD, autofill::PROBABLY_NEW_PASSWORD, |
| autofill::NOT_NEW_PASSWORD}) { |
| SCOPED_TRACE(testing::Message("expected_vote=") << expected_vote); |
| CreateFormManager(observed_form_only_password_fields_); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = saved_match_.password_value; |
| auto new_password = saved_match_.password_value + ASCIIToUTF16("1"); |
| submitted_form.fields[1].value = new_password; |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| |
| std::map<base::string16, autofill::ServerFieldType> expected_types; |
| expected_types[ASCIIToUTF16("password")] = autofill::PASSWORD; |
| expected_types[ASCIIToUTF16("password2")] = expected_vote; |
| |
| testing::InSequence in_sequence; |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(UploadedAutofillTypesAre(expected_types), |
| false, _, _, true, nullptr)); |
| if (expected_vote == autofill::NEW_PASSWORD) { |
| // An unrelated |FIRST_USE| vote. |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(_, _, _, _, _, _)); |
| } |
| |
| if (expected_vote == autofill::NEW_PASSWORD) |
| form_manager_->Save(); |
| else if (expected_vote == autofill::PROBABLY_NEW_PASSWORD) |
| form_manager_->OnNoInteraction(true /* is_update */); |
| else |
| form_manager_->OnNopeUpdateClicked(); |
| Mock::VerifyAndClearExpectations(&mock_autofill_download_manager_); |
| } |
| } |
| |
| TEST_P(PasswordFormManagerTest, UpdateUsernameEmptyStore) { |
| fetcher_->NotifyFetchCompleted(); |
| |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| |
| base::string16 new_username = |
| parsed_submitted_form_.username_value + ASCIIToUTF16("1"); |
| PasswordForm expected = parsed_submitted_form_; |
| expected.username_value = new_username; |
| expected.username_element.clear(); |
| |
| form_manager_->OnUpdateUsernameFromPrompt(new_username); |
| |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| } |
| |
| TEST_P(PasswordFormManagerTest, UpdateUsernameToAnotherFieldValue) { |
| fetcher_->NotifyFetchCompleted(); |
| |
| base::string16 user_chosen_username = ASCIIToUTF16("user_chosen_username"); |
| base::string16 automatically_chosen_username = |
| ASCIIToUTF16("automatically_chosen_username"); |
| submitted_form_.fields[0].value = user_chosen_username; |
| submitted_form_.fields[1].value = automatically_chosen_username; |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| |
| EXPECT_EQ(automatically_chosen_username, |
| form_manager_->GetPendingCredentials().username_value); |
| |
| form_manager_->OnUpdateUsernameFromPrompt(user_chosen_username); |
| |
| EXPECT_EQ(user_chosen_username, |
| form_manager_->GetPendingCredentials().username_value); |
| |
| FieldTypeMap expected_types = { |
| {ASCIIToUTF16("firstname"), autofill::USERNAME}, |
| {ASCIIToUTF16("password"), autofill::PASSWORD}}; |
| VoteTypeMap expected_vote_types = { |
| {ASCIIToUTF16("firstname"), |
| AutofillUploadContents::Field::USERNAME_EDITED}}; |
| EXPECT_CALL( |
| mock_autofill_download_manager_, |
| StartUploadRequest( |
| AllOf(UploadedAutofillTypesAre(expected_types), |
| HasGenerationVote(false), VoteTypesAre(expected_vote_types)), |
| _, Contains(autofill::USERNAME), _, _, nullptr)); |
| form_manager_->Save(); |
| } |
| |
| TEST_P(PasswordFormManagerTest, UpdateUsernameToAlreadyExisting) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| |
| base::string16 new_username = saved_match_.username_value; |
| base::string16 expected_password = parsed_submitted_form_.password_value; |
| PasswordForm expected = saved_match_; |
| expected.password_value = expected_password; |
| |
| form_manager_->OnUpdateUsernameFromPrompt(new_username); |
| |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_TRUE(form_manager_->IsPasswordUpdate()); |
| } |
| |
| TEST_P(PasswordFormManagerTest, UpdatePasswordValueEmptyStore) { |
| fetcher_->NotifyFetchCompleted(); |
| |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| |
| base::string16 new_password = |
| parsed_submitted_form_.password_value + ASCIIToUTF16("1"); |
| PasswordForm expected = parsed_submitted_form_; |
| expected.password_value = new_password; |
| expected.password_element.clear(); |
| |
| form_manager_->OnUpdatePasswordFromPrompt(new_password); |
| |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| |
| // TODO(https://crbug.com/928690): implement not sending incorrect votes and |
| // check that StartUploadRequest is not called. |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(_, _, _, _, _, _)) |
| .Times(1); |
| form_manager_->Save(); |
| } |
| |
| TEST_P(PasswordFormManagerTest, UpdatePasswordValueToAlreadyExisting) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| // Emulate submitting form with known username and different password. |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| |
| // The user changes password to already saved one. |
| base::string16 password = saved_match_.password_value; |
| form_manager_->OnUpdatePasswordFromPrompt(password); |
| |
| CheckPendingCredentials(saved_match_, form_manager_->GetPendingCredentials()); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_FALSE(form_manager_->IsPasswordUpdate()); |
| } |
| |
| TEST_P(PasswordFormManagerTest, UpdatePasswordValueMultiplePasswordFields) { |
| FormData form = observed_form_only_password_fields_; |
| |
| CreateFormManager(form); |
| fetcher_->NotifyFetchCompleted(); |
| base::string16 password = ASCIIToUTF16("password1"); |
| base::string16 pin = ASCIIToUTF16("pin"); |
| form.fields[0].value = password; |
| form.fields[1].value = pin; |
| form_manager_->ProvisionallySave(form, &driver_, nullptr); |
| |
| // Check that a second password field is chosen for saving. |
| EXPECT_EQ(pin, form_manager_->GetPendingCredentials().password_value); |
| |
| PasswordForm expected = form_manager_->GetPendingCredentials(); |
| expected.password_value = password; |
| expected.password_element = form.fields[0].name; |
| |
| // Simulate that the user updates value to save for the first password field. |
| form_manager_->OnUpdatePasswordFromPrompt(password); |
| |
| // Check that newly created pending credentials are correct. |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| |
| // Check that a vote is sent for the field with the value which is chosen by |
| // the user. |
| std::map<base::string16, ServerFieldType> expected_types; |
| expected_types[expected.password_element] = autofill::PASSWORD; |
| |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(UploadedAutofillTypesAre(expected_types), |
| false, _, _, true, nullptr)); |
| |
| // Check that the password which was chosen by the user is saved. |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, Save(_, _, _)).WillOnce(SaveArg<0>(&saved_form)); |
| |
| form_manager_->Save(); |
| CheckPendingCredentials(expected, saved_form); |
| } |
| |
| TEST_P(PasswordFormManagerTest, Blocklist) { |
| fetcher_->NotifyFetchCompleted(); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| PasswordForm actual_blocklisted_form = |
| password_manager_util::MakeNormalizedBlocklistedForm( |
| PasswordStore::FormDigest(observed_form_)); |
| EXPECT_CALL(form_saver, Blocklist(PasswordStore::FormDigest(observed_form_))) |
| .WillOnce(Return(actual_blocklisted_form)); |
| |
| form_manager_->Blocklist(); |
| EXPECT_TRUE(form_manager_->IsBlocklisted()); |
| } |
| |
| TEST_P(PasswordFormManagerTest, Clone) { |
| fetcher_->NotifyFetchCompleted(); |
| |
| // Provisionally save in order to create pending credentials. |
| ASSERT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| |
| std::unique_ptr<PasswordFormManager> cloned_manager = form_manager_->Clone(); |
| |
| EXPECT_TRUE(cloned_manager->DoesManage(observed_form_, nullptr)); |
| EXPECT_TRUE(cloned_manager->GetFormFetcher()); |
| // Check that |form_fetcher| was cloned. |
| EXPECT_NE(form_manager_->GetFormFetcher(), cloned_manager->GetFormFetcher()); |
| |
| EXPECT_EQ(form_manager_->GetPendingCredentials(), |
| cloned_manager->GetPendingCredentials()); |
| ASSERT_TRUE(cloned_manager->GetSubmittedForm()); |
| EXPECT_EQ(*form_manager_->GetSubmittedForm(), |
| *cloned_manager->GetSubmittedForm()); |
| EXPECT_TRUE(cloned_manager->is_submitted()); |
| } |
| |
| // Extracts the information whether parsing was successful from a metric |
| // specified by |metric_name| stored in |entry|. The metric name should be one |
| // of ukm::builders::PasswordForm::kReadonlyWhenSavingName and |
| // ukm::builders::PasswordForm::kReadonlyWhenFillingName. |
| bool ParsingSuccessReported(const ukm::mojom::UkmEntry* entry, |
| base::StringPiece metric_name) { |
| const int64_t* value = |
| ukm::TestUkmRecorder::GetEntryMetric(entry, metric_name); |
| EXPECT_TRUE(value); |
| // Ideally, an ASSERT_TRUE above would prevent the test suite from crashing on |
| // dereferencing |value| below. But ASSERT_* is not available in non-void |
| // returning functions, so the null value is handled explicitly. |
| if (!value) |
| return false; // Value does not matter, the test already failed. |
| return 1 == (1 & *value); |
| } |
| |
| // Test that an attempt to log to ReadonlyWhenFilling UKM is made when filling. |
| TEST_P(PasswordFormManagerTest, RecordReadonlyWhenFilling) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| EXPECT_CALL(driver_, FillPasswordForm(_)); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| |
| // Destroy the form manager to destroy the UKM recorder it owns. The recorder |
| // only records metrics in its destructor. |
| form_manager_.reset(); |
| |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::PasswordForm::kEntryName); |
| ASSERT_EQ(1u, entries.size()); |
| |
| EXPECT_TRUE(ParsingSuccessReported( |
| entries[0], ukm::builders::PasswordForm::kReadonlyWhenFillingName)); |
| } |
| |
| // Test that an attempt to log to ReadonlyWhenFilling UKM is made when filling, |
| // even when the parsing itself is unsuccessful. |
| TEST_P(PasswordFormManagerTest, RecordReadonlyWhenFilling_ParsingFailed) { |
| FormData malformed_form = observed_form_; |
| malformed_form.fields.clear(); |
| CreateFormManager(malformed_form); |
| // Only create the recorder after the current form manager is created, |
| // otherwise the destruction of the previous one will add unwanted UKM entries |
| // in it. |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| |
| // Destroy the form manager to destroy the UKM recorder it owns. The recorder |
| // only records metrics in its destructor. |
| form_manager_.reset(); |
| |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::PasswordForm::kEntryName); |
| ASSERT_EQ(1u, entries.size()); |
| |
| EXPECT_FALSE(ParsingSuccessReported( |
| entries[0], ukm::builders::PasswordForm::kReadonlyWhenFillingName)); |
| } |
| |
| // Test that an attempt to log to ReadonlyWhenSaving UKM is made when creating |
| // pending credentials. |
| TEST_P(PasswordFormManagerTest, RecordReadonlyWhenSaving) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| |
| // Destroy the form manager to destroy the UKM recorder it owns. The recorder |
| // only records metrics in its destructor. |
| form_manager_.reset(); |
| |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::PasswordForm::kEntryName); |
| ASSERT_EQ(1u, entries.size()); |
| |
| EXPECT_TRUE(ParsingSuccessReported( |
| entries[0], ukm::builders::PasswordForm::kReadonlyWhenSavingName)); |
| } |
| |
| // Test that an attempt to log to ReadonlyWhenSaving UKM is made when creating |
| // pending credentials, even when their parsing itself is unsuccessful. |
| TEST_P(PasswordFormManagerTest, RecordReadonlyWhenSaving_ParsingFailed) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| FormData malformed_form = submitted_form_; |
| malformed_form.fields.clear(); |
| EXPECT_FALSE( |
| form_manager_->ProvisionallySave(malformed_form, &driver_, nullptr)); |
| |
| // Destroy the form manager to destroy the UKM recorder it owns. The recorder |
| // only records metrics in its destructor. |
| form_manager_.reset(); |
| |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::PasswordForm::kEntryName); |
| ASSERT_EQ(1u, entries.size()); |
| |
| EXPECT_FALSE(ParsingSuccessReported( |
| entries[0], ukm::builders::PasswordForm::kReadonlyWhenSavingName)); |
| } |
| |
| TEST_P(PasswordFormManagerTest, PresaveGeneratedPasswordEmptyStore) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->NotifyFetchCompleted(); |
| |
| EXPECT_FALSE(form_manager_->HasGeneratedPassword()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| form_manager_->SetGenerationPopupWasShown(PasswordGenerationType::kAutomatic); |
| |
| // Check that the generated password is presaved. |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, Save(_, IsEmpty(), base::string16())) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| |
| PasswordForm form_with_generated_password = parsed_submitted_form_; |
| FormData& form_data = form_with_generated_password.form_data; |
| form_manager_->PresaveGeneratedPassword( |
| form_data, form_with_generated_password.password_value); |
| |
| EXPECT_TRUE(form_manager_->HasGeneratedPassword()); |
| EXPECT_EQ(saved_form.username_value, |
| form_data.fields[kUsernameFieldIndex].value); |
| EXPECT_EQ(saved_form.password_value, |
| form_data.fields[kPasswordFieldIndex].value); |
| |
| Mock::VerifyAndClearExpectations(&form_saver); |
| |
| // Check that when the generated password is edited, then it's presaved. |
| form_with_generated_password.password_value += ASCIIToUTF16("1"); |
| form_data.fields[kPasswordFieldIndex].value = |
| form_with_generated_password.password_value; |
| EXPECT_CALL(form_saver, |
| UpdateReplace(_, IsEmpty(), ASCIIToUTF16(""), |
| FormHasUniqueKey(form_with_generated_password))) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| |
| form_manager_->PresaveGeneratedPassword( |
| form_with_generated_password.form_data, |
| form_with_generated_password.password_value); |
| |
| EXPECT_TRUE(form_manager_->HasGeneratedPassword()); |
| EXPECT_EQ(saved_form.username_value, |
| form_data.fields[kUsernameFieldIndex].value); |
| EXPECT_EQ(saved_form.password_value, |
| form_with_generated_password.password_value); |
| |
| Mock::VerifyAndClearExpectations(&form_saver); |
| |
| // Check UKM metrics. |
| form_manager_.reset(); |
| ExpectedGenerationUKM expected_metrics = { |
| base::make_optional(1u) /* shown automatically */, |
| 1 /* password generated */, |
| base::make_optional(1u) /* password modified */}; |
| |
| CheckPasswordGenerationUKM(test_ukm_recorder, expected_metrics); |
| } |
| |
| TEST_P(PasswordFormManagerTest, PresaveGenerated_ModifiedUsername) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->NotifyFetchCompleted(); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| form_manager_->SetGenerationPopupWasShown(PasswordGenerationType::kAutomatic); |
| |
| // Check that the generated password is presaved. |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, Save(_, _, _)).WillOnce(SaveArg<0>(&saved_form)); |
| PasswordForm form_with_generated_password = parsed_submitted_form_; |
| FormData& form_data = form_with_generated_password.form_data; |
| form_manager_->PresaveGeneratedPassword( |
| form_with_generated_password.form_data, |
| form_with_generated_password.password_value); |
| Mock::VerifyAndClearExpectations(&form_saver); |
| |
| // Check that when the username is edited, then it's presaved. |
| form_with_generated_password.username_value += ASCIIToUTF16("1"); |
| form_data.fields[kUsernameFieldIndex].value = |
| form_with_generated_password.username_value; |
| |
| EXPECT_CALL(form_saver, UpdateReplace(_, IsEmpty(), ASCIIToUTF16(""), |
| FormHasUniqueKey(saved_form))) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| form_manager_->PresaveGeneratedPassword( |
| form_with_generated_password.form_data, |
| form_with_generated_password.password_value); |
| |
| EXPECT_TRUE(form_manager_->HasGeneratedPassword()); |
| EXPECT_EQ(saved_form.username_value, |
| form_with_generated_password.username_value); |
| EXPECT_EQ(saved_form.password_value, |
| form_with_generated_password.password_value); |
| |
| // Check UKM metrics. |
| form_manager_.reset(); |
| ExpectedGenerationUKM expected_metrics = { |
| base::make_optional(1u) /* shown automatically */, |
| 1 /* password generated */, |
| base::make_optional(0u) /* password modified */}; |
| |
| CheckPasswordGenerationUKM(test_ukm_recorder, expected_metrics); |
| } |
| |
| TEST_P(PasswordFormManagerTest, GeneratedPasswordWhichIsNotInFormData) { |
| fetcher_->NotifyFetchCompleted(); |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| // Create a password form such that |form_data| do not contain the generated |
| // password. |
| PasswordForm form_with_generated_password; |
| form_with_generated_password.form_data = submitted_form_; |
| const base::string16 generated_password = ASCIIToUTF16("gen_pw"); |
| // |password_value| should contain the generated password. |
| form_with_generated_password.password_value = generated_password; |
| |
| // Check that the generated password is presaved. |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, Save(_, _, _)).WillOnce(SaveArg<0>(&saved_form)); |
| |
| form_manager_->PresaveGeneratedPassword( |
| form_with_generated_password.form_data, |
| form_with_generated_password.password_value); |
| EXPECT_EQ(submitted_form_.fields[kUsernameFieldIndex].value, |
| saved_form.username_value); |
| EXPECT_EQ(generated_password, saved_form.password_value); |
| EXPECT_TRUE(form_manager_->HasGeneratedPassword()); |
| |
| // Check that the generated password is saved. |
| EXPECT_CALL(form_saver, UpdateReplace(_, IsEmpty(), ASCIIToUTF16(""), |
| FormHasUniqueKey(saved_form))) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| EXPECT_CALL(client_, UpdateFormManagers()); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| form_manager_->Save(); |
| |
| EXPECT_EQ(submitted_form_.fields[kUsernameFieldIndex].value, |
| saved_form.username_value); |
| EXPECT_EQ(generated_password, saved_form.password_value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, PresaveGenerationWhenParsingFails) { |
| fetcher_->NotifyFetchCompleted(); |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| // Create a password form with empty |form_data|. On this form the form parser |
| // should fail. |
| PasswordForm form_with_empty_form_data; |
| const base::string16 generated_password = ASCIIToUTF16("gen_pw"); |
| form_with_empty_form_data.password_value = generated_password; |
| |
| // Check that nevertheless the generated password is presaved. |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, Save(_, IsEmpty(), base::string16())) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| form_manager_->PresaveGeneratedPassword( |
| form_with_empty_form_data.form_data, |
| form_with_empty_form_data.password_value); |
| EXPECT_EQ(generated_password, saved_form.password_value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, PasswordNoLongerGenerated) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->NotifyFetchCompleted(); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| form_manager_->SetGenerationPopupWasShown(PasswordGenerationType::kManual); |
| |
| EXPECT_CALL(form_saver, Save(_, _, _)); |
| |
| PasswordForm form = parsed_submitted_form_; |
| form_manager_->PresaveGeneratedPassword(form.form_data, form.password_value); |
| Mock::VerifyAndClearExpectations(&form_saver); |
| |
| EXPECT_TRUE(form_manager_->HasGeneratedPassword()); |
| |
| // Check when the user removes the generated password on the page, it is |
| // removed from the store. |
| EXPECT_CALL(form_saver, Remove(_)); |
| form_manager_->PasswordNoLongerGenerated(); |
| |
| EXPECT_FALSE(form_manager_->HasGeneratedPassword()); |
| |
| // Check UKM metrics. |
| form_manager_.reset(); |
| ExpectedGenerationUKM expected_metrics = { |
| base::make_optional(2u) /* shown manually */, |
| 0 /* password generated */, |
| {} /* generated password is not modified */}; |
| |
| CheckPasswordGenerationUKM(test_ukm_recorder, expected_metrics); |
| } |
| |
| TEST_P(PasswordFormManagerTest, PresaveGeneratedPasswordExistingCredential) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| form_manager_->SetGenerationPopupWasShown(PasswordGenerationType::kAutomatic); |
| |
| // Check that the generated password is presaved. |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, Save(_, _, _)).WillOnce(SaveArg<0>(&saved_form)); |
| |
| PasswordForm form_with_generated_password = parsed_submitted_form_; |
| FormData& form_data = form_with_generated_password.form_data; |
| |
| // Check that the generated password is saved with the empty username when |
| // there is already a saved credetial with the same username. |
| form_data.fields[kUsernameFieldIndex].value = saved_match_.username_value; |
| form_manager_->PresaveGeneratedPassword( |
| form_with_generated_password.form_data, |
| form_with_generated_password.password_value); |
| |
| EXPECT_TRUE(form_manager_->HasGeneratedPassword()); |
| EXPECT_TRUE(saved_form.username_value.empty()); |
| EXPECT_EQ(form_with_generated_password.password_value, |
| saved_form.password_value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, UserEventsForGeneration) { |
| using GeneratedPasswordStatus = |
| PasswordFormMetricsRecorder::GeneratedPasswordStatus; |
| |
| PasswordForm submitted_form(parsed_observed_form_); |
| submitted_form.form_data = submitted_form_; |
| FormData& form_data = submitted_form.form_data; |
| |
| { // User accepts a generated password. |
| base::HistogramTester histogram_tester; |
| CreateFormManager(observed_form_); |
| form_manager_->PresaveGeneratedPassword(submitted_form.form_data, |
| submitted_form.password_value); |
| form_manager_.reset(); |
| histogram_tester.ExpectUniqueSample( |
| "PasswordGeneration.UserDecision", |
| GeneratedPasswordStatus::kPasswordAccepted, 1); |
| } |
| |
| { // User edits the generated password. |
| base::HistogramTester histogram_tester; |
| CreateFormManager(observed_form_); |
| form_manager_->PresaveGeneratedPassword(submitted_form.form_data, |
| submitted_form.password_value); |
| form_data.fields[kPasswordFieldIndex].value += ASCIIToUTF16("1"); |
| submitted_form.password_value = form_data.fields[kPasswordFieldIndex].value; |
| form_manager_->PresaveGeneratedPassword(submitted_form.form_data, |
| submitted_form.password_value); |
| form_manager_.reset(); |
| histogram_tester.ExpectUniqueSample( |
| "PasswordGeneration.UserDecision", |
| GeneratedPasswordStatus::kPasswordEdited, 1); |
| } |
| |
| { // User clears the generated password. |
| base::HistogramTester histogram_tester; |
| CreateFormManager(observed_form_); |
| form_manager_->PresaveGeneratedPassword(submitted_form.form_data, |
| submitted_form.password_value); |
| form_data.fields[kPasswordFieldIndex].value += ASCIIToUTF16("2"); |
| submitted_form.password_value = form_data.fields[kPasswordFieldIndex].value; |
| form_manager_->PresaveGeneratedPassword(submitted_form.form_data, |
| submitted_form.password_value); |
| form_manager_->PasswordNoLongerGenerated(); |
| form_manager_.reset(); |
| histogram_tester.ExpectUniqueSample( |
| "PasswordGeneration.UserDecision", |
| GeneratedPasswordStatus::kPasswordDeleted, 1); |
| } |
| } |
| |
| TEST_P(PasswordFormManagerTest, FillForm) { |
| for (bool observed_form_changed : {false, true}) { |
| SCOPED_TRACE(testing::Message("observed_form_changed=") |
| << observed_form_changed); |
| CreateFormManager(observed_form_); |
| EXPECT_CALL(driver_, FillPasswordForm(_)); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| FormData form = observed_form_; |
| |
| if (observed_form_changed) { |
| form.fields[kUsernameFieldIndex].unique_renderer_id.value() += 1000; |
| form.fields[kUsernameFieldIndex].name += ASCIIToUTF16("1"); |
| form.fields[kUsernameFieldIndex].id_attribute += ASCIIToUTF16("1"); |
| #if defined(OS_IOS) |
| form.fields[kUsernameFieldIndex].unique_id += ASCIIToUTF16("1"); |
| #endif |
| form.fields[kPasswordFieldIndex].unique_renderer_id.value() += 1000; |
| } |
| |
| PasswordFormFillData fill_data; |
| EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data)); |
| form_manager_->FillForm(form, {}); |
| |
| EXPECT_EQ(form.fields[kUsernameFieldIndex].name, |
| fill_data.username_field.name); |
| EXPECT_EQ(form.fields[kUsernameFieldIndex].unique_renderer_id, |
| fill_data.username_field.unique_renderer_id); |
| EXPECT_EQ(saved_match_.username_value, fill_data.username_field.value); |
| EXPECT_EQ(form.fields[kPasswordFieldIndex].name, |
| fill_data.password_field.name); |
| EXPECT_EQ(form.fields[kPasswordFieldIndex].unique_renderer_id, |
| fill_data.password_field.unique_renderer_id); |
| EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value); |
| |
| base::HistogramTester histogram_tester; |
| form_manager_.reset(); |
| uint32_t expected_differences_mask = 0; |
| if (observed_form_changed) |
| expected_differences_mask = 2; // renderer_id changes. |
| histogram_tester.ExpectUniqueSample("PasswordManager.DynamicFormChanges", |
| expected_differences_mask, 1); |
| } |
| } |
| |
| TEST_P(PasswordFormManagerTest, FillFormWaitForServerPredictions) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| FormData changed_form = observed_form_; |
| |
| changed_form.fields[kUsernameFieldIndex].unique_renderer_id.value() += 1000; |
| changed_form.fields[kPasswordFieldIndex].unique_renderer_id.value() += 1000; |
| |
| // Check that no filling until server predicions or filling timeout |
| // expiration. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| form_manager_->FillForm(changed_form, {}); |
| |
| // Check that the changed form is filled after the filling timeout expires. |
| |
| PasswordFormFillData fill_data; |
| EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data)); |
| |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| EXPECT_EQ(changed_form.fields[kUsernameFieldIndex].unique_renderer_id, |
| fill_data.username_field.unique_renderer_id); |
| EXPECT_EQ(changed_form.fields[kPasswordFieldIndex].unique_renderer_id, |
| fill_data.password_field.unique_renderer_id); |
| |
| base::HistogramTester histogram_tester; |
| form_manager_.reset(); |
| uint32_t expected_differences_mask = 2; // renderer_id changes. |
| histogram_tester.ExpectUniqueSample("PasswordManager.DynamicFormChanges", |
| expected_differences_mask, 1); |
| } |
| |
| TEST_P(PasswordFormManagerTest, Update) { |
| PasswordForm not_best_saved_match = saved_match_; |
| PasswordForm saved_match_another_username = saved_match_; |
| saved_match_another_username.username_value += ASCIIToUTF16("1"); |
| SetNonFederatedAndNotifyFetchCompleted( |
| {&saved_match_, &saved_match_another_username}); |
| |
| FormData submitted_form = observed_form_; |
| base::string16 username = saved_match_.username_value; |
| base::string16 new_password = saved_match_.password_value + ASCIIToUTF16("1"); |
| submitted_form.fields[kUsernameFieldIndex].value = username; |
| submitted_form.fields[kPasswordFieldIndex].value = new_password; |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm updated_form; |
| EXPECT_CALL(form_saver, Update(_, |
| UnorderedElementsAre( |
| Pointee(saved_match_), |
| Pointee(saved_match_another_username)), |
| saved_match_.password_value)) |
| .WillOnce(SaveArg<0>(&updated_form)); |
| EXPECT_CALL(client_, UpdateFormManagers()); |
| |
| const base::Time kNow = base::Time::Now(); |
| form_manager_->Update(saved_match_); |
| |
| EXPECT_TRUE(ArePasswordFormUniqueKeysEqual(saved_match_, updated_form)); |
| EXPECT_EQ(new_password, updated_form.password_value); |
| EXPECT_GE(updated_form.date_last_used, kNow); |
| } |
| |
| TEST_P(PasswordFormManagerTest, FillingAssistanceMetric) { |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match_}); |
| |
| // Simulate that the user fills the saved credentials manually. |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| submitted_form_.fields[kUsernameFieldIndex].properties_mask = |
| FieldPropertiesFlags::kAutofilledOnUserTrigger; |
| submitted_form_.fields[kPasswordFieldIndex].value = |
| saved_match_.password_value; |
| submitted_form_.fields[kPasswordFieldIndex].properties_mask = |
| FieldPropertiesFlags::kAutofilledOnUserTrigger; |
| |
| base::HistogramTester histogram_tester; |
| // Simulate successful submission. |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| form_manager_->GetMetricsRecorder()->LogSubmitPassed(); |
| |
| form_manager_.reset(); |
| histogram_tester.ExpectUniqueSample( |
| "PasswordManager.FillingAssistance", |
| PasswordFormMetricsRecorder::FillingAssistance::kManual, 1); |
| } |
| |
| TEST_P(PasswordFormManagerTest, PasswordRevealedVote) { |
| for (bool password_revealed : {false, true}) { |
| SCOPED_TRACE(testing::Message("password_revealed=") << password_revealed); |
| CreateFormManager(observed_form_); |
| fetcher_->NotifyFetchCompleted(); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| |
| if (password_revealed) |
| form_manager_->OnPasswordsRevealed(); |
| |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(PasswordsWereRevealed(password_revealed), |
| false, _, _, true, nullptr)); |
| form_manager_->Save(); |
| Mock::VerifyAndClearExpectations(&mock_autofill_download_manager_); |
| } |
| } |
| |
| TEST_P(PasswordFormManagerTest, GenerationUploadOnNoInteraction) { |
| for (bool generation_popup_shown : {false, true}) { |
| SCOPED_TRACE(testing::Message("generation_popup_shown=") |
| << generation_popup_shown); |
| CreateFormManager(observed_form_); |
| fetcher_->NotifyFetchCompleted(); |
| |
| if (generation_popup_shown) { |
| form_manager_->SetGenerationElement(FieldRendererId(3)); |
| form_manager_->SetGenerationPopupWasShown( |
| PasswordGenerationType::kAutomatic); |
| } |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| |
| EXPECT_CALL( |
| mock_autofill_download_manager_, |
| StartUploadRequest(HasGenerationVote(true), false, _, _, true, nullptr)) |
| .Times(generation_popup_shown ? 1 : 0); |
| form_manager_->OnNoInteraction(false /*is_update */); |
| Mock::VerifyAndClearExpectations(&mock_autofill_download_manager_); |
| } |
| } |
| |
| TEST_P(PasswordFormManagerTest, GenerationUploadOnNeverClicked) { |
| for (bool generation_popup_shown : {false, true}) { |
| SCOPED_TRACE(testing::Message("generation_popup_shown=") |
| << generation_popup_shown); |
| CreateFormManager(observed_form_); |
| fetcher_->NotifyFetchCompleted(); |
| |
| if (generation_popup_shown) { |
| form_manager_->SetGenerationElement(FieldRendererId(3)); |
| form_manager_->SetGenerationPopupWasShown( |
| PasswordGenerationType::kAutomatic); |
| } |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| |
| EXPECT_CALL( |
| mock_autofill_download_manager_, |
| StartUploadRequest(HasGenerationVote(true), false, _, _, true, nullptr)) |
| .Times(generation_popup_shown ? 1 : 0); |
| form_manager_->OnNeverClicked(); |
| Mock::VerifyAndClearExpectations(&mock_autofill_download_manager_); |
| } |
| } |
| |
| TEST_P(PasswordFormManagerTest, SaveHttpAuthNoHttpAuthStored) { |
| for (bool html_credentials_saved : {false, true}) { |
| SCOPED_TRACE(testing::Message("html_credentials_saved=") |
| << html_credentials_saved); |
| PasswordForm http_auth_form = parsed_observed_form_; |
| http_auth_form.scheme = PasswordForm::Scheme::kBasic; |
| |
| // Check that no filling because no http auth credentials are stored. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| EXPECT_CALL(client_, AutofillHttpAuth(_, _)).Times(0); |
| |
| CreateFormManagerForNonWebForm(http_auth_form); |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| std::vector<const PasswordForm*> saved_matches; |
| if (html_credentials_saved) |
| saved_matches.push_back(&saved_match_); |
| SetNonFederatedAndNotifyFetchCompleted(saved_matches); |
| |
| base::string16 username = ASCIIToUTF16("user1"); |
| base::string16 password = ASCIIToUTF16("pass1"); |
| http_auth_form.username_value = username; |
| http_auth_form.password_value = password; |
| |
| // Check that submitted credentials are saved. |
| ASSERT_TRUE(form_manager_->ProvisionallySaveHttpAuthForm(http_auth_form)); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, Save(_, _, _)).WillOnce(SaveArg<0>(&saved_form)); |
| form_manager_->Save(); |
| |
| EXPECT_EQ(http_auth_form.signon_realm, saved_form.signon_realm); |
| EXPECT_EQ(username, saved_form.username_value); |
| EXPECT_EQ(password, saved_form.password_value); |
| } |
| } |
| |
| TEST_P(PasswordFormManagerTest, HTTPAuthAlreadySaved) { |
| PasswordForm http_auth_form = parsed_observed_form_; |
| http_auth_form.scheme = PasswordForm::Scheme::kBasic; |
| |
| CreateFormManagerForNonWebForm(http_auth_form); |
| |
| const base::string16 username = ASCIIToUTF16("user1"); |
| const base::string16 password = ASCIIToUTF16("pass1"); |
| http_auth_form.username_value = username; |
| http_auth_form.password_value = password; |
| EXPECT_CALL(client_, AutofillHttpAuth(http_auth_form, _)).Times(1); |
| SetNonFederatedAndNotifyFetchCompleted({&http_auth_form}); |
| |
| // Check that if known credentials are submitted, then |form_manager_| is not |
| // in state new login nor password overridden. |
| ASSERT_TRUE(form_manager_->ProvisionallySaveHttpAuthForm(http_auth_form)); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_FALSE(form_manager_->IsPasswordUpdate()); |
| } |
| |
| TEST_P(PasswordFormManagerTest, HTTPAuthPasswordOverridden) { |
| PasswordForm http_auth_form = parsed_observed_form_; |
| http_auth_form.scheme = PasswordForm::Scheme::kBasic; |
| |
| CreateFormManagerForNonWebForm(http_auth_form); |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| PasswordForm saved_http_auth_form = http_auth_form; |
| const base::string16 username = ASCIIToUTF16("user1"); |
| const base::string16 password = ASCIIToUTF16("pass1"); |
| saved_http_auth_form.username_value = username; |
| saved_http_auth_form.password_value = password; |
| EXPECT_CALL(client_, AutofillHttpAuth(saved_http_auth_form, _)).Times(1); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_http_auth_form}); |
| |
| // Check that if new password is submitted, then |form_manager_| is in state |
| // password overridden. |
| PasswordForm submitted_http_auth_form = saved_http_auth_form; |
| base::string16 new_password = password + ASCIIToUTF16("1"); |
| submitted_http_auth_form.password_value = new_password; |
| ASSERT_TRUE( |
| form_manager_->ProvisionallySaveHttpAuthForm(submitted_http_auth_form)); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_TRUE(form_manager_->IsPasswordUpdate()); |
| |
| // Check that the password is updated in the stored credential. |
| PasswordForm updated_form; |
| EXPECT_CALL(form_saver, |
| Update(_, ElementsAre(Pointee(saved_http_auth_form)), password)) |
| .WillOnce(SaveArg<0>(&updated_form)); |
| |
| form_manager_->Save(); |
| |
| EXPECT_TRUE( |
| ArePasswordFormUniqueKeysEqual(saved_http_auth_form, updated_form)); |
| EXPECT_EQ(new_password, updated_form.password_value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, BlocklistHttpAuthCredentials) { |
| PasswordForm http_auth_form = parsed_observed_form_; |
| http_auth_form.signon_realm += "my-auth-realm"; |
| http_auth_form.scheme = PasswordForm::Scheme::kBasic; |
| |
| CreateFormManagerForNonWebForm(http_auth_form); |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| // Simulate that the user submits http auth credentials. |
| http_auth_form.username_value = ASCIIToUTF16("user1"); |
| http_auth_form.password_value = ASCIIToUTF16("pass1"); |
| ASSERT_TRUE(form_manager_->ProvisionallySaveHttpAuthForm(http_auth_form)); |
| |
| // Simulate that the user clicks never. |
| PasswordForm blocklisted_form; |
| EXPECT_CALL(form_saver, Blocklist(PasswordStore::FormDigest(http_auth_form))); |
| form_manager_->OnNeverClicked(); |
| } |
| |
| #if defined(OS_IOS) |
| TEST_P(PasswordFormManagerTest, iOSPresavedGeneratedPassword) { |
| fetcher_->NotifyFetchCompleted(); |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| FormData form_to_presave = observed_form_; |
| const base::string16 typed_username = ASCIIToUTF16("user1"); |
| FormFieldData& username_field = form_to_presave.fields[kUsernameFieldIndex]; |
| FormFieldData& password_field = form_to_presave.fields[kPasswordFieldIndex]; |
| username_field.value = typed_username; |
| password_field.value = ASCIIToUTF16("not_password"); |
| // Use |generated_password| different from value in field to test that the |
| // generated password is saved. |
| const base::string16 generated_password = ASCIIToUTF16("gen_pw"); |
| // Use different |unique_id| and |name| to test that |unique_id| is taken. |
| password_field.unique_id = password_field.name + ASCIIToUTF16("1"); |
| FieldRendererId generation_element = password_field.unique_renderer_id; |
| |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, Save(_, IsEmpty(), base::string16())) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| form_manager_->PresaveGeneratedPassword( |
| &driver_, form_to_presave, generated_password, generation_element); |
| EXPECT_EQ(generated_password, saved_form.password_value); |
| |
| Mock::VerifyAndClearExpectations(&form_saver); |
| |
| const base::string16 changed_password = |
| generated_password + ASCIIToUTF16("1"); |
| EXPECT_CALL(form_saver, UpdateReplace(_, _, base::string16(), _)) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| |
| form_manager_->UpdateStateOnUserInput(form_to_presave.unique_renderer_id, |
| generation_element, changed_password); |
| EXPECT_EQ(username_field.value, saved_form.username_value); |
| EXPECT_EQ(changed_password, saved_form.password_value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, iOSUpdateStateWithoutPresaving) { |
| fetcher_->NotifyFetchCompleted(); |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| FieldRendererId password_field = |
| observed_form_.fields[kPasswordFieldIndex].unique_renderer_id; |
| const base::string16 new_field_value = ASCIIToUTF16("some_password"); |
| |
| // Check that nothing is saved on changing password, in case when there was no |
| // pre-saving. |
| EXPECT_CALL(form_saver, Save(_, _, _)).Times(0); |
| EXPECT_TRUE(form_manager_->UpdateStateOnUserInput( |
| observed_form_.unique_renderer_id, password_field, new_field_value)); |
| |
| EXPECT_EQ(new_field_value, |
| form_manager_->observed_form()->fields[kPasswordFieldIndex].value); |
| } |
| |
| TEST_P(PasswordFormManagerTest, iOSUsingFieldDataManagerData) { |
| CreateFormManager(observed_form_); |
| |
| auto field_data_manager = base::MakeRefCounted<autofill::FieldDataManager>(); |
| field_data_manager->UpdateFieldDataMap( |
| observed_form_.fields[1].unique_renderer_id, |
| base::UTF8ToUTF16("typed_username"), FieldPropertiesFlags::kUserTyped); |
| field_data_manager->UpdateFieldDataWithAutofilledValue( |
| observed_form_.fields[2].unique_renderer_id, |
| base::UTF8ToUTF16("autofilled_pw"), |
| FieldPropertiesFlags::kAutofilledOnPageLoad); |
| |
| form_manager_->UpdateObservedFormDataWithFieldDataManagerInfo( |
| field_data_manager.get()); |
| |
| EXPECT_EQ(form_manager_->observed_form()->fields[1].typed_value, |
| base::UTF8ToUTF16("typed_username")); |
| EXPECT_EQ(form_manager_->observed_form()->fields[1].properties_mask, |
| FieldPropertiesFlags::kUserTyped); |
| |
| EXPECT_EQ(form_manager_->observed_form()->fields[2].value, |
| base::UTF8ToUTF16("autofilled_pw")); |
| EXPECT_EQ(form_manager_->observed_form()->fields[2].properties_mask, |
| FieldPropertiesFlags::kAutofilledOnPageLoad); |
| } |
| |
| #endif // defined(OS_IOS) |
| |
| // Tests that username is taken during username first flow. |
| TEST_P(PasswordFormManagerTest, UsernameFirstFlow) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kUsernameFirstFlow); |
| |
| CreateFormManager(observed_form_only_password_fields_); |
| fetcher_->NotifyFetchCompleted(); |
| const base::string16 possible_username = ASCIIToUTF16("possible_username"); |
| PossibleUsernameData possible_username_data( |
| saved_match_.signon_realm, autofill::FieldRendererId(1), |
| possible_username, base::Time::Now(), 0 /* driver_id */); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| |
| ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_, |
| &possible_username_data)); |
| |
| #if !defined(OS_ANDROID) |
| // Check that a username is chosen from |possible_username_data|. |
| EXPECT_EQ(possible_username, |
| form_manager_->GetPendingCredentials().username_value); |
| #else |
| // Local heuristics on Android for username first flow are not supported, so |
| // the username should not be taken from the username form. |
| EXPECT_TRUE(form_manager_->GetPendingCredentials().username_value.empty()); |
| #endif // !defined(OS_ANDROID) |
| } |
| |
| // Tests that username is not taken when a possible username is not valid. |
| TEST_P(PasswordFormManagerTest, UsernameFirstFlowDifferentDomains) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kUsernameFirstFlow); |
| |
| CreateFormManager(observed_form_only_password_fields_); |
| fetcher_->NotifyFetchCompleted(); |
| base::string16 possible_username = ASCIIToUTF16("possible_username"); |
| PossibleUsernameData possible_username_data( |
| "https://another.domain.com", autofill::FieldRendererId(1u), |
| possible_username, base::Time::Now(), 0 /* driver_id */); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| |
| ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_, |
| &possible_username_data)); |
| |
| // |possible_username_data| has different domain then |submitted_form|. Check |
| // that no username is chosen. |
| EXPECT_TRUE(form_manager_->GetPendingCredentials().username_value.empty()); |
| } |
| |
| // Tests that username is taken during username first flow. |
| TEST_P(PasswordFormManagerTest, UsernameFirstFlowVotes) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kUsernameFirstFlow); |
| |
| CreateFormManager(observed_form_only_password_fields_); |
| fetcher_->NotifyFetchCompleted(); |
| const base::string16 possible_username = ASCIIToUTF16("possible_username"); |
| constexpr autofill::FieldRendererId kUsernameFieldRendererId(100); |
| PossibleUsernameData possible_username_data( |
| saved_match_.signon_realm, kUsernameFieldRendererId, possible_username, |
| base::Time::Now(), 0 /* driver_id */); |
| |
| // Create form predictions and set them to |possible_username_data|. |
| FormPredictions predictions; |
| constexpr autofill::FormSignature kUsernameFormSignature(1000); |
| predictions.form_signature = kUsernameFormSignature; |
| PasswordFieldPrediction field_prediction; |
| field_prediction.renderer_id = kUsernameFieldRendererId; |
| field_prediction.signature.value() = 123; |
| field_prediction.type = autofill::SINGLE_USERNAME; |
| predictions.fields.push_back(field_prediction); |
| possible_username_data.form_predictions = predictions; |
| |
| MockFieldInfoManager mock_field_manager; |
| ON_CALL(mock_field_manager, GetFieldType(_, _)) |
| .WillByDefault(Return(UNKNOWN_TYPE)); |
| ON_CALL(client_, GetFieldInfoManager()) |
| .WillByDefault(Return(&mock_field_manager)); |
| |
| // Simulate submission a form without username. Data from |
| // |possible_username_data| will be taken for setting username. |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| |
| ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_, |
| &possible_username_data)); |
| |
| // Check that uploads for both username and password form happen. |
| testing::InSequence in_sequence; |
| // Upload for the password form. |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(_, false, _, _, true, nullptr)); |
| |
| // Upload for the username form. |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(SignatureIs(kUsernameFormSignature), false, _, |
| _, true, nullptr)); |
| |
| form_manager_->Save(); |
| } |
| |
| // Tests that server prediction are taken into consideration for offering |
| // username on username first flow. |
| TEST_P(PasswordFormManagerTest, PossibleUsernameServerPredictions) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kUsernameFirstFlow); |
| |
| const base::string16 possible_username = ASCIIToUTF16("possible_username"); |
| PossibleUsernameData possible_username_data( |
| saved_match_.signon_realm, autofill::FieldRendererId(101u), |
| possible_username, base::Time::Now(), 0 /* driver_id */); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| |
| for (ServerFieldType prediction : {SINGLE_USERNAME, NOT_USERNAME}) { |
| SCOPED_TRACE(testing::Message("prediction=") << prediction); |
| |
| FormPredictions form_predictions; |
| form_predictions.fields.push_back( |
| {.renderer_id = possible_username_data.renderer_id, |
| .type = prediction}); |
| |
| possible_username_data.form_predictions = form_predictions; |
| |
| CreateFormManager(observed_form_only_password_fields_); |
| fetcher_->NotifyFetchCompleted(); |
| |
| ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_, |
| &possible_username_data)); |
| |
| if (prediction == SINGLE_USERNAME) { |
| // Check that a username is chosen from |possible_username_data|. |
| EXPECT_EQ(possible_username, |
| form_manager_->GetPendingCredentials().username_value); |
| } else { |
| // Check that a username is not chosen from |possible_username_data|. |
| EXPECT_TRUE( |
| form_manager_->GetPendingCredentials().username_value.empty()); |
| } |
| } |
| } |
| |
| #if !defined(OS_ANDROID) |
| // Tests that data from FieldInfoManager is taken into consideration for |
| // offering username on username first flow. |
| // Local heuristics on Android for username first flow is not supported. |
| TEST_P(PasswordFormManagerTest, PossibleUsernameFieldManager) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kUsernameFirstFlow); |
| |
| const base::string16 possible_username = ASCIIToUTF16("possible_username"); |
| PossibleUsernameData possible_username_data( |
| saved_match_.signon_realm, autofill::FieldRendererId(101u), |
| possible_username, base::Time::Now(), 0 /* driver_id */); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| |
| for (ServerFieldType prediction : {SINGLE_USERNAME, NOT_USERNAME}) { |
| SCOPED_TRACE(testing::Message("prediction=") << prediction); |
| |
| constexpr autofill::FormSignature kFormSignature(1234); |
| constexpr autofill::FieldSignature kFieldSignature(12); |
| FormPredictions form_predictions; |
| form_predictions.form_signature = kFormSignature; |
| // Simulate that the server knows nothing about username field. |
| form_predictions.fields.push_back( |
| {.renderer_id = possible_username_data.renderer_id, |
| .signature = kFieldSignature, |
| .type = autofill::UNKNOWN_TYPE}); |
| possible_username_data.form_predictions = form_predictions; |
| |
| MockFieldInfoManager mock_field_manager; |
| EXPECT_CALL(client_, GetFieldInfoManager()) |
| .WillOnce(Return(&mock_field_manager)); |
| EXPECT_CALL(mock_field_manager, |
| GetFieldType(kFormSignature, kFieldSignature)) |
| .WillOnce(Return(prediction)); |
| |
| CreateFormManager(observed_form_only_password_fields_); |
| fetcher_->NotifyFetchCompleted(); |
| |
| ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_, |
| &possible_username_data)); |
| |
| if (prediction == SINGLE_USERNAME) { |
| // Check that a username is chosen from |possible_username_data|. |
| EXPECT_EQ(possible_username, |
| form_manager_->GetPendingCredentials().username_value); |
| } else { |
| // Check that a username is not chosen from |possible_username_data|. |
| EXPECT_TRUE( |
| form_manager_->GetPendingCredentials().username_value.empty()); |
| } |
| Mock::VerifyAndClearExpectations(&client_); |
| } |
| } |
| #endif // !defined(OS_ANDROID) |
| |
| // Tests that the a form with the username field but without a password field is |
| // not provisionally saved. |
| TEST_P(PasswordFormManagerTest, ProvisinallySavedOnSingleUsernameForm) { |
| CreateFormManager(non_password_form_); |
| std::map<FormSignature, FormPredictions> predictions = |
| CreatePredictions(non_password_form_, |
| {std::make_pair(kUsernameFieldIndex, SINGLE_USERNAME)}); |
| form_manager_->ProcessServerPredictions(predictions); |
| |
| EXPECT_FALSE(form_manager_->ProvisionallySave(submitted_non_password_form_, |
| &driver_, nullptr)); |
| } |
| |
| TEST_P(PasswordFormManagerTest, NotMovableToAccountStoreWhenBlocked) { |
| const std::string kEmail = "email@gmail.com"; |
| const std::string kGaiaId = signin::GetTestGaiaIdForEmail(kEmail); |
| |
| PasswordForm saved_match(saved_match_); |
| saved_match.in_store = PasswordForm::Store::kProfileStore; |
| // Moving stored credentials is blocked for |kEmail|. |
| saved_match.moving_blocked_for_list.push_back( |
| GaiaIdHash::FromGaiaId(kGaiaId)); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match}); |
| |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| submitted_form_.fields[kPasswordFieldIndex].value = |
| saved_match_.password_value; |
| |
| form_manager_->Fill(); |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| |
| // Even with |kEmail| is signed in, credentials should NOT be movable. |
| ON_CALL(client_, GetIdentityManager()) |
| .WillByDefault(Return(identity_test_env_.identity_manager())); |
| identity_test_env_.SetPrimaryAccount(kEmail); |
| EXPECT_FALSE(form_manager_->IsMovableToAccountStore()); |
| } |
| |
| TEST_P(PasswordFormManagerTest, MovableToAccountStore) { |
| const std::string kEmail = "email@gmail.com"; |
| const std::string kGaiaId = signin::GetTestGaiaIdForEmail(kEmail); |
| |
| PasswordForm saved_match(saved_match_); |
| saved_match.in_store = PasswordForm::Store::kProfileStore; |
| // Moving stored credentials is blocked for |kEmail|. |
| saved_match.moving_blocked_for_list.push_back( |
| GaiaIdHash::FromGaiaId(kGaiaId)); |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match}); |
| |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| submitted_form_.fields[kPasswordFieldIndex].value = |
| saved_match_.password_value; |
| |
| form_manager_->Fill(); |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| |
| // If another user is signed in, credentials should be movable. |
| identity_test_env_.SetPrimaryAccount("another-user@gmail.com"); |
| ON_CALL(client_, GetIdentityManager()) |
| .WillByDefault(Return(identity_test_env_.identity_manager())); |
| EXPECT_TRUE(form_manager_->IsMovableToAccountStore()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| PasswordFormManagerTest, |
| testing::Values(false, true)); |
| |
| class MockPasswordSaveManager : public PasswordSaveManager { |
| public: |
| MockPasswordSaveManager() = default; |
| MockPasswordSaveManager(const MockPasswordSaveManager&) = delete; |
| MockPasswordSaveManager& operator=(const MockPasswordSaveManager&) = delete; |
| ~MockPasswordSaveManager() override = default; |
| MOCK_METHOD4(Init, |
| void(PasswordManagerClient*, |
| const FormFetcher*, |
| scoped_refptr<PasswordFormMetricsRecorder>, |
| VotesUploader*)); |
| MOCK_CONST_METHOD0(GetPendingCredentials, const PasswordForm&()); |
| MOCK_CONST_METHOD0(GetGeneratedPassword, const base::string16&()); |
| MOCK_CONST_METHOD0(GetFormSaver, FormSaver*()); |
| MOCK_METHOD5(CreatePendingCredentials, |
| void(const PasswordForm&, |
| const autofill::FormData*, |
| const autofill::FormData&, |
| bool, |
| bool)); |
| MOCK_METHOD0(ResetPendingCredentials, void()); |
| MOCK_METHOD2(Save, void(const autofill::FormData*, const PasswordForm&)); |
| MOCK_METHOD3(Update, |
| void(const PasswordForm&, |
| const autofill::FormData*, |
| const PasswordForm&)); |
| MOCK_METHOD1(Blocklist, void(const PasswordStore::FormDigest&)); |
| MOCK_METHOD1(Unblocklist, void(const PasswordStore::FormDigest&)); |
| MOCK_METHOD1(PresaveGeneratedPassword, void(PasswordForm)); |
| MOCK_METHOD2(GeneratedPasswordAccepted, |
| void(PasswordForm, base::WeakPtr<PasswordManagerDriver>)); |
| MOCK_METHOD0(PasswordNoLongerGenerated, void()); |
| MOCK_CONST_METHOD0(IsNewLogin, bool()); |
| MOCK_CONST_METHOD0(IsPasswordUpdate, bool()); |
| MOCK_CONST_METHOD0(HasGeneratedPassword, bool()); |
| std::unique_ptr<PasswordSaveManager> Clone() override { |
| return std::make_unique<MockPasswordSaveManager>(); |
| } |
| MOCK_METHOD1(MoveCredentialsToAccountStore, |
| void(metrics_util::MoveToAccountStoreTrigger)); |
| MOCK_METHOD1(BlockMovingToAccountStoreFor, void(const autofill::GaiaIdHash&)); |
| }; |
| |
| class PasswordFormManagerTestWithMockedSaver : public PasswordFormManagerTest { |
| public: |
| PasswordFormManagerTestWithMockedSaver() = default; |
| PasswordFormManagerTestWithMockedSaver( |
| const PasswordFormManagerTestWithMockedSaver&) = delete; |
| PasswordFormManagerTestWithMockedSaver& operator=( |
| const PasswordFormManagerTestWithMockedSaver&) = delete; |
| |
| MockPasswordSaveManager* mock_password_save_manager() { |
| return mock_password_save_manager_; |
| } |
| |
| // Creates PasswordFormManager and sets it to |form_manager_|. Along the |
| // way a new |fetcher_| is created. |
| void CreateFormManager(const FormData& observed_form) override { |
| auto mock_password_save_manager = |
| std::make_unique<NiceMock<MockPasswordSaveManager>>(); |
| mock_password_save_manager_ = mock_password_save_manager.get(); |
| EXPECT_CALL(*mock_password_save_manager_, Init(_, _, _, _)); |
| form_manager_ = std::make_unique<PasswordFormManager>( |
| &client_, driver_.AsWeakPtr(), observed_form, fetcher_.get(), |
| std::move(mock_password_save_manager), nullptr); |
| } |
| |
| // Creates PasswordFormManager and sets it to |form_manager_| for |
| // |base_auth_observed_form|. Along the way a new |fetcher_| is created. |
| void CreateFormManagerForNonWebForm( |
| const PasswordForm& base_auth_observed_form) override { |
| fetcher_->set_scheme( |
| PasswordStore::FormDigest(base_auth_observed_form).scheme); |
| auto mock_password_save_manager = |
| std::make_unique<NiceMock<MockPasswordSaveManager>>(); |
| mock_password_save_manager_ = mock_password_save_manager.get(); |
| EXPECT_CALL(*mock_password_save_manager_, Init(_, _, _, _)); |
| form_manager_ = std::make_unique<PasswordFormManager>( |
| &client_, PasswordStore::FormDigest(base_auth_observed_form), |
| fetcher_.get(), std::move(mock_password_save_manager)); |
| } |
| |
| private: |
| NiceMock<MockPasswordSaveManager>* mock_password_save_manager_; |
| }; |
| |
| TEST_F( |
| PasswordFormManagerTestWithMockedSaver, |
| ProviosnallySaveShouldCreatePendingPasswordFormManagerTestWithMockedSaverCredentials) { |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials(_, _, _, _, _)); |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| } |
| |
| // Tests that when credentials are saved, the PasswordSaveManager is called |
| // accordingly. |
| TEST_F(PasswordFormManagerTestWithMockedSaver, SaveCredentials) { |
| fetcher_->NotifyFetchCompleted(); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| FormData submitted_form = observed_form_; |
| base::string16 new_username = saved_match_.username_value + ASCIIToUTF16("1"); |
| base::string16 new_password = saved_match_.password_value + ASCIIToUTF16("1"); |
| submitted_form.fields[kUsernameFieldIndex].value = new_username; |
| submitted_form.fields[kPasswordFieldIndex].value = new_password; |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form, &driver_, nullptr)); |
| PasswordForm updated_form; |
| EXPECT_CALL(*mock_password_save_manager(), |
| Save(FormDataPointeeEqualTo(observed_form_), _)) |
| .WillOnce(SaveArg<1>(&updated_form)); |
| EXPECT_CALL(client_, UpdateFormManagers()); |
| form_manager_->Save(); |
| std::string expected_signon_realm = submitted_form.url.GetOrigin().spec(); |
| EXPECT_EQ(submitted_form.url, updated_form.url); |
| EXPECT_EQ(expected_signon_realm, updated_form.signon_realm); |
| EXPECT_EQ(new_username, updated_form.username_value); |
| EXPECT_EQ(new_password, updated_form.password_value); |
| EXPECT_EQ(submitted_form.fields[kUsernameFieldIndex].name, |
| updated_form.username_element); |
| EXPECT_EQ(submitted_form.fields[kPasswordFieldIndex].name, |
| updated_form.password_element); |
| // Check UKM metrics. |
| form_manager_.reset(); |
| ExpectedGenerationUKM expected_metrics = { |
| {} /* shown manually */, |
| 0 /* password generated */, |
| {} /* generated password is not modified */}; |
| CheckPasswordGenerationUKM(test_ukm_recorder, expected_metrics); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, UpdateUsernameEmptyStore) { |
| fetcher_->NotifyFetchCompleted(); |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| base::string16 new_username = |
| parsed_submitted_form_.username_value + ASCIIToUTF16("1"); |
| PasswordForm expected = parsed_submitted_form_; |
| expected.username_value = new_username; |
| expected.username_element.clear(); |
| EXPECT_CALL( |
| *mock_password_save_manager(), |
| CreatePendingCredentials(FormHasUsernameValue(new_username), _, _, _, _)); |
| form_manager_->OnUpdateUsernameFromPrompt(new_username); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, |
| UpdateUsernameToAnotherFieldValue) { |
| fetcher_->NotifyFetchCompleted(); |
| base::string16 user_chosen_username = ASCIIToUTF16("user_chosen_username"); |
| base::string16 automatically_chosen_username = |
| ASCIIToUTF16("automatically_chosen_username"); |
| submitted_form_.fields[0].value = user_chosen_username; |
| submitted_form_.fields[1].value = automatically_chosen_username; |
| EXPECT_CALL( |
| *mock_password_save_manager(), |
| CreatePendingCredentials( |
| FormHasUsernameValue(automatically_chosen_username), _, _, _, _)); |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials( |
| FormHasUsernameValue(user_chosen_username), _, _, _, _)); |
| form_manager_->OnUpdateUsernameFromPrompt(user_chosen_username); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, UpdateUsernameFromPrompt) { |
| fetcher_->NotifyFetchCompleted(); |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials( |
| FormHasUsernameValue(parsed_submitted_form_.username_value), |
| _, _, _, _)); |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| base::string16 new_username = saved_match_.username_value; |
| base::string16 expected_password = parsed_submitted_form_.password_value; |
| PasswordForm expected = saved_match_; |
| expected.password_value = expected_password; |
| EXPECT_CALL( |
| *mock_password_save_manager(), |
| CreatePendingCredentials(FormHasUsernameValue(new_username), _, _, _, _)); |
| form_manager_->OnUpdateUsernameFromPrompt(new_username); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, UpdatePasswordValueFromPrompt) { |
| fetcher_->NotifyFetchCompleted(); |
| EXPECT_CALL( |
| *mock_password_save_manager(), |
| CreatePendingCredentials( |
| FormHasPassword(parsed_submitted_form_.password_value), _, _, _, _)); |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr); |
| base::string16 new_password = ASCIIToUTF16("new_password"); |
| EXPECT_CALL( |
| *mock_password_save_manager(), |
| CreatePendingCredentials(FormHasPassword(new_password), _, _, _, _)); |
| form_manager_->OnUpdatePasswordFromPrompt(new_password); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, |
| UpdatePasswordValueMultiplePasswordFields) { |
| FormData form = observed_form_only_password_fields_; |
| CreateFormManager(form); |
| fetcher_->NotifyFetchCompleted(); |
| base::string16 password = ASCIIToUTF16("password1"); |
| base::string16 pin = ASCIIToUTF16("pin"); |
| form.fields[0].value = password; |
| form.fields[1].value = pin; |
| // Check that a second password field is chosen for saving. |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials(FormHasPassword(pin), _, _, _, _)); |
| form_manager_->ProvisionallySave(form, &driver_, nullptr); |
| // Simulate that the user updates value to save for the first password |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials(FormHasPassword(password), _, _, _, _)); |
| form_manager_->OnUpdatePasswordFromPrompt(password); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, Blocklist) { |
| fetcher_->NotifyFetchCompleted(); |
| PasswordForm actual_blocklisted_form = |
| password_manager_util::MakeNormalizedBlocklistedForm( |
| PasswordStore::FormDigest(observed_form_)); |
| EXPECT_CALL(*mock_password_save_manager(), |
| Blocklist(PasswordStore::FormDigest(observed_form_))); |
| form_manager_->Blocklist(); |
| EXPECT_TRUE(form_manager_->IsBlocklisted()); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, MoveCredentialsToAccountStore) { |
| ON_CALL(*client_.GetPasswordFeatureManager(), IsOptedInForAccountStorage) |
| .WillByDefault(Return(true)); |
| EXPECT_CALL(*mock_password_save_manager(), |
| MoveCredentialsToAccountStore( |
| metrics_util::MoveToAccountStoreTrigger:: |
| kSuccessfulLoginWithProfileStorePassword)); |
| form_manager_->MoveCredentialsToAccountStore(); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, |
| BlockMovingCredentialsToAccountStore) { |
| const std::string kEmail = "email@gmail.com"; |
| const std::string kGaiaId = signin::GetTestGaiaIdForEmail(kEmail); |
| |
| PasswordForm saved_match(saved_match_); |
| saved_match.in_store = PasswordForm::Store::kProfileStore; |
| SetNonFederatedAndNotifyFetchCompleted({&saved_match}); |
| |
| ON_CALL(*mock_password_save_manager(), GetPendingCredentials) |
| .WillByDefault(ReturnRef(saved_match)); |
| |
| ON_CALL(client_, GetIdentityManager()) |
| .WillByDefault(Return(identity_test_env_.identity_manager())); |
| |
| identity_test_env_.SetPrimaryAccount(kEmail); |
| |
| EXPECT_CALL(*mock_password_save_manager(), |
| BlockMovingToAccountStoreFor(GaiaIdHash::FromGaiaId(kGaiaId))); |
| form_manager_->BlockMovingCredentialsToAccountStore(); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, IsNewLogin) { |
| EXPECT_CALL(*mock_password_save_manager(), IsNewLogin()); |
| form_manager_->IsNewLogin(); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, IsPasswordUpdate) { |
| EXPECT_CALL(*mock_password_save_manager(), IsPasswordUpdate()); |
| form_manager_->IsPasswordUpdate(); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, HasGeneratedPassword) { |
| EXPECT_CALL(*mock_password_save_manager(), HasGeneratedPassword()); |
| form_manager_->HasGeneratedPassword(); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, GetPendingCredentials) { |
| PasswordForm password_form; |
| EXPECT_CALL(*mock_password_save_manager(), GetPendingCredentials()) |
| .WillOnce(ReturnRef(password_form)); |
| form_manager_->GetPendingCredentials(); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, PresaveGeneratedPassword) { |
| fetcher_->NotifyFetchCompleted(); |
| EXPECT_FALSE(form_manager_->HasGeneratedPassword()); |
| form_manager_->SetGenerationPopupWasShown(PasswordGenerationType::kAutomatic); |
| PasswordForm form_with_generated_password = parsed_submitted_form_; |
| FormData& form_data = form_with_generated_password.form_data; |
| // Check that the generated password is forwarded to the save manager. |
| EXPECT_CALL(*mock_password_save_manager(), |
| PresaveGeneratedPassword(FormHasPassword( |
| form_data.fields[kPasswordFieldIndex].value))); |
| form_manager_->PresaveGeneratedPassword( |
| form_with_generated_password.form_data, |
| form_with_generated_password.password_value); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, |
| GeneratedPasswordWhichIsNotInFormData) { |
| fetcher_->NotifyFetchCompleted(); |
| // Create a password form such that |form_data| do not contain the generated |
| // password. |
| PasswordForm form_with_generated_password; |
| form_with_generated_password.form_data = submitted_form_; |
| const base::string16 generated_password = ASCIIToUTF16("gen_pw"); |
| // |password_value| should contain the generated password. |
| form_with_generated_password.password_value = generated_password; |
| // Check that the generated password is forwarded to the |
| PasswordForm updated_form; |
| EXPECT_CALL(*mock_password_save_manager(), |
| PresaveGeneratedPassword(FormHasPassword(generated_password))) |
| .WillOnce(SaveArg<0>(&updated_form)); |
| form_manager_->PresaveGeneratedPassword( |
| form_with_generated_password.form_data, |
| form_with_generated_password.password_value); |
| // Now, the password save manager should have a generated password. |
| ON_CALL(*mock_password_save_manager(), HasGeneratedPassword()) |
| .WillByDefault(Return(true)); |
| EXPECT_EQ(submitted_form_.fields[kUsernameFieldIndex].value, |
| updated_form.username_value); |
| EXPECT_TRUE(form_manager_->HasGeneratedPassword()); |
| // Check that the generated password is saved. |
| EXPECT_CALL(client_, UpdateFormManagers()); |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials(_, _, _, _, _)); |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr)); |
| EXPECT_CALL(*mock_password_save_manager(), |
| Save(FormDataPointeeEqualTo(submitted_form_), _)) |
| .WillOnce(SaveArg<1>(&updated_form)); |
| form_manager_->Save(); |
| EXPECT_EQ(submitted_form_.fields[kUsernameFieldIndex].value, |
| updated_form.username_value); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, |
| PresaveGenerationWhenParsingFails) { |
| fetcher_->NotifyFetchCompleted(); |
| // Create a password form with empty |form_data|. On this form the form parser |
| // should fail. |
| PasswordForm form_with_empty_form_data; |
| const base::string16 generated_password = ASCIIToUTF16("gen_pw"); |
| form_with_empty_form_data.password_value = generated_password; |
| // Check that nevertheless the generated password is forwarded to the |
| // PasswordSaveManager. |
| EXPECT_CALL(*mock_password_save_manager(), |
| PresaveGeneratedPassword(FormHasPassword(generated_password))); |
| form_manager_->PresaveGeneratedPassword( |
| form_with_empty_form_data.form_data, |
| form_with_empty_form_data.password_value); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, PasswordNoLongerGenerated) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->NotifyFetchCompleted(); |
| form_manager_->SetGenerationPopupWasShown(PasswordGenerationType::kManual); |
| EXPECT_CALL(*mock_password_save_manager(), PresaveGeneratedPassword(_)); |
| PasswordForm form = parsed_submitted_form_; |
| form_manager_->PresaveGeneratedPassword(form.form_data, form.password_value); |
| Mock::VerifyAndClearExpectations(mock_password_save_manager()); |
| // Now, the password save manager should have a generated password. |
| ON_CALL(*mock_password_save_manager(), HasGeneratedPassword()) |
| .WillByDefault(Return(true)); |
| EXPECT_TRUE(form_manager_->HasGeneratedPassword()); |
| // Check when the user removes the generated password on the page, it is |
| // removed from the store. |
| EXPECT_CALL(*mock_password_save_manager(), PasswordNoLongerGenerated()); |
| form_manager_->PasswordNoLongerGenerated(); |
| // Now, the password save manager should not have a generated password. |
| ON_CALL(*mock_password_save_manager(), HasGeneratedPassword()) |
| .WillByDefault(Return(false)); |
| EXPECT_FALSE(form_manager_->HasGeneratedPassword()); |
| // Check UKM metrics. |
| form_manager_.reset(); |
| ExpectedGenerationUKM expected_metrics = { |
| base::make_optional(2u) /* shown manually */, |
| 0 /* password generated */, |
| {} /* generated password is not modified */}; |
| CheckPasswordGenerationUKM(test_ukm_recorder, expected_metrics); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, |
| OnGeneratedPasswordAccepted_InvalidField) { |
| FormData form_data = observed_form_only_password_fields_; |
| // A call with a non-existent password field should not crash. |
| form_manager_->OnGeneratedPasswordAccepted(std::move(form_data), |
| autofill::FieldRendererId(12345), |
| ASCIIToUTF16("new_password")); |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, SaveHttpAuthNoHttpAuthStored) { |
| for (bool html_credentials_saved : {false, true}) { |
| SCOPED_TRACE(testing::Message("html_credentials_saved=") |
| << html_credentials_saved); |
| PasswordForm http_auth_form = parsed_observed_form_; |
| http_auth_form.scheme = PasswordForm::Scheme::kBasic; |
| // Check that no filling because no http auth credentials are stored. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| EXPECT_CALL(client_, AutofillHttpAuth(_, _)).Times(0); |
| CreateFormManagerForNonWebForm(http_auth_form); |
| std::vector<const PasswordForm*> saved_matches; |
| if (html_credentials_saved) |
| saved_matches.push_back(&saved_match_); |
| SetNonFederatedAndNotifyFetchCompleted(saved_matches); |
| base::string16 username = ASCIIToUTF16("user1"); |
| base::string16 password = ASCIIToUTF16("pass1"); |
| http_auth_form.username_value = username; |
| http_auth_form.password_value = password; |
| // Check that submitted credentials are forwarded to the password save |
| // manager. |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials(http_auth_form, _, _, true, _)); |
| ASSERT_TRUE(form_manager_->ProvisionallySaveHttpAuthForm(http_auth_form)); |
| PasswordForm updated_form; |
| // Check that the password save manager is invoked. |
| EXPECT_CALL(*mock_password_save_manager(), Save(_, http_auth_form)); |
| form_manager_->Save(); |
| } |
| } |
| |
| TEST_F(PasswordFormManagerTestWithMockedSaver, HTTPAuthAlreadySaved) { |
| PasswordForm http_auth_form = parsed_observed_form_; |
| http_auth_form.scheme = PasswordForm::Scheme::kBasic; |
| CreateFormManagerForNonWebForm(http_auth_form); |
| const base::string16 username = ASCIIToUTF16("user1"); |
| const base::string16 password = ASCIIToUTF16("pass1"); |
| http_auth_form.username_value = username; |
| http_auth_form.password_value = password; |
| EXPECT_CALL(client_, AutofillHttpAuth(http_auth_form, _)).Times(1); |
| SetNonFederatedAndNotifyFetchCompleted({&http_auth_form}); |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials(http_auth_form, _, _, true, _)); |
| ASSERT_TRUE(form_manager_->ProvisionallySaveHttpAuthForm(http_auth_form)); |
| // Check that the password save manager is invoked. |
| EXPECT_CALL(*mock_password_save_manager(), Save(_, http_auth_form)); |
| form_manager_->Save(); |
| } |
| |
| #if !defined(OS_ANDROID) |
| // Tests that username is taken during username first flow. |
| // Local heuristics on Android for username first flow is not supported. |
| TEST_F(PasswordFormManagerTestWithMockedSaver, UsernameFirstFlow) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kUsernameFirstFlow); |
| CreateFormManager(observed_form_only_password_fields_); |
| fetcher_->NotifyFetchCompleted(); |
| const base::string16 possible_username = ASCIIToUTF16("possible_username"); |
| PossibleUsernameData possible_username_data( |
| saved_match_.signon_realm, autofill::FieldRendererId(1u), |
| possible_username, base::Time::Now(), 0 /* driver_id */); |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| // Check that a username is chosen from |possible_username_data|. |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials(FormHasUsernameValue(possible_username), |
| _, _, _, _)); |
| ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_, |
| &possible_username_data)); |
| } |
| #endif // !defined(OS_ANDROID) |
| |
| // Tests that username is not taken when a possible username is not valid. |
| TEST_F(PasswordFormManagerTestWithMockedSaver, |
| UsernameFirstFlowDifferentDomains) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kUsernameFirstFlow); |
| CreateFormManager(observed_form_only_password_fields_); |
| fetcher_->NotifyFetchCompleted(); |
| base::string16 possible_username = ASCIIToUTF16("possible_username"); |
| PossibleUsernameData possible_username_data( |
| "https://another.domain.com", autofill::FieldRendererId(1u), |
| possible_username, base::Time::Now(), 0 /* driver_id */); |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| PasswordForm parsed_submitted_form; |
| EXPECT_CALL(*mock_password_save_manager(), |
| CreatePendingCredentials(_, _, _, _, _)) |
| .WillOnce(SaveArg<0>(&parsed_submitted_form)); |
| ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_, |
| &possible_username_data)); |
| EXPECT_TRUE(parsed_submitted_form.username_value.empty()); |
| } |
| |
| } // namespace |
| |
| } // namespace password_manager |