diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-31 16:33:43 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-02-06 16:33:22 +0000 |
commit | da51f56cc21233c2d30f0fe0d171727c3102b2e0 (patch) | |
tree | 4e579ab70ce4b19bee7984237f3ce05a96d59d83 /chromium/components/password_manager/core | |
parent | c8c2d1901aec01e934adf561a9fdf0cc776cdef8 (diff) | |
download | qtwebengine-chromium-da51f56cc21233c2d30f0fe0d171727c3102b2e0.tar.gz |
BASELINE: Update Chromium to 65.0.3525.40
Also imports missing submodules
Change-Id: I36901b7c6a325cda3d2c10cedb2186c25af3b79b
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/password_manager/core')
53 files changed, 1082 insertions, 800 deletions
diff --git a/chromium/components/password_manager/core/browser/BUILD.gn b/chromium/components/password_manager/core/browser/BUILD.gn index ae73f912d27..d4f2b3c4bf6 100644 --- a/chromium/components/password_manager/core/browser/BUILD.gn +++ b/chromium/components/password_manager/core/browser/BUILD.gn @@ -49,7 +49,6 @@ static_library("browser") { "credentials_filter.h", "export/csv_writer.cc", "export/csv_writer.h", - "export/destination.h", "export/password_csv_writer.cc", "export/password_csv_writer.h", "export/password_manager_exporter.cc", @@ -160,11 +159,8 @@ static_library("browser") { "password_reuse_detector_consumer.cc", "password_reuse_detector_consumer.h", ] - - if (is_win || is_mac || (is_linux && !is_chromeos)) { + if (is_win || is_mac || is_linux || is_chromeos) { sources += [ - "hash_password_manager.cc", - "hash_password_manager.h", "password_store_signin_notifier.cc", "password_store_signin_notifier.h", ] @@ -176,6 +172,7 @@ static_library("browser") { "//components/sync", ] deps = [ + ":hash_password_manager", ":proto", "//base", "//base:i18n", @@ -193,7 +190,6 @@ static_library("browser") { "//components/url_formatter", "//components/variations", "//components/webdata/common", - "//crypto", "//google_apis", "//net", "//services/metrics/public/cpp:metrics_cpp", @@ -229,6 +225,20 @@ proto_library("proto") { ] } +static_library("hash_password_manager") { + sources = [ + "hash_password_manager.cc", + "hash_password_manager.h", + ] + deps = [ + "//base:base", + "//components/os_crypt", + "//components/password_manager/core/common", + "//components/prefs", + "//crypto", + ] +} + static_library("test_support") { testonly = true sources = [ @@ -376,10 +386,13 @@ source_set("unit_tests") { "password_reuse_detector_unittest.cc", ] } - if (is_win || is_mac || (is_linux && !is_chromeos)) { + + if (is_win || is_mac || is_linux || is_chromeos) { sources += [ "hash_password_manager_unittest.cc" ] } + deps = [ + ":hash_password_manager", ":test_support", ":unit_tests_bundle_data", "//base/test:test_support", diff --git a/chromium/components/password_manager/core/browser/DEPS b/chromium/components/password_manager/core/browser/DEPS index 0a0aeeabe7c..abf89f4cae0 100644 --- a/chromium/components/password_manager/core/browser/DEPS +++ b/chromium/components/password_manager/core/browser/DEPS @@ -11,6 +11,7 @@ include_rules = [ "+crypto", "+google_apis", "+grit", + "+net", "+services/metrics/public/cpp", ] diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc index eac6188b986..83e9f38d65b 100644 --- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc +++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc @@ -11,8 +11,8 @@ #include <string> #include <utility> +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" -#include "base/metrics/sparse_histogram.h" #include "components/password_manager/core/browser/android_affiliation/affiliation_api.pb.h" #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h" #include "components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h" @@ -46,11 +46,11 @@ void ReportStatistics(AffiliationFetchResult result, UMA_HISTOGRAM_ENUMERATION("PasswordManager.AffiliationFetcher.FetchResult", result, AFFILIATION_FETCH_RESULT_MAX); if (fetcher) { - UMA_HISTOGRAM_SPARSE_SLOWLY( + base::UmaHistogramSparse( "PasswordManager.AffiliationFetcher.FetchHttpResponseCode", fetcher->GetResponseCode()); // Network error codes are negative. See: src/net/base/net_error_list.h. - UMA_HISTOGRAM_SPARSE_SLOWLY( + base::UmaHistogramSparse( "PasswordManager.AffiliationFetcher.FetchErrorCode", -fetcher->GetStatus().error()); } diff --git a/chromium/components/password_manager/core/browser/credential_manager_impl.cc b/chromium/components/password_manager/core/browser/credential_manager_impl.cc index cb80feeb77d..c8d85dd967d 100644 --- a/chromium/components/password_manager/core/browser/credential_manager_impl.cc +++ b/chromium/components/password_manager/core/browser/credential_manager_impl.cc @@ -103,7 +103,7 @@ void CredentialManagerImpl::Get(CredentialMediationRequirement mediation, if (pending_request_ || !store) { // Callback error. std::move(callback).Run( - pending_request_ ? CredentialManagerError::PENDINGREQUEST + pending_request_ ? CredentialManagerError::PENDING_REQUEST : CredentialManagerError::PASSWORDSTOREUNAVAILABLE, base::nullopt); LogCredentialManagerGetResult(metrics_util::CREDENTIAL_MANAGER_GET_REJECTED, @@ -244,11 +244,7 @@ void CredentialManagerImpl::OnProvisionalSaveComplete() { // 'skip_zero_click' state, as we've gotten an explicit signal that the page // understands the credential management API and so can be trusted to notify // us when they sign the user out. - auto best_match = form_manager_->best_matches().find(form.username_value); - // NOTE: We can't use DCHECK_NE here, since std::map<>::iterator does not - // support operator<<. - DCHECK(best_match != form_manager_->best_matches().end()); - form_manager_->Update(*best_match->second); + form_manager_->Update(form_manager_->pending_credentials()); return; } diff --git a/chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc b/chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc index 4a09597d5ec..46fca823813 100644 --- a/chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc +++ b/chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc @@ -457,7 +457,13 @@ TEST_F(CredentialManagerImplTest, StoreFederatedAfterPassword) { } TEST_F(CredentialManagerImplTest, CredentialManagerStoreOverwrite) { + // Add an unrelated form to complicate the task. + origin_path_form_.preferred = true; + store_->AddLogin(origin_path_form_); // Populate the PasswordStore with a form. + form_.preferred = false; + form_.display_name = base::ASCIIToUTF16("Old Name"); + form_.icon_url = GURL(); store_->AddLogin(form_); RunAllPendingTasks(); @@ -465,6 +471,8 @@ TEST_F(CredentialManagerImplTest, CredentialManagerStoreOverwrite) { // the password without prompting the user. CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD); info.password = base::ASCIIToUTF16("Totally new password."); + info.name = base::ASCIIToUTF16("New Name"); + info.icon = GURL("https://example.com/icon.png"); EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(_)).Times(0); EXPECT_CALL(*client_, NotifyStorePasswordCalled()); bool called = false; @@ -478,9 +486,15 @@ TEST_F(CredentialManagerImplTest, CredentialManagerStoreOverwrite) { TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); EXPECT_EQ(1U, passwords.size()); - EXPECT_EQ(1U, passwords[form_.signon_realm].size()); + EXPECT_EQ(2U, passwords[form_.signon_realm].size()); + origin_path_form_.preferred = false; + EXPECT_EQ(origin_path_form_, passwords[form_.signon_realm][0]); EXPECT_EQ(base::ASCIIToUTF16("Totally new password."), - passwords[form_.signon_realm][0].password_value); + passwords[form_.signon_realm][1].password_value); + EXPECT_EQ(base::ASCIIToUTF16("New Name"), + passwords[form_.signon_realm][1].display_name); + EXPECT_EQ(GURL("https://example.com/icon.png"), + passwords[form_.signon_realm][1].icon_url); } TEST_F(CredentialManagerImplTest, @@ -1215,7 +1229,7 @@ TEST_F(CredentialManagerImplTest, // Check that the second request triggered a rejection. EXPECT_TRUE(called_2); - EXPECT_EQ(CredentialManagerError::PENDINGREQUEST, error_2); + EXPECT_EQ(CredentialManagerError::PENDING_REQUEST, error_2); EXPECT_FALSE(credential_2); // Check that the first request resolves. diff --git a/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc b/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc index 23588189cf1..b3e71fabb46 100644 --- a/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc +++ b/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc @@ -19,6 +19,7 @@ #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/common/credential_manager_types.h" +#include "net/cert/cert_status_flags.h" #include "url/gurl.h" #include "url/origin.h" @@ -121,7 +122,7 @@ CredentialManagerPendingRequestTask::CredentialManagerPendingRequestTask( mediation_(mediation), origin_(delegate_->GetOrigin()), include_passwords_(include_passwords) { - CHECK(!delegate_->client()->DidLastPageLoadEncounterSSLErrors()); + CHECK(!net::IsCertStatusError(delegate_->client()->GetMainFrameCertStatus())); for (const GURL& federation : request_federations) federations_.insert( url::Origin::Create(federation.GetOrigin()).Serialize()); diff --git a/chromium/components/password_manager/core/browser/export/destination.h b/chromium/components/password_manager/core/browser/export/destination.h deleted file mode 100644 index 342601e3c72..00000000000 --- a/chromium/components/password_manager/core/browser/export/destination.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017 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. - -#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_EXPORT_DESTINATION_H_ -#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_EXPORT_DESTINATION_H_ - -#include <string> - -namespace password_manager { - -// Interface of a medium, to where a serialised list of passwords can be -// exported. -class Destination { - public: - Destination() = default; - virtual ~Destination() = default; - - // Send the data to the destination, synchronously. - virtual bool Write(const std::string& data) = 0; -}; - -} // namespace password_manager - -#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_EXPORT_DESTINATION_H_ diff --git a/chromium/components/password_manager/core/browser/export/password_manager_exporter.cc b/chromium/components/password_manager/core/browser/export/password_manager_exporter.cc index b0aa794fd4e..d606ad497aa 100644 --- a/chromium/components/password_manager/core/browser/export/password_manager_exporter.cc +++ b/chromium/components/password_manager/core/browser/export/password_manager_exporter.cc @@ -6,12 +6,12 @@ #include <utility> +#include "base/files/file_util.h" #include "base/location.h" #include "base/metrics/histogram_macros.h" #include "base/task_runner_util.h" #include "base/task_scheduler/post_task.h" #include "components/autofill/core/common/password_form.h" -#include "components/password_manager/core/browser/export/destination.h" #include "components/password_manager/core/browser/export/password_csv_writer.h" #include "components/password_manager/core/browser/ui/credential_provider_interface.h" @@ -34,6 +34,7 @@ PasswordManagerExporter::PasswordManagerExporter( password_manager::CredentialProviderInterface* credential_provider_interface) : credential_provider_interface_(credential_provider_interface), + write_function_(&base::WriteFile), task_runner_(base::CreateSequencedTaskRunnerWithTraits( {base::TaskPriority::USER_VISIBLE, base::MayBlock()})), weak_factory_(this) {} @@ -48,8 +49,8 @@ void PasswordManagerExporter::PreparePasswordsForExport() { } void PasswordManagerExporter::SetDestination( - std::unique_ptr<Destination> destination) { - destination_ = std::move(destination); + const base::FilePath& destination) { + destination_ = destination; if (IsReadyForExport()) Export(); @@ -59,12 +60,19 @@ void PasswordManagerExporter::Cancel() { // Tasks which had their pointers invalidated won't run. weak_factory_.InvalidateWeakPtrs(); - destination_.reset(); + destination_.clear(); password_list_.clear(); } +void PasswordManagerExporter::SetWriteForTesting( + int (*write_function)(const base::FilePath& filename, + const char* data, + int size)) { + write_function_ = write_function; +} + bool PasswordManagerExporter::IsReadyForExport() { - return destination_ && !password_list_.empty(); + return !destination_.empty() && !password_list_.empty(); } void PasswordManagerExporter::Export() { @@ -80,13 +88,13 @@ void PasswordManagerExporter::Export() { base::Passed(std::move(destination_)))); password_list_.clear(); - destination_.reset(); + destination_.clear(); } void PasswordManagerExporter::OnPasswordsSerialised( - std::unique_ptr<Destination> destination, + base::FilePath destination, const std::string& serialised) { - destination->Write(serialised); + write_function_(destination, serialised.c_str(), serialised.size()); } } // namespace password_manager diff --git a/chromium/components/password_manager/core/browser/export/password_manager_exporter.h b/chromium/components/password_manager/core/browser/export/password_manager_exporter.h index 471601b570e..154832097c4 100644 --- a/chromium/components/password_manager/core/browser/export/password_manager_exporter.h +++ b/chromium/components/password_manager/core/browser/export/password_manager_exporter.h @@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner.h" @@ -20,7 +21,6 @@ struct PasswordForm; namespace password_manager { class CredentialProviderInterface; -class Destination; // Controls the exporting of passwords. PasswordManagerExporter will perform // the export asynchrnously as soon as all the required info is available @@ -38,11 +38,17 @@ class PasswordManagerExporter { // Set the destination, where the passwords will be written when they are // ready. - virtual void SetDestination(std::unique_ptr<Destination> destination); + virtual void SetDestination(const base::FilePath& destination); // Best-effort canceling of any on-going task related to exporting. virtual void Cancel(); + // Replace the function which writes to the filesystem with a custom action. + // The return value is -1 on error, otherwise the number of bytes written. + void SetWriteForTesting(int (*write_function)(const base::FilePath& filename, + const char* data, + int size)); + private: // Returns true if all the necessary data is available. bool IsReadyForExport(); @@ -53,7 +59,7 @@ class PasswordManagerExporter { void Export(); // Callback after the passwords have been serialised. - void OnPasswordsSerialised(std::unique_ptr<Destination> destination, + void OnPasswordsSerialised(base::FilePath destination, const std::string& serialised); // The source of the password list which will be exported. @@ -65,7 +71,13 @@ class PasswordManagerExporter { // The destination which was provided and where the password list will be // sent. It will be cleared once exporting is complete. - std::unique_ptr<Destination> destination_; + base::FilePath destination_; + + // The function which does the actual writing. It should point to + // base::WriteFile, unless it's changed for testing purposes. + int (*write_function_)(const base::FilePath& filename, + const char* data, + int size); // |task_runner_| is used for time-consuming tasks during exporting. The tasks // will dereference a WeakPtr to |*this|, which means they all need to run on diff --git a/chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc b/chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc index 48dec4b0fd6..17312a3f159 100644 --- a/chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc +++ b/chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc @@ -10,9 +10,10 @@ #include <vector> #include "base/strings/utf_string_conversions.h" +#include "base/test/mock_callback.h" #include "base/test/scoped_task_environment.h" +#include "build/build_config.h" #include "components/autofill/core/common/password_form.h" -#include "components/password_manager/core/browser/export/destination.h" #include "components/password_manager/core/browser/export/password_csv_writer.h" #include "components/password_manager/core/browser/ui/credential_provider_interface.h" #include "testing/gmock/include/gmock/gmock.h" @@ -21,8 +22,20 @@ namespace { using ::testing::_; +using ::testing::ReturnArg; +using ::testing::StrEq; using ::testing::StrictMock; +// A callback that matches the signature of base::WriteFile +using WriteCallback = + base::RepeatingCallback<int(const base::FilePath&, const char*, int)>; + +#if defined(OS_WIN) +const base::FilePath::CharType kNullFileName[] = FILE_PATH_LITERAL("/nul"); +#else +const base::FilePath::CharType kNullFileName[] = FILE_PATH_LITERAL("/dev/null"); +#endif + // Provides a predetermined set of credentials class FakeCredentialProvider : public password_manager::CredentialProviderInterface { @@ -54,16 +67,16 @@ class FakeCredentialProvider DISALLOW_COPY_AND_ASSIGN(FakeCredentialProvider); }; -class MockDestination : public password_manager::Destination { - public: - MockDestination() = default; - ~MockDestination() override = default; +// WriteFunction will delegate to this callback, if set. Use for setting +// expectations for base::WriteFile in PasswordManagerExporter. +base::MockCallback<WriteCallback>* g_write_callback = nullptr; - MOCK_METHOD1(Write, bool(const std::string& data)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockDestination); -}; +// Mock for base::WriteFile. Expectations should be set on |g_write_callback|. +int WriteFunction(const base::FilePath& filename, const char* data, int size) { + if (g_write_callback) + return g_write_callback->Get().Run(filename, data, size); + return size; +} // Creates a hardcoded set of credentials for tests. std::vector<std::unique_ptr<autofill::PasswordForm>> CreatePasswordList() { @@ -82,13 +95,20 @@ class PasswordManagerExporterTest : public testing::Test { PasswordManagerExporterTest() : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI), - exporter_(&fake_credential_provider_) {} - ~PasswordManagerExporterTest() override = default; + exporter_(&fake_credential_provider_), + destination_path_(kNullFileName) { + g_write_callback = &mock_write_file_; + exporter_.SetWriteForTesting(&WriteFunction); + } + + ~PasswordManagerExporterTest() override { g_write_callback = nullptr; } protected: base::test::ScopedTaskEnvironment scoped_task_environment_; FakeCredentialProvider fake_credential_provider_; password_manager::PasswordManagerExporter exporter_; + StrictMock<base::MockCallback<WriteCallback>> mock_write_file_; + base::FilePath destination_path_; private: DISALLOW_COPY_AND_ASSIGN(PasswordManagerExporterTest); @@ -101,12 +121,12 @@ TEST_F(PasswordManagerExporterTest, PasswordExportSetPasswordListFirst) { const std::string serialised( password_manager::PasswordCSVWriter::SerializePasswords(password_list)); - std::unique_ptr<MockDestination> mock_destination = - std::make_unique<StrictMock<MockDestination>>(); - EXPECT_CALL(*mock_destination, Write(serialised)); + EXPECT_CALL(mock_write_file_, + Run(destination_path_, StrEq(serialised), serialised.size())) + .WillOnce(ReturnArg<2>()); exporter_.PreparePasswordsForExport(); - exporter_.SetDestination(std::move(mock_destination)); + exporter_.SetDestination(destination_path_); scoped_task_environment_.RunUntilIdle(); } @@ -118,11 +138,11 @@ TEST_F(PasswordManagerExporterTest, PasswordExportSetDestinationFirst) { const std::string serialised( password_manager::PasswordCSVWriter::SerializePasswords(password_list)); - std::unique_ptr<MockDestination> mock_destination = - std::make_unique<MockDestination>(); - EXPECT_CALL(*mock_destination, Write(serialised)); + EXPECT_CALL(mock_write_file_, + Run(destination_path_, StrEq(serialised), serialised.size())) + .WillOnce(ReturnArg<2>()); - exporter_.SetDestination(std::move(mock_destination)); + exporter_.SetDestination(destination_path_); exporter_.PreparePasswordsForExport(); scoped_task_environment_.RunUntilIdle(); @@ -133,11 +153,9 @@ TEST_F(PasswordManagerExporterTest, DontExportWithOnlyDestination) { CreatePasswordList(); fake_credential_provider_.SetPasswordList(password_list); - std::unique_ptr<MockDestination> mock_destination = - std::make_unique<MockDestination>(); - EXPECT_CALL(*mock_destination, Write(_)).Times(0); + EXPECT_CALL(mock_write_file_, Run(_, _, _)).Times(0); - exporter_.SetDestination(std::move(mock_destination)); + exporter_.SetDestination(destination_path_); scoped_task_environment_.RunUntilIdle(); } @@ -146,14 +164,12 @@ TEST_F(PasswordManagerExporterTest, CancelAfterPasswords) { std::vector<std::unique_ptr<autofill::PasswordForm>> password_list = CreatePasswordList(); fake_credential_provider_.SetPasswordList(password_list); - std::unique_ptr<MockDestination> mock_destination = - std::make_unique<MockDestination>(); - EXPECT_CALL(*mock_destination, Write(_)).Times(0); + EXPECT_CALL(mock_write_file_, Run(_, _, _)).Times(0); exporter_.PreparePasswordsForExport(); exporter_.Cancel(); - exporter_.SetDestination(std::move(mock_destination)); + exporter_.SetDestination(destination_path_); scoped_task_environment_.RunUntilIdle(); } @@ -162,12 +178,10 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestination) { std::vector<std::unique_ptr<autofill::PasswordForm>> password_list = CreatePasswordList(); fake_credential_provider_.SetPasswordList(password_list); - std::unique_ptr<MockDestination> mock_destination = - std::make_unique<MockDestination>(); - EXPECT_CALL(*mock_destination, Write(_)).Times(0); + EXPECT_CALL(mock_write_file_, Run(_, _, _)).Times(0); - exporter_.SetDestination(std::move(mock_destination)); + exporter_.SetDestination(destination_path_); exporter_.Cancel(); exporter_.PreparePasswordsForExport(); @@ -182,14 +196,14 @@ TEST_F(PasswordManagerExporterTest, CancelAfterPasswordsThenExport) { const std::string serialised( password_manager::PasswordCSVWriter::SerializePasswords(password_list)); fake_credential_provider_.SetPasswordList(password_list); - std::unique_ptr<MockDestination> mock_destination = - std::make_unique<MockDestination>(); - EXPECT_CALL(*mock_destination, Write(serialised)); + EXPECT_CALL(mock_write_file_, + Run(destination_path_, StrEq(serialised), serialised.size())) + .WillOnce(ReturnArg<2>()); exporter_.PreparePasswordsForExport(); exporter_.Cancel(); - exporter_.SetDestination(std::move(mock_destination)); + exporter_.SetDestination(destination_path_); exporter_.PreparePasswordsForExport(); scoped_task_environment_.RunUntilIdle(); @@ -203,18 +217,18 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestinationThenExport) { const std::string serialised( password_manager::PasswordCSVWriter::SerializePasswords(password_list)); fake_credential_provider_.SetPasswordList(password_list); - std::unique_ptr<MockDestination> mock_destination_cancelled = - std::make_unique<MockDestination>(); - std::unique_ptr<MockDestination> mock_destination = - std::make_unique<MockDestination>(); - EXPECT_CALL(*mock_destination_cancelled, Write(_)).Times(0); - EXPECT_CALL(*mock_destination, Write(serialised)); + base::FilePath cancelled_path(FILE_PATH_LITERAL("clean_me_up")); + + EXPECT_CALL(mock_write_file_, Run(cancelled_path, _, _)).Times(0); + EXPECT_CALL(mock_write_file_, + Run(destination_path_, StrEq(serialised), serialised.size())) + .WillOnce(ReturnArg<2>()); - exporter_.SetDestination(std::move(mock_destination_cancelled)); + exporter_.SetDestination(std::move(cancelled_path)); exporter_.Cancel(); exporter_.PreparePasswordsForExport(); - exporter_.SetDestination(std::move(mock_destination)); + exporter_.SetDestination(destination_path_); scoped_task_environment_.RunUntilIdle(); } diff --git a/chromium/components/password_manager/core/browser/form_saver.h b/chromium/components/password_manager/core/browser/form_saver.h index 634c6a776ed..651853372b7 100644 --- a/chromium/components/password_manager/core/browser/form_saver.h +++ b/chromium/components/password_manager/core/browser/form_saver.h @@ -28,12 +28,11 @@ class FormSaver { virtual void PermanentlyBlacklist(autofill::PasswordForm* observed) = 0; // Saves the |pending| form and updates the stored preference on - // |best_matches|. If |old_primary_key| is given, uses it for saving - // |pending|. - virtual void Save(const autofill::PasswordForm& pending, - const std::map<base::string16, - const autofill::PasswordForm*>& best_matches, - const autofill::PasswordForm* old_primary_key) = 0; + // |best_matches|. + virtual void Save( + const autofill::PasswordForm& pending, + const std::map<base::string16, const autofill::PasswordForm*>& + best_matches) = 0; // Updates the |pending| form and updates the stored preference on // |best_matches|. If |old_primary_key| is given, uses it for saving diff --git a/chromium/components/password_manager/core/browser/form_saver_impl.cc b/chromium/components/password_manager/core/browser/form_saver_impl.cc index be12e073c05..802cd1c5fe6 100644 --- a/chromium/components/password_manager/core/browser/form_saver_impl.cc +++ b/chromium/components/password_manager/core/browser/form_saver_impl.cc @@ -37,9 +37,8 @@ void FormSaverImpl::PermanentlyBlacklist(PasswordForm* observed) { void FormSaverImpl::Save( const PasswordForm& pending, - const std::map<base::string16, const PasswordForm*>& best_matches, - const PasswordForm* old_primary_key) { - SaveImpl(pending, true, best_matches, nullptr, old_primary_key); + const std::map<base::string16, const PasswordForm*>& best_matches) { + SaveImpl(pending, true, best_matches, nullptr, nullptr); } void FormSaverImpl::Update( diff --git a/chromium/components/password_manager/core/browser/form_saver_impl.h b/chromium/components/password_manager/core/browser/form_saver_impl.h index 7fa8459873e..0833dc0ab63 100644 --- a/chromium/components/password_manager/core/browser/form_saver_impl.h +++ b/chromium/components/password_manager/core/browser/form_saver_impl.h @@ -27,8 +27,7 @@ class FormSaverImpl : public FormSaver { void PermanentlyBlacklist(autofill::PasswordForm* observed) override; void Save(const autofill::PasswordForm& pending, const std::map<base::string16, const autofill::PasswordForm*>& - best_matches, - const autofill::PasswordForm* old_primary_key) override; + best_matches) override; void Update(const autofill::PasswordForm& pending, const std::map<base::string16, const autofill::PasswordForm*>& best_matches, diff --git a/chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc b/chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc index bd5ed867496..49766a74282 100644 --- a/chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc +++ b/chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc @@ -107,8 +107,7 @@ TEST_F(FormSaverImplTest, Save_AsNew) { EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved)); EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0); - form_saver_.Save(pending, std::map<base::string16, const PasswordForm*>(), - nullptr); + form_saver_.Save(pending, std::map<base::string16, const PasswordForm*>()); EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value); EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value); } @@ -206,7 +205,7 @@ TEST_F(FormSaverImplTest, Save_AndUpdatePreferredLoginState) { EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved)); EXPECT_CALL(*mock_store_, UpdateLogin(_)).WillOnce(SaveArg<0>(&updated)); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0); - form_saver_.Save(pending, best_matches, nullptr); + form_saver_.Save(pending, best_matches); EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value); EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value); EXPECT_TRUE(saved.preferred); @@ -237,7 +236,7 @@ TEST_F(FormSaverImplTest, Save_AndDeleteEmptyUsernameCredentials) { EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0); EXPECT_CALL(*mock_store_, RemoveLogin(_)).WillOnce(SaveArg<0>(&removed)); - form_saver_.Save(pending, best_matches, nullptr); + form_saver_.Save(pending, best_matches); EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value); EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value); EXPECT_TRUE(removed.username_value.empty()); @@ -265,7 +264,7 @@ TEST_F(FormSaverImplTest, EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0); EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0); - form_saver_.Save(pending, best_matches, nullptr); + form_saver_.Save(pending, best_matches); EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value); EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value); } @@ -291,7 +290,7 @@ TEST_F(FormSaverImplTest, EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0); EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0); - form_saver_.Save(pending, best_matches, nullptr); + form_saver_.Save(pending, best_matches); EXPECT_EQ(ASCIIToUTF16("abc"), saved.username_value); EXPECT_EQ(ASCIIToUTF16("def"), saved.password_value); } @@ -314,7 +313,7 @@ TEST_F(FormSaverImplTest, Save_EmptyUsernameWillNotCauseDeletion) { EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0); EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0); - form_saver_.Save(pending, best_matches, nullptr); + form_saver_.Save(pending, best_matches); EXPECT_TRUE(saved.username_value.empty()); EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value); } @@ -338,7 +337,7 @@ TEST_F(FormSaverImplTest, Save_AndDoNotDeleteEmptyUsernamePSLCredentials) { EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0); EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0); - form_saver_.Save(pending, best_matches, nullptr); + form_saver_.Save(pending, best_matches); EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value); EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value); } @@ -361,7 +360,7 @@ TEST_F(FormSaverImplTest, Save_AndDoNotDeleteNonEmptyUsernameCredentials) { EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)).Times(0); EXPECT_CALL(*mock_store_, RemoveLogin(_)).Times(0); - form_saver_.Save(pending, best_matches, nullptr); + form_saver_.Save(pending, best_matches); EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved.username_value); EXPECT_EQ(ASCIIToUTF16("wordToP4a55"), saved.password_value); } @@ -416,8 +415,7 @@ TEST_F(FormSaverImplTest, PresaveGeneratedPassword_ThenSaveAsNew) { EXPECT_CALL(*mock_store_, UpdateLogin(_)).Times(0); EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _)) .WillOnce(DoAll(SaveArg<0>(&saved_new), SaveArg<1>(&saved_old))); - form_saver_.Save(pending, std::map<base::string16, const PasswordForm*>(), - nullptr); + form_saver_.Save(pending, std::map<base::string16, const PasswordForm*>()); EXPECT_EQ(ASCIIToUTF16("generatedU"), saved_old.username_value); EXPECT_EQ(ASCIIToUTF16("generatedP"), saved_old.password_value); EXPECT_EQ(ASCIIToUTF16("nameofuser"), saved_new.username_value); diff --git a/chromium/components/password_manager/core/browser/hash_password_manager.cc b/chromium/components/password_manager/core/browser/hash_password_manager.cc index d65b27d652d..ebbcf2ee168 100644 --- a/chromium/components/password_manager/core/browser/hash_password_manager.cc +++ b/chromium/components/password_manager/core/browser/hash_password_manager.cc @@ -7,11 +7,11 @@ #include "base/base64.h" #include "base/strings/string_number_conversions.h" #include "components/os_crypt/os_crypt.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/common/password_manager_pref_names.h" #include "components/prefs/pref_service.h" +#include "crypto/openssl_util.h" #include "crypto/random.h" +#include "third_party/boringssl/src/include/openssl/evp.h" namespace { constexpr size_t kSyncPasswordSaltLength = 16; @@ -20,6 +20,22 @@ constexpr char kSeparator = '.'; namespace password_manager { +SyncPasswordData::SyncPasswordData(const base::string16& password, + bool force_update) + : length(password.size()), + salt(HashPasswordManager::CreateRandomSalt()), + hash(HashPasswordManager::CalculateSyncPasswordHash(password, salt)), + force_update(force_update) {} + +bool SyncPasswordData::MatchesPassword(const base::string16& password) { + if (password.size() != this->length) + return false; + return HashPasswordManager::CalculateSyncPasswordHash(password, this->salt) == + this->hash; +} + +HashPasswordManager::HashPasswordManager(PrefService* prefs) : prefs_(prefs) {} + bool HashPasswordManager::SavePasswordHash(const base::string16& password) { if (!prefs_) return false; @@ -28,21 +44,25 @@ bool HashPasswordManager::SavePasswordHash(const base::string16& password) { RetrievePasswordHash(); // If it is the same password, no need to save password hash again. if (current_sync_password_data.has_value() && - password_manager_util::CalculateSyncPasswordHash( - password, current_sync_password_data->salt) == - current_sync_password_data->hash) { + current_sync_password_data->MatchesPassword(password)) { return true; } - std::string salt = CreateRandomSalt(); - std::string hash = base::Uint64ToString( - password_manager_util::CalculateSyncPasswordHash(password, salt)); - EncryptAndSaveToPrefs(prefs::kSyncPasswordHash, hash); + return SavePasswordHash(SyncPasswordData(password, true)); +} - // Password length and salt are stored together. - std::string length_salt = LengthAndSaltToString(salt, password.size()); - return EncryptAndSaveToPrefs(prefs::kSyncPasswordLengthAndHashSalt, - length_salt); +bool HashPasswordManager::SavePasswordHash( + const SyncPasswordData& sync_password_data) { + bool should_save = sync_password_data.force_update || + !prefs_->HasPrefPath(prefs::kSyncPasswordHash); + return should_save ? (EncryptAndSaveToPrefs( + prefs::kSyncPasswordHash, + base::NumberToString(sync_password_data.hash)) && + EncryptAndSaveToPrefs( + prefs::kSyncPasswordLengthAndHashSalt, + LengthAndSaltToString(sync_password_data.salt, + sync_password_data.length))) + : false; } void HashPasswordManager::ClearSavedPasswordHash() { @@ -66,16 +86,11 @@ base::Optional<SyncPasswordData> HashPasswordManager::RetrievePasswordHash() { return result; } -void HashPasswordManager::ReportIsSyncPasswordHashSavedMetric() { - if (!prefs_) - return; - auto hash_password_state = - prefs_->HasPrefPath(prefs::kSyncPasswordHash) - ? metrics_util::IsSyncPasswordHashSaved::SAVED - : metrics_util::IsSyncPasswordHashSaved::NOT_SAVED; - metrics_util::LogIsSyncPasswordHashSaved(hash_password_state); +bool HashPasswordManager::HasPasswordHash() { + return prefs_ ? prefs_->HasPrefPath(prefs::kSyncPasswordHash) : false; } +// static std::string HashPasswordManager::CreateRandomSalt() { char buffer[kSyncPasswordSaltLength]; crypto::RandBytes(buffer, kSyncPasswordSaltLength); @@ -85,6 +100,42 @@ std::string HashPasswordManager::CreateRandomSalt() { return result; } +// static +uint64_t HashPasswordManager::CalculateSyncPasswordHash( + const base::StringPiece16& text, + const std::string& salt) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + constexpr size_t kBytesFromHash = 8; + constexpr uint64_t kScryptCost = 32; // It must be power of 2. + constexpr uint64_t kScryptBlockSize = 8; + constexpr uint64_t kScryptParallelization = 1; + constexpr size_t kScryptMaxMemory = 1024 * 1024; + + uint8_t hash[kBytesFromHash]; + base::StringPiece text_8bits(reinterpret_cast<const char*>(text.data()), + text.size() * 2); + const uint8_t* salt_ptr = reinterpret_cast<const uint8_t*>(salt.c_str()); + + int scrypt_ok = EVP_PBE_scrypt(text_8bits.data(), text_8bits.size(), salt_ptr, + salt.size(), kScryptCost, kScryptBlockSize, + kScryptParallelization, kScryptMaxMemory, hash, + kBytesFromHash); + + // EVP_PBE_scrypt can only fail due to memory allocation error (which aborts + // Chromium) or invalid parameters. In case of a failure a hash could leak + // information from the stack, so using CHECK is better than DCHECK. + CHECK(scrypt_ok); + + // Take 37 bits of |hash|. + uint64_t hash37 = ((static_cast<uint64_t>(hash[0]))) | + ((static_cast<uint64_t>(hash[1])) << 8) | + ((static_cast<uint64_t>(hash[2])) << 16) | + ((static_cast<uint64_t>(hash[3])) << 24) | + (((static_cast<uint64_t>(hash[4])) & 0x1F) << 32); + + return hash37; +} + std::string HashPasswordManager::LengthAndSaltToString(const std::string& salt, size_t password_length) { return base::NumberToString(password_length) + kSeparator + salt; diff --git a/chromium/components/password_manager/core/browser/hash_password_manager.h b/chromium/components/password_manager/core/browser/hash_password_manager.h index 217f4d4f816..8fc62196a96 100644 --- a/chromium/components/password_manager/core/browser/hash_password_manager.h +++ b/chromium/components/password_manager/core/browser/hash_password_manager.h @@ -14,9 +14,17 @@ class PrefService; namespace password_manager { struct SyncPasswordData { - uint64_t hash; - std::string salt; + SyncPasswordData() = default; + SyncPasswordData(const SyncPasswordData& other) = default; + SyncPasswordData(const base::string16& password, bool force_update); + bool MatchesPassword(const base::string16& password); + size_t length; + std::string salt; + uint64_t hash; + // Signal that we need to update password hash, salt, and length in profile + // prefs. + bool force_update; }; // Responsible for saving, clearing, retrieving and encryption of a sync @@ -25,21 +33,29 @@ struct SyncPasswordData { class HashPasswordManager { public: HashPasswordManager() = default; + explicit HashPasswordManager(PrefService* prefs); ~HashPasswordManager() = default; bool SavePasswordHash(const base::string16& password); + bool SavePasswordHash(const SyncPasswordData& sync_password_data); void ClearSavedPasswordHash(); // Returns empty if no hash is available. base::Optional<SyncPasswordData> RetrievePasswordHash(); - void ReportIsSyncPasswordHashSavedMetric(); + // Whether |prefs_| has |kSyncPasswordHash| pref path. + bool HasPasswordHash(); void set_prefs(PrefService* prefs) { prefs_ = prefs; } - private: - std::string CreateRandomSalt(); + static std::string CreateRandomSalt(); + // Calculates 37 bits hash for a sync password. The calculation is based on a + // slow hash function. The running time is ~10^{-4} seconds on Desktop. + static uint64_t CalculateSyncPasswordHash(const base::StringPiece16& text, + const std::string& salt); + + private: // Packs |salt| and |password_length| to a string. std::string LengthAndSaltToString(const std::string& salt, size_t password_length); diff --git a/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc b/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc index 74559c5bfcc..5ff06f30fb7 100644 --- a/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc +++ b/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc @@ -6,7 +6,6 @@ #include "base/strings/utf_string_conversions.h" #include "components/os_crypt/os_crypt_mocker.h" -#include "components/password_manager/core/browser/password_manager_util.h" #include "components/password_manager/core/common/password_manager_pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" @@ -39,24 +38,34 @@ TEST_F(HashPasswordManagerTest, Saving) { ASSERT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash)); HashPasswordManager hash_password_manager; hash_password_manager.set_prefs(&prefs_); - hash_password_manager.SavePasswordHash(base::ASCIIToUTF16("sync_password")); + base::string16 password(base::UTF8ToUTF16("sync_password")); + + // Verify |SavePasswordHash(const base::string16&)| behavior. + hash_password_manager.SavePasswordHash(password); EXPECT_TRUE(prefs_.HasPrefPath(prefs::kSyncPasswordHash)); // Saves the same password again won't change password hash, length or salt. const std::string current_hash = prefs_.GetString(prefs::kSyncPasswordHash); const std::string current_length_and_salt = prefs_.GetString(prefs::kSyncPasswordLengthAndHashSalt); - hash_password_manager.SavePasswordHash(base::ASCIIToUTF16("sync_password")); + hash_password_manager.SavePasswordHash(password); EXPECT_EQ(current_hash, prefs_.GetString(prefs::kSyncPasswordHash)); EXPECT_EQ(current_length_and_salt, prefs_.GetString(prefs::kSyncPasswordLengthAndHashSalt)); + + // Verify |SavePasswordHash(const SyncPasswordData&)| behavior. + base::string16 new_password(base::UTF8ToUTF16("new_sync_password")); + SyncPasswordData sync_password_data(new_password, /*force_update=*/true); + EXPECT_TRUE(sync_password_data.MatchesPassword(new_password)); + hash_password_manager.SavePasswordHash(sync_password_data); + EXPECT_TRUE(prefs_.HasPrefPath(prefs::kSyncPasswordHash)); } TEST_F(HashPasswordManagerTest, Clearing) { ASSERT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash)); HashPasswordManager hash_password_manager; hash_password_manager.set_prefs(&prefs_); - hash_password_manager.SavePasswordHash(base::ASCIIToUTF16("sync_password")); + hash_password_manager.SavePasswordHash(base::UTF8ToUTF16("sync_password")); hash_password_manager.ClearSavedPasswordHash(); EXPECT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash)); } @@ -65,7 +74,7 @@ TEST_F(HashPasswordManagerTest, Retrieving) { ASSERT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash)); HashPasswordManager hash_password_manager; hash_password_manager.set_prefs(&prefs_); - hash_password_manager.SavePasswordHash(base::ASCIIToUTF16("sync_password")); + hash_password_manager.SavePasswordHash(base::UTF8ToUTF16("sync_password")); EXPECT_TRUE(prefs_.HasPrefPath(prefs::kSyncPasswordLengthAndHashSalt)); base::Optional<SyncPasswordData> sync_password_data = @@ -73,10 +82,32 @@ TEST_F(HashPasswordManagerTest, Retrieving) { ASSERT_TRUE(sync_password_data); EXPECT_EQ(13u, sync_password_data->length); EXPECT_EQ(16u, sync_password_data->salt.size()); - uint64_t expected_hash = password_manager_util::CalculateSyncPasswordHash( - base::ASCIIToUTF16("sync_password"), sync_password_data->salt); + uint64_t expected_hash = HashPasswordManager::CalculateSyncPasswordHash( + base::UTF8ToUTF16("sync_password"), sync_password_data->salt); EXPECT_EQ(expected_hash, sync_password_data->hash); } +TEST_F(HashPasswordManagerTest, CalculateSyncPasswordHash) { + const char* kPlainText[] = {"", "password", "password", "secret"}; + const char* kSalt[] = {"", "salt", "123", "456"}; + + constexpr uint64_t kExpectedHash[] = { + UINT64_C(0x1c610a7950), UINT64_C(0x1927dc525e), UINT64_C(0xf72f81aa6), + UINT64_C(0x3645af77f), + }; + + static_assert(arraysize(kPlainText) == arraysize(kSalt), + "Arrays must have the same size"); + static_assert(arraysize(kPlainText) == arraysize(kExpectedHash), + "Arrays must have the same size"); + + for (size_t i = 0; i < arraysize(kPlainText); ++i) { + SCOPED_TRACE(i); + base::string16 text = base::UTF8ToUTF16(kPlainText[i]); + EXPECT_EQ(kExpectedHash[i], + HashPasswordManager::CalculateSyncPasswordHash(text, kSalt[i])); + } +} + } // namespace } // namespace password_manager diff --git a/chromium/components/password_manager/core/browser/login_database.cc b/chromium/components/password_manager/core/browser/login_database.cc index 5870ea411bc..8c0767774f2 100644 --- a/chromium/components/password_manager/core/browser/login_database.cc +++ b/chromium/components/password_manager/core/browser/login_database.cc @@ -16,8 +16,8 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/macros.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" -#include "base/metrics/sparse_histogram.h" #include "base/numerics/safe_conversions.h" #include "base/pickle.h" #include "base/stl_util.h" @@ -600,8 +600,8 @@ bool LoginDatabase::Init() { meta_table_.SetVersionNumber(kCurrentVersionNumber); } else { LogDatabaseInitError(MIGRATION_ERROR); - UMA_HISTOGRAM_SPARSE_SLOWLY("PasswordManager.LoginDatabaseFailedVersion", - meta_table_.GetVersionNumber()); + base::UmaHistogramSparse("PasswordManager.LoginDatabaseFailedVersion", + meta_table_.GetVersionNumber()); LOG(ERROR) << "Unable to migrate database from " << meta_table_.GetVersionNumber() << " to " << kCurrentVersionNumber; diff --git a/chromium/components/password_manager/core/browser/mock_password_store.h b/chromium/components/password_manager/core/browser/mock_password_store.h index 18a9cc62bfb..11603b98e2b 100644 --- a/chromium/components/password_manager/core/browser/mock_password_store.h +++ b/chromium/components/password_manager/core/browser/mock_password_store.h @@ -76,11 +76,9 @@ class MockPasswordStore : public PasswordStore { void(const base::string16&, const std::string&, PasswordReuseDetectorConsumer*)); -#if !defined(OS_CHROMEOS) MOCK_METHOD1(SaveSyncPasswordHash, void(const base::string16&)); MOCK_METHOD0(ClearSyncPasswordHash, void()); #endif -#endif PasswordStoreSync* GetSyncInterface() { return this; } diff --git a/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc index 1fa952acc98..187b77b4630 100644 --- a/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc +++ b/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc @@ -32,6 +32,7 @@ #include "components/strings/grit/components_strings.h" #include "components/sync/driver/fake_sync_service.h" #include "components/ukm/test_ukm_recorder.h" +#include "services/metrics/public/cpp/ukm_builders.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" @@ -55,6 +56,8 @@ using autofill::SuggestionVectorValuesAre; using autofill::SuggestionVectorLabelsAre; using testing::_; +using UkmEntry = ukm::builders::PageWithPassword; + namespace autofill { class AutofillPopupDelegate; } @@ -1178,17 +1181,18 @@ TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnPasswordField) { kAcceptedContextHistogram, metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD, 1); // Trigger UKM reporting, which happens at destruction time. + ukm::SourceId expected_source_id = client->GetUkmSourceId(); manager.reset(); autofill_client.reset(); client.reset(); const auto& entries = - test_ukm_recorder.GetEntriesByName("PageWithPassword"); + test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kMainFrameUrl)); + EXPECT_EQ(expected_source_id, entry->source_id); test_ukm_recorder.ExpectEntryMetric( - entry, password_manager::kUkmPageLevelUserAction, + entry, UkmEntry::kPageLevelUserActionName, static_cast<int64_t>( password_manager::PasswordManagerMetricsRecorder:: PageLevelUserAction::kShowAllPasswordsWhileSomeAreSuggested)); @@ -1267,6 +1271,7 @@ TEST_F(PasswordAutofillManagerTest, ShowStandaloneShowAllPasswords) { kAcceptedContextHistogram, metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK, 1); // Trigger UKM reporting, which happens at destruction time. + ukm::SourceId expected_source_id = client->GetUkmSourceId(); manager.reset(); autofill_client.reset(); client.reset(); @@ -1275,9 +1280,9 @@ TEST_F(PasswordAutofillManagerTest, ShowStandaloneShowAllPasswords) { test_ukm_recorder.GetEntriesByName("PageWithPassword"); EXPECT_EQ(1u, entries.size()); for (const auto* entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kMainFrameUrl)); + EXPECT_EQ(expected_source_id, entry->source_id); test_ukm_recorder.ExpectEntryMetric( - entry, password_manager::kUkmPageLevelUserAction, + entry, UkmEntry::kPageLevelUserActionName, static_cast<int64_t>( password_manager::PasswordManagerMetricsRecorder:: PageLevelUserAction::kShowAllPasswordsWhileNoneAreSuggested)); diff --git a/chromium/components/password_manager/core/browser/password_form_manager.cc b/chromium/components/password_manager/core/browser/password_form_manager.cc index ee374a28cb9..18b678fcf10 100644 --- a/chromium/components/password_manager/core/browser/password_form_manager.cc +++ b/chromium/components/password_manager/core/browser/password_form_manager.cc @@ -56,12 +56,6 @@ std::vector<std::string> SplitPathToSegments(const std::string& path) { base::SPLIT_WANT_ALL); } -// Return false iff the strings are neither empty nor equal. -bool AreStringsEqualOrEmpty(const base::string16& s1, - const base::string16& s2) { - return s1.empty() || s2.empty() || s1 == s2; -} - bool DoesStringContainOnlyDigits(const base::string16& s) { for (auto c : s) { if (!base::IsAsciiDigit(c)) @@ -192,8 +186,11 @@ void LabelFields(const FieldTypeMap& field_types, if (iter != field_types.end()) { type = iter->second; available_field_types->insert(type); - if (type == autofill::USERNAME) + if (type == autofill::USERNAME) { field->set_username_vote_type(username_vote_type); + DCHECK_NE(autofill::AutofillUploadContents::Field::NO_INFORMATION, + username_vote_type); + } } } @@ -261,8 +258,7 @@ void PasswordFormManager::Init( metrics_recorder_ = std::move(metrics_recorder); if (!metrics_recorder_) { metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>( - client_->IsMainFrameSecure(), client_->GetUkmRecorder(), - client_->GetUkmSourceId(), client_->GetMainFrameURL()); + client_->IsMainFrameSecure(), client_->GetUkmSourceId()); } if (owned_form_fetcher_) @@ -409,17 +405,17 @@ void PasswordFormManager::Save() { DidPreferenceChange(best_matches_, pending_credentials_.username_value)) { SetUserAction(UserAction::kChoose); } - base::Optional<PasswordForm> old_primary_key; + if (is_new_login_) { SanitizePossibleUsernames(&pending_credentials_); pending_credentials_.date_created = base::Time::Now(); SendVotesOnSave(); - form_saver_->Save(pending_credentials_, best_matches_, - old_primary_key ? &old_primary_key.value() : nullptr); + form_saver_->Save(pending_credentials_, best_matches_); } else { ProcessUpdate(); std::vector<PasswordForm> credentials_to_update; - old_primary_key = UpdatePendingAndGetOldKey(&credentials_to_update); + base::Optional<PasswordForm> old_primary_key = + UpdatePendingAndGetOldKey(&credentials_to_update); form_saver_->Update(pending_credentials_, best_matches_, &credentials_to_update, old_primary_key ? &old_primary_key.value() : nullptr); @@ -463,28 +459,41 @@ void PasswordFormManager::Update( } void PasswordFormManager::UpdateUsername(const base::string16& new_username) { - pending_credentials_.username_value = new_username; - // Check if the username already exists. - const PasswordForm* match = FindBestSavedMatch(&pending_credentials_); - is_new_login_ = !match || match->is_public_suffix_match; - // Searching for the field of |match| where |new_username| was typed. If it is - // found, the field name is saved to |corrected_username_element_|. Otherwise, - // |corrected_username_element_| has no value. - base::string16 trimmed_username_value; - base::TrimString(new_username, base::ASCIIToUTF16(" "), - &trimmed_username_value); - corrected_username_element_.reset(); - if (!trimmed_username_value.empty()) { - for (size_t i = 0; i < pending_credentials_.other_possible_usernames.size(); - ++i) { - if (pending_credentials_.other_possible_usernames[i].first == - trimmed_username_value) { - corrected_username_element_ = - pending_credentials_.other_possible_usernames[i].second; + PasswordForm credential(*submitted_form_); + credential.username_value = new_username; + // If |new_username| is not found in |other_possible_usernames|, store empty + // |username_element|. + credential.username_element.clear(); + + // |has_username_edited_vote_| is true iff |new_username| was typed in another + // field. Otherwise, |has_username_edited_vote_| is false and no vote will be + // uploaded. + has_username_edited_vote_ = false; + if (!new_username.empty()) { + for (size_t i = 0; i < credential.other_possible_usernames.size(); ++i) { + if (credential.other_possible_usernames[i].first == new_username) { + credential.username_element = + credential.other_possible_usernames[i].second; + + credential.other_possible_usernames.erase( + credential.other_possible_usernames.begin() + i); + + // Set |corrected_username_element_| to upload a username vote. + has_username_edited_vote_ = true; break; } } } + // A user may make a mistake and remove the correct username. So, save + // |username_value| and |username_element| of the submitted form. When the + // user has to override the username, Chrome will send a username vote. + if (!submitted_form_->username_value.empty()) { + credential.other_possible_usernames.push_back( + autofill::PossibleUsernamePair(submitted_form_->username_value, + submitted_form_->username_element)); + } + + ProvisionallySave(credential, IGNORE_OTHER_POSSIBLE_USERNAMES); } void PasswordFormManager::UpdatePasswordValue( @@ -517,7 +526,7 @@ void PasswordFormManager::SaveSubmittedFormTypeForMetrics( !form.new_password_value.empty() && !form.password_value.empty(); bool is_signup_form = !form.new_password_value.empty() && form.password_value.empty(); - bool no_username = form.username_element.empty(); + bool no_username = form.username_value.empty(); PasswordFormMetricsRecorder::SubmittedFormType type = PasswordFormMetricsRecorder::kSubmittedFormTypeUnspecified; @@ -597,7 +606,6 @@ void PasswordFormManager::ProcessMatches( size_t filtered_count) { blacklisted_matches_.clear(); new_blacklisted_.reset(); - blacklisted_origin_found_ = false; std::unique_ptr<BrowserSavePasswordProgressLogger> logger; if (password_manager_util::IsLoggingActive(client_)) { @@ -614,15 +622,6 @@ void PasswordFormManager::ProcessMatches( [this](const PasswordForm* form) { return IsMatch(*form); }); ScoreMatches(matches); - auto find_blacklisted_match_it = std::find_if( - non_federated.begin(), non_federated.end(), - [this](const PasswordForm* form) { - return form->blacklisted_by_user && - form->origin.GetOrigin() == observed_form_.origin.GetOrigin(); - }); - blacklisted_origin_found_ = - (find_blacklisted_match_it != non_federated.end()); - // Copy out blacklisted matches. blacklisted_matches_.resize(std::count_if( non_federated.begin(), non_federated.end(), @@ -674,7 +673,7 @@ void PasswordFormManager::ProcessFrameInternal( if (!driver) return; - if (blacklisted_origin_found_) + if (IsBlacklisted()) driver->MatchingBlacklistedFormFound(); driver->AllowPasswordGenerationForForm(observed_form_); @@ -794,6 +793,8 @@ bool PasswordFormManager::FindUsernameInOtherPossibleUsernames( bool PasswordFormManager::FindCorrectedUsernameElement( const base::string16& username, const base::string16& password) { + if (username.empty()) + return false; for (const auto& key_value : best_matches_) { const PasswordForm* match = key_value.second; if ((match->password_value == password) && @@ -856,10 +857,10 @@ void PasswordFormManager::SendVoteOnCredentialsReuse( bool PasswordFormManager::UploadPasswordVote( const autofill::PasswordForm& form_to_upload, - const autofill::ServerFieldType& password_type, + const autofill::ServerFieldType& autofill_type, const std::string& login_form_signature) { // Check if there is any vote to be sent. - bool has_autofill_vote = password_type != autofill::UNKNOWN_TYPE; + bool has_autofill_vote = autofill_type != autofill::UNKNOWN_TYPE; bool has_password_generation_vote = generation_popup_was_shown_; if (!has_autofill_vote && !has_password_generation_vote) return false; @@ -883,37 +884,44 @@ bool PasswordFormManager::UploadPasswordVote( FieldTypeMap field_types; autofill::AutofillUploadContents::Field::UsernameVoteType username_vote_type = autofill::AutofillUploadContents::Field::NO_INFORMATION; - if (password_type != autofill::USERNAME) { + if (autofill_type != autofill::USERNAME) { if (has_autofill_vote) { DCHECK(submitted_form_); - bool is_update = password_type == autofill::NEW_PASSWORD || - password_type == autofill::PROBABLY_NEW_PASSWORD || - password_type == autofill::NOT_NEW_PASSWORD; + bool is_update = autofill_type == autofill::NEW_PASSWORD || + autofill_type == autofill::PROBABLY_NEW_PASSWORD || + autofill_type == autofill::NOT_NEW_PASSWORD; if (is_update) { if (submitted_form_->new_password_element.empty()) return false; - SetFieldLabelsOnUpdate(password_type, *submitted_form_, &field_types); + SetFieldLabelsOnUpdate(autofill_type, *submitted_form_, &field_types); } else { // Saving. - SetFieldLabelsOnSave(password_type, *submitted_form_, &field_types); + SetFieldLabelsOnSave(autofill_type, *submitted_form_, &field_types); } field_types[submitted_form_->confirmation_password_element] = autofill::CONFIRMATION_PASSWORD; } - if (password_type != autofill::ACCOUNT_CREATION_PASSWORD) { + if (autofill_type != autofill::ACCOUNT_CREATION_PASSWORD) { if (generation_popup_was_shown_) AddGeneratedVote(&form_structure); if (form_classifier_outcome_ != kNoOutcome) AddFormClassifierVote(&form_structure); + if (has_username_edited_vote_) { + field_types[form_to_upload.username_element] = autofill::USERNAME; + username_vote_type = + autofill::AutofillUploadContents::Field::USERNAME_EDITED; + } } else { // User reuses credentials. - field_types[form_to_upload.username_element] = autofill::USERNAME; - username_vote_type = - autofill::AutofillUploadContents::Field::CREDENTIALS_REUSED; - } - if (corrected_username_element_.has_value()) { - field_types[corrected_username_element_.value()] = autofill::USERNAME; - username_vote_type = - autofill::AutofillUploadContents::Field::USERNAME_EDITED; + // If the saved username value was used, then send a confirmation vote for + // username. + if (!submitted_form_->username_value.empty()) { + DCHECK(submitted_form_->username_value == + form_to_upload.username_value); + field_types[form_to_upload.username_element] = autofill::USERNAME; + username_vote_type = + autofill::AutofillUploadContents::Field::CREDENTIALS_REUSED; + } } + } else { // User overwrites username. field_types[form_to_upload.username_element] = autofill::USERNAME; field_types[form_to_upload.password_element] = @@ -1085,7 +1093,7 @@ void PasswordFormManager::CreatePendingCredentials() { is_new_login_ = false; } else if (!best_matches_.empty() && submitted_form_->type != autofill::PasswordForm::TYPE_API && - submitted_form_->username_element.empty()) { + submitted_form_->username_value.empty()) { // This branch deals with the case that the submitted form has no username // element and needs to decide whether to offer to update any credentials. // In that case, the user can select any previously stored credential as @@ -1099,8 +1107,8 @@ void PasswordFormManager::CreatePendingCredentials() { // A retry password form is one that consists of only an "old password" // field, i.e. one that is not a "new password". retry_password_form_password_update_ = - submitted_form_->username_element.empty() && - submitted_form_->new_password_element.empty(); + submitted_form_->username_value.empty() && + submitted_form_->new_password_value.empty(); is_new_login_ = false; if (best_update_match) { @@ -1120,6 +1128,7 @@ void PasswordFormManager::CreatePendingCredentials() { pending_credentials_.origin = submitted_form_->origin; } } else { + is_new_login_ = true; // No stored credentials can be matched to the submitted form. Offer to // save new credentials. CreatePendingCredentialsForNewCredentials(); @@ -1246,25 +1255,11 @@ bool PasswordFormManager::IsMatch(const autofill::PasswordForm& form) const { bool PasswordFormManager::IsBlacklistMatch( const autofill::PasswordForm& blacklisted_form) const { - if (!blacklisted_form.blacklisted_by_user || - blacklisted_form.is_public_suffix_match || - blacklisted_form.scheme != observed_form_.scheme || - blacklisted_form.origin.GetOrigin() != - observed_form_.origin.GetOrigin()) { - return false; - } - - if (observed_form_.scheme == PasswordForm::SCHEME_HTML) { - return (blacklisted_form.origin.path_piece() == - observed_form_.origin.path_piece()) || - (AreStringsEqualOrEmpty(blacklisted_form.submit_element, - observed_form_.submit_element) && - AreStringsEqualOrEmpty(blacklisted_form.password_element, - observed_form_.password_element) && - AreStringsEqualOrEmpty(blacklisted_form.username_element, - observed_form_.username_element)); - } - return true; + return blacklisted_form.blacklisted_by_user && + !blacklisted_form.is_public_suffix_match && + blacklisted_form.scheme == observed_form_.scheme && + blacklisted_form.origin.GetOrigin() == + observed_form_.origin.GetOrigin(); } const PasswordForm* PasswordFormManager::FindBestMatchForUpdatePassword( @@ -1306,9 +1301,9 @@ const PasswordForm* PasswordFormManager::FindBestSavedMatch( // Verify that the submitted form has no username and no "new password" // and bail out with a nullptr otherwise. - bool submitted_form_has_username = !submitted_form->username_element.empty(); + bool submitted_form_has_username = !submitted_form->username_value.empty(); bool submitted_form_has_new_password_element = - !submitted_form->new_password_element.empty(); + !submitted_form->new_password_value.empty(); if (submitted_form_has_username || submitted_form_has_new_password_element) return nullptr; @@ -1326,8 +1321,7 @@ void PasswordFormManager::CreatePendingCredentialsForNewCredentials() { // User typed in a new, unknown username. SetUserAction(UserAction::kOverrideUsernameAndPassword); pending_credentials_ = observed_form_; - if (submitted_form_->was_parsed_using_autofill_predictions) - pending_credentials_.username_element = submitted_form_->username_element; + pending_credentials_.username_element = submitted_form_->username_element; pending_credentials_.username_value = submitted_form_->username_value; pending_credentials_.other_possible_usernames = submitted_form_->other_possible_usernames; diff --git a/chromium/components/password_manager/core/browser/password_form_manager.h b/chromium/components/password_manager/core/browser/password_form_manager.h index c40125a6780..ea76f0b5c94 100644 --- a/chromium/components/password_manager/core/browser/password_form_manager.h +++ b/chromium/components/password_manager/core/browser/password_form_manager.h @@ -597,14 +597,9 @@ class PasswordFormManager : public FormFetcher::Consumer { // Make sure to call Init before using |*this|, to ensure it is not null. scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder_; - // Set if the user has edited username value in prompt. The value is the - // matched field name from |PasswordForm.other_possible_usernames| if the - // match found. - base::Optional<base::string16> corrected_username_element_; - - // Tracks if a form with same origin as |observed_form_| found in blacklisted - // forms. - bool blacklisted_origin_found_ = false; + // True iff a user edited the username value in a prompt and new username is + // the value of another field of the observed form. + bool has_username_edited_vote_ = false; // If Chrome has already autofilled a few times, it is probable that autofill // is triggered by programmatic changes in the page. We set a maximum number diff --git a/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc index 7625fe3256d..9f66ed38774 100644 --- a/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc +++ b/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc @@ -57,11 +57,13 @@ using autofill::PasswordForm; using autofill::PossibleUsernamePair; using base::ASCIIToUTF16; using ::testing::_; +using ::testing::Contains; using ::testing::ElementsAre; using ::testing::InSequence; using ::testing::IsEmpty; using ::testing::Mock; using ::testing::NiceMock; +using ::testing::Not; using ::testing::Pair; using ::testing::Pointee; using ::testing::Return; @@ -85,11 +87,10 @@ class MockFormSaver : public StubFormSaver { // FormSaver: MOCK_METHOD1(PermanentlyBlacklist, void(autofill::PasswordForm* observed)); - MOCK_METHOD3( + MOCK_METHOD2( Save, void(const autofill::PasswordForm& pending, - const std::map<base::string16, const PasswordForm*>& best_matches, - const autofill::PasswordForm* old_primary_key)); + const std::map<base::string16, const PasswordForm*>& best_matches)); MOCK_METHOD4( Update, void(const autofill::PasswordForm& pending, @@ -296,11 +297,12 @@ class MockAutofillManager : public autofill::AutofillManager { } // Workaround for std::unique_ptr<> lacking a copy constructor. - void StartUploadProcess(std::unique_ptr<FormStructure> form_structure, + bool StartUploadProcess(std::unique_ptr<FormStructure> form_structure, const base::TimeTicks& timestamp, bool observed_submission) { StartUploadProcessPtr(form_structure.release(), timestamp, observed_submission); + return true; } MOCK_METHOD3(StartUploadProcessPtr, @@ -406,13 +408,6 @@ class PasswordFormManagerTest : public testing::Test { saved_match_.other_possible_usernames.push_back(PossibleUsernamePair( ASCIIToUTF16("test2@gmail.com"), ASCIIToUTF16("full_name"))); - psl_saved_match_ = saved_match_; - psl_saved_match_.is_public_suffix_match = true; - psl_saved_match_.origin = - GURL("http://m.accounts.google.com/a/ServiceLoginAuth"); - psl_saved_match_.action = GURL("http://m.accounts.google.com/a/Login"); - psl_saved_match_.signon_realm = "http://m.accounts.google.com"; - autofill::FormFieldData field; field.label = ASCIIToUTF16("Full name"); field.name = ASCIIToUTF16("full_name"); @@ -429,6 +424,13 @@ class PasswordFormManagerTest : public testing::Test { field.form_control_type = "password"; saved_match_.form_data.fields.push_back(field); + psl_saved_match_ = saved_match_; + psl_saved_match_.is_public_suffix_match = true; + psl_saved_match_.origin = + GURL("http://m.accounts.google.com/a/ServiceLoginAuth"); + psl_saved_match_.action = GURL("http://m.accounts.google.com/a/Login"); + psl_saved_match_.signon_realm = "http://m.accounts.google.com"; + password_manager_.reset(new PasswordManager(&client_)); form_manager_.reset(new PasswordFormManager( password_manager_.get(), &client_, client_.driver(), observed_form_, @@ -992,63 +994,35 @@ TEST_F(PasswordFormManagerTest, TestBlacklist) { // Test that stored blacklisted forms are correctly evaluated for whether they // apply to the observed form. TEST_F(PasswordFormManagerTest, TestBlacklistMatching) { - observed_form()->origin = GURL("http://accounts.google.com/a/LoginAuth"); - observed_form()->action = GURL("http://accounts.google.com/a/Login"); - observed_form()->signon_realm = "http://accounts.google.com"; - FakeFormFetcher fetcher; - fetcher.Fetch(); - PasswordFormManager form_manager(password_manager(), client(), - client()->driver(), *observed_form(), - std::make_unique<MockFormSaver>(), &fetcher); - form_manager.Init(nullptr); - // Doesn't apply because it is just a PSL match of the observed form. PasswordForm blacklisted_psl = *observed_form(); blacklisted_psl.signon_realm = "http://m.accounts.google.com"; blacklisted_psl.is_public_suffix_match = true; blacklisted_psl.blacklisted_by_user = true; - // Doesn't apply because of different origin. - PasswordForm blacklisted_not_match = *observed_form(); - blacklisted_not_match.origin = GURL("http://google.com/a/LoginAuth"); - blacklisted_not_match.blacklisted_by_user = true; - - // Doesn't apply because of different username element and different page. - PasswordForm blacklisted_not_match2 = *observed_form(); - blacklisted_not_match2.origin = GURL("http://accounts.google.com/a/Login123"); - blacklisted_not_match2.username_element = ASCIIToUTF16("Element"); - blacklisted_not_match2.blacklisted_by_user = true; - // Doesn't apply because of different PasswordForm::Scheme. - PasswordForm blacklisted_not_match3 = *observed_form(); - blacklisted_not_match3.scheme = PasswordForm::SCHEME_BASIC; + PasswordForm blacklisted_not_match = *observed_form(); + blacklisted_not_match.scheme = PasswordForm::SCHEME_BASIC; - // Applies because of same element names, despite different page + // Applies despite different element names and path. PasswordForm blacklisted_match = *observed_form(); blacklisted_match.origin = GURL("http://accounts.google.com/a/LoginAuth1234"); + blacklisted_match.username_element = ASCIIToUTF16("Element1"); + blacklisted_match.password_element = ASCIIToUTF16("Element2"); + blacklisted_match.submit_element = ASCIIToUTF16("Element3"); blacklisted_match.blacklisted_by_user = true; - // Applies because of same page, despite different element names - PasswordForm blacklisted_match2 = *observed_form(); - blacklisted_match2.origin = GURL("http://accounts.google.com/a/LoginAuth"); - blacklisted_match2.username_element = ASCIIToUTF16("Element"); - blacklisted_match2.blacklisted_by_user = true; - std::vector<const PasswordForm*> matches = {&blacklisted_psl, &blacklisted_not_match, - &blacklisted_not_match2, - &blacklisted_not_match3, &blacklisted_match, - &blacklisted_match2, saved_match()}; - fetcher.SetNonFederated(matches, 0u); + fake_form_fetcher()->SetNonFederated(matches, 0u); - EXPECT_TRUE(form_manager.IsBlacklisted()); - EXPECT_THAT(form_manager.blacklisted_matches(), - UnorderedElementsAre(Pointee(blacklisted_match), - Pointee(blacklisted_match2))); - EXPECT_EQ(1u, form_manager.best_matches().size()); - EXPECT_EQ(*saved_match(), *form_manager.preferred_match()); + EXPECT_TRUE(form_manager()->IsBlacklisted()); + EXPECT_THAT(form_manager()->blacklisted_matches(), + ElementsAre(Pointee(blacklisted_match))); + EXPECT_EQ(1u, form_manager()->best_matches().size()); + EXPECT_EQ(*saved_match(), *form_manager()->preferred_match()); } // Test that even in the presence of blacklisted matches, the non-blacklisted @@ -1122,7 +1096,7 @@ TEST_F(PasswordFormManagerTest, PSLMatchedCredentialsMetadataUpdated) { EXPECT_CALL( *client()->mock_driver()->mock_autofill_download_manager(), StartUploadRequest(_, false, expected_available_field_types, _, true)); - EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, nullptr)) + EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _)) .WillOnce(SaveArg<0>(&actual_saved_form)); form_manager()->Save(); @@ -1538,7 +1512,7 @@ TEST_F(PasswordFormManagerTest, TestSanitizePossibleUsernames) { credentials, PasswordFormManager::ALLOW_OTHER_POSSIBLE_USERNAMES); PasswordForm saved_result; - EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, nullptr)) + EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _)) .WillOnce(SaveArg<0>(&saved_result)); form_manager()->Save(); @@ -1574,7 +1548,7 @@ TEST_F(PasswordFormManagerTest, TestSanitizePossibleUsernamesDuplicates) { credentials, PasswordFormManager::ALLOW_OTHER_POSSIBLE_USERNAMES); PasswordForm saved_result; - EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, nullptr)) + EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _)) .WillOnce(SaveArg<0>(&saved_result)); form_manager()->Save(); @@ -2097,7 +2071,7 @@ TEST_F(PasswordFormManagerTest, CorrectlySavePasswordWithoutUsernameFields) { EXPECT_TRUE(form_manager()->IsNewLogin()); PasswordForm saved_result; - EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _, nullptr)) + EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, _)) .WillOnce(SaveArg<0>(&saved_result)); form_manager()->Save(); @@ -2356,101 +2330,296 @@ TEST_F(PasswordFormManagerTest, TestUpdateNoUsernameTextfieldPresent) { EXPECT_EQ(saved_match()->submit_element, new_credentials.submit_element); } -// Test that when user updates username, the pending credentials is updated -// accordingly. -TEST_F(PasswordFormManagerTest, TestUpdateUsernameMethod) { - fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u); +// Test the case when a user changes the username to the value of another field +// from the form. +TEST_F(PasswordFormManagerTest, UpdateUsername_ValueOfAnotherField) { + for (bool captured_username_is_empty : {false, true}) { + SCOPED_TRACE(testing::Message() << "captured_username_is_empty=" + << captured_username_is_empty); + PasswordForm observed(*observed_form()); + // Set |FormData| to upload a username vote. + autofill::FormFieldData field; + field.name = ASCIIToUTF16("full_name"); + field.form_control_type = "text"; + observed.form_data.fields.push_back(field); + field.name = ASCIIToUTF16("correct_username_element"); + field.form_control_type = "text"; + observed.form_data.fields.push_back(field); + field.name = ASCIIToUTF16("Passwd"); + field.form_control_type = "password"; + observed.form_data.fields.push_back(field); - // User enters credential in the form. - PasswordForm credential(*observed_form()); - credential.username_value = ASCIIToUTF16("oldusername"); - credential.password_value = ASCIIToUTF16("password"); - form_manager()->ProvisionallySave( - credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); - // User edits username in a prompt. - form_manager()->UpdateUsername(ASCIIToUTF16("newusername")); - EXPECT_EQ(form_manager()->pending_credentials().username_value, - ASCIIToUTF16("newusername")); - EXPECT_EQ(form_manager()->pending_credentials().password_value, - ASCIIToUTF16("password")); - EXPECT_EQ(form_manager()->IsNewLogin(), true); + PasswordFormManager form_manager( + password_manager(), client(), client()->driver(), observed, + std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher()); + form_manager.Init(nullptr); + fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), + 0u); + + // User enters credential in the form. + PasswordForm credential(observed); + credential.username_value = captured_username_is_empty + ? base::string16() + : ASCIIToUTF16("typed_username"); + credential.password_value = ASCIIToUTF16("password"); + credential.other_possible_usernames.push_back( + PossibleUsernamePair(ASCIIToUTF16("edited_username"), + ASCIIToUTF16("correct_username_element"))); + form_manager.ProvisionallySave( + credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); + + // User edits username in a prompt. + form_manager.UpdateUsername(ASCIIToUTF16("edited_username")); + EXPECT_EQ(form_manager.pending_credentials().username_value, + ASCIIToUTF16("edited_username")); + EXPECT_EQ(form_manager.pending_credentials().username_element, + ASCIIToUTF16("correct_username_element")); + EXPECT_EQ(form_manager.pending_credentials().password_value, + ASCIIToUTF16("password")); + EXPECT_TRUE(form_manager.IsNewLogin()); - // User clicks save, edited username is saved. - PasswordForm saved_result; - EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, IsEmpty(), nullptr)) - .WillOnce(SaveArg<0>(&saved_result)); - form_manager()->Save(); - EXPECT_EQ(ASCIIToUTF16("newusername"), saved_result.username_value); - EXPECT_EQ(ASCIIToUTF16("password"), saved_result.password_value); + // User clicks save, the edited username is saved. + PasswordForm saved_result; + EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty())) + .WillOnce(SaveArg<0>(&saved_result)); + // Expect a username edited vote. + FieldTypeMap expected_types; + expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE; + expected_types[ASCIIToUTF16("correct_username_element")] = + autofill::USERNAME; + expected_types[ASCIIToUTF16("Passwd")] = autofill::PASSWORD; + EXPECT_CALL( + *client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest( + CheckUploadedAutofillTypesAndSignature( + autofill::FormStructure(observed.form_data) + .FormSignatureAsStr(), + expected_types, false /* expect_generation_vote */, + autofill::AutofillUploadContents::Field::USERNAME_EDITED), + _, Contains(autofill::USERNAME), _, _)); + form_manager.Save(); + + // Check what is saved. + EXPECT_EQ(ASCIIToUTF16("edited_username"), saved_result.username_value); + EXPECT_EQ(ASCIIToUTF16("correct_username_element"), + saved_result.username_element); + EXPECT_EQ(ASCIIToUTF16("password"), saved_result.password_value); + if (captured_username_is_empty) { + EXPECT_TRUE(saved_result.other_possible_usernames.empty()); + } else { + EXPECT_THAT( + saved_result.other_possible_usernames, + ElementsAre(PossibleUsernamePair(ASCIIToUTF16("typed_username"), + observed_form()->username_element))); + } + } } -// Test that when user updates username to an already existing one, is_new_login -// status is false. -TEST_F(PasswordFormManagerTest, TestUpdateUsernameToExisting) { - // We have an already existing credential. - fake_form_fetcher()->SetNonFederated({saved_match()}, 0u); +// Test the case when a user updates the username to an already existing one. +TEST_F(PasswordFormManagerTest, UpdateUsername_ValueSavedInStore) { + for (bool captured_username_is_empty : {false, true}) { + SCOPED_TRACE(testing::Message() << "captured_username_is_empty=" + << captured_username_is_empty); + // We have an already existing credential. + fake_form_fetcher()->SetNonFederated({saved_match()}, 0u); - // User enters credential in the form. - PasswordForm credential(*observed_form()); - credential.username_value = ASCIIToUTF16("different_username"); - credential.password_value = ASCIIToUTF16("different_pass"); - credential.preferred = true; - form_manager()->ProvisionallySave( - credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); + // User enters credential in the form. + PasswordForm credential(*observed_form()); + credential.username_value = captured_username_is_empty + ? base::string16() + : ASCIIToUTF16("different_username"); + credential.password_value = ASCIIToUTF16("different_pass"); + credential.preferred = true; + form_manager()->ProvisionallySave( + credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); + + // User edits username in a prompt to one already existing. + form_manager()->UpdateUsername(saved_match()->username_value); + + // The username in credentials is expected to be updated. + EXPECT_EQ(saved_match()->username_value, + form_manager()->pending_credentials().username_value); + EXPECT_EQ(saved_match()->username_element, + form_manager()->pending_credentials().username_element); + EXPECT_EQ(ASCIIToUTF16("different_pass"), + form_manager()->pending_credentials().password_value); + EXPECT_FALSE(form_manager()->IsNewLogin()); + + // Create the expected credential to be saved. + PasswordForm expected_pending(credential); + expected_pending.origin = saved_match()->origin; + expected_pending.form_data = saved_match()->form_data; + expected_pending.times_used = 1; + expected_pending.username_value = saved_match()->username_value; + + // User clicks save, edited username is saved, password updated. + EXPECT_CALL(MockFormSaver::Get(form_manager()), + Update(expected_pending, + ElementsAre(Pair(saved_match()->username_value, + Pointee(*saved_match()))), + Pointee(IsEmpty()), nullptr)); + // Expect a username reusing vote. + FieldTypeMap expected_types; + expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE; + expected_types[expected_pending.username_element] = autofill::USERNAME; + expected_types[expected_pending.password_element] = + autofill::ACCOUNT_CREATION_PASSWORD; + EXPECT_CALL( + *client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest( + CheckUploadedAutofillTypesAndSignature( + autofill::FormStructure(expected_pending.form_data) + .FormSignatureAsStr(), + expected_types, false /* expect_generation_vote */, + autofill::AutofillUploadContents::Field::CREDENTIALS_REUSED), + _, Contains(autofill::USERNAME), _, _)); + form_manager()->Save(); + } +} - // User edits username in a prompt to one already existing. - form_manager()->UpdateUsername(saved_match()->username_value); +// Tests the case when the username value edited in prompt doesn't coincides +// neither with other values on the form nor with saved usernames. +TEST_F(PasswordFormManagerTest, UpdateUsername_NoMatchNeitherOnFormNorInStore) { + for (bool captured_username_is_empty : {false, true}) { + SCOPED_TRACE(testing::Message() << "captured_username_is_empty=" + << captured_username_is_empty); + PasswordForm observed(*observed_form()); + // Assign any |FormData| to allow crowdsourcing of this form. + observed.form_data = saved_match()->form_data; + PasswordFormManager form_manager( + password_manager(), client(), client()->driver(), observed, + std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher()); + form_manager.Init(nullptr); + // We have an already existing credential. + fake_form_fetcher()->SetNonFederated({saved_match()}, 0u); - // The username in credentials is expected to be updated. - EXPECT_EQ(saved_match()->username_value, - form_manager()->pending_credentials().username_value); - EXPECT_EQ(ASCIIToUTF16("different_pass"), - form_manager()->pending_credentials().password_value); - EXPECT_EQ(form_manager()->IsNewLogin(), false); + // A user enters a credential in the form. + PasswordForm credential(observed); + credential.username_value = captured_username_is_empty + ? base::string16() + : ASCIIToUTF16("captured_username"); + credential.password_value = ASCIIToUTF16("different_pass"); + credential.preferred = true; + form_manager.ProvisionallySave( + credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); - // Create the expected variables for the test. - PasswordForm expected_pending(credential); - expected_pending.times_used = 1; - expected_pending.username_value = saved_match()->username_value; + // User edits username. The username doesn't exist neither in the store nor + // on the form. + form_manager.UpdateUsername(ASCIIToUTF16("new_username")); - // User clicks save, edited username is saved, password updated. + // As there is no match on the form, |username_element| is empty. + EXPECT_TRUE(form_manager.pending_credentials().username_element.empty()); + EXPECT_EQ(ASCIIToUTF16("new_username"), + form_manager.pending_credentials().username_value); + EXPECT_EQ(ASCIIToUTF16("different_pass"), + form_manager.pending_credentials().password_value); + EXPECT_TRUE(form_manager.IsNewLogin()); + + PasswordForm expected_pending(credential); + expected_pending.username_value = ASCIIToUTF16("new_username"); + expected_pending.username_element = base::string16(); + if (!captured_username_is_empty) { + // A non-empty captured username value should be saved to recover later if + // a user makes a mistake in username editing. + expected_pending.other_possible_usernames.push_back(PossibleUsernamePair( + ASCIIToUTF16("captured_username"), ASCIIToUTF16("Email"))); + } + + // User clicks save, the edited username is saved, the password is updated, + // no username vote is uploaded. + PasswordForm actual_saved_form; + EXPECT_CALL(MockFormSaver::Get(&form_manager), + Save(_, ElementsAre(Pair(saved_match()->username_value, + Pointee(*saved_match()))))) + .WillOnce(SaveArg<0>(&actual_saved_form)); + EXPECT_CALL( + *client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest(_, _, Not(Contains(autofill::USERNAME)), _, _)); + form_manager.Save(); + + // Can't verify |date_created |, so ignore it in form comparison. + actual_saved_form.date_created = expected_pending.date_created; + EXPECT_EQ(expected_pending, actual_saved_form); + } +} + +// Tests the case when a user clears the username value in a prompt. +TEST_F(PasswordFormManagerTest, UpdateUsername_UserRemovedUsername) { + PasswordForm observed(*observed_form()); + // Assign any |FormData| to allow crowdsourcing of this form. + observed.form_data = saved_match()->form_data; + PasswordFormManager form_manager( + password_manager(), client(), client()->driver(), observed, + std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher()); + form_manager.Init(nullptr); + fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u); + + // The user enters credential in the form. + PasswordForm credential(observed); + credential.username_value = ASCIIToUTF16("pin_code"); + credential.password_value = ASCIIToUTF16("password"); + credential.other_possible_usernames.push_back( + PossibleUsernamePair(base::string16(), ASCIIToUTF16("empty_field"))); + form_manager.ProvisionallySave( + credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); + + // The user clears the username value in the prompt. + form_manager.UpdateUsername(base::string16()); + EXPECT_TRUE(form_manager.pending_credentials().username_value.empty()); + EXPECT_TRUE(form_manager.pending_credentials().username_element.empty()); + EXPECT_EQ(form_manager.pending_credentials().password_value, + ASCIIToUTF16("password")); + EXPECT_TRUE(form_manager.IsNewLogin()); + + // The user clicks save, empty username is saved. PasswordForm saved_result; - EXPECT_CALL(MockFormSaver::Get(form_manager()), - Update(expected_pending, - ElementsAre(Pair(saved_match()->username_value, - Pointee(*saved_match()))), - Pointee(IsEmpty()), nullptr)); - form_manager()->Save(); + EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, IsEmpty())) + .WillOnce(SaveArg<0>(&saved_result)); + EXPECT_CALL( + *client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest(_, _, Not(Contains(autofill::USERNAME)), _, _)); + form_manager.Save(); + + EXPECT_TRUE(saved_result.username_value.empty()); + EXPECT_TRUE(saved_result.username_element.empty()); + EXPECT_EQ(ASCIIToUTF16("password"), saved_result.password_value); + EXPECT_THAT( + saved_result.other_possible_usernames, + ElementsAre(PossibleUsernamePair(ASCIIToUTF16("pin_code"), + observed_form()->username_element))); } // Test that when user updates username to a PSL matching credential, we should // handle it as a new login. -TEST_F(PasswordFormManagerTest, TestUpdatePSLUsername) { +TEST_F(PasswordFormManagerTest, UpdateUsername_PslMatch) { fake_form_fetcher()->SetNonFederated({psl_saved_match()}, 0u); - // User submits credential to the observed form. + // The user submits a credential of the observed form. PasswordForm credential(*observed_form()); credential.username_value = ASCIIToUTF16("some_username"); credential.password_value = ASCIIToUTF16("some_pass"); form_manager()->ProvisionallySave( credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); - // User edits username to match the PSL. + // The user edits the username to match the PSL entry. form_manager()->UpdateUsername(psl_saved_match()->username_value); EXPECT_EQ(psl_saved_match()->username_value, form_manager()->pending_credentials().username_value); - EXPECT_EQ(true, form_manager()->IsNewLogin()); + EXPECT_TRUE(form_manager()->IsNewLogin()); EXPECT_EQ(ASCIIToUTF16("some_pass"), form_manager()->pending_credentials().password_value); - // User clicks save, edited username is saved. + // The user clicks save, the edited username is saved. PasswordForm saved_result; EXPECT_CALL(MockFormSaver::Get(form_manager()), - Save(_, - ElementsAre(Pair(psl_saved_match()->username_value, - Pointee(*psl_saved_match()))), - nullptr)) + Save(_, ElementsAre(Pair(psl_saved_match()->username_value, + Pointee(*psl_saved_match()))))) .WillOnce(SaveArg<0>(&saved_result)); + // As the credential is re-used successfully, expect a username vote. + EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest(_, _, Contains(autofill::USERNAME), _, _)); form_manager()->Save(); + + // Check what is saved. EXPECT_EQ(psl_saved_match()->username_value, saved_result.username_value); EXPECT_EQ(ASCIIToUTF16("some_pass"), saved_result.password_value); } @@ -2472,11 +2641,11 @@ TEST_F(PasswordFormManagerTest, TestSelectPasswordMethod) { ASCIIToUTF16("username")); EXPECT_EQ(form_manager()->pending_credentials().password_value, ASCIIToUTF16("newpassword")); - EXPECT_EQ(form_manager()->IsNewLogin(), true); + EXPECT_TRUE(form_manager()->IsNewLogin()); // User clicks save, selected password is saved. PasswordForm saved_result; - EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, IsEmpty(), nullptr)) + EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, IsEmpty())) .WillOnce(SaveArg<0>(&saved_result)); form_manager()->Save(); EXPECT_EQ(ASCIIToUTF16("username"), saved_result.username_value); @@ -2772,7 +2941,7 @@ TEST_F(PasswordFormManagerTest, EXPECT_EQ(saved_match()->origin, new_credentials.origin); } -TEST_F(PasswordFormManagerTest, TestNotUpdateWhenOnlyPSLMatched) { +TEST_F(PasswordFormManagerTest, TestNotUpdateWhenOnlyPslMatched) { FakeFormFetcher fetcher; fetcher.Fetch(); PasswordFormManager form_manager(password_manager(), client(), @@ -2794,9 +2963,11 @@ TEST_F(PasswordFormManagerTest, TestNotUpdateWhenOnlyPSLMatched) { // PSL matched credential should not be updated, since we are not sure that // this is the same credential as submitted one. PasswordForm new_credentials; - EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, _, nullptr)) + EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, _)) .WillOnce(testing::SaveArg<0>(&new_credentials)); - + // As the username is re-used, expect a username vote. + EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest(_, _, Contains(autofill::USERNAME), _, _)); form_manager.Save(); EXPECT_EQ(credentials.password_value, new_credentials.password_value); @@ -3140,7 +3311,7 @@ TEST_F(PasswordFormManagerTest, TestSavingAPIFormsWithSamePassword) { EXPECT_TRUE(form_manager.IsNewLogin()); PasswordForm new_credentials; - EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, _, nullptr)) + EXPECT_CALL(MockFormSaver::Get(&form_manager), Save(_, _)) .WillOnce(SaveArg<0>(&new_credentials)); form_manager.Save(); @@ -3205,7 +3376,7 @@ TEST_F(PasswordFormManagerTest, ProbablyAccountCreationUpload) { // A user submits a form and edits the username in the prompt. form_manager.ProvisionallySave( form_to_save, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); - form_manager.UpdateUsername(ASCIIToUTF16(" test2@gmail.com")); + form_manager.UpdateUsername(ASCIIToUTF16("test2@gmail.com")); autofill::FormStructure pending_structure(form_to_save.form_data); autofill::ServerFieldTypeSet expected_available_field_types; @@ -3263,8 +3434,7 @@ TEST_F(PasswordFormManagerTest, ReportProcessingUpdate) { // Sanity check for calling ProcessMatches with empty vector. Should not crash // or make sanitizers scream. TEST_F(PasswordFormManagerTest, ProcessMatches_Empty) { - static_cast<FormFetcher::Consumer*>(form_manager()) - ->ProcessMatches(std::vector<const PasswordForm*>(), 0u); + fake_form_fetcher()->SetNonFederated({}, 0u); } // For all combinations of PasswordForm schemes, test that ProcessMatches @@ -3300,16 +3470,14 @@ TEST_F(PasswordFormManagerTest, RemoveResultsWithWrongScheme_ObservingHTML) { non_match.scheme = kWrongScheme; // First try putting the correct scheme first in returned matches. - static_cast<FormFetcher::Consumer*>(&form_manager) - ->ProcessMatches({&match, &non_match}, 0u); + fetcher.SetNonFederated({&match, &non_match}, 0); EXPECT_EQ(1u, form_manager.best_matches().size()); EXPECT_EQ(kCorrectScheme, form_manager.best_matches().begin()->second->scheme); // Now try putting the correct scheme last in returned matches. - static_cast<FormFetcher::Consumer*>(&form_manager) - ->ProcessMatches({&non_match, &match}, 0u); + fetcher.SetNonFederated({&non_match, &match}, 0); EXPECT_EQ(1u, form_manager.best_matches().size()); EXPECT_EQ(kCorrectScheme, @@ -3354,101 +3522,109 @@ TEST_F(PasswordFormManagerTest, UploadUsernameCorrectionVote) { // relaxed. base::test::ScopedFeatureList features; features.InitAndEnableFeature(kAutofillEnforceMinRequiredFieldsForUpload); - for (bool is_form_with_2_fields : {false, true}) { - SCOPED_TRACE(testing::Message() - << "is_form_with_2_fields=" << is_form_with_2_fields); - // Observed and saved forms have the same password, but different usernames. - PasswordForm new_login(*observed_form()); - autofill::FormFieldData field; - if (!is_form_with_2_fields) { - field.label = ASCIIToUTF16("Full name"); - field.name = ASCIIToUTF16("full_name"); + for (bool is_pending_credential_psl_match : {false, true}) { + for (bool is_form_with_2_fields : {false, true}) { + SCOPED_TRACE(testing::Message() + << "is_form_with_2_fields=" << is_form_with_2_fields); + // Observed and saved forms have the same password, but different + // usernames. + PasswordForm new_login(*observed_form()); + autofill::FormFieldData field; + if (!is_form_with_2_fields) { + field.label = ASCIIToUTF16("Full name"); + field.name = ASCIIToUTF16("full_name"); + field.form_control_type = "text"; + new_login.form_data.fields.push_back(field); + new_login.does_look_like_signup_form = true; + } + field.label = ASCIIToUTF16("Email"); + field.name = ASCIIToUTF16("observed-username-field"); field.form_control_type = "text"; new_login.form_data.fields.push_back(field); - new_login.does_look_like_signup_form = true; - } - field.label = ASCIIToUTF16("Email"); - field.name = ASCIIToUTF16("observed-username-field"); - field.form_control_type = "text"; - new_login.form_data.fields.push_back(field); - - field.label = ASCIIToUTF16("Password"); - field.name = ASCIIToUTF16("Passwd"); - field.form_control_type = "password"; - new_login.form_data.fields.push_back(field); - - new_login.username_value = saved_match()->other_possible_usernames[0].first; - new_login.password_value = saved_match()->password_value; - PasswordFormManager form_manager( - password_manager(), client(), client()->driver(), new_login, - std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher()); - form_manager.Init(nullptr); - - base::HistogramTester histogram_tester; - fake_form_fetcher()->SetNonFederated({saved_match()}, 0u); - form_manager.ProvisionallySave( - new_login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); - histogram_tester.ExpectUniqueSample( - "PasswordManager.UsernameCorrectionFound", 1, 1); - // No match found (because usernames are different). - EXPECT_TRUE(form_manager.IsNewLogin()); - - // Checks the username correction vote is saved. - PasswordForm expected_username_vote(*saved_match()); - expected_username_vote.username_element = - saved_match()->other_possible_usernames[0].second; + field.label = ASCIIToUTF16("Password"); + field.name = ASCIIToUTF16("Passwd"); + field.form_control_type = "password"; + new_login.form_data.fields.push_back(field); - // Checks the username vote type is saved. - autofill::AutofillUploadContents::Field::UsernameVoteType - expected_username_vote_type = - autofill::AutofillUploadContents::Field::USERNAME_OVERWRITTEN; + const PasswordForm* saved_credential = + is_pending_credential_psl_match ? psl_saved_match() : saved_match(); - // Checks the upload. - autofill::ServerFieldTypeSet expected_available_field_types; - expected_available_field_types.insert(autofill::USERNAME); - expected_available_field_types.insert(autofill::ACCOUNT_CREATION_PASSWORD); + new_login.username_value = + saved_credential->other_possible_usernames[0].first; + new_login.password_value = saved_credential->password_value; - FormStructure expected_upload(expected_username_vote.form_data); + PasswordFormManager form_manager( + password_manager(), client(), client()->driver(), new_login, + std::make_unique<NiceMock<MockFormSaver>>(), fake_form_fetcher()); + form_manager.Init(nullptr); - std::string expected_login_signature = - FormStructure(form_manager.observed_form().form_data) - .FormSignatureAsStr(); + base::HistogramTester histogram_tester; + fake_form_fetcher()->SetNonFederated({saved_credential}, 0u); + form_manager.ProvisionallySave( + new_login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); + histogram_tester.ExpectUniqueSample( + "PasswordManager.UsernameCorrectionFound", 1, 1); + // No match found (because usernames are different). + EXPECT_TRUE(form_manager.IsNewLogin()); + + // Checks the username correction vote is saved. + PasswordForm expected_username_vote(*saved_credential); + expected_username_vote.username_element = + saved_credential->other_possible_usernames[0].second; + + // Checks the type of the username vote is saved. + autofill::AutofillUploadContents::Field::UsernameVoteType + expected_username_vote_type = + autofill::AutofillUploadContents::Field::USERNAME_OVERWRITTEN; + + // Checks the upload. + autofill::ServerFieldTypeSet expected_available_field_types; + expected_available_field_types.insert(autofill::USERNAME); + expected_available_field_types.insert( + autofill::ACCOUNT_CREATION_PASSWORD); + + FormStructure expected_upload(expected_username_vote.form_data); + + std::string expected_login_signature = + FormStructure(form_manager.observed_form().form_data) + .FormSignatureAsStr(); + + std::map<base::string16, autofill::ServerFieldType> expected_types; + expected_types[expected_username_vote.username_element] = + autofill::USERNAME; + expected_types[expected_username_vote.password_element] = + autofill::ACCOUNT_CREATION_PASSWORD; + expected_types[ASCIIToUTF16("Email")] = autofill::UNKNOWN_TYPE; + + InSequence s; + std::unique_ptr<FormStructure> signin_vote_form_structure; + if (is_form_with_2_fields) { + // Make signin vote upload synchronous and free |FormStructure| passed + // for upload. + auto* mock_autofill_manager = + client()->mock_driver()->mock_autofill_manager(); + EXPECT_CALL(*mock_autofill_manager, StartUploadProcessPtr(_, _, true)) + .WillOnce(WithArg<0>(SaveToUniquePtr(&signin_vote_form_structure))); + } else { + // The observed form has 2 text fields and 1 password field, + // PROBABLY_ACCOUNT_CREATION_PASSWORD should be uploaded. + autofill::ServerFieldTypeSet field_types; + field_types.insert(autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD); + EXPECT_CALL( + *client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest(_, false, field_types, std::string(), true)); + } - std::map<base::string16, autofill::ServerFieldType> expected_types; - expected_types[expected_username_vote.username_element] = - autofill::USERNAME; - expected_types[expected_username_vote.password_element] = - autofill::ACCOUNT_CREATION_PASSWORD; - expected_types[ASCIIToUTF16("Email")] = autofill::UNKNOWN_TYPE; - - InSequence s; - std::unique_ptr<FormStructure> signin_vote_form_structure; - if (is_form_with_2_fields) { - // Make signin vote upload synchronous and free |FormStructure| passed for - // upload. - auto* mock_autofill_manager = - client()->mock_driver()->mock_autofill_manager(); - EXPECT_CALL(*mock_autofill_manager, StartUploadProcessPtr(_, _, true)) - .WillOnce(WithArg<0>(SaveToUniquePtr(&signin_vote_form_structure))); - } else { - // The observed form has 2 text fields and 1 password field, - // PROBABLY_ACCOUNT_CREATION_PASSWORD should be uploaded. - autofill::ServerFieldTypeSet field_types; - field_types.insert(autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD); - EXPECT_CALL( - *client()->mock_driver()->mock_autofill_download_manager(), - StartUploadRequest(_, false, field_types, std::string(), true)); + EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest( + CheckUploadedAutofillTypesAndSignature( + expected_upload.FormSignatureAsStr(), expected_types, + false, expected_username_vote_type), + false, expected_available_field_types, + expected_login_signature, true)); + form_manager.Save(); } - - EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(), - StartUploadRequest( - CheckUploadedAutofillTypesAndSignature( - expected_upload.FormSignatureAsStr(), expected_types, - false, expected_username_vote_type), - false, expected_available_field_types, - expected_login_signature, true)); - form_manager.Save(); } } @@ -3474,6 +3650,52 @@ TEST_F(PasswordFormManagerTest, NoUsernameCorrectionVote) { form_manager()->Save(); } +TEST_F(PasswordFormManagerTest, + UsernameCorrectionVote_PasswordReusedWithoutUsername) { + for (bool saved_username_is_empty : {false, true}) { + SCOPED_TRACE(testing::Message() + << "saved_username_is_empty=" << saved_username_is_empty); + + if (saved_username_is_empty) { + saved_match()->username_value.clear(); + // |username_element| must be empty as well, but it may be non-empty in + // credentials saved in past versions. Let's test there will be not vote + // for this element even if |username_element| has a non-empty value. + } + + saved_match()->other_possible_usernames.push_back( + PossibleUsernamePair(base::string16(), ASCIIToUTF16("empty_field"))); + fake_form_fetcher()->SetNonFederated({saved_match()}, 0u); + + // A user enters the password in the form. The username is absent or not + // captured, but the user doesn't enter the username in a prompt. + PasswordForm credential(*saved_match()); + credential.username_value.clear(); + credential.preferred = true; + form_manager()->ProvisionallySave( + credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); + EXPECT_FALSE(form_manager()->IsNewLogin()); + + // Create the expected credential to be saved. + PasswordForm expected_pending(*saved_match()); + expected_pending.times_used = 1; + // As a credential is reused, |other_possible_usernames| will be cleared. + // In fact, the username wasn't reused, only password, but for the sake of + // simplicity |other_possible_usernames| is cleared even in this case. + expected_pending.other_possible_usernames.clear(); + + EXPECT_CALL(MockFormSaver::Get(form_manager()), + Update(expected_pending, + ElementsAre(Pair(saved_match()->username_value, + Pointee(*saved_match()))), + Pointee(IsEmpty()), nullptr)); + EXPECT_CALL( + *client()->mock_driver()->mock_autofill_download_manager(), + StartUploadRequest(_, _, Not(Contains(autofill::USERNAME)), _, _)); + form_manager()->Save(); + } +} + // Test that ResetStoredMatches removes references to previously fetched store // results. TEST_F(PasswordFormManagerTest, ResetStoredMatches) { @@ -3834,11 +4056,6 @@ TEST_F(PasswordFormManagerTest, SuppressedFormsHistograms) { form_data->password_value, form_data->manual_or_generated)); } - // Bind the UKM SourceId to any URL, it does not matter. The SourceId - // needs to be bound, though, for reporting to happen. - client()->GetUkmRecorder()->UpdateSourceURL(client()->GetUkmSourceId(), - GURL("https://example.com/")); - std::vector<const PasswordForm*> suppressed_forms_ptrs; for (const auto& form : suppressed_forms) suppressed_forms_ptrs.push_back(&form); @@ -3955,7 +4172,7 @@ TEST_F(PasswordFormManagerTest, Clone_OnSave) { std::unique_ptr<PasswordFormManager> clone = form_manager->Clone(); PasswordForm passed; - EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty(), nullptr)) + EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty())) .WillOnce(SaveArg<0>(&passed)); clone->Save(); // The date is expected to be different. Reset it so that we can easily @@ -4008,7 +4225,7 @@ TEST_F(PasswordFormManagerTest, Clone_SurvivesOriginal) { form_manager.reset(); PasswordForm passed; - EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty(), nullptr)) + EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty())) .WillOnce(SaveArg<0>(&passed)); clone->Save(); // The date is expected to be different. Reset it so that we can easily @@ -4053,8 +4270,8 @@ TEST_F(PasswordFormManagerTest, TestUkmForFilling) { ukm::TestAutoSetUkmRecorder test_ukm_recorder; { auto metrics_recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( - form_to_fill.origin.SchemeIsCryptographic(), &test_ukm_recorder, - test_ukm_recorder.GetNewSourceID(), form_to_fill.origin); + form_to_fill.origin.SchemeIsCryptographic(), + client()->GetUkmSourceId()); FakeFormFetcher fetcher; PasswordFormManager form_manager( password_manager(), client(), @@ -4068,7 +4285,7 @@ TEST_F(PasswordFormManagerTest, TestUkmForFilling) { ukm::builders::PasswordForm::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* const entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, form_to_fill.origin); + EXPECT_EQ(client()->GetUkmSourceId(), entry->source_id); test_ukm_recorder.ExpectEntryMetric( entry, ukm::builders::PasswordForm::kManagerFill_ActionName, test.expected_event); diff --git a/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc index d69da19cf24..843c05c3091 100644 --- a/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc +++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc @@ -61,13 +61,9 @@ PasswordFormMetricsRecorder::BubbleDismissalReason GetBubbleDismissalReason( PasswordFormMetricsRecorder::PasswordFormMetricsRecorder( bool is_main_frame_secure, - ukm::UkmRecorder* ukm_recorder, - ukm::SourceId source_id, - const GURL& main_frame_url) + ukm::SourceId source_id) : is_main_frame_secure_(is_main_frame_secure), - ukm_recorder_(ukm_recorder), source_id_(source_id), - main_frame_url_(main_frame_url), ukm_entry_builder_(source_id) {} PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() { @@ -133,12 +129,7 @@ PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() { } } - ukm_entry_builder_.Record(ukm_recorder_); - - // Bind |main_frame_url_| to |source_id_| directly before sending the content - // of |ukm_recorder_| to ensure that the binding has not been purged already. - if (ukm_recorder_) - ukm_recorder_->UpdateSourceURL(source_id_, main_frame_url_); + ukm_entry_builder_.Record(ukm::UkmRecorder::Get()); } void PasswordFormMetricsRecorder::MarkGenerationAvailable() { diff --git a/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h index 52fab3a610d..1fbd7811a88 100644 --- a/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h +++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h @@ -41,17 +41,9 @@ class PasswordFormMetricsRecorder : public base::RefCounted<PasswordFormMetricsRecorder> { public: // Records UKM metrics and reports them on destruction. The |source_id| is - // (re-)bound to |main_frame_url| shortly before reporting. As such it is - // crucial that the |source_id| is never bound to a different URL by another - // consumer. The reason for this late binding is that metrics can be - // collected for a WebContents for a long period of time and by the time the - // reporting happens, the binding of |source_id| to |main_frame_url| is - // already purged. |ukm_recorder| may be a nullptr, in which case no UKM - // metrics are recorded. + // the ID of the WebContents document that the forms belong to. PasswordFormMetricsRecorder(bool is_main_frame_secure, - ukm::UkmRecorder* ukm_recorder, - ukm::SourceId source_id, - const GURL& main_frame_url); + ukm::SourceId source_id); // ManagerAction - What does the PasswordFormManager do with this form? Either // it fills it, or it doesn't. If it doesn't fill it, that's either @@ -307,18 +299,9 @@ class PasswordFormMetricsRecorder // data the user has entered. SubmittedFormType submitted_form_type_ = kSubmittedFormTypeUnspecified; - // Recorder to which metrics are sent. Has to outlive this - // PasswordFormMetricsRecorder. - ukm::UkmRecorder* ukm_recorder_; - - // A SourceId of |ukm_recorder_|. This id gets bound to |main_frame_url_| on - // destruction. It can be shared across multiple metrics recorders as long as - // they all bind it to the same URL. + // The UKM SourceId of the document the form belongs to. ukm::SourceId source_id_; - // URL for which UKMs are reported. - GURL main_frame_url_; - // Holds URL keyed metrics (UKMs) to be recorded on destruction. ukm::builders::PasswordForm ukm_entry_builder_; diff --git a/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc b/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc index b928e2a664d..589c89f36a4 100644 --- a/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc +++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc @@ -21,17 +21,16 @@ namespace password_manager { namespace { -constexpr char kTestUrl[] = "https://www.example.com/"; +constexpr ukm::SourceId kTestSourceId = 0x1234; using UkmEntry = ukm::builders::PasswordForm; -// Create a UkmEntryBuilder with a SourceId that is initialized for kTestUrl. +// Create a UkmEntryBuilder with kTestSourceId. scoped_refptr<PasswordFormMetricsRecorder> CreatePasswordFormMetricsRecorder( bool is_main_frame_secure, ukm::TestUkmRecorder* test_ukm_recorder) { - return base::MakeRefCounted<PasswordFormMetricsRecorder>( - is_main_frame_secure, test_ukm_recorder, - test_ukm_recorder->GetNewSourceID(), GURL(kTestUrl)); + return base::MakeRefCounted<PasswordFormMetricsRecorder>(is_main_frame_secure, + kTestSourceId); } // TODO(crbug.com/738921) Replace this with generalized infrastructure. @@ -44,7 +43,7 @@ void ExpectUkmValueCount(ukm::TestUkmRecorder* test_ukm_recorder, auto entries = test_ukm_recorder->GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* const entry : entries) { - test_ukm_recorder->ExpectEntrySourceHasUrl(entry, GURL(kTestUrl)); + EXPECT_EQ(kTestSourceId, entry->source_id); if (expected_count) { test_ukm_recorder->ExpectEntryMetric(entry, metric_name, value); } else { @@ -467,7 +466,7 @@ TEST(PasswordFormMetricsRecorder, RecordPasswordBubbleShown) { auto entries = test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* const entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kTestUrl)); + EXPECT_EQ(kTestSourceId, entry->source_id); if (test.credential_source_type != metrics_util::CredentialSourceType::kUnknown) { @@ -532,7 +531,7 @@ TEST(PasswordFormMetricsRecorder, RecordUIDismissalReason) { auto entries = test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* const entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kTestUrl)); + EXPECT_EQ(kTestSourceId, entry->source_id); test_ukm_recorder.ExpectEntryMetric( entry, test.expected_trigger_metric, static_cast<int64_t>(test.expected_metric_value)); @@ -566,7 +565,7 @@ TEST(PasswordFormMetricsRecorder, SequencesOfBubbles) { auto entries = test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* const entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kTestUrl)); + EXPECT_EQ(kTestSourceId, entry->source_id); test_ukm_recorder.ExpectEntryMetric( entry, UkmEntry::kSaving_Prompt_InteractionName, static_cast<int64_t>(BubbleDismissalReason::kAccepted)); @@ -603,7 +602,7 @@ TEST(PasswordFormMetricsRecorder, RecordDetailedUserAction) { auto entries = test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* const entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kTestUrl)); + EXPECT_EQ(kTestSourceId, entry->source_id); test_ukm_recorder.ExpectEntryMetric( entry, UkmEntry::kUser_Action_CorrectedUsernameInFormName, 2u); test_ukm_recorder.ExpectEntryMetric( diff --git a/chromium/components/password_manager/core/browser/password_manager.cc b/chromium/components/password_manager/core/browser/password_manager.cc index 8db09406bad..c4beff277c1 100644 --- a/chromium/components/password_manager/core/browser/password_manager.cc +++ b/chromium/components/password_manager/core/browser/password_manager.cc @@ -32,6 +32,7 @@ #include "components/password_manager/core/browser/password_manager_metrics_recorder.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_reuse_defines.h" #include "components/password_manager/core/common/password_manager_features.h" #include "components/password_manager/core/common/password_manager_pref_names.h" #include "components/pref_registry/pref_registry_syncable.h" @@ -486,6 +487,20 @@ void PasswordManager::OnPasswordFormSubmitted( pending_login_managers_.clear(); } +void PasswordManager::OnPasswordFormSubmittedNoChecks( + password_manager::PasswordManagerDriver* driver, + const autofill::PasswordForm& password_form) { + if (password_manager_util::IsLoggingActive(client_)) { + BrowserSavePasswordProgressLogger logger(client_->GetLogManager()); + logger.LogMessage(Logger::STRING_ON_IN_PAGE_NAVIGATION); + } + + ProvisionallySavePassword(password_form, driver); + + if (CanProvisionalManagerSave()) + OnLoginSuccessful(); +} + void PasswordManager::OnPasswordFormForceSaveRequested( password_manager::PasswordManagerDriver* driver, const PasswordForm& password_form) { @@ -567,6 +582,30 @@ void PasswordManager::CreatePendingLoginManagers( // Record whether or not this top-level URL has at least one password field. client_->AnnotateNavigationEntry(!forms.empty()); + // Only report SSL error status for cases where there are potentially forms to + // fill or save from. + if (!forms.empty()) { + metrics_util::CertificateError cert_error = + metrics_util::CertificateError::NONE; + const net::CertStatus cert_status = client_->GetMainFrameCertStatus(); + // The order of the if statements matters -- if the status involves multiple + // errors, Chrome should report the one highest up in the list below. + if (cert_status & net::CERT_STATUS_AUTHORITY_INVALID) + cert_error = metrics_util::CertificateError::AUTHORITY_INVALID; + else if (cert_status & net::CERT_STATUS_COMMON_NAME_INVALID) + cert_error = metrics_util::CertificateError::COMMON_NAME_INVALID; + else if (cert_status & net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM) + cert_error = metrics_util::CertificateError::WEAK_SIGNATURE_ALGORITHM; + else if (cert_status & net::CERT_STATUS_DATE_INVALID) + cert_error = metrics_util::CertificateError::DATE_INVALID; + else if (net::IsCertStatusError(cert_status)) + cert_error = metrics_util::CertificateError::OTHER; + + UMA_HISTOGRAM_ENUMERATION( + "PasswordManager.CertificateErrorsWhileSeeingForms", cert_error, + metrics_util::CertificateError::COUNT); + } + if (!client_->IsFillingEnabledForCurrentPage()) return; @@ -785,19 +824,7 @@ void PasswordManager::OnPasswordFormsRendered( void PasswordManager::OnInPageNavigation( password_manager::PasswordManagerDriver* driver, const PasswordForm& password_form) { - std::unique_ptr<BrowserSavePasswordProgressLogger> logger; - if (password_manager_util::IsLoggingActive(client_)) { - logger.reset( - new BrowserSavePasswordProgressLogger(client_->GetLogManager())); - logger->LogMessage(Logger::STRING_ON_IN_PAGE_NAVIGATION); - } - - ProvisionallySavePassword(password_form, driver); - - if (!CanProvisionalManagerSave()) - return; - - OnLoginSuccessful(); + OnPasswordFormSubmittedNoChecks(driver, password_form); } void PasswordManager::OnLoginSuccessful() { @@ -822,8 +849,7 @@ void PasswordManager::OnLoginSuccessful() { DCHECK(provisional_save_manager_->submitted_form()); if (!client_->GetStoreResultFilter()->ShouldSave( *provisional_save_manager_->submitted_form())) { -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) // When |username_value| is empty, it's not clear whether the submitted // credentials are really sync credentials. Don't save sync password hash // in that case. diff --git a/chromium/components/password_manager/core/browser/password_manager.h b/chromium/components/password_manager/core/browser/password_manager.h index 7e4f02f8f66..bfafe7fcd7a 100644 --- a/chromium/components/password_manager/core/browser/password_manager.h +++ b/chromium/components/password_manager/core/browser/password_manager.h @@ -153,6 +153,12 @@ class PasswordManager : public LoginModel { password_manager::PasswordManagerDriver* driver, const autofill::PasswordForm& password_form); + // Handles a password form being submitted, assumes that submission is + // successful and does not do any checks on success of submission. + void OnPasswordFormSubmittedNoChecks( + password_manager::PasswordManagerDriver* driver, + const autofill::PasswordForm& password_form); + // Handles a manual request to save password. void OnPasswordFormForceSaveRequested( password_manager::PasswordManagerDriver* driver, @@ -170,6 +176,7 @@ class PasswordManager : public LoginModel { // Called if |password_form| was filled upon in-page navigation. This often // means history.pushState being called from JavaScript. If this causes false // positive in password saving, update http://crbug.com/357696. + // TODO(https://crbug.com/795462): find better name for this function. void OnInPageNavigation(password_manager::PasswordManagerDriver* driver, const autofill::PasswordForm& password_form); diff --git a/chromium/components/password_manager/core/browser/password_manager_client.cc b/chromium/components/password_manager/core/browser/password_manager_client.cc index ead9e485d92..ca18e83f821 100644 --- a/chromium/components/password_manager/core/browser/password_manager_client.cc +++ b/chromium/components/password_manager/core/browser/password_manager_client.cc @@ -48,8 +48,8 @@ bool PasswordManagerClient::WasLastNavigationHTTPError() const { return false; } -bool PasswordManagerClient::DidLastPageLoadEncounterSSLErrors() const { - return false; +net::CertStatus PasswordManagerClient::GetMainFrameCertStatus() const { + return 0; } bool PasswordManagerClient::IsIncognito() const { diff --git a/chromium/components/password_manager/core/browser/password_manager_client.h b/chromium/components/password_manager/core/browser/password_manager_client.h index 7d45719bb23..7bbf998a2d0 100644 --- a/chromium/components/password_manager/core/browser/password_manager_client.h +++ b/chromium/components/password_manager/core/browser/password_manager_client.h @@ -12,6 +12,7 @@ #include "components/autofill/core/common/password_form.h" #include "components/password_manager/core/browser/credentials_filter.h" #include "components/password_manager/core/browser/password_store.h" +#include "net/cert/cert_status_flags.h" #include "services/metrics/public/cpp/ukm_recorder.h" class PrefService; @@ -182,9 +183,8 @@ class PasswordManagerClient { // Returns true if last navigation page had HTTP error i.e 5XX or 4XX virtual bool WasLastNavigationHTTPError() const; - // Returns whether any SSL certificate errors were encountered as a result of - // the last page load. - virtual bool DidLastPageLoadEncounterSSLErrors() const; + // Obtains the cert status for the main frame. + virtual net::CertStatus GetMainFrameCertStatus() const; // If this browsing session should not be persisted. virtual bool IsIncognito() const; @@ -238,12 +238,8 @@ class PasswordManagerClient { virtual void LogPasswordReuseDetectedEvent() = 0; #endif - // Gets the UKM service associated with this client (for metrics). - virtual ukm::UkmRecorder* GetUkmRecorder() = 0; - // Gets a ukm::SourceId that is associated with the WebContents object - // and its last committed main frame navigation. Note that the URL binding - // has to happen by the caller at a later point. + // and its last committed main frame navigation. virtual ukm::SourceId GetUkmSourceId() = 0; // Gets a metrics recorder for the currently committed navigation. diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.cc b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.cc index b92e79a28c0..4ba9a27e892 100644 --- a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.cc +++ b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.cc @@ -7,6 +7,7 @@ #include "base/metrics/histogram_macros.h" #include "components/autofill/core/common/save_password_progress_logger.h" #include "components/password_manager/core/browser/browser_save_password_progress_logger.h" +#include "services/metrics/public/cpp/ukm_builders.h" #include "url/gurl.h" // Shorten the name to spare line breaks. The code provides enough context @@ -15,34 +16,20 @@ typedef autofill::SavePasswordProgressLogger Logger; namespace password_manager { -// URL Keyed Metrics. -const char kUkmUserModifiedPasswordField[] = "UserModifiedPasswordField"; -const char kUkmProvisionalSaveFailure[] = "ProvisionalSaveFailure"; -const char kUkmPageLevelUserAction[] = "PageLevelUserAction"; - PasswordManagerMetricsRecorder::PasswordManagerMetricsRecorder( - ukm::UkmRecorder* ukm_recorder, ukm::SourceId source_id, const GURL& main_frame_url) - : ukm_recorder_(ukm_recorder), - source_id_(source_id), - main_frame_url_(main_frame_url), + : main_frame_url_(main_frame_url), ukm_entry_builder_( - ukm_recorder - ? ukm_recorder->GetEntryBuilder(source_id, "PageWithPassword") - : nullptr) {} + base::MakeUnique<ukm::builders::PageWithPassword>(source_id)) {} PasswordManagerMetricsRecorder::PasswordManagerMetricsRecorder( PasswordManagerMetricsRecorder&& that) noexcept = default; PasswordManagerMetricsRecorder::~PasswordManagerMetricsRecorder() { if (user_modified_password_field_) - RecordUkmMetric(kUkmUserModifiedPasswordField, 1); - - // Bind |main_frame_url_| to |source_id_| directly before sending the content - // of |ukm_recorder_| to ensure that the binding has not been purged already. - if (ukm_recorder_) - ukm_recorder_->UpdateSourceURL(source_id_, main_frame_url_); + ukm_entry_builder_->SetUserModifiedPasswordField(1); + ukm_entry_builder_->Record(ukm::UkmRecorder::Get()); } PasswordManagerMetricsRecorder& PasswordManagerMetricsRecorder::operator=( @@ -59,7 +46,7 @@ void PasswordManagerMetricsRecorder::RecordProvisionalSaveFailure( BrowserSavePasswordProgressLogger* logger) { UMA_HISTOGRAM_ENUMERATION("PasswordManager.ProvisionalSaveFailure", failure, MAX_FAILURE_VALUE); - RecordUkmMetric(kUkmProvisionalSaveFailure, static_cast<int64_t>(failure)); + ukm_entry_builder_->SetProvisionalSaveFailure(static_cast<int64_t>(failure)); if (logger) { switch (failure) { @@ -99,13 +86,7 @@ void PasswordManagerMetricsRecorder::RecordProvisionalSaveFailure( void PasswordManagerMetricsRecorder::RecordPageLevelUserAction( PasswordManagerMetricsRecorder::PageLevelUserAction action) { - RecordUkmMetric(kUkmPageLevelUserAction, static_cast<int64_t>(action)); -} - -void PasswordManagerMetricsRecorder::RecordUkmMetric(const char* metric_name, - int64_t value) { - if (ukm_entry_builder_) - ukm_entry_builder_->AddMetric(metric_name, value); + ukm_entry_builder_->SetPageLevelUserAction(static_cast<int64_t>(action)); } } // namespace password_manager diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h index 551f4ebbdf7..1f348f5a0b6 100644 --- a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h +++ b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h @@ -10,26 +10,18 @@ #include <memory> #include "base/macros.h" -#include "services/metrics/public/cpp/ukm_recorder.h" +#include "services/metrics/public/cpp/ukm_source_id.h" #include "url/gurl.h" class GURL; -namespace password_manager { - -// URL Keyed Metrics. - -// Records a 1 for every page on which a user has modified the content of a -// password field - regardless of how many password fields a page contains or -// the user modifies. -extern const char kUkmUserModifiedPasswordField[]; - -// UKM that records a ProvisionalSaveFailure in case the password manager cannot -// offer to save a credential. -extern const char kUkmProvisionalSaveFailure[]; +namespace ukm { +namespace builders { +class PageWithPassword; +} +} // namespace ukm -// UKM that records a PasswordManagerMetricsRecorder::PageLevelUserAction. -extern const char kUkmPageLevelUserAction[]; +namespace password_manager { class BrowserSavePasswordProgressLogger; @@ -75,16 +67,8 @@ class PasswordManagerMetricsRecorder { kShowAllPasswordsWhileNoneAreSuggested, }; - // Records UKM metrics and reports them on destruction. The |source_id| is - // (re-)bound to |main_frame_url| shortly before reporting. As such it is - // crucial that the |source_id| is never bound to a different URL by another - // consumer. The reason for this late binding is that metrics can be - // collected for a WebContents for a long period of time and by the time the - // reporting happens, the binding of |source_id| to |main_frame_url| is - // already purged. |ukm_recorder| may be a nullptr, in which case no UKM - // metrics are recorded. - PasswordManagerMetricsRecorder(ukm::UkmRecorder* ukm_recorder, - ukm::SourceId source_id, + // Records UKM metrics and reports them on destruction. + PasswordManagerMetricsRecorder(ukm::SourceId source_id, const GURL& main_frame_url); PasswordManagerMetricsRecorder( @@ -109,24 +93,11 @@ class PasswordManagerMetricsRecorder { void RecordPageLevelUserAction(PageLevelUserAction action); private: - // Records a metric into |ukm_entry_builder_| if it is not nullptr. - void RecordUkmMetric(const char* metric_name, int64_t value); - - // Recorder to which metrics are sent. Has to outlive this - // PasswordManagerMetircsRecorder. - ukm::UkmRecorder* ukm_recorder_; - - // A SourceId of |ukm_recorder_|. This id gets bound to |main_frame_url_| on - // destruction. It can be shared across multiple metrics recorders as long as - // they all bind it to the same URL. - ukm::SourceId source_id_; - // URL for which UKMs are reported. GURL main_frame_url_; - // Records URL keyed metrics (UKMs) and submits them on its destruction. May - // be a nullptr in which case no recording is expected. - std::unique_ptr<ukm::UkmEntryBuilder> ukm_entry_builder_; + // Records URL keyed metrics (UKMs) and submits them on its destruction. + std::unique_ptr<ukm::builders::PageWithPassword> ukm_entry_builder_; bool user_modified_password_field_ = false; diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder_unittest.cc index c0be3964a91..ace6340facc 100644 --- a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder_unittest.cc +++ b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder_unittest.cc @@ -10,7 +10,7 @@ #include "base/test/scoped_task_environment.h" #include "components/ukm/test_ukm_recorder.h" #include "components/ukm/ukm_source.h" -#include "services/metrics/public/cpp/ukm_recorder.h" +#include "services/metrics/public/cpp/ukm_builders.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,12 +22,13 @@ namespace password_manager { namespace { constexpr char kTestUrl[] = "https://www.example.com/"; +constexpr ukm::SourceId kTestSourceId = 0x1234; + +using UkmEntry = ukm::builders::PageWithPassword; // Creates a PasswordManagerMetricsRecorder that reports metrics for kTestUrl. -PasswordManagerMetricsRecorder CreateMetricsRecorder( - ukm::UkmRecorder* ukm_recorder) { - return PasswordManagerMetricsRecorder( - ukm_recorder, ukm_recorder->GetNewSourceID(), GURL(kTestUrl)); +PasswordManagerMetricsRecorder CreateMetricsRecorder() { + return PasswordManagerMetricsRecorder(kTestSourceId, GURL(kTestUrl)); } } // namespace @@ -36,17 +37,17 @@ TEST(PasswordManagerMetricsRecorder, UserModifiedPasswordField) { base::test::ScopedTaskEnvironment scoped_task_environment_; ukm::TestAutoSetUkmRecorder test_ukm_recorder; { - PasswordManagerMetricsRecorder recorder( - CreateMetricsRecorder(&test_ukm_recorder)); + PasswordManagerMetricsRecorder recorder(CreateMetricsRecorder()); recorder.RecordUserModifiedPasswordField(); } - const auto& entries = test_ukm_recorder.GetEntriesByName("PageWithPassword"); + const auto& entries = + test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kTestUrl)); - test_ukm_recorder.ExpectEntryMetric(entry, kUkmUserModifiedPasswordField, - 1); + EXPECT_EQ(kTestSourceId, entry->source_id); + test_ukm_recorder.ExpectEntryMetric( + entry, UkmEntry::kUserModifiedPasswordFieldName, 1); } } @@ -54,34 +55,32 @@ TEST(PasswordManagerMetricsRecorder, UserModifiedPasswordFieldMultipleTimes) { base::test::ScopedTaskEnvironment scoped_task_environment_; ukm::TestAutoSetUkmRecorder test_ukm_recorder; { - PasswordManagerMetricsRecorder recorder( - CreateMetricsRecorder(&test_ukm_recorder)); + PasswordManagerMetricsRecorder recorder(CreateMetricsRecorder()); // Multiple calls should not create more than one entry. recorder.RecordUserModifiedPasswordField(); recorder.RecordUserModifiedPasswordField(); } - const auto& entries = test_ukm_recorder.GetEntriesByName("PageWithPassword"); + const auto& entries = + test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kTestUrl)); - test_ukm_recorder.ExpectEntryMetric(entry, kUkmUserModifiedPasswordField, - 1); + EXPECT_EQ(kTestSourceId, entry->source_id); + test_ukm_recorder.ExpectEntryMetric( + entry, UkmEntry::kUserModifiedPasswordFieldName, 1); } } TEST(PasswordManagerMetricsRecorder, UserModifiedPasswordFieldNotCalled) { base::test::ScopedTaskEnvironment scoped_task_environment_; ukm::TestAutoSetUkmRecorder test_ukm_recorder; - { - PasswordManagerMetricsRecorder recorder( - CreateMetricsRecorder(&test_ukm_recorder)); - } - const auto& entries = test_ukm_recorder.GetEntriesByName("PageWithPassword"); + { PasswordManagerMetricsRecorder recorder(CreateMetricsRecorder()); } + const auto& entries = + test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); EXPECT_EQ(1u, entries.size()); for (const auto* entry : entries) { - test_ukm_recorder.ExpectEntrySourceHasUrl(entry, GURL(kTestUrl)); - EXPECT_FALSE( - test_ukm_recorder.EntryHasMetric(entry, kUkmUserModifiedPasswordField)); + EXPECT_EQ(kTestSourceId, entry->source_id); + EXPECT_FALSE(test_ukm_recorder.EntryHasMetric( + entry, UkmEntry::kUserModifiedPasswordFieldName)); } } diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_util.cc b/chromium/components/password_manager/core/browser/password_manager_metrics_util.cc index 8ddf90ccd11..9a2eda5cc9e 100644 --- a/chromium/components/password_manager/core/browser/password_manager_metrics_util.cc +++ b/chromium/components/password_manager/core/browser/password_manager_metrics_util.cc @@ -212,8 +212,7 @@ void LogSubmittedFormFrame(SubmittedFormFrame frame) { SubmittedFormFrame::SUBMITTED_FORM_FRAME_COUNT); } -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) void LogSyncPasswordHashChange(SyncPasswordHashChange event) { UMA_HISTOGRAM_ENUMERATION( "PasswordManager.SyncPasswordHashChange", event, diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_util.h b/chromium/components/password_manager/core/browser/password_manager_metrics_util.h index 1155dcb5fdd..73d7876608a 100644 --- a/chromium/components/password_manager/core/browser/password_manager_metrics_util.h +++ b/chromium/components/password_manager/core/browser/password_manager_metrics_util.h @@ -10,6 +10,7 @@ #include <string> #include "components/autofill/core/common/password_form.h" +#include "components/password_manager/core/browser/password_reuse_defines.h" #include "components/password_manager/core/common/credential_manager_types.h" namespace password_manager { @@ -227,8 +228,7 @@ enum class CredentialSourceType { kCredentialManagementAPI }; -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) enum class SyncPasswordHashChange { SAVED_ON_CHROME_SIGNIN, SAVED_IN_CONTENT_AREA, @@ -263,6 +263,17 @@ enum ShowAllSavedPasswordsContext { SHOW_ALL_SAVED_PASSWORDS_CONTEXT_COUNT }; +// Metrics: "PasswordManager.CertificateErrorsWhileSeeingForms" +enum class CertificateError { + NONE = 0, + OTHER = 1, + AUTHORITY_INVALID = 2, + DATE_INVALID = 3, + COMMON_NAME_INVALID = 4, + WEAK_SIGNATURE_ALGORITHM = 5, + COUNT +}; + // A version of the UMA_HISTOGRAM_BOOLEAN macro that allows the |name| // to vary over the program's runtime. void LogUMAHistogramBoolean(const std::string& name, bool sample); @@ -361,8 +372,7 @@ void LogPasswordAcceptedSaveUpdateSubmissionIndicatorEvent( // Log a frame of a submitted password form. void LogSubmittedFormFrame(SubmittedFormFrame frame); -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) // Log a save sync password change event. void LogSyncPasswordHashChange(SyncPasswordHashChange event); diff --git a/chromium/components/password_manager/core/browser/password_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_unittest.cc index b2d6c5bc488..ac1a60eaab9 100644 --- a/chromium/components/password_manager/core/browser/password_manager_unittest.cc +++ b/chromium/components/password_manager/core/browser/password_manager_unittest.cc @@ -21,6 +21,7 @@ #include "components/password_manager/core/browser/mock_password_store.h" #include "components/password_manager/core/browser/password_autofill_manager.h" #include "components/password_manager/core/browser/password_manager_driver.h" +#include "components/password_manager/core/browser/password_reuse_defines.h" #include "components/password_manager/core/browser/password_store.h" #include "components/password_manager/core/browser/statistics_table.h" #include "components/password_manager/core/browser/stub_credentials_filter.h" @@ -28,6 +29,7 @@ #include "components/password_manager/core/browser/stub_password_manager_driver.h" #include "components/password_manager/core/common/password_manager_features.h" #include "components/password_manager/core/common/password_manager_pref_names.h" +#include "net/cert/cert_status_flags.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -61,7 +63,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient { } MOCK_CONST_METHOD0(IsSavingAndFillingEnabledForCurrentPage, bool()); - MOCK_CONST_METHOD0(DidLastPageLoadEncounterSSLErrors, bool()); + MOCK_CONST_METHOD0(GetMainFrameCertStatus, net::CertStatus()); MOCK_CONST_METHOD0(GetPasswordStore, PasswordStore*()); // The code inside EXPECT_CALL for PromptUserToSaveOrUpdatePasswordPtr and // ShowManualFallbackForSavingPtr owns the PasswordFormManager* argument. @@ -154,8 +156,7 @@ class PasswordManagerTest : public testing::Test { .WillRepeatedly(Return(manager_.get())); EXPECT_CALL(driver_, GetPasswordAutofillManager()) .WillRepeatedly(Return(password_autofill_manager_.get())); - EXPECT_CALL(client_, DidLastPageLoadEncounterSSLErrors()) - .WillRepeatedly(Return(false)); + EXPECT_CALL(client_, GetMainFrameCertStatus()).WillRepeatedly(Return(0)); ON_CALL(client_, GetMainFrameURL()).WillByDefault(ReturnRef(test_url_)); } @@ -757,8 +758,7 @@ TEST_F(PasswordManagerTest, SyncCredentialsNotSaved) { // User should not be prompted and password should not be saved. EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0); EXPECT_CALL(*store_, AddLogin(_)).Times(0); -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) EXPECT_CALL(*store_, SaveSyncPasswordHash(form.password_value)); #endif // Prefs are needed for failure logging about sync credentials. @@ -836,8 +836,7 @@ TEST_F(PasswordManagerTest, SyncCredentialsNotDroppedIfUpToDate) { EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage()) .WillRepeatedly(Return(true)); EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr)); -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) EXPECT_CALL(*store_, SaveSyncPasswordHash(form.password_value)); #endif manager()->ProvisionallySavePassword(form, nullptr); @@ -873,8 +872,7 @@ TEST_F(PasswordManagerTest, SyncCredentialsDroppedWhenObsolete) { EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage()) .WillRepeatedly(Return(true)); EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr)); -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) EXPECT_CALL(*store_, SaveSyncPasswordHash(ASCIIToUTF16("n3w passw0rd"))); #endif manager()->ProvisionallySavePassword(updated_form, nullptr); @@ -1712,10 +1710,10 @@ TEST_F(PasswordManagerTest, } TEST_F(PasswordManagerTest, ForceSavingPasswords) { - // Add the enable-password-force-saving feature. + // Add the PasswordForceSaving feature. base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature( - features::kEnablePasswordForceSaving); + features::kPasswordForceSaving); PasswordForm form(MakeSimpleForm()); std::vector<PasswordForm> observed; @@ -1739,10 +1737,10 @@ TEST_F(PasswordManagerTest, ForceSavingPasswords) { // Forcing Chrome to save an empty passwords should fail without a crash. TEST_F(PasswordManagerTest, ForceSavingPasswords_Empty) { - // Add the enable-password-force-saving feature. + // Add the PasswordForceSaving feature. base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature( - features::kEnablePasswordForceSaving); + features::kPasswordForceSaving); PasswordForm empty_password_form; std::vector<PasswordForm> observed; @@ -1928,8 +1926,7 @@ TEST_F(PasswordManagerTest, ClearedFieldsSuccessCriteria) { manager()->OnPasswordFormsRendered(&driver_, observed, true); } -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) // Check that no sync password hash is saved when no username is available, // because we it's not clear whether the submitted credentials are sync // credentials. @@ -2202,8 +2199,7 @@ TEST_F(PasswordManagerTest, SaveSyncPasswordHashOnChangePasswordPage) { EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage()) .WillRepeatedly(Return(true)); EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr)); -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) EXPECT_CALL(*store_, SaveSyncPasswordHash(form.new_password_value)); #endif client_.FilterAllResultsForSaving(); @@ -2214,4 +2210,60 @@ TEST_F(PasswordManagerTest, SaveSyncPasswordHashOnChangePasswordPage) { manager()->OnPasswordFormsRendered(&driver_, observed, true); } +// If there are no forms to parse, certificate errors should not be reported. +TEST_F(PasswordManagerTest, CertErrorReported_NoForms) { + const std::vector<PasswordForm> observed; + EXPECT_CALL(client_, GetMainFrameCertStatus()) + .WillRepeatedly(Return(net::CERT_STATUS_AUTHORITY_INVALID)); + EXPECT_CALL(*store_, GetLogins(_, _)) + .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms())); + + base::HistogramTester histogram_tester; + manager()->OnPasswordFormsParsed(&driver_, observed); + histogram_tester.ExpectTotalCount( + "PasswordManager.CertificateErrorsWhileSeeingForms", 0); +} + +TEST_F(PasswordManagerTest, CertErrorReported) { + constexpr struct { + net::CertStatus cert_status; + metrics_util::CertificateError expected_error; + } kCases[] = { + {0, metrics_util::CertificateError::NONE}, + {net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, // not an error + metrics_util::CertificateError::NONE}, + {net::CERT_STATUS_COMMON_NAME_INVALID, + metrics_util::CertificateError::COMMON_NAME_INVALID}, + {net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM, + metrics_util::CertificateError::WEAK_SIGNATURE_ALGORITHM}, + {net::CERT_STATUS_DATE_INVALID, + metrics_util::CertificateError::DATE_INVALID}, + {net::CERT_STATUS_AUTHORITY_INVALID, + metrics_util::CertificateError::AUTHORITY_INVALID}, + {net::CERT_STATUS_WEAK_KEY, metrics_util::CertificateError::OTHER}, + {net::CERT_STATUS_DATE_INVALID | net::CERT_STATUS_WEAK_KEY, + metrics_util::CertificateError::DATE_INVALID}, + {net::CERT_STATUS_DATE_INVALID | net::CERT_STATUS_AUTHORITY_INVALID, + metrics_util::CertificateError::AUTHORITY_INVALID}, + {net::CERT_STATUS_DATE_INVALID | net::CERT_STATUS_AUTHORITY_INVALID | + net::CERT_STATUS_WEAK_KEY, + metrics_util::CertificateError::AUTHORITY_INVALID}, + }; + + const std::vector<PasswordForm> observed = {PasswordForm()}; + + for (const auto& test_case : kCases) { + SCOPED_TRACE(testing::Message("index of test_case = ") + << (&test_case - kCases)); + EXPECT_CALL(client_, GetMainFrameCertStatus()) + .WillRepeatedly(Return(test_case.cert_status)); + base::HistogramTester histogram_tester; + EXPECT_CALL(*store_, GetLogins(_, _)); + manager()->OnPasswordFormsParsed(&driver_, observed); + histogram_tester.ExpectUniqueSample( + "PasswordManager.CertificateErrorsWhileSeeingForms", + test_case.expected_error, 1); + } +} + } // namespace password_manager diff --git a/chromium/components/password_manager/core/browser/password_manager_util.cc b/chromium/components/password_manager/core/browser/password_manager_util.cc index 837afe8f4e0..3cccd72398c 100644 --- a/chromium/components/password_manager/core/browser/password_manager_util.cc +++ b/chromium/components/password_manager/core/browser/password_manager_util.cc @@ -16,8 +16,6 @@ #include "components/password_manager/core/browser/password_manager_metrics_util.h" #include "components/password_manager/core/common/password_manager_features.h" #include "components/sync/driver/sync_service.h" -#include "crypto/openssl_util.h" -#include "third_party/boringssl/src/include/openssl/evp.h" namespace password_manager_util { @@ -90,40 +88,6 @@ bool IsLoggingActive(const password_manager::PasswordManagerClient* client) { return log_manager && log_manager->IsLoggingActive(); } -uint64_t CalculateSyncPasswordHash(const base::StringPiece16& text, - const std::string& salt) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - constexpr size_t kBytesFromHash = 8; - constexpr uint64_t kScryptCost = 32; // It must be power of 2. - constexpr uint64_t kScryptBlockSize = 8; - constexpr uint64_t kScryptParallelization = 1; - constexpr size_t kScryptMaxMemory = 1024 * 1024; - - uint8_t hash[kBytesFromHash]; - base::StringPiece text_8bits(reinterpret_cast<const char*>(text.data()), - text.size() * 2); - const uint8_t* salt_ptr = reinterpret_cast<const uint8_t*>(salt.c_str()); - - int scrypt_ok = EVP_PBE_scrypt(text_8bits.data(), text_8bits.size(), salt_ptr, - salt.size(), kScryptCost, kScryptBlockSize, - kScryptParallelization, kScryptMaxMemory, hash, - kBytesFromHash); - - // EVP_PBE_scrypt can only fail due to memory allocation error (which aborts - // Chromium) or invalid parameters. In case of a failure a hash could leak - // information from the stack, so using CHECK is better than DCHECK. - CHECK(scrypt_ok); - - // Take 37 bits of |hash|. - uint64_t hash37 = ((static_cast<uint64_t>(hash[0]))) | - ((static_cast<uint64_t>(hash[1])) << 8) | - ((static_cast<uint64_t>(hash[2])) << 16) | - ((static_cast<uint64_t>(hash[3])) << 24) | - (((static_cast<uint64_t>(hash[4])) & 0x1F) << 32); - - return hash37; -} - bool ManualPasswordGenerationEnabled(syncer::SyncService* sync_service) { if (!(base::FeatureList::IsEnabled( password_manager::features::kEnableManualPasswordGeneration) && diff --git a/chromium/components/password_manager/core/browser/password_manager_util.h b/chromium/components/password_manager/core/browser/password_manager_util.h index 2e0a1e3c2cd..1fa020d1e30 100644 --- a/chromium/components/password_manager/core/browser/password_manager_util.h +++ b/chromium/components/password_manager/core/browser/password_manager_util.h @@ -54,11 +54,6 @@ void TrimUsernameOnlyCredentials( // and required to always return non-null. bool IsLoggingActive(const password_manager::PasswordManagerClient* client); -// Calculates 37 bits hash for a sync password. The calculation is based on a -// slow hash function. The running time is ~10^{-4} seconds on Desktop. -uint64_t CalculateSyncPasswordHash(const base::StringPiece16& text, - const std::string& salt); - // True iff the manual password generation is enabled and the user is sync user // without custom passphrase. bool ManualPasswordGenerationEnabled(syncer::SyncService* sync_service); diff --git a/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc index 481284bc361..838270074a6 100644 --- a/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc +++ b/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc @@ -59,25 +59,3 @@ TEST(PasswordManagerUtil, TrimUsernameOnlyCredentials) { EXPECT_THAT(forms, UnorderedPasswordFormElementsAre(&expected_forms)); } - -TEST(PasswordManagerUtil, CalculateSyncPasswordHash) { - const char* kPlainText[] = {"", "password", "password", "secret"}; - const char* kSalt[] = {"", "salt", "123", "456"}; - - constexpr uint64_t kExpectedHash[] = { - UINT64_C(0x1c610a7950), UINT64_C(0x1927dc525e), UINT64_C(0xf72f81aa6), - UINT64_C(0x3645af77f), - }; - - static_assert(arraysize(kPlainText) == arraysize(kSalt), - "Arrays must have the same size"); - static_assert(arraysize(kPlainText) == arraysize(kExpectedHash), - "Arrays must have the same size"); - - for (size_t i = 0; i < arraysize(kPlainText); ++i) { - SCOPED_TRACE(i); - base::string16 text = base::UTF8ToUTF16(kPlainText[i]); - EXPECT_EQ(kExpectedHash[i], - password_manager_util::CalculateSyncPasswordHash(text, kSalt[i])); - } -} diff --git a/chromium/components/password_manager/core/browser/password_reuse_defines.h b/chromium/components/password_manager/core/browser/password_reuse_defines.h index 99c8f73a742..a45e9ff6825 100644 --- a/chromium/components/password_manager/core/browser/password_reuse_defines.h +++ b/chromium/components/password_manager/core/browser/password_reuse_defines.h @@ -6,7 +6,7 @@ #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DEFINES_H_ #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) + defined(OS_LINUX) || defined(OS_CHROMEOS) // Enable the detection when the sync password is typed not on the sync domain. #define SYNC_PASSWORD_REUSE_DETECTION_ENABLED #endif diff --git a/chromium/components/password_manager/core/browser/password_reuse_detector.cc b/chromium/components/password_manager/core/browser/password_reuse_detector.cc index cd7b8f8f81d..cf8cbb9fbe8 100644 --- a/chromium/components/password_manager/core/browser/password_reuse_detector.cc +++ b/chromium/components/password_manager/core/browser/password_reuse_detector.cc @@ -8,7 +8,7 @@ #include <utility> #include "components/autofill/core/common/password_form.h" -#include "components/password_manager/core/browser/password_manager_util.h" +#include "components/password_manager/core/browser/hash_password_manager.h" #include "components/password_manager/core/browser/password_reuse_detector_consumer.h" #include "components/password_manager/core/browser/psl_matching_helper.h" #include "google_apis/gaia/gaia_auth_util.h" @@ -103,7 +103,7 @@ size_t PasswordReuseDetector::CheckSyncPasswordReuse( size_t offset = input.size() - sync_password_data_->length; base::string16 reuse_candidate = input.substr(offset); - if (password_manager_util::CalculateSyncPasswordHash( + if (HashPasswordManager::CalculateSyncPasswordHash( reuse_candidate, sync_password_data_->salt) == sync_password_data_->hash) { return reuse_candidate.size(); diff --git a/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc b/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc index 93bb2454197..760f35a38c9 100644 --- a/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc +++ b/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc @@ -13,7 +13,6 @@ #include "components/autofill/core/common/password_form.h" #include "components/password_manager/core/browser/hash_password_manager.h" #include "components/password_manager/core/browser/password_manager_test_utils.h" -#include "components/password_manager/core/browser/password_manager_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -80,7 +79,7 @@ SyncPasswordData GetSyncPasswordData(const std::string& sync_password) { SyncPasswordData sync_password_data; sync_password_data.salt = "1234567890123456"; sync_password_data.length = sync_password.size(); - sync_password_data.hash = password_manager_util::CalculateSyncPasswordHash( + sync_password_data.hash = HashPasswordManager::CalculateSyncPasswordHash( base::ASCIIToUTF16(sync_password), sync_password_data.salt); return sync_password_data; } diff --git a/chromium/components/password_manager/core/browser/password_store.cc b/chromium/components/password_manager/core/browser/password_store.cc index 9432109a686..a04fb42a544 100644 --- a/chromium/components/password_manager/core/browser/password_store.cc +++ b/chromium/components/password_manager/core/browser/password_store.cc @@ -19,12 +19,14 @@ #include "build/build_config.h" #include "components/autofill/core/common/password_form.h" #include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.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_reuse_defines.h" #include "components/password_manager/core/browser/password_store_consumer.h" #include "components/password_manager/core/browser/password_syncable_service.h" #include "components/password_manager/core/browser/statistics_table.h" -#if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) #include "components/password_manager/core/browser/password_store_signin_notifier.h" #endif @@ -63,7 +65,7 @@ void PasswordStore::GetLoginsRequest::NotifyWithSiteStatistics( } // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) PasswordStore::CheckReuseRequest::CheckReuseRequest( PasswordReuseDetectorConsumer* consumer) : origin_task_runner_(base::SequencedTaskRunnerHandle::Get()), @@ -273,10 +275,14 @@ void PasswordStore::ReportMetrics(const std::string& sync_username, base::TimeDelta::FromSeconds(30)); } -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) - if (!sync_username.empty()) - hash_password_manager_.ReportIsSyncPasswordHashSavedMetric(); +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) + if (!sync_username.empty()) { + auto hash_password_state = + hash_password_manager_.HasPasswordHash() + ? metrics_util::IsSyncPasswordHashSaved::SAVED + : metrics_util::IsSyncPasswordHashSaved::NOT_SAVED; + metrics_util::LogIsSyncPasswordHashSaved(hash_password_state); + } #endif } @@ -335,7 +341,7 @@ PasswordStore::GetPasswordSyncableService() { } // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) void PasswordStore::CheckReuse(const base::string16& input, const std::string& domain, PasswordReuseDetectorConsumer* consumer) { @@ -355,6 +361,13 @@ void PasswordStore::SaveSyncPasswordHash(const base::string16& password) { std::move(sync_password_data))); } +void PasswordStore::SaveSyncPasswordHash( + const SyncPasswordData& sync_password_data) { + hash_password_manager_.SavePasswordHash(sync_password_data); + ScheduleTask(base::BindRepeating(&PasswordStore::SaveSyncPasswordHashImpl, + this, sync_password_data)); +} + void PasswordStore::ClearSyncPasswordHash() { hash_password_manager_.ClearSavedPasswordHash(); ScheduleTask(base::Bind(&PasswordStore::ClearSyncPasswordHashImpl, this)); @@ -386,7 +399,7 @@ void PasswordStore::InitOnBackgroundSequence( syncable_service_.reset(new PasswordSyncableService(this)); syncable_service_->InjectStartSyncFlare(flare); // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) reuse_detector_ = new PasswordReuseDetector; GetAutofillableLoginsImpl( std::make_unique<GetLoginsRequest>(reuse_detector_)); @@ -449,7 +462,7 @@ void PasswordStore::NotifyLoginsChanged( if (syncable_service_) syncable_service_->ActOnPasswordStoreChanges(changes); // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) if (reuse_detector_) reuse_detector_->OnLoginsChanged(changes); #endif @@ -457,7 +470,7 @@ void PasswordStore::NotifyLoginsChanged( } // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) void PasswordStore::CheckReuseImpl(std::unique_ptr<CheckReuseRequest> request, const base::string16& input, const std::string& domain) { @@ -789,7 +802,7 @@ void PasswordStore::DestroyOnBackgroundSequence() { DCHECK(background_task_runner_->RunsTasksInCurrentSequence()); syncable_service_.reset(); // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) delete reuse_detector_; reuse_detector_ = nullptr; #endif diff --git a/chromium/components/password_manager/core/browser/password_store.h b/chromium/components/password_manager/core/browser/password_store.h index be44ebadefb..859c03ca859 100644 --- a/chromium/components/password_manager/core/browser/password_store.h +++ b/chromium/components/password_manager/core/browser/password_store.h @@ -24,7 +24,7 @@ #include "components/sync/model/syncable_service.h" // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) #include "components/password_manager/core/browser/hash_password_manager.h" #include "components/password_manager/core/browser/password_reuse_detector.h" #include "components/password_manager/core/browser/password_reuse_detector_consumer.h" @@ -248,7 +248,7 @@ class PasswordStore : protected PasswordStoreSync, base::WeakPtr<syncer::SyncableService> GetPasswordSyncableService(); // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) // Checks that some suffix of |input| equals to a password saved on another // registry controlled domain than |domain|. // If such suffix is found, |consumer|->OnReuseFound() is called on the same @@ -258,10 +258,12 @@ class PasswordStore : protected PasswordStoreSync, const std::string& domain, PasswordReuseDetectorConsumer* consumer); -#if !defined(OS_CHROMEOS) // Saves a hash of |password| for password reuse checking. virtual void SaveSyncPasswordHash(const base::string16& password); + // Saves |sync_password_data| for password reuse checking. + virtual void SaveSyncPasswordHash(const SyncPasswordData& sync_password_data); + // Clears the saved sync password hash. virtual void ClearSyncPasswordHash(); @@ -269,7 +271,6 @@ class PasswordStore : protected PasswordStoreSync, void SetPasswordStoreSigninNotifier( std::unique_ptr<PasswordStoreSigninNotifier> notifier); #endif -#endif protected: friend class base::RefCountedThreadSafe<PasswordStore>; @@ -307,7 +308,7 @@ class PasswordStore : protected PasswordStoreSync, }; // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) // Represents a single CheckReuse() request. Implements functionality to // listen to reuse events and propagate them to |consumer| on the sequence on // which CheckReuseRequest is created. @@ -434,7 +435,7 @@ class PasswordStore : protected PasswordStoreSync, void NotifyLoginsChanged(const PasswordStoreChangeList& changes) override; // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) // Synchronous implementation of CheckReuse(). void CheckReuseImpl(std::unique_ptr<CheckReuseRequest> request, const base::string16& input, @@ -601,13 +602,11 @@ class PasswordStore : protected PasswordStoreSync, std::unique_ptr<PasswordSyncableService> syncable_service_; std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper_; // TODO(crbug.com/706392): Fix password reuse detection for Android. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) // PasswordReuseDetector can be only destroyed on the background sequence. It // can't be owned by PasswordStore because PasswordStore can be destroyed on // the UI thread and DestroyOnBackgroundThread isn't guaranteed to be called. PasswordReuseDetector* reuse_detector_ = nullptr; -#endif -#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) std::unique_ptr<PasswordStoreSigninNotifier> notifier_; HashPasswordManager hash_password_manager_; #endif diff --git a/chromium/components/password_manager/core/browser/password_store_default_unittest.cc b/chromium/components/password_manager/core/browser/password_store_default_unittest.cc index 1614515b93c..b241c016b65 100644 --- a/chromium/components/password_manager/core/browser/password_store_default_unittest.cc +++ b/chromium/components/password_manager/core/browser/password_store_default_unittest.cc @@ -207,7 +207,6 @@ TEST(PasswordStoreDefaultTest, Notifications) { // Adding a login should trigger a notification. store->AddLogin(*form); - delegate.FinishAsyncProcessing(); // Change the password. form->password_value = base::ASCIIToUTF16("a different password"); @@ -221,7 +220,6 @@ TEST(PasswordStoreDefaultTest, Notifications) { // Updating the login with the new password should trigger a notification. store->UpdateLogin(*form); - delegate.FinishAsyncProcessing(); const PasswordStoreChange expected_delete_changes[] = { PasswordStoreChange(PasswordStoreChange::REMOVE, *form), @@ -232,6 +230,7 @@ TEST(PasswordStoreDefaultTest, Notifications) { // Deleting the login should trigger a notification. store->RemoveLogin(*form); + // Run the tasks to allow all the above expected calls to take place. delegate.FinishAsyncProcessing(); store->RemoveObserver(&observer); diff --git a/chromium/components/password_manager/core/browser/password_store_factory_util.cc b/chromium/components/password_manager/core/browser/password_store_factory_util.cc index f7a98244596..4e8cdb8db09 100644 --- a/chromium/components/password_manager/core/browser/password_store_factory_util.cc +++ b/chromium/components/password_manager/core/browser/password_store_factory_util.cc @@ -30,16 +30,21 @@ void ActivateAffiliationBasedMatching( PasswordStore* password_store, net::URLRequestContextGetter* request_context_getter, const base::FilePath& db_path) { - // The PasswordStore is so far the only consumer of the AffiliationService, - // therefore the service is owned by the AffiliatedMatchHelper, which in - // turn is owned by the PasswordStore. + // Subsequent instances of the AffiliationService must use the same sequenced + // task runner for their backends. This guarantees that the backend of the + // first instance will have closed the affiliation database before the second + // instance attempts to open it again. See: https://crbug.com/786157. + // // Task priority is USER_VISIBLE, because AffiliationService-related tasks - // block obtaining credentials from PasswordStore, which matches the - // USER_VISIBLE example: "Loading data that might be shown in the UI after a - // future user interaction." + // block obtaining credentials from PasswordStore, hence password autofill. + static auto backend_task_runner = base::CreateSequencedTaskRunnerWithTraits( + {base::MayBlock(), base::TaskPriority::USER_VISIBLE}); + + // The PasswordStore is so far the only consumer of the AffiliationService, + // therefore the service is owned by the AffiliatedMatchHelper, which in turn + // is owned by the PasswordStore. std::unique_ptr<AffiliationService> affiliation_service( - new AffiliationService(base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::USER_VISIBLE}))); + new AffiliationService(backend_task_runner)); affiliation_service->Initialize(request_context_getter, db_path); std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper( new AffiliatedMatchHelper(password_store, diff --git a/chromium/components/password_manager/core/browser/stub_form_saver.h b/chromium/components/password_manager/core/browser/stub_form_saver.h index 769fe436f3e..af31ff8766d 100644 --- a/chromium/components/password_manager/core/browser/stub_form_saver.h +++ b/chromium/components/password_manager/core/browser/stub_form_saver.h @@ -21,8 +21,7 @@ class StubFormSaver : public FormSaver { void PermanentlyBlacklist(autofill::PasswordForm* observed) override {} void Save(const autofill::PasswordForm& pending, const std::map<base::string16, const autofill::PasswordForm*>& - best_matches, - const autofill::PasswordForm* old_primary_key) override {} + best_matches) override {} void Update(const autofill::PasswordForm& pending, const std::map<base::string16, const autofill::PasswordForm*>& best_matches, diff --git a/chromium/components/password_manager/core/browser/stub_password_manager_client.cc b/chromium/components/password_manager/core/browser/stub_password_manager_client.cc index b24baff278b..7654e2bc971 100644 --- a/chromium/components/password_manager/core/browser/stub_password_manager_client.cc +++ b/chromium/components/password_manager/core/browser/stub_password_manager_client.cc @@ -12,9 +12,7 @@ namespace password_manager { StubPasswordManagerClient::StubPasswordManagerClient() - : ukm_source_id_(ukm::UkmRecorder::Get() - ? ukm::UkmRecorder::Get()->GetNewSourceID() - : 0) {} + : ukm_source_id_(ukm::UkmRecorder::GetNewSourceID()) {} StubPasswordManagerClient::~StubPasswordManagerClient() {} @@ -92,10 +90,6 @@ void StubPasswordManagerClient::CheckProtectedPasswordEntry( void StubPasswordManagerClient::LogPasswordReuseDetectedEvent() {} #endif -ukm::UkmRecorder* StubPasswordManagerClient::GetUkmRecorder() { - return ukm::UkmRecorder::Get(); -} - ukm::SourceId StubPasswordManagerClient::GetUkmSourceId() { return ukm_source_id_; } @@ -103,8 +97,7 @@ ukm::SourceId StubPasswordManagerClient::GetUkmSourceId() { PasswordManagerMetricsRecorder& StubPasswordManagerClient::GetMetricsRecorder() { if (!metrics_recorder_) { - metrics_recorder_.emplace(GetUkmRecorder(), GetUkmSourceId(), - GetMainFrameURL()); + metrics_recorder_.emplace(GetUkmSourceId(), GetMainFrameURL()); } return metrics_recorder_.value(); } diff --git a/chromium/components/password_manager/core/browser/stub_password_manager_client.h b/chromium/components/password_manager/core/browser/stub_password_manager_client.h index 1578d649981..cee51f056b9 100644 --- a/chromium/components/password_manager/core/browser/stub_password_manager_client.h +++ b/chromium/components/password_manager/core/browser/stub_password_manager_client.h @@ -61,7 +61,6 @@ class StubPasswordManagerClient : public PasswordManagerClient { bool password_field_exists) override; void LogPasswordReuseDetectedEvent() override; #endif - ukm::UkmRecorder* GetUkmRecorder() override; ukm::SourceId GetUkmSourceId() override; PasswordManagerMetricsRecorder& GetMetricsRecorder() override; diff --git a/chromium/components/password_manager/core/common/credential_manager_types.h b/chromium/components/password_manager/core/common/credential_manager_types.h index 876b44790d1..cc2a6d50bc4 100644 --- a/chromium/components/password_manager/core/common/credential_manager_types.h +++ b/chromium/components/password_manager/core/common/credential_manager_types.h @@ -35,8 +35,7 @@ enum class CredentialType { enum class CredentialManagerError { SUCCESS, - DISABLED, - PENDINGREQUEST, + PENDING_REQUEST, PASSWORDSTOREUNAVAILABLE, UNKNOWN, }; diff --git a/chromium/components/password_manager/core/common/experiments.cc b/chromium/components/password_manager/core/common/experiments.cc index c2329b1cdd0..7ff43cecca0 100644 --- a/chromium/components/password_manager/core/common/experiments.cc +++ b/chromium/components/password_manager/core/common/experiments.cc @@ -10,7 +10,7 @@ namespace password_manager { bool ForceSavingExperimentEnabled() { return base::FeatureList::IsEnabled( - password_manager::features::kEnablePasswordForceSaving); + password_manager::features::kPasswordForceSaving); } } // namespace password_manager diff --git a/chromium/components/password_manager/core/common/password_manager_features.cc b/chromium/components/password_manager/core/common/password_manager_features.cc index b0dcae4184b..b7d2818cbf3 100644 --- a/chromium/components/password_manager/core/common/password_manager_features.cc +++ b/chromium/components/password_manager/core/common/password_manager_features.cc @@ -14,11 +14,11 @@ namespace features { // application will also be considered matches for, and be filled into // corresponding Web applications. const base::Feature kAffiliationBasedMatching = { - "affiliation-based-matching", base::FEATURE_ENABLED_BY_DEFAULT}; + "AffiliationBasedMatching", base::FEATURE_ENABLED_BY_DEFAULT}; // Use HTML based username detector. const base::Feature kEnableHtmlBasedUsernameDetector = { - "EnableHtmlBaseUsernameDetector", base::FEATURE_DISABLED_BY_DEFAULT}; + "EnableHtmlBaseUsernameDetector", base::FEATURE_ENABLED_BY_DEFAULT}; // Enable additional elements in the form popup UI, which will allow the user to // view all saved passwords. @@ -44,21 +44,13 @@ const base::Feature kEnableManualSaving = {"EnableManualSaving", // Enable a context menu item in the password field that allows the user // to manually enforce saving of their password. -const base::Feature kEnablePasswordForceSaving = { - "enable-password-force-saving", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kPasswordForceSaving = { + "PasswordForceSaving", base::FEATURE_DISABLED_BY_DEFAULT}; // Enable the user to trigger password generation manually. const base::Feature kEnableManualPasswordGeneration = { "enable-manual-password-generation", base::FEATURE_ENABLED_BY_DEFAULT}; -// Enables username correction while saving username and password details. -const base::Feature kEnableUsernameCorrection{"EnableUsernameCorrection", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables password selection while saving username and password details. -const base::Feature kEnablePasswordSelection{"EnablePasswordSelection", - base::FEATURE_ENABLED_BY_DEFAULT}; - // Enables the "Show all saved passwords" option in Context Menu. const base::Feature kEnableShowAllSavedPasswordsContextMenu{ "kEnableShowAllSavedPasswordsContextMenu", @@ -71,20 +63,20 @@ const base::Feature kProtectSyncCredential = { // Disallow autofilling of the sync credential only for transactional reauth // pages. const base::Feature kProtectSyncCredentialOnReauth = { - "protect-sync-credential-on-reauth", base::FEATURE_DISABLED_BY_DEFAULT}; + "ProtectSyncCredentialOnReauth", base::FEATURE_DISABLED_BY_DEFAULT}; // Controls the ability to export passwords from Chrome's settings page. -const base::Feature kPasswordExport = {"password-export", +const base::Feature kPasswordExport = {"PasswordExport", base::FEATURE_DISABLED_BY_DEFAULT}; // Controls the ability to import passwords from Chrome's settings page. -const base::Feature kPasswordImport = {"password-import", +const base::Feature kPasswordImport = {"PasswordImport", base::FEATURE_DISABLED_BY_DEFAULT}; // Control whether users can view and copy passwords. This is only used for // mobile, the desktop version of Chrome always allows users to view // passwords. -const base::Feature kViewPasswords = {"view-passwords", +const base::Feature kViewPasswords = {"ViewPasswords", base::FEATURE_ENABLED_BY_DEFAULT}; // Enables the experiment for the password manager to only fill on account diff --git a/chromium/components/password_manager/core/common/password_manager_features.h b/chromium/components/password_manager/core/common/password_manager_features.h index 2c10d4ccc47..cd495b9fd9e 100644 --- a/chromium/components/password_manager/core/common/password_manager_features.h +++ b/chromium/components/password_manager/core/common/password_manager_features.h @@ -24,10 +24,8 @@ extern const base::Feature kEnableManualFallbacksFillingStandalone; extern const base::Feature kEnableManualFallbacksGeneration; extern const base::Feature kEnableManualPasswordGeneration; extern const base::Feature kEnableManualSaving; -extern const base::Feature kEnablePasswordForceSaving; -extern const base::Feature kEnablePasswordSelection; +extern const base::Feature kPasswordForceSaving; extern const base::Feature kEnableShowAllSavedPasswordsContextMenu; -extern const base::Feature kEnableUsernameCorrection; extern const base::Feature kFillOnAccountSelect; extern const base::Feature kPasswordExport; extern const base::Feature kPasswordImport; |