| // 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/new_password_form_manager.h" |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/test_mock_time_task_runner.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/password_form.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/password_manager/core/browser/fake_form_fetcher.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/ukm/test_ukm_recorder.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using autofill::FieldPropertiesFlags; |
| using autofill::FormData; |
| using autofill::FormFieldData; |
| using autofill::FormSignature; |
| using autofill::FormStructure; |
| using autofill::NewPasswordFormGenerationData; |
| using autofill::PasswordForm; |
| using autofill::PasswordFormFillData; |
| using autofill::ServerFieldType; |
| using base::ASCIIToUTF16; |
| using base::TestMockTimeTaskRunner; |
| using testing::_; |
| using testing::AllOf; |
| using testing::Mock; |
| using testing::NiceMock; |
| using testing::Return; |
| using testing::SaveArg; |
| using testing::SaveArgPointee; |
| |
| namespace password_manager { |
| |
| namespace { |
| |
| // Indices of username and password fields in the observed form. |
| const int kUsernameFieldIndex = 1; |
| const int kPasswordFieldIndex = 2; |
| |
| class MockPasswordManagerDriver : public StubPasswordManagerDriver { |
| public: |
| MockPasswordManagerDriver() {} |
| |
| ~MockPasswordManagerDriver() override {} |
| |
| MOCK_METHOD1(FillPasswordForm, void(const PasswordFormFillData&)); |
| MOCK_METHOD1(AllowPasswordGenerationForForm, void(const PasswordForm&)); |
| MOCK_METHOD1(FormEligibleForGenerationFound, |
| void(const autofill::NewPasswordFormGenerationData&)); |
| }; |
| |
| class MockAutofillDownloadManager : public autofill::AutofillDownloadManager { |
| public: |
| MockAutofillDownloadManager() |
| : AutofillDownloadManager(nullptr, &fake_observer) {} |
| |
| 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<std::string>& form_signatures) override {} |
| }; |
| |
| StubObserver fake_observer; |
| DISALLOW_COPY_AND_ASSIGN(MockAutofillDownloadManager); |
| }; |
| |
| class MockPasswordManagerClient : public StubPasswordManagerClient { |
| public: |
| MockPasswordManagerClient() = default; |
| ~MockPasswordManagerClient() override = default; |
| |
| MOCK_CONST_METHOD0(IsIncognito, bool()); |
| |
| MOCK_METHOD0(GetAutofillDownloadManager, |
| autofill::AutofillDownloadManager*()); |
| |
| MOCK_METHOD0(UpdateFormManagers, void()); |
| }; |
| |
| void CheckPendingCredentials(const PasswordForm& expected, |
| const PasswordForm& actual) { |
| EXPECT_EQ(expected.signon_realm, actual.signon_realm); |
| EXPECT_EQ(expected.origin, actual.origin); |
| 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.blacklisted_by_user, actual.blacklisted_by_user); |
| EXPECT_EQ(expected.form_data, actual.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) { |
| uint32_t renderer_id = |
| form.fields[index_prediction.first].unique_renderer_id; |
| ServerFieldType server_type = index_prediction.second; |
| predictions[renderer_id] = PasswordFieldPrediction{.type = server_type}; |
| } |
| FormSignature form_signature = CalculateFormSignature(form); |
| return {{form_signature, predictions}}; |
| } |
| |
| class MockFormSaver : public StubFormSaver { |
| public: |
| MockFormSaver() = default; |
| |
| ~MockFormSaver() override = default; |
| |
| // FormSaver: |
| MOCK_METHOD1(PermanentlyBlacklist, void(autofill::PasswordForm* observed)); |
| MOCK_METHOD2( |
| Save, |
| void(const autofill::PasswordForm& pending, |
| const std::map<base::string16, const PasswordForm*>& best_matches)); |
| MOCK_METHOD4( |
| Update, |
| void(const autofill::PasswordForm& pending, |
| const std::map<base::string16, const PasswordForm*>& best_matches, |
| const std::vector<autofill::PasswordForm>* credentials_to_update, |
| const autofill::PasswordForm* old_primary_key)); |
| MOCK_METHOD1(PresaveGeneratedPassword, |
| void(const autofill::PasswordForm& generated)); |
| MOCK_METHOD0(RemovePresavedPassword, void()); |
| |
| std::unique_ptr<FormSaver> Clone() override { |
| return std::make_unique<MockFormSaver>(); |
| } |
| |
| // Convenience downcasting method. |
| static MockFormSaver& Get(NewPasswordFormManager* form_manager) { |
| return *static_cast<MockFormSaver*>(form_manager->form_saver()); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockFormSaver); |
| }; |
| |
| // TODO(https://crbug.com/831123): Test sending metrics. |
| // TODO(https://crbug.com/831123): Test create pending credentials when |
| // generation happened. |
| // TODO(https://crbug.com/831123): Test create pending credentials with |
| // Credential API. |
| class NewPasswordFormManagerTest : public testing::Test { |
| public: |
| NewPasswordFormManagerTest() : task_runner_(new TestMockTimeTaskRunner) { |
| 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_.origin = origin; |
| observed_form_.action = action; |
| observed_form_.name = ASCIIToUTF16("sign-in"); |
| observed_form_.unique_renderer_id = 1; |
| 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 = 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 = 2; |
| observed_form_.fields.push_back(field); |
| |
| field.name = ASCIIToUTF16("password"); |
| field.id_attribute = field.name; |
| field.name_attribute = field.name; |
| field.form_control_type = "password"; |
| field.unique_renderer_id = 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 = 5; |
| 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"); |
| |
| saved_match_.origin = origin; |
| saved_match_.action = action; |
| saved_match_.signon_realm = "https://accounts.google.com/"; |
| saved_match_.preferred = true; |
| 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_HTML; |
| |
| psl_saved_match_ = saved_match_; |
| psl_saved_match_.origin = 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; |
| |
| blacklisted_match_ = saved_match_; |
| blacklisted_match_.blacklisted_by_user = true; |
| |
| EXPECT_CALL(client_, GetAutofillDownloadManager()) |
| .WillRepeatedly(testing::Return(&mock_autofill_download_manager_)); |
| ON_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(_, _, _, _, _, _)) |
| .WillByDefault(testing::Return(true)); |
| |
| CreateFormManager(observed_form_); |
| } |
| |
| protected: |
| MockAutofillDownloadManager mock_autofill_download_manager_; |
| FormData observed_form_; |
| FormData submitted_form_; |
| FormData observed_form_only_password_fields_; |
| PasswordForm saved_match_; |
| PasswordForm psl_saved_match_; |
| PasswordForm blacklisted_match_; |
| PasswordForm parsed_observed_form_; |
| PasswordForm parsed_submitted_form_; |
| MockPasswordManagerClient client_; |
| MockPasswordManagerDriver driver_; |
| scoped_refptr<TestMockTimeTaskRunner> task_runner_; |
| // Define |fetcher_| before |form_manager_|, because the former needs to |
| // outlive the latter. |
| std::unique_ptr<FakeFormFetcher> fetcher_; |
| std::unique_ptr<NewPasswordFormManager> form_manager_; |
| |
| // Creates NewPasswordFormManager and sets it to |form_manager_|. Along the |
| // way a new |fetcher_| is created. |
| void CreateFormManager(const FormData& observed_form) { |
| fetcher_.reset(new FakeFormFetcher()); |
| fetcher_->Fetch(); |
| form_manager_.reset(new NewPasswordFormManager( |
| &client_, driver_.AsWeakPtr(), observed_form, fetcher_.get(), |
| std::make_unique<NiceMock<MockFormSaver>>(), nullptr)); |
| } |
| }; |
| |
| TEST_F(NewPasswordFormManagerTest, DoesManage) { |
| EXPECT_TRUE(form_manager_->DoesManage(observed_form_, &driver_)); |
| // Forms on other drivers are not considered managed. |
| EXPECT_FALSE(form_manager_->DoesManage(observed_form_, nullptr)); |
| FormData another_form = observed_form_; |
| another_form.is_form_tag = false; |
| EXPECT_FALSE(form_manager_->DoesManage(another_form, &driver_)); |
| |
| // On non-iOS platforms unique_renderer_id is the form identifier. |
| another_form = observed_form_; |
| another_form.unique_renderer_id = observed_form_.unique_renderer_id + 1; |
| #if defined(OS_IOS) |
| EXPECT_TRUE(form_manager_->DoesManage(another_form, &driver_)); |
| #else |
| EXPECT_FALSE(form_manager_->DoesManage(another_form, &driver_)); |
| #endif |
| |
| // On iOS platforms form name is the form identifier. |
| another_form = observed_form_; |
| another_form.name = observed_form_.name + ASCIIToUTF16("1"); |
| #if defined(OS_IOS) |
| EXPECT_FALSE(form_manager_->DoesManage(another_form, &driver_)); |
| #else |
| EXPECT_TRUE(form_manager_->DoesManage(another_form, &driver_)); |
| #endif |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, 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.push_back(FormFieldData()); |
| 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_F(NewPasswordFormManagerTest, Autofill) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| |
| CreateFormManager(observed_form_); |
| EXPECT_CALL(driver_, AllowPasswordGenerationForForm(_)); |
| EXPECT_CALL(driver_, FormEligibleForGenerationFound(_)).Times(0); |
| PasswordFormFillData fill_data; |
| EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data)); |
| CreateFormManager(observed_form_); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| task_runner_->FastForwardUntilNoTasksRemain(); |
| |
| EXPECT_EQ(observed_form_.origin, fill_data.origin); |
| EXPECT_FALSE(fill_data.wait_for_username); |
| 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_F(NewPasswordFormManagerTest, AutofillNotMoreThan5Times) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| |
| EXPECT_CALL(driver_, FillPasswordForm(_)); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| task_runner_->FastForwardUntilNoTasksRemain(); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| for (size_t i = 0; i < NewPasswordFormManager::kMaxTimesAutofill - 1; ++i) { |
| EXPECT_CALL(driver_, FillPasswordForm(_)); |
| form_manager_->Fill(); |
| Mock::VerifyAndClearExpectations(&driver_); |
| } |
| |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| form_manager_->Fill(); |
| } |
| |
| // NewPasswordFormManager 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_F(NewPasswordFormManagerTest, AutofillSignUpForm) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| // 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)); |
| |
| NewPasswordFormGenerationData generation_data; |
| EXPECT_CALL(driver_, FormEligibleForGenerationFound(_)) |
| .WillOnce(SaveArg<0>(&generation_data)); |
| |
| CreateFormManager(observed_form_); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| task_runner_->FastForwardUntilNoTasksRemain(); |
| constexpr uint32_t kNoID = FormFieldData::kNotSetFormControlRendererId; |
| EXPECT_EQ(kNoID, fill_data.password_field.unique_renderer_id); |
| EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value); |
| #if defined(OS_IOS) |
| EXPECT_EQ(ASCIIToUTF16("sign-in"), generation_data.form_name); |
| EXPECT_EQ(ASCIIToUTF16("password"), generation_data.new_password_element); |
| EXPECT_EQ(base::string16(), generation_data.confirmation_password_element); |
| #else |
| EXPECT_EQ(observed_form_.fields.back().unique_renderer_id, |
| generation_data.new_password_renderer_id); |
| EXPECT_EQ(kNoID, generation_data.confirmation_password_renderer_id); |
| #endif |
| } |
| |
| // Check that generation signal is sent the the renderer when new password |
| // fields are marked with autocomplete attribute. |
| TEST_F(NewPasswordFormManagerTest, GenerationOnNewAndConfirmPasswordFields) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| // Make |observed_form_| to be sign-up form. |
| observed_form_.fields.back().autocomplete_attribute = "new-password"; |
| const uint32_t new_password_render_id = |
| observed_form_.fields.back().unique_renderer_id; |
| // Add a confirmation field. |
| FormFieldData field; |
| const uint32_t confirm_password_render_id = new_password_render_id + 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); |
| |
| NewPasswordFormGenerationData generation_data; |
| EXPECT_CALL(driver_, FormEligibleForGenerationFound(_)) |
| .WillOnce(SaveArg<0>(&generation_data)); |
| |
| CreateFormManager(observed_form_); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| task_runner_->FastForwardUntilNoTasksRemain(); |
| #if defined(OS_IOS) |
| EXPECT_EQ(ASCIIToUTF16("sign-in"), generation_data.form_name); |
| EXPECT_EQ(ASCIIToUTF16("password"), generation_data.new_password_element); |
| EXPECT_EQ(base::string16(), generation_data.confirmation_password_element); |
| #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_F(NewPasswordFormManagerTest, AutofillWithBlacklistedMatch) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| PasswordFormFillData fill_data; |
| EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data)); |
| fetcher_->SetNonFederated({&saved_match_, &blacklisted_match_}, 0u); |
| |
| task_runner_->FastForwardUntilNoTasksRemain(); |
| |
| EXPECT_EQ(observed_form_.origin, fill_data.origin); |
| EXPECT_EQ(saved_match_.username_value, fill_data.username_field.value); |
| EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, SetSubmitted) { |
| EXPECT_FALSE(form_manager_->is_submitted()); |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| 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_->ProvisionallySaveIfIsManaged(another_form, &driver_)); |
| EXPECT_TRUE(form_manager_->is_submitted()); |
| #endif |
| |
| form_manager_->set_not_submitted(); |
| EXPECT_FALSE(form_manager_->is_submitted()); |
| |
| another_form.unique_renderer_id = observed_form_.unique_renderer_id + 1; |
| EXPECT_FALSE( |
| form_manager_->ProvisionallySaveIfIsManaged(another_form, &driver_)); |
| EXPECT_FALSE(form_manager_->is_submitted()); |
| |
| // An identical form but in a different frame (represented here by a null |
| // driver) is also not considered managed. |
| EXPECT_FALSE( |
| form_manager_->ProvisionallySaveIfIsManaged(observed_form_, nullptr)); |
| EXPECT_FALSE(form_manager_->is_submitted()); |
| |
| // Check if the subbmitted form can not be parsed then form manager does not |
| // became submitted. |
| FormData malformed_form = submitted_form_; |
| malformed_form.fields.clear(); |
| EXPECT_FALSE( |
| form_manager_->ProvisionallySaveIfIsManaged(malformed_form, &driver_)); |
| EXPECT_FALSE(form_manager_->is_submitted()); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, SetSubmittedMultipleTimes) { |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| EXPECT_TRUE(form_manager_->is_submitted()); |
| |
| // Make the submitted form to be invalid password form. |
| submitted_form_.fields.clear(); |
| |
| // Expect that |form_manager_| is still in submitted state because the first |
| // time the submited form was valid. |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| EXPECT_TRUE(form_manager_->is_submitted()); |
| EXPECT_TRUE(form_manager_->GetSubmittedForm()); |
| } |
| |
| // Tests that when NewPasswordFormManager receives saved matches it waits for |
| // server predictions and fills on receving them. |
| TEST_F(NewPasswordFormManagerTest, ServerPredictionsWithinDelay) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| |
| // Expects no filling on save matches receiving. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| 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); |
| } |
| |
| // Tests that NewPasswordFormManager fills after some delay even without |
| // server predictions. |
| TEST_F(NewPasswordFormManagerTest, ServerPredictionsAfterDelay) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| // Expect filling after passing filling delay. |
| |
| // Simulate passing filling delay. |
| task_runner_->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_runner_->FastForwardUntilNoTasksRemain(); |
| } |
| |
| // Tests that filling happens immediately if server predictions are received |
| // before saved matches. |
| TEST_F(NewPasswordFormManagerTest, ServerPredictionsBeforeFetcher) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| // 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); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| } |
| |
| // Tests creating pending credentials when the password store is empty. |
| TEST_F(NewPasswordFormManagerTest, CreatePendingCredentialsEmptyStore) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| CheckPendingCredentials(parsed_submitted_form_, |
| form_manager_->GetPendingCredentials()); |
| EXPECT_EQ(UserAction::kOverrideUsernameAndPassword, |
| form_manager_->GetMetricsRecorder()->GetUserAction()); |
| } |
| |
| // Tests creating pending credentials when new credentials are submitted and the |
| // store has another credentials saved. |
| TEST_F(NewPasswordFormManagerTest, CreatePendingCredentialsNewCredentials) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| CheckPendingCredentials(parsed_submitted_form_, |
| form_manager_->GetPendingCredentials()); |
| EXPECT_EQ(UserAction::kOverrideUsernameAndPassword, |
| form_manager_->GetMetricsRecorder()->GetUserAction()); |
| } |
| |
| // Tests that when submitted credentials are equal to already saved one then |
| // pending credentials equal to saved match. |
| TEST_F(NewPasswordFormManagerTest, CreatePendingCredentialsAlreadySaved) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| CheckPendingCredentials(/* expected */ saved_match_, |
| form_manager_->GetPendingCredentials()); |
| EXPECT_EQ(is_incognito ? UserAction::kChoose : UserAction::kNone, |
| form_manager_->GetMetricsRecorder()->GetUserAction()); |
| } |
| } |
| |
| // Tests that when submitted credentials are equal to already saved PSL |
| // credentials. |
| TEST_F(NewPasswordFormManagerTest, CreatePendingCredentialsPSLMatchSaved) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| PasswordForm expected = saved_match_; |
| |
| saved_match_.origin = GURL("https://m.accounts.google.com/auth"); |
| saved_match_.signon_realm = "https://m.accounts.google.com/"; |
| saved_match_.is_public_suffix_match = true; |
| |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| submitted_form_.fields[kPasswordFieldIndex].value = |
| saved_match_.password_value; |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_EQ(UserAction::kChoosePslMatch, |
| form_manager_->GetMetricsRecorder()->GetUserAction()); |
| } |
| |
| // Tests creating pending credentials when new credentials are different only in |
| // password with already saved one. |
| TEST_F(NewPasswordFormManagerTest, CreatePendingCredentialsPasswordOverriden) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_EQ(UserAction::kOverridePassword, |
| form_manager_->GetMetricsRecorder()->GetUserAction()); |
| } |
| |
| // Tests that when submitted credentials are equal to already saved one then |
| // pending credentials equal to saved match. |
| TEST_F(NewPasswordFormManagerTest, CreatePendingCredentialsUpdate) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_EQ(UserAction::kOverridePassword, |
| form_manager_->GetMetricsRecorder()->GetUserAction()); |
| } |
| |
| // Tests creating pending credentials when a change password form is submitted |
| // and there are multipe saved forms. |
| TEST_F(NewPasswordFormManagerTest, |
| CreatePendingCredentialsUpdateMultipleSaved) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| PasswordForm another_saved_match = saved_match_; |
| another_saved_match.username_value += ASCIIToUTF16("1"); |
| fetcher_->SetNonFederated({&saved_match_, &another_saved_match}, 0u); |
| |
| FormData submitted_form = observed_form_only_password_fields_; |
| submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); |
| submitted_form.fields[1].value = ASCIIToUTF16("verystrongpassword"); |
| |
| PasswordForm expected; |
| expected.origin = observed_form_.origin; |
| expected.action = observed_form_.action; |
| expected.password_value = ASCIIToUTF16("verystrongpassword"); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| } |
| |
| // Tests creating pending credentials when the password field has an empty name. |
| TEST_F(NewPasswordFormManagerTest, CreatePendingCredentialsEmptyName) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(anonymous_signup, &driver_)); |
| 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_F(NewPasswordFormManagerTest, NoCrashOnNonPasswordForm) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| FormData form_without_password_fields = observed_form_; |
| // Remove the password field. |
| form_without_password_fields.fields.resize(kPasswordFieldIndex); |
| CreateFormManager(form_without_password_fields); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| FormData submitted_form = observed_form_; |
| submitted_form.fields[kUsernameFieldIndex].value = ASCIIToUTF16("username"); |
| submitted_form.fields[kPasswordFieldIndex].value = ASCIIToUTF16("password"); |
| |
| // Expect no crash. |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form, &driver_); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, IsEqualToSubmittedForm) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| |
| observed_form_.unique_renderer_id += 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_F(NewPasswordFormManagerTest, SaveNewCredentials) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm saved_form; |
| std::map<base::string16, 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.origin.GetOrigin().spec(); |
| EXPECT_EQ(submitted_form.origin, saved_form.origin); |
| 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_TRUE(saved_form.preferred); |
| |
| EXPECT_EQ(submitted_form.fields[kUsernameFieldIndex].name, |
| saved_form.username_element); |
| EXPECT_EQ(submitted_form.fields[kPasswordFieldIndex].name, |
| saved_form.password_element); |
| EXPECT_EQ(1u, best_matches.size()); |
| base::string16 saved_username = saved_match_.username_value; |
| ASSERT_TRUE(best_matches.find(saved_username) != best_matches.end()); |
| EXPECT_EQ(saved_match_, *best_matches[saved_username]); |
| |
| // 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_F(NewPasswordFormManagerTest, SavePSLToAlreadySaved) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&psl_saved_match_}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| EXPECT_TRUE(form_manager_->IsPendingCredentialsPublicSuffixMatch()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm saved_form; |
| std::map<base::string16, 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.origin, saved_form.origin); |
| EXPECT_EQ(GetSignonRealm(submitted_form.origin), 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_TRUE(saved_form.preferred); |
| |
| EXPECT_EQ(1u, best_matches.size()); |
| base::string16 saved_username = psl_saved_match_.username_value; |
| ASSERT_TRUE(best_matches.find(saved_username) != best_matches.end()); |
| EXPECT_EQ(psl_saved_match_, *best_matches[saved_username]); |
| } |
| |
| // Tests that when credentials with already saved username but with a new |
| // password are submitted, then the saved password is updated. |
| TEST_F(NewPasswordFormManagerTest, OverridePassword) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_TRUE(form_manager_->IsPasswordOverridden()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm updated_form; |
| std::map<base::string16, const PasswordForm*> best_matches; |
| std::vector<PasswordForm> credentials_to_update; |
| EXPECT_CALL(form_saver, Update(_, _, _, nullptr)) |
| .WillOnce(DoAll(SaveArg<0>(&updated_form), SaveArg<1>(&best_matches), |
| SaveArgPointee<2>(&credentials_to_update))); |
| |
| form_manager_->Save(); |
| |
| EXPECT_TRUE(ArePasswordFormUniqueKeyEqual(saved_match_, updated_form)); |
| EXPECT_TRUE(updated_form.preferred); |
| EXPECT_EQ(new_password, updated_form.password_value); |
| EXPECT_EQ(1u, best_matches.size()); |
| ASSERT_TRUE(best_matches.find(username) != best_matches.end()); |
| EXPECT_EQ(saved_match_, *best_matches[username]); |
| EXPECT_TRUE(credentials_to_update.empty()); |
| } |
| |
| // Tests that when the user changes password on a change password form then the |
| // saved password is updated. |
| TEST_F(NewPasswordFormManagerTest, UpdatePasswordOnChangePasswordForm) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| CreateFormManager(observed_form_only_password_fields_); |
| PasswordForm not_best_saved_match = saved_match_; |
| not_best_saved_match.preferred = false; |
| PasswordForm saved_match_another_username = saved_match_; |
| saved_match_another_username.username_value += ASCIIToUTF16("1"); |
| |
| fetcher_->SetNonFederated( |
| {&saved_match_, ¬_best_saved_match, &saved_match_another_username}, |
| 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_FALSE(form_manager_->IsPasswordOverridden()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm updated_form; |
| std::map<base::string16, const PasswordForm*> best_matches; |
| std::vector<PasswordForm> credentials_to_update; |
| EXPECT_CALL(form_saver, Update(_, _, _, nullptr)) |
| .WillOnce(DoAll(SaveArg<0>(&updated_form), SaveArg<1>(&best_matches), |
| SaveArgPointee<2>(&credentials_to_update))); |
| |
| form_manager_->Save(); |
| |
| EXPECT_TRUE(ArePasswordFormUniqueKeyEqual(saved_match_, updated_form)); |
| EXPECT_TRUE(updated_form.preferred); |
| EXPECT_EQ(new_password, updated_form.password_value); |
| |
| EXPECT_EQ(2u, best_matches.size()); |
| base::string16 username = saved_match_.username_value; |
| ASSERT_TRUE(best_matches.find(username) != best_matches.end()); |
| EXPECT_EQ(saved_match_, *best_matches[username]); |
| base::string16 another_username = saved_match_another_username.username_value; |
| ASSERT_TRUE(best_matches.find(another_username) != best_matches.end()); |
| EXPECT_EQ(saved_match_another_username, *best_matches[another_username]); |
| |
| ASSERT_EQ(1u, credentials_to_update.size()); |
| not_best_saved_match.password_value = new_password; |
| EXPECT_EQ(not_best_saved_match, credentials_to_update[0]); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, VotesUploadingOnPasswordUpdate) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| CreateFormManager(observed_form_only_password_fields_); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| EXPECT_TRUE(form_manager_->IsPasswordUpdate()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm updated_form; |
| std::map<base::string16, const PasswordForm*> best_matches; |
| std::vector<PasswordForm> credentials_to_update; |
| EXPECT_CALL(form_saver, Update(_, _, _, nullptr)) |
| .WillOnce(DoAll(SaveArg<0>(&updated_form), SaveArg<1>(&best_matches), |
| SaveArgPointee<2>(&credentials_to_update))); |
| |
| std::map<base::string16, autofill::ServerFieldType> expected_types; |
| expected_types[ASCIIToUTF16("password")] = autofill::PASSWORD; |
| expected_types[ASCIIToUTF16("password2")] = autofill::NEW_PASSWORD; |
| |
| testing::InSequence in_sequence; |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(UploadedAutofillTypesAre(expected_types), |
| false, _, _, true, nullptr)); |
| // An unrelated |FIRST_USE| vote. |
| EXPECT_CALL(mock_autofill_download_manager_, |
| StartUploadRequest(_, _, _, _, _, _)); |
| |
| form_manager_->Save(); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, UpdateUsernameEmptyStore) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_); |
| |
| 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_->UpdateUsername(new_username); |
| |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, UpdateUsernameToAlreadyExisting) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_); |
| |
| 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_->UpdateUsername(new_username); |
| |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_TRUE(form_manager_->IsPasswordOverridden()); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, UpdatePasswordEmptyStore) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_); |
| |
| 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_->UpdatePasswordValue(new_password); |
| |
| CheckPendingCredentials(expected, form_manager_->GetPendingCredentials()); |
| EXPECT_TRUE(form_manager_->IsNewLogin()); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, UpdatePasswordToAlreadyExisting) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| // Emulate submitting form with known username and different password. |
| submitted_form_.fields[kUsernameFieldIndex].value = |
| saved_match_.username_value; |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_); |
| |
| // The user changes password to already saved one. |
| base::string16 password = saved_match_.password_value; |
| form_manager_->UpdatePasswordValue(password); |
| |
| CheckPendingCredentials(saved_match_, form_manager_->GetPendingCredentials()); |
| EXPECT_FALSE(form_manager_->IsNewLogin()); |
| EXPECT_FALSE(form_manager_->IsPasswordOverridden()); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, PermanentlyBlacklist) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| PasswordForm* new_blacklisted_form = nullptr; |
| EXPECT_CALL(form_saver, PermanentlyBlacklist(_)) |
| .WillOnce(SaveArg<0>(&new_blacklisted_form)); |
| |
| form_manager_->PermanentlyBlacklist(); |
| ASSERT_TRUE(new_blacklisted_form); |
| EXPECT_EQ(observed_form_.origin, new_blacklisted_form->origin); |
| EXPECT_EQ(GetSignonRealm(observed_form_.origin), |
| new_blacklisted_form->signon_realm); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, Clone) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| |
| // Provisionally save in order to create pending credentials. |
| ASSERT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| |
| std::unique_ptr<NewPasswordFormManager> 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_->metrics_recorder(), |
| cloned_manager->metrics_recorder()); |
| |
| 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_F(NewPasswordFormManagerTest, RecordReadonlyWhenFilling) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| EXPECT_CALL(driver_, AllowPasswordGenerationForForm(_)); |
| EXPECT_CALL(driver_, FillPasswordForm(_)); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| task_runner_->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_F(NewPasswordFormManagerTest, RecordReadonlyWhenFilling_ParsingFailed) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| |
| 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; |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| task_runner_->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_F(NewPasswordFormManagerTest, RecordReadonlyWhenSaving) { |
| // The scoped context is needed for the UKM recorder. |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| |
| // 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_F(NewPasswordFormManagerTest, RecordReadonlyWhenSaving_ParsingFailed) { |
| // The scoped context is needed for the UKM recorder. |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| FormData malformed_form = submitted_form_; |
| malformed_form.fields.clear(); |
| EXPECT_FALSE( |
| form_manager_->ProvisionallySaveIfIsManaged(malformed_form, &driver_)); |
| |
| // 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_F(NewPasswordFormManagerTest, PresaveGeneratedPasswordEmptyStore) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->SetNonFederated({}, 0u); |
| |
| EXPECT_FALSE(form_manager_->HasGeneratedPassword()); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| form_manager_->SetGenerationPopupWasShown( |
| true /* generation_popup_was_shown */, false /* is_manual_generation */); |
| |
| // Check that the generated password is presaved. |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, PresaveGeneratedPassword(_)) |
| .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); |
| |
| 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, PresaveGeneratedPassword(_)) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| |
| form_manager_->PresaveGeneratedPassword(form_with_generated_password); |
| |
| 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_F(NewPasswordFormManagerTest, PresaveGenerated_ModifiedUsername) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->SetNonFederated({}, 0u); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| form_manager_->SetGenerationPopupWasShown( |
| true /* generation_popup_was_shown */, false /* is_manual_generation */); |
| |
| // Check that the generated password is presaved. |
| EXPECT_CALL(form_saver, PresaveGeneratedPassword(_)); |
| PasswordForm form_with_generated_password = parsed_submitted_form_; |
| FormData& form_data = form_with_generated_password.form_data; |
| form_manager_->PresaveGeneratedPassword(form_with_generated_password); |
| 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; |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, PresaveGeneratedPassword(_)) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| |
| form_manager_->PresaveGeneratedPassword(form_with_generated_password); |
| |
| 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_F(NewPasswordFormManagerTest, GeneratedPasswordWhichIsNotInFormData) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| 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, PresaveGeneratedPassword(_)) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| |
| form_manager_->PresaveGeneratedPassword(form_with_generated_password); |
| 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, Save(_, _)).WillOnce(SaveArg<0>(&saved_form)); |
| EXPECT_CALL(client_, UpdateFormManagers()); |
| |
| EXPECT_TRUE( |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_)); |
| form_manager_->Save(); |
| |
| EXPECT_EQ(submitted_form_.fields[kUsernameFieldIndex].value, |
| saved_form.username_value); |
| EXPECT_EQ(generated_password, saved_form.password_value); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, PresaveGenerationWhenParsingFails) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({}, 0u); |
| 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, PresaveGeneratedPassword(_)) |
| .WillOnce(SaveArg<0>(&saved_form)); |
| form_manager_->PresaveGeneratedPassword(form_with_empty_form_data); |
| EXPECT_EQ(generated_password, saved_form.password_value); |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, PasswordNoLongerGenerated) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->SetNonFederated({}, 0u); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| form_manager_->SetGenerationPopupWasShown( |
| true /* generation_popup_was_shown */, true /* is_manual_generation */); |
| |
| EXPECT_CALL(form_saver, PresaveGeneratedPassword(_)); |
| |
| PasswordForm form = parsed_submitted_form_; |
| form_manager_->PresaveGeneratedPassword(form); |
| 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, RemovePresavedPassword()); |
| 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_F(NewPasswordFormManagerTest, PresaveGeneratedPasswordExistingCredential) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| |
| form_manager_->SetGenerationPopupWasShown( |
| true /* generation_popup_was_shown */, false /* is_manual_generation */); |
| |
| // Check that the generated password is presaved. |
| PasswordForm saved_form; |
| EXPECT_CALL(form_saver, PresaveGeneratedPassword(_)) |
| .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); |
| |
| 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_F(NewPasswordFormManagerTest, 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_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.fields[kPasswordFieldIndex].value += ASCIIToUTF16("1"); |
| submitted_form.password_value = form_data.fields[kPasswordFieldIndex].value; |
| form_manager_->PresaveGeneratedPassword(submitted_form); |
| 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.fields[kPasswordFieldIndex].value += ASCIIToUTF16("2"); |
| submitted_form.password_value = form_data.fields[kPasswordFieldIndex].value; |
| form_manager_->PresaveGeneratedPassword(submitted_form); |
| form_manager_->PasswordNoLongerGenerated(); |
| form_manager_.reset(); |
| histogram_tester.ExpectUniqueSample( |
| "PasswordGeneration.UserDecision", |
| GeneratedPasswordStatus::kPasswordDeleted, 1); |
| } |
| } |
| |
| TEST_F(NewPasswordFormManagerTest, FillForm) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| |
| for (bool observed_form_changed : {false, true}) { |
| SCOPED_TRACE(testing::Message("observed_form_changed=") |
| << observed_form_changed); |
| CreateFormManager(observed_form_); |
| EXPECT_CALL(driver_, FillPasswordForm(_)); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| task_runner_->FastForwardUntilNoTasksRemain(); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| FormData form = observed_form_; |
| |
| if (observed_form_changed) { |
| form.fields[kUsernameFieldIndex].unique_renderer_id += 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 += 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_F(NewPasswordFormManagerTest, FillFormWaitForServerPredictions) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| FormData changed_form = observed_form_; |
| |
| changed_form.fields[kUsernameFieldIndex].unique_renderer_id += 1000; |
| changed_form.fields[kPasswordFieldIndex].unique_renderer_id += 1000; |
| |
| // Check that no filling until server predicions or filling timeout |
| // expiration. |
| EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0); |
| form_manager_->FillForm(changed_form); |
| Mock::VerifyAndClearExpectations(&driver_); |
| |
| // 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_runner_->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_F(NewPasswordFormManagerTest, Update) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| |
| PasswordForm not_best_saved_match = saved_match_; |
| not_best_saved_match.preferred = false; |
| PasswordForm saved_match_another_username = saved_match_; |
| saved_match_another_username.username_value += ASCIIToUTF16("1"); |
| fetcher_->SetNonFederated({&saved_match_, &saved_match_another_username}, 0u); |
| |
| 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_->ProvisionallySaveIfIsManaged(submitted_form, &driver_)); |
| |
| MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get()); |
| PasswordForm updated_form; |
| std::map<base::string16, const PasswordForm*> best_matches; |
| std::vector<PasswordForm> credentials_to_update; |
| EXPECT_CALL(form_saver, Update(_, _, _, nullptr)) |
| .WillOnce(DoAll(SaveArg<0>(&updated_form), SaveArg<1>(&best_matches), |
| SaveArgPointee<2>(&credentials_to_update))); |
| EXPECT_CALL(client_, UpdateFormManagers()); |
| |
| form_manager_->Update(saved_match_); |
| |
| EXPECT_TRUE(ArePasswordFormUniqueKeyEqual(saved_match_, updated_form)); |
| EXPECT_TRUE(updated_form.preferred); |
| EXPECT_EQ(new_password, updated_form.password_value); |
| EXPECT_EQ(2u, best_matches.size()); |
| ASSERT_TRUE(best_matches.find(username) != best_matches.end()); |
| EXPECT_EQ(saved_match_, *best_matches[username]); |
| EXPECT_TRUE(credentials_to_update.empty()); |
| } |
| |
| // TODO(https://crbug.com/918846): implement FillingAssistance metric on iOS. |
| #if defined(OS_IOS) |
| #define MAYBE_FillingAssistanceMetric DISABLED_FillingAssistanceMetric |
| #else |
| #define MAYBE_FillingAssistanceMetric FillingAssistanceMetric |
| #endif |
| TEST_F(NewPasswordFormManagerTest, MAYBE_FillingAssistanceMetric) { |
| TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get()); |
| fetcher_->SetNonFederated({&saved_match_}, 0u); |
| |
| // 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::AUTOFILLED_ON_USER_TRIGGER; |
| submitted_form_.fields[kPasswordFieldIndex].value = |
| saved_match_.password_value; |
| submitted_form_.fields[kPasswordFieldIndex].properties_mask = |
| FieldPropertiesFlags::AUTOFILLED_ON_USER_TRIGGER; |
| |
| base::HistogramTester histogram_tester; |
| // Simulate successful submission. |
| form_manager_->ProvisionallySaveIfIsManaged(submitted_form_, &driver_); |
| form_manager_->GetMetricsRecorder()->LogSubmitPassed(); |
| |
| form_manager_.reset(); |
| histogram_tester.ExpectUniqueSample( |
| "PasswordManager.FillingAssistance", |
| PasswordFormMetricsRecorder::FillingAssistance::kManual, 1); |
| } |
| |
| } // namespace |
| |
| } // namespace password_manager |