Commit 56bde75e authored by Christos Froussios's avatar Christos Froussios Committed by Commit Bot

[Password Manager] Preparing the passwords for export also serialises them

I also changed the timing on the IN_PROGRESS update to occur as soon as
the destination is known. If we happen to still be serialising, that is an
implementation detail that should not be affect the UI.

Bug: 785237
Change-Id: Ie9466840b1e4bf11ddfaa4c69045326ab3b11fe3
Reviewed-on: https://chromium-review.googlesource.com/789330
Commit-Queue: Christos Froussios <cfroussios@chromium.org>
Reviewed-by: default avatarVaclav Brozek <vabr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533264}
parent 0ca719f5
...@@ -17,13 +17,16 @@ ...@@ -17,13 +17,16 @@
namespace { namespace {
std::vector<std::unique_ptr<autofill::PasswordForm>> CopyOf( // A wrapper for |write_function|, which can be bound and keep a copy of its
const std::vector<std::unique_ptr<autofill::PasswordForm>>& password_list) { // data on the closure.
std::vector<std::unique_ptr<autofill::PasswordForm>> ret_val; bool Write(int (*write_function)(const base::FilePath& filename,
for (const auto& form : password_list) { const char* data,
ret_val.push_back(std::make_unique<autofill::PasswordForm>(*form)); int size),
} const base::FilePath& destination,
return ret_val; const std::string& serialised) {
int written =
write_function(destination, serialised.c_str(), serialised.size());
return written == static_cast<int>(serialised.size());
} }
} // namespace } // namespace
...@@ -45,16 +48,34 @@ PasswordManagerExporter::PasswordManagerExporter( ...@@ -45,16 +48,34 @@ PasswordManagerExporter::PasswordManagerExporter(
PasswordManagerExporter::~PasswordManagerExporter() {} PasswordManagerExporter::~PasswordManagerExporter() {}
void PasswordManagerExporter::PreparePasswordsForExport() { void PasswordManagerExporter::PreparePasswordsForExport() {
password_list_ = credential_provider_interface_->GetAllPasswords(); std::vector<std::unique_ptr<autofill::PasswordForm>> password_list =
credential_provider_interface_->GetAllPasswords();
size_t password_list_size = password_list.size();
if (IsReadyForExport()) base::PostTaskAndReplyWithResult(
Export(); task_runner_.get(), FROM_HERE,
base::BindOnce(&password_manager::PasswordCSVWriter::SerializePasswords,
base::Passed(std::move(password_list))),
base::BindOnce(&PasswordManagerExporter::SetSerialisedPasswordList,
weak_factory_.GetWeakPtr(), password_list_size));
} }
void PasswordManagerExporter::SetDestination( void PasswordManagerExporter::SetDestination(
const base::FilePath& destination) { const base::FilePath& destination) {
destination_ = destination; destination_ = destination;
if (IsReadyForExport())
Export();
OnProgress(ExportProgressStatus::IN_PROGRESS, std::string());
}
void PasswordManagerExporter::SetSerialisedPasswordList(
size_t count,
const std::string& serialised) {
serialised_password_list_ = serialised;
password_count_ = count;
if (IsReadyForExport()) if (IsReadyForExport())
Export(); Export();
} }
...@@ -64,7 +85,8 @@ void PasswordManagerExporter::Cancel() { ...@@ -64,7 +85,8 @@ void PasswordManagerExporter::Cancel() {
weak_factory_.InvalidateWeakPtrs(); weak_factory_.InvalidateWeakPtrs();
destination_.clear(); destination_.clear();
password_list_.clear(); serialised_password_list_.clear();
password_count_ = 0;
OnProgress(ExportProgressStatus::FAILED_CANCELLED, std::string()); OnProgress(ExportProgressStatus::FAILED_CANCELLED, std::string());
} }
...@@ -82,33 +104,31 @@ void PasswordManagerExporter::SetWriteForTesting( ...@@ -82,33 +104,31 @@ void PasswordManagerExporter::SetWriteForTesting(
} }
bool PasswordManagerExporter::IsReadyForExport() { bool PasswordManagerExporter::IsReadyForExport() {
return !destination_.empty() && !password_list_.empty(); return !destination_.empty() && !serialised_password_list_.empty();
} }
void PasswordManagerExporter::Export() { void PasswordManagerExporter::Export() {
UMA_HISTOGRAM_COUNTS("PasswordManager.ExportedPasswordsPerUserInCSV", base::FilePath destination_copy_(destination_);
password_list_.size());
OnProgress(ExportProgressStatus::IN_PROGRESS, std::string());
base::PostTaskAndReplyWithResult( base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE, task_runner_.get(), FROM_HERE,
base::BindOnce(&password_manager::PasswordCSVWriter::SerializePasswords, base::BindOnce(::Write, write_function_, std::move(destination_copy_),
base::Passed(CopyOf(password_list_))), std::move(serialised_password_list_)),
base::BindOnce(&PasswordManagerExporter::OnPasswordsSerialised, base::BindOnce(&PasswordManagerExporter::OnPasswordsExported,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(), std::move(destination_),
base::Passed(std::move(destination_)))); password_count_));
password_list_.clear();
destination_.clear(); destination_.clear();
serialised_password_list_.clear();
password_count_ = 0;
} }
void PasswordManagerExporter::OnPasswordsSerialised( void PasswordManagerExporter::OnPasswordsExported(
const base::FilePath& destination, const base::FilePath& destination,
const std::string& serialised) { int count,
int written = bool success) {
write_function_(destination, serialised.c_str(), serialised.size()); if (success) {
if (written == static_cast<int>(serialised.size())) { UMA_HISTOGRAM_COUNTS("PasswordManager.ExportedPasswordsPerUserInCSV",
count);
OnProgress(ExportProgressStatus::SUCCEEDED, std::string()); OnProgress(ExportProgressStatus::SUCCEEDED, std::string());
} else { } else {
OnProgress(ExportProgressStatus::FAILED_WRITE_FAILED, OnProgress(ExportProgressStatus::FAILED_WRITE_FAILED,
......
...@@ -15,10 +15,6 @@ ...@@ -15,10 +15,6 @@
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
#include "components/password_manager/core/browser/ui/export_progress_status.h" #include "components/password_manager/core/browser/ui/export_progress_status.h"
namespace autofill {
struct PasswordForm;
}
namespace password_manager { namespace password_manager {
class CredentialProviderInterface; class CredentialProviderInterface;
...@@ -59,6 +55,11 @@ class PasswordManagerExporter { ...@@ -59,6 +55,11 @@ class PasswordManagerExporter {
int size)); int size));
private: private:
// Caches the serialised password list. It proceeds to export, if all the
// parts are ready. |count| is the number of passwords which were serialised.
// |serialised| is the serialised list of passwords.
void SetSerialisedPasswordList(size_t count, const std::string& serialised);
// Returns true if all the necessary data is available. // Returns true if all the necessary data is available.
bool IsReadyForExport(); bool IsReadyForExport();
...@@ -66,9 +67,13 @@ class PasswordManagerExporter { ...@@ -66,9 +67,13 @@ class PasswordManagerExporter {
// At the end, it clears cached fields. // At the end, it clears cached fields.
void Export(); void Export();
// Callback after the passwords have been serialised. // Callback after the passwords have been serialised. It reports the result to
void OnPasswordsSerialised(const base::FilePath& destination, // the UI and to metrics. |destination| is the folder we wrote to. |count| is
const std::string& serialised); // the number of passwords exported. |success| is whether they were actually
// written.
void OnPasswordsExported(const base::FilePath& destination,
int count,
bool success);
// Wrapper for the |on_progress_| callback, which caches |status|, so that // Wrapper for the |on_progress_| callback, which caches |status|, so that
// it can be provided by GetProgressStatus. // it can be provided by GetProgressStatus.
...@@ -83,9 +88,10 @@ class PasswordManagerExporter { ...@@ -83,9 +88,10 @@ class PasswordManagerExporter {
// The most recent progress status update, as was seen on |on_progress_|. // The most recent progress status update, as was seen on |on_progress_|.
ExportProgressStatus last_progress_status_; ExportProgressStatus last_progress_status_;
// The password list that was read from the store. It will be cleared once // The password list that was read from the store and serialised.
// exporting is complete. std::string serialised_password_list_;
std::vector<std::unique_ptr<autofill::PasswordForm>> password_list_; // The number of passwords that were serialised. Useful for metrics.
size_t password_count_;
// The destination which was provided and where the password list will be // The destination which was provided and where the password list will be
// sent. It will be cleared once exporting is complete. // sent. It will be cleared once exporting is complete.
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/histogram_tester.h"
#include "base/test/mock_callback.h" #include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "build/build_config.h" #include "build/build_config.h"
...@@ -117,6 +118,7 @@ class PasswordManagerExporterTest : public testing::Test { ...@@ -117,6 +118,7 @@ class PasswordManagerExporterTest : public testing::Test {
password_manager::PasswordManagerExporter exporter_; password_manager::PasswordManagerExporter exporter_;
StrictMock<base::MockCallback<WriteCallback>> mock_write_file_; StrictMock<base::MockCallback<WriteCallback>> mock_write_file_;
base::FilePath destination_path_; base::FilePath destination_path_;
base::HistogramTester histogram_tester_;
private: private:
DISALLOW_COPY_AND_ASSIGN(PasswordManagerExporterTest); DISALLOW_COPY_AND_ASSIGN(PasswordManagerExporterTest);
...@@ -143,6 +145,8 @@ TEST_F(PasswordManagerExporterTest, PasswordExportSetPasswordListFirst) { ...@@ -143,6 +145,8 @@ TEST_F(PasswordManagerExporterTest, PasswordExportSetPasswordListFirst) {
exporter_.SetDestination(destination_path_); exporter_.SetDestination(destination_path_);
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"PasswordManager.ExportedPasswordsPerUserInCSV", password_list.size(), 1);
} }
TEST_F(PasswordManagerExporterTest, PasswordExportSetDestinationFirst) { TEST_F(PasswordManagerExporterTest, PasswordExportSetDestinationFirst) {
...@@ -166,6 +170,8 @@ TEST_F(PasswordManagerExporterTest, PasswordExportSetDestinationFirst) { ...@@ -166,6 +170,8 @@ TEST_F(PasswordManagerExporterTest, PasswordExportSetDestinationFirst) {
exporter_.PreparePasswordsForExport(); exporter_.PreparePasswordsForExport();
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"PasswordManager.ExportedPasswordsPerUserInCSV", password_list.size(), 1);
} }
TEST_F(PasswordManagerExporterTest, WriteFileFailed) { TEST_F(PasswordManagerExporterTest, WriteFileFailed) {
...@@ -187,6 +193,8 @@ TEST_F(PasswordManagerExporterTest, WriteFileFailed) { ...@@ -187,6 +193,8 @@ TEST_F(PasswordManagerExporterTest, WriteFileFailed) {
exporter_.PreparePasswordsForExport(); exporter_.PreparePasswordsForExport();
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
histogram_tester_.ExpectTotalCount(
"PasswordManager.ExportedPasswordsPerUserInCSV", 0);
} }
// Test that GetProgressStatus() returns the last ExportProgressStatus sent // Test that GetProgressStatus() returns the last ExportProgressStatus sent
...@@ -222,11 +230,15 @@ TEST_F(PasswordManagerExporterTest, DontExportWithOnlyDestination) { ...@@ -222,11 +230,15 @@ TEST_F(PasswordManagerExporterTest, DontExportWithOnlyDestination) {
fake_credential_provider_.SetPasswordList(password_list); fake_credential_provider_.SetPasswordList(password_list);
EXPECT_CALL(mock_write_file_, Run(_, _, _)).Times(0); EXPECT_CALL(mock_write_file_, Run(_, _, _)).Times(0);
EXPECT_CALL(mock_on_progress_, Run(_, _)).Times(0); EXPECT_CALL(
mock_on_progress_,
Run(password_manager::ExportProgressStatus::IN_PROGRESS, IsEmpty()));
exporter_.SetDestination(destination_path_); exporter_.SetDestination(destination_path_);
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
histogram_tester_.ExpectTotalCount(
"PasswordManager.ExportedPasswordsPerUserInCSV", 0);
} }
TEST_F(PasswordManagerExporterTest, CancelAfterPasswords) { TEST_F(PasswordManagerExporterTest, CancelAfterPasswords) {
...@@ -238,12 +250,17 @@ TEST_F(PasswordManagerExporterTest, CancelAfterPasswords) { ...@@ -238,12 +250,17 @@ TEST_F(PasswordManagerExporterTest, CancelAfterPasswords) {
EXPECT_CALL( EXPECT_CALL(
mock_on_progress_, mock_on_progress_,
Run(password_manager::ExportProgressStatus::FAILED_CANCELLED, IsEmpty())); Run(password_manager::ExportProgressStatus::FAILED_CANCELLED, IsEmpty()));
EXPECT_CALL(
mock_on_progress_,
Run(password_manager::ExportProgressStatus::IN_PROGRESS, IsEmpty()));
exporter_.PreparePasswordsForExport(); exporter_.PreparePasswordsForExport();
exporter_.Cancel(); exporter_.Cancel();
exporter_.SetDestination(destination_path_); exporter_.SetDestination(destination_path_);
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
histogram_tester_.ExpectTotalCount(
"PasswordManager.ExportedPasswordsPerUserInCSV", 0);
} }
TEST_F(PasswordManagerExporterTest, CancelAfterDestination) { TEST_F(PasswordManagerExporterTest, CancelAfterDestination) {
...@@ -252,6 +269,9 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestination) { ...@@ -252,6 +269,9 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestination) {
fake_credential_provider_.SetPasswordList(password_list); fake_credential_provider_.SetPasswordList(password_list);
EXPECT_CALL(mock_write_file_, Run(_, _, _)).Times(0); EXPECT_CALL(mock_write_file_, Run(_, _, _)).Times(0);
EXPECT_CALL(
mock_on_progress_,
Run(password_manager::ExportProgressStatus::IN_PROGRESS, IsEmpty()));
EXPECT_CALL( EXPECT_CALL(
mock_on_progress_, mock_on_progress_,
Run(password_manager::ExportProgressStatus::FAILED_CANCELLED, IsEmpty())); Run(password_manager::ExportProgressStatus::FAILED_CANCELLED, IsEmpty()));
...@@ -261,6 +281,8 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestination) { ...@@ -261,6 +281,8 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestination) {
exporter_.PreparePasswordsForExport(); exporter_.PreparePasswordsForExport();
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
histogram_tester_.ExpectTotalCount(
"PasswordManager.ExportedPasswordsPerUserInCSV", 0);
} }
// Test that PasswordManagerExporter is reusable, after an export has been // Test that PasswordManagerExporter is reusable, after an export has been
...@@ -291,6 +313,8 @@ TEST_F(PasswordManagerExporterTest, CancelAfterPasswordsThenExport) { ...@@ -291,6 +313,8 @@ TEST_F(PasswordManagerExporterTest, CancelAfterPasswordsThenExport) {
exporter_.PreparePasswordsForExport(); exporter_.PreparePasswordsForExport();
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"PasswordManager.ExportedPasswordsPerUserInCSV", password_list.size(), 1);
} }
// Test that PasswordManagerExporter is reusable, after an export has been // Test that PasswordManagerExporter is reusable, after an export has been
...@@ -313,7 +337,8 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestinationThenExport) { ...@@ -313,7 +337,8 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestinationThenExport) {
Run(password_manager::ExportProgressStatus::FAILED_CANCELLED, IsEmpty())); Run(password_manager::ExportProgressStatus::FAILED_CANCELLED, IsEmpty()));
EXPECT_CALL( EXPECT_CALL(
mock_on_progress_, mock_on_progress_,
Run(password_manager::ExportProgressStatus::IN_PROGRESS, IsEmpty())); Run(password_manager::ExportProgressStatus::IN_PROGRESS, IsEmpty()))
.Times(2);
EXPECT_CALL( EXPECT_CALL(
mock_on_progress_, mock_on_progress_,
Run(password_manager::ExportProgressStatus::SUCCEEDED, IsEmpty())); Run(password_manager::ExportProgressStatus::SUCCEEDED, IsEmpty()));
...@@ -324,6 +349,8 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestinationThenExport) { ...@@ -324,6 +349,8 @@ TEST_F(PasswordManagerExporterTest, CancelAfterDestinationThenExport) {
exporter_.SetDestination(destination_path_); exporter_.SetDestination(destination_path_);
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"PasswordManager.ExportedPasswordsPerUserInCSV", password_list.size(), 1);
} }
} // namespace } // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment