Commit 4f967db1 authored by Vadym Doroshenko's avatar Vadym Doroshenko Committed by Commit Bot

Propagate form predictions for uploading on username first flow.

Bug: 959776
Change-Id: I0e2ad7cf484d2fcabafaa8afa819b821cfc9df4a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1864662
Commit-Queue: Vadym Doroshenko <dvadym@chromium.org>
Reviewed-by: default avatarDominic Battré <battre@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707101}
parent a13bf85a
...@@ -16,15 +16,6 @@ using autofill::ServerFieldType; ...@@ -16,15 +16,6 @@ using autofill::ServerFieldType;
namespace password_manager { namespace password_manager {
namespace {
// Returns true if the field is password or username prediction.
bool IsCredentialRelatedPrediction(ServerFieldType type) {
return DeriveFromServerFieldType(type) != CredentialFieldType::kNone;
}
} // namespace
CredentialFieldType DeriveFromServerFieldType(ServerFieldType type) { CredentialFieldType DeriveFromServerFieldType(ServerFieldType type) {
switch (type) { switch (type) {
case autofill::USERNAME: case autofill::USERNAME:
...@@ -89,23 +80,20 @@ FormPredictions ConvertToFormPredictions(int driver_id, ...@@ -89,23 +80,20 @@ FormPredictions ConvertToFormPredictions(int driver_id,
} }
} }
if (IsCredentialRelatedPrediction(server_type)) { bool may_use_prefilled_placeholder = false;
bool may_use_prefilled_placeholder = false; for (const auto& predictions : field->server_predictions()) {
for (const auto& predictions : field->server_predictions()) { may_use_prefilled_placeholder |=
may_use_prefilled_placeholder |= predictions.may_use_prefilled_placeholder();
predictions.may_use_prefilled_placeholder(); }
}
field_predictions.emplace_back();
field_predictions.back().renderer_id = field->unique_renderer_id, field_predictions.emplace_back();
field_predictions.back().type = server_type, field_predictions.back().renderer_id = field->unique_renderer_id;
field_predictions.back().may_use_prefilled_placeholder = field_predictions.back().type = server_type;
may_use_prefilled_placeholder; field_predictions.back().may_use_prefilled_placeholder =
may_use_prefilled_placeholder;
#if defined(OS_IOS) #if defined(OS_IOS)
field_predictions.back().unique_id = field->unique_id; field_predictions.back().unique_id = field->unique_id;
#endif #endif
}
} }
FormPredictions predictions; FormPredictions predictions;
......
...@@ -37,16 +37,6 @@ namespace password_manager { ...@@ -37,16 +37,6 @@ namespace password_manager {
namespace { namespace {
const PasswordFieldPrediction* FindFormPrediction(
const FormPredictions& predictions,
uint32_t renderer_id) {
for (const PasswordFieldPrediction& prediction : predictions.fields) {
if (prediction.renderer_id == renderer_id)
return &prediction;
}
return nullptr;
}
TEST(FormPredictionsTest, ConvertToFormPredictions) { TEST(FormPredictionsTest, ConvertToFormPredictions) {
struct TestField { struct TestField {
std::string name; std::string name;
...@@ -57,7 +47,7 @@ TEST(FormPredictionsTest, ConvertToFormPredictions) { ...@@ -57,7 +47,7 @@ TEST(FormPredictionsTest, ConvertToFormPredictions) {
} test_fields[] = { } test_fields[] = {
{"full_name", "text", UNKNOWN_TYPE, UNKNOWN_TYPE, false}, {"full_name", "text", UNKNOWN_TYPE, UNKNOWN_TYPE, false},
// Password Manager is interested only in credential related types. // Password Manager is interested only in credential related types.
{"Email", "email", EMAIL_ADDRESS, UNKNOWN_TYPE, false}, {"Email", "email", EMAIL_ADDRESS, EMAIL_ADDRESS, false},
{"username", "text", USERNAME, USERNAME, true}, {"username", "text", USERNAME, USERNAME, true},
{"Password", "password", PASSWORD, PASSWORD, false}, {"Password", "password", PASSWORD, PASSWORD, false},
{"confirm_password", "password", CONFIRMATION_PASSWORD, {"confirm_password", "password", CONFIRMATION_PASSWORD,
...@@ -73,20 +63,15 @@ TEST(FormPredictionsTest, ConvertToFormPredictions) { ...@@ -73,20 +63,15 @@ TEST(FormPredictionsTest, ConvertToFormPredictions) {
} }
FormStructure form_structure(form_data); FormStructure form_structure(form_data);
size_t expected_predictions = 0;
// Set server predictions and create expected votes. // Set server predictions and create expected votes.
for (size_t i = 0; i < base::size(test_fields); ++i) { for (size_t i = 0; i < base::size(test_fields); ++i) {
AutofillField* field = form_structure.field(i); AutofillField* field = form_structure.field(i);
field->set_server_type(test_fields[i].input_type); field->set_server_type(test_fields[i].input_type);
ServerFieldType expected_type = test_fields[i].expected_type;
FieldPrediction prediction; FieldPrediction prediction;
prediction.set_may_use_prefilled_placeholder( prediction.set_may_use_prefilled_placeholder(
test_fields[i].may_use_prefilled_placeholder); test_fields[i].may_use_prefilled_placeholder);
field->set_server_predictions({prediction}); field->set_server_predictions({prediction});
if (expected_type != UNKNOWN_TYPE)
++expected_predictions;
} }
constexpr int driver_id = 1000; constexpr int driver_id = 1000;
...@@ -96,20 +81,14 @@ TEST(FormPredictionsTest, ConvertToFormPredictions) { ...@@ -96,20 +81,14 @@ TEST(FormPredictionsTest, ConvertToFormPredictions) {
// Check whether actual predictions are equal to expected ones. // Check whether actual predictions are equal to expected ones.
EXPECT_EQ(driver_id, actual_predictions.driver_id); EXPECT_EQ(driver_id, actual_predictions.driver_id);
EXPECT_EQ(form_structure.form_signature(), actual_predictions.form_signature); EXPECT_EQ(form_structure.form_signature(), actual_predictions.form_signature);
EXPECT_EQ(expected_predictions, actual_predictions.fields.size()); EXPECT_EQ(base::size(test_fields), actual_predictions.fields.size());
for (size_t i = 0; i < base::size(test_fields); ++i) { for (size_t i = 0; i < base::size(test_fields); ++i) {
uint32_t unique_renderer_id = form_data.fields[i].unique_renderer_id; const PasswordFieldPrediction& actual_prediction =
const PasswordFieldPrediction* actual_prediction = actual_predictions.fields[i];
FindFormPrediction(actual_predictions, unique_renderer_id); EXPECT_EQ(test_fields[i].expected_type, actual_prediction.type);
if (test_fields[i].expected_type == UNKNOWN_TYPE) { EXPECT_EQ(test_fields[i].may_use_prefilled_placeholder,
EXPECT_FALSE(actual_prediction); actual_prediction.may_use_prefilled_placeholder);
} else {
ASSERT_TRUE(actual_prediction);
EXPECT_EQ(test_fields[i].expected_type, actual_prediction->type);
EXPECT_EQ(test_fields[i].may_use_prefilled_placeholder,
actual_prediction->may_use_prefilled_placeholder);
}
} }
} }
...@@ -171,10 +150,7 @@ TEST(FormPredictionsTest, ConvertToFormPredictions_SynthesiseConfirmation) { ...@@ -171,10 +150,7 @@ TEST(FormPredictionsTest, ConvertToFormPredictions_SynthesiseConfirmation) {
<< ", input type=" << test_form[i].input_type << ", input type=" << test_form[i].input_type
<< ", expected type=" << test_form[i].expected_type << ", expected type=" << test_form[i].expected_type
<< ", synthesised FormFieldData=" << form_data.fields[i]); << ", synthesised FormFieldData=" << form_data.fields[i]);
const PasswordFieldPrediction* actual_prediction = FindFormPrediction( EXPECT_EQ(test_form[i].expected_type, actual_predictions.fields[i].type);
actual_predictions, form_data.fields[i].unique_renderer_id);
ASSERT_TRUE(actual_prediction);
EXPECT_EQ(test_form[i].expected_type, actual_prediction->type);
} }
} }
} }
......
...@@ -700,8 +700,10 @@ bool PasswordFormManager::ProvisionallySave( ...@@ -700,8 +700,10 @@ bool PasswordFormManager::ProvisionallySave(
base::Time::Now())) { base::Time::Now())) {
parsed_submitted_form_->username_value = possible_username->value; parsed_submitted_form_->username_value = possible_username->value;
metrics_recorder_->set_possible_username_used(true); metrics_recorder_->set_possible_username_used(true);
votes_uploader_.set_single_username_vote_data( if (possible_username->form_predictions) {
possible_username->renderer_id, possible_username->form_predictions); votes_uploader_.set_single_username_vote_data(
possible_username->renderer_id, *possible_username->form_predictions);
}
} }
CreatePendingCredentials(); CreatePendingCredentials();
return true; return true;
......
...@@ -2055,7 +2055,7 @@ TEST_F(PasswordFormManagerTest, UsernameFirstFlow) { ...@@ -2055,7 +2055,7 @@ TEST_F(PasswordFormManagerTest, UsernameFirstFlow) {
const base::string16 possible_username = ASCIIToUTF16("possible_username"); const base::string16 possible_username = ASCIIToUTF16("possible_username");
PossibleUsernameData possible_username_data( PossibleUsernameData possible_username_data(
saved_match_.signon_realm, 1u /* renderer_id */, possible_username, saved_match_.signon_realm, 1u /* renderer_id */, possible_username,
base::Time::Now()); base::Time::Now(), 0 /* driver_id */);
FormData submitted_form = observed_form_only_password_fields_; FormData submitted_form = observed_form_only_password_fields_;
submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); submitted_form.fields[0].value = ASCIIToUTF16("strongpassword");
...@@ -2079,7 +2079,7 @@ TEST_F(PasswordFormManagerTest, UsernameFirstFlowDifferentDomains) { ...@@ -2079,7 +2079,7 @@ TEST_F(PasswordFormManagerTest, UsernameFirstFlowDifferentDomains) {
base::string16 possible_username = ASCIIToUTF16("possible_username"); base::string16 possible_username = ASCIIToUTF16("possible_username");
PossibleUsernameData possible_username_data( PossibleUsernameData possible_username_data(
"https://another.domain.com", 1u /* renderer_id */, possible_username, "https://another.domain.com", 1u /* renderer_id */, possible_username,
base::Time::Now()); base::Time::Now(), 0 /* driver_id */);
FormData submitted_form = observed_form_only_password_fields_; FormData submitted_form = observed_form_only_password_fields_;
submitted_form.fields[0].value = ASCIIToUTF16("strongpassword"); submitted_form.fields[0].value = ASCIIToUTF16("strongpassword");
...@@ -2092,6 +2092,53 @@ TEST_F(PasswordFormManagerTest, UsernameFirstFlowDifferentDomains) { ...@@ -2092,6 +2092,53 @@ TEST_F(PasswordFormManagerTest, UsernameFirstFlowDifferentDomains) {
EXPECT_TRUE(form_manager_->GetPendingCredentials().username_value.empty()); EXPECT_TRUE(form_manager_->GetPendingCredentials().username_value.empty());
} }
// Tests that username is taken during username first flow.
TEST_F(PasswordFormManagerTest, UsernameFirstFlowVotes) {
TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
CreateFormManager(observed_form_only_password_fields_);
fetcher_->NotifyFetchCompleted();
const base::string16 possible_username = ASCIIToUTF16("possible_username");
constexpr uint64_t kUsernameFieldRendererId = 100;
PossibleUsernameData possible_username_data(
saved_match_.signon_realm, kUsernameFieldRendererId, possible_username,
base::Time::Now(), 0 /* driver_id */);
// Create form predictions and set them to |possible_username_data|.
FormPredictions predictions;
constexpr uint64_t kUsernameFormSignature = 1000;
predictions.form_signature = kUsernameFormSignature;
PasswordFieldPrediction field_prediction;
field_prediction.renderer_id = kUsernameFieldRendererId;
field_prediction.signature = 123;
field_prediction.type = autofill::SINGLE_USERNAME;
predictions.fields.push_back(field_prediction);
possible_username_data.form_predictions = predictions;
// Simulate submission a form without username. Data from
// |possible_username_data| will be taken for setting username.
FormData submitted_form = observed_form_only_password_fields_;
submitted_form.fields[0].value = ASCIIToUTF16("strongpassword");
ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_,
&possible_username_data));
// Check that uploads for both username and password form happen.
testing::InSequence in_sequence;
// Upload for the password form.
EXPECT_CALL(mock_autofill_download_manager_,
StartUploadRequest(_, false, _, _, true, nullptr));
// Upload for the username form.
EXPECT_CALL(mock_autofill_download_manager_,
StartUploadRequest(SignatureIs(kUsernameFormSignature), false, _,
_, true, nullptr));
form_manager_->Save();
}
} // namespace } // namespace
} // namespace password_manager } // namespace password_manager
...@@ -279,6 +279,8 @@ void PasswordManager::DidNavigateMainFrame(bool form_may_be_submitted) { ...@@ -279,6 +279,8 @@ void PasswordManager::DidNavigateMainFrame(bool form_may_be_submitted) {
} }
} }
form_managers_.clear(); form_managers_.clear();
TryToFindPredictionsToPossibleUsernameData();
predictions_.clear(); predictions_.clear();
store_password_called_ = false; store_password_called_ = false;
} }
...@@ -322,6 +324,7 @@ void PasswordManager::DropFormManagers() { ...@@ -322,6 +324,7 @@ void PasswordManager::DropFormManagers() {
form_managers_.clear(); form_managers_.clear();
owned_submitted_form_manager_.reset(); owned_submitted_form_manager_.reset();
all_visible_forms_.clear(); all_visible_forms_.clear();
TryToFindPredictionsToPossibleUsernameData();
predictions_.clear(); predictions_.clear();
} }
...@@ -386,8 +389,10 @@ void PasswordManager::OnUserModifiedNonPasswordField( ...@@ -386,8 +389,10 @@ void PasswordManager::OnUserModifiedNonPasswordField(
PasswordManagerDriver* driver, PasswordManagerDriver* driver,
int32_t renderer_id, int32_t renderer_id,
const base::string16& value) { const base::string16& value) {
// |driver| might be empty on iOS or in tests.
int driver_id = driver ? driver->GetId() : 0;
possible_username_.emplace(GetSignonRealm(driver->GetLastCommittedURL()), possible_username_.emplace(GetSignonRealm(driver->GetLastCommittedURL()),
renderer_id, value, base::Time::Now()); renderer_id, value, base::Time::Now(), driver_id);
} }
void PasswordManager::ShowManualFallbackForSaving( void PasswordManager::ShowManualFallbackForSaving(
...@@ -586,6 +591,7 @@ PasswordFormManager* PasswordManager::ProvisionallySaveForm( ...@@ -586,6 +591,7 @@ PasswordFormManager* PasswordManager::ProvisionallySaveForm(
return nullptr; return nullptr;
} }
TryToFindPredictionsToPossibleUsernameData();
const PossibleUsernameData* possible_username = const PossibleUsernameData* possible_username =
possible_username_ ? &possible_username_.value() : nullptr; possible_username_ ? &possible_username_.value() : nullptr;
if (!matched_manager->ProvisionallySave(submitted_form, driver, if (!matched_manager->ProvisionallySave(submitted_form, driver,
...@@ -1050,4 +1056,20 @@ void PasswordManager::ReportSubmittedFormFrameMetric( ...@@ -1050,4 +1056,20 @@ void PasswordManager::ReportSubmittedFormFrameMetric(
metrics_util::LogSubmittedFormFrame(frame); metrics_util::LogSubmittedFormFrame(frame);
} }
void PasswordManager::TryToFindPredictionsToPossibleUsernameData() {
if (!possible_username_ || possible_username_->form_predictions)
return;
for (auto it : predictions_) {
if (it.second.driver_id != possible_username_->driver_id)
continue;
for (const PasswordFieldPrediction& field : it.second.fields) {
if (field.renderer_id == possible_username_->renderer_id) {
possible_username_->form_predictions = it.second;
return;
}
}
}
}
} // namespace password_manager } // namespace password_manager
...@@ -299,6 +299,11 @@ class PasswordManager : public FormSubmissionObserver { ...@@ -299,6 +299,11 @@ class PasswordManager : public FormSubmissionObserver {
void ReportSubmittedFormFrameMetric(const PasswordManagerDriver* driver, void ReportSubmittedFormFrameMetric(const PasswordManagerDriver* driver,
const autofill::PasswordForm& form); const autofill::PasswordForm& form);
// If |possible_username_.form_predictions| is missing, this functions tries
// to find predictions for the form which contains |possible_username_| in
// |predictions_|.
void TryToFindPredictionsToPossibleUsernameData();
// PasswordFormManager transition schemes: // PasswordFormManager transition schemes:
// 1. HTML submission with navigation afterwads. // 1. HTML submission with navigation afterwads.
// form "seen" // form "seen"
......
...@@ -12,13 +12,17 @@ using base::TimeDelta; ...@@ -12,13 +12,17 @@ using base::TimeDelta;
namespace password_manager { namespace password_manager {
PossibleUsernameData::PossibleUsernameData(std::string signon_realm, PossibleUsernameData::PossibleUsernameData(std::string signon_realm,
int32_t renderer_id, uint32_t renderer_id,
base::string16 value, base::string16 value,
base::Time last_change) base::Time last_change,
int driver_id)
: signon_realm(std::move(signon_realm)), : signon_realm(std::move(signon_realm)),
renderer_id(renderer_id), renderer_id(renderer_id),
value(std::move(value)), value(std::move(value)),
last_change(last_change) {} last_change(last_change),
driver_id(driver_id) {}
PossibleUsernameData::PossibleUsernameData(const PossibleUsernameData&) =
default;
PossibleUsernameData::~PossibleUsernameData() = default; PossibleUsernameData::~PossibleUsernameData() = default;
bool IsPossibleUsernameValid(const PossibleUsernameData& possible_username, bool IsPossibleUsernameValid(const PossibleUsernameData& possible_username,
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <string> #include <string>
#include "base/optional.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "components/password_manager/core/browser/form_parsing/password_field_prediction.h" #include "components/password_manager/core/browser/form_parsing/password_field_prediction.h"
...@@ -23,18 +24,24 @@ constexpr base::TimeDelta kMaxDelayBetweenTypingUsernameAndSubmission = ...@@ -23,18 +24,24 @@ constexpr base::TimeDelta kMaxDelayBetweenTypingUsernameAndSubmission =
// username during username first flow. // username during username first flow.
struct PossibleUsernameData { struct PossibleUsernameData {
PossibleUsernameData(std::string signon_realm, PossibleUsernameData(std::string signon_realm,
int32_t renderer_id, uint32_t renderer_id,
base::string16 value, base::string16 value,
base::Time last_change); base::Time last_change,
int driver_id);
PossibleUsernameData(const PossibleUsernameData&);
~PossibleUsernameData(); ~PossibleUsernameData();
std::string signon_realm; std::string signon_realm;
int32_t renderer_id; uint32_t renderer_id;
base::string16 value; base::string16 value;
base::Time last_change; base::Time last_change;
// Id of PasswordManagerDriver which corresponds to the frame of this field.
// Paired with the |renderer_id|, this identifies a field globally.
int driver_id;
// Predictions for the form which contains a field with |renderer_id|. // Predictions for the form which contains a field with |renderer_id|.
FormPredictions* form_predictions = nullptr; base::Optional<FormPredictions> form_predictions;
}; };
// Checks that |possible_username| might represent an username: // Checks that |possible_username| might represent an username:
......
...@@ -22,7 +22,8 @@ class IsPossibleUsernameValidTest : public testing::Test { ...@@ -22,7 +22,8 @@ class IsPossibleUsernameValidTest : public testing::Test {
"https://example.com/" /* submitted_signon_realm */, "https://example.com/" /* submitted_signon_realm */,
1u /* renderer_id */, 1u /* renderer_id */,
ASCIIToUTF16("username") /* value */, ASCIIToUTF16("username") /* value */,
base::Time::Now() /* last_change */) {} base::Time::Now() /* last_change */,
10 /* driver_id */) {}
protected: protected:
PossibleUsernameData possible_username_data_; PossibleUsernameData possible_username_data_;
......
...@@ -163,9 +163,8 @@ class VotesUploader { ...@@ -163,9 +163,8 @@ class VotesUploader {
void clear_single_username_vote_data() { single_username_vote_data_.reset(); } void clear_single_username_vote_data() { single_username_vote_data_.reset(); }
void set_single_username_vote_data(int renderer_id, void set_single_username_vote_data(int renderer_id,
const FormPredictions* form_predictions) { const FormPredictions& form_predictions) {
if (form_predictions) single_username_vote_data_.emplace(renderer_id, form_predictions);
single_username_vote_data_.emplace(renderer_id, *form_predictions);
} }
private: private:
......
...@@ -405,7 +405,7 @@ TEST_F(VotesUploaderTest, UploadSingleUsername) { ...@@ -405,7 +405,7 @@ TEST_F(VotesUploaderTest, UploadSingleUsername) {
form_predictions.fields.back().signature = kUsernameFieldSignature; form_predictions.fields.back().signature = kUsernameFieldSignature;
votes_uploader.set_single_username_vote_data(kUsernameRendererId, votes_uploader.set_single_username_vote_data(kUsernameRendererId,
&form_predictions); form_predictions);
ServerFieldTypeSet expected_types = {credentials_saved ? SINGLE_USERNAME ServerFieldTypeSet expected_types = {credentials_saved ? SINGLE_USERNAME
: NOT_USERNAME}; : NOT_USERNAME};
......
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