Commit 3391f9fa authored by Vadym Doroshenko's avatar Vadym Doroshenko Committed by Commit Bot

Use locally saved predictions for filling.

Bug: 959776

Change-Id: I3c6a42ab454d57b7072fc1b752ea1dcb1c7c5991
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1945817Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Commit-Queue: Vadym Doroshenko <dvadym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#721480}
parent 715b0ecc
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "components/autofill/core/common/form_data_predictions.h" #include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/save_password_progress_logger.h" #include "components/autofill/core/common/save_password_progress_logger.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h" #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/field_info_manager.h"
#include "components/password_manager/core/browser/password_autofill_manager.h" #include "components/password_manager/core/browser/password_autofill_manager.h"
#include "components/password_manager/core/browser/password_form_manager.h" #include "components/password_manager/core/browser/password_form_manager.h"
#include "components/password_manager/core/browser/password_generation_frame_helper.h" #include "components/password_manager/core/browser/password_generation_frame_helper.h"
...@@ -43,9 +44,13 @@ ...@@ -43,9 +44,13 @@
#include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_registry_simple.h"
#endif #endif
using autofill::ACCOUNT_CREATION_PASSWORD;
using autofill::FormData; using autofill::FormData;
using autofill::FormStructure; using autofill::FormStructure;
using autofill::NEW_PASSWORD;
using autofill::NOT_USERNAME;
using autofill::PasswordForm; using autofill::PasswordForm;
using autofill::SINGLE_USERNAME;
using autofill::mojom::PasswordFormFieldPredictionType; using autofill::mojom::PasswordFormFieldPredictionType;
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
using password_manager::metrics_util::GaiaPasswordHashChange; using password_manager::metrics_util::GaiaPasswordHashChange;
...@@ -123,9 +128,9 @@ PasswordFormManager* FindMatchedManagerByRendererId( ...@@ -123,9 +128,9 @@ PasswordFormManager* FindMatchedManagerByRendererId(
return nullptr; return nullptr;
} }
bool HasSingleUsernameVote(const FormStructure& form) { bool HasSingleUsernameVote(const FormPredictions& form) {
for (const auto& field : form) { for (const auto& field : form.fields) {
if (field->server_type() == autofill::SINGLE_USERNAME) if (field.type == autofill::SINGLE_USERNAME)
return true; return true;
} }
return false; return false;
...@@ -133,20 +138,36 @@ bool HasSingleUsernameVote(const FormStructure& form) { ...@@ -133,20 +138,36 @@ bool HasSingleUsernameVote(const FormStructure& form) {
// Returns true if at least one of the fields in |form| has a prediction to be a // Returns true if at least one of the fields in |form| has a prediction to be a
// new-password related field. // new-password related field.
bool HasNewPasswordVote(const FormStructure& form) { bool HasNewPasswordVote(const FormPredictions& form) {
if (!base::FeatureList::IsEnabled( if (!base::FeatureList::IsEnabled(
password_manager::features:: password_manager::features::
KEnablePasswordGenerationForClearTextFields)) KEnablePasswordGenerationForClearTextFields))
return false; return false;
for (const auto& field : form) { for (const auto& field : form.fields) {
if (field->server_type() == autofill::ACCOUNT_CREATION_PASSWORD || if (field.type == ACCOUNT_CREATION_PASSWORD || field.type == NEW_PASSWORD)
field->server_type() == autofill::NEW_PASSWORD) {
return true; return true;
}
} }
return false; return false;
} }
// Adds predictions to |predictions->fields| if |field_info_manager| has
// predictions for corresponding fields. Predictions from |field_info_manager|
// have priority over server predictions.
void AddLocallySavedPredictions(FieldInfoManager* field_info_manager,
FormPredictions* predictions) {
DCHECK(predictions);
if (!field_info_manager)
return;
for (PasswordFieldPrediction& field : predictions->fields) {
auto local_prediction = field_info_manager->GetFieldType(
predictions->form_signature, field.signature);
if (local_prediction != SINGLE_USERNAME && local_prediction != NOT_USERNAME)
continue;
field.type = local_prediction;
}
}
} // namespace } // namespace
// static // static
...@@ -962,32 +983,38 @@ void PasswordManager::ProcessAutofillPredictions( ...@@ -962,32 +983,38 @@ void PasswordManager::ProcessAutofillPredictions(
int driver_id = driver ? driver->GetId() : 0; int driver_id = driver ? driver->GetId() : 0;
predictions_[form->form_signature()] = predictions_[form->form_signature()] =
ConvertToFormPredictions(driver_id, *form); ConvertToFormPredictions(driver_id, *form);
AddLocallySavedPredictions(client_->GetFieldInfoManager(),
&predictions_[form->form_signature()]);
} }
for (auto& manager : form_managers_)
manager->ProcessServerPredictions(predictions_);
// Create form managers for non-password forms with single usernames. // Create form managers for non-password forms if |predictions_| has evidence
// that these forms are password related.
for (const FormStructure* form : forms) { for (const FormStructure* form : forms) {
if (logger) if (logger)
logger->LogFormStructure(Logger::STRING_SERVER_PREDICTIONS, *form); logger->LogFormStructure(Logger::STRING_SERVER_PREDICTIONS, *form);
if (FindMatchedManagerByRendererId(form->unique_renderer_id(),
form_managers_, driver)) {
// The form manager is already created.
continue;
}
if (form->has_password_field()) if (form->has_password_field())
continue; continue;
const FormPredictions* form_predictions =
&predictions_[form->form_signature()];
// Do not skip the form if it either contains a field for the Username // Do not skip the form if it either contains a field for the Username
// first flow or a clear-text password field. // first flow or a clear-text password field.
if (!(HasSingleUsernameVote(*form) || HasNewPasswordVote(*form))) if (!(HasSingleUsernameVote(*form_predictions) ||
continue; HasNewPasswordVote(*form_predictions))) {
if (FindMatchedManagerByRendererId(form->unique_renderer_id(),
form_managers_, driver)) {
// The form manager is already created.
continue; continue;
} }
FormData form_data = form->ToFormData(); CreateFormManager(driver, form->ToFormData());
auto* manager = CreateFormManager(driver, form_data);
manager->ProcessServerPredictions(predictions_);
} }
for (auto& manager : form_managers_)
manager->ProcessServerPredictions(predictions_);
} }
PasswordFormManager* PasswordManager::GetSubmittedManager() const { PasswordFormManager* PasswordManager::GetSubmittedManager() const {
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/form_field_data.h"
#include "components/password_manager/core/browser/field_info_manager.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h" #include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_check.h" #include "components/password_manager/core/browser/leak_detection/leak_detection_check.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory.h" #include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory.h"
...@@ -60,6 +61,7 @@ using autofill::FormFieldData; ...@@ -60,6 +61,7 @@ using autofill::FormFieldData;
using autofill::FormStructure; using autofill::FormStructure;
using autofill::PasswordForm; using autofill::PasswordForm;
using autofill::PasswordFormFillData; using autofill::PasswordFormFillData;
using autofill::ServerFieldType;
using autofill::mojom::PasswordFormFieldPredictionType; using autofill::mojom::PasswordFormFieldPredictionType;
using base::ASCIIToUTF16; using base::ASCIIToUTF16;
using base::Feature; using base::Feature;
...@@ -161,6 +163,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient { ...@@ -161,6 +163,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
MOCK_METHOD0(GetMetricsRecorder, PasswordManagerMetricsRecorder*()); MOCK_METHOD0(GetMetricsRecorder, PasswordManagerMetricsRecorder*());
MOCK_CONST_METHOD0(IsNewTabPage, bool()); MOCK_CONST_METHOD0(IsNewTabPage, bool());
MOCK_CONST_METHOD0(GetPasswordSyncState, SyncState()); MOCK_CONST_METHOD0(GetPasswordSyncState, SyncState());
MOCK_CONST_METHOD0(GetFieldInfoManager, FieldInfoManager*());
// Workaround for std::unique_ptr<> lacking a copy constructor. // Workaround for std::unique_ptr<> lacking a copy constructor.
bool PromptUserToSaveOrUpdatePassword( bool PromptUserToSaveOrUpdatePassword(
...@@ -287,6 +290,12 @@ void SetUniqueIdIfNeeded(FormData* form) { ...@@ -287,6 +290,12 @@ void SetUniqueIdIfNeeded(FormData* form) {
#endif #endif
} }
class MockFieldInfoManager : public FieldInfoManager {
public:
MOCK_METHOD3(AddFieldType, void(uint64_t, uint32_t, ServerFieldType));
MOCK_CONST_METHOD2(GetFieldType, ServerFieldType(uint64_t, uint32_t));
};
} // namespace } // namespace
class PasswordManagerTest : public testing::Test { class PasswordManagerTest : public testing::Test {
...@@ -3306,7 +3315,7 @@ TEST_F(PasswordManagerTest, UsernameFirstFlow) { ...@@ -3306,7 +3315,7 @@ TEST_F(PasswordManagerTest, UsernameFirstFlow) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)) EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save))); .WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
// Simlates successful submission. // Simulates successful submission.
manager()->OnPasswordFormsRendered(&driver_, {} /* observed */, true); manager()->OnPasswordFormsRendered(&driver_, {} /* observed */, true);
// Simulate saving the form, as if the info bar was accepted. // Simulate saving the form, as if the info bar was accepted.
...@@ -3319,6 +3328,37 @@ TEST_F(PasswordManagerTest, UsernameFirstFlow) { ...@@ -3319,6 +3328,37 @@ TEST_F(PasswordManagerTest, UsernameFirstFlow) {
EXPECT_EQ(password, saved_form.password_value); EXPECT_EQ(password, saved_form.password_value);
} }
// Checks that username is saved on username first flow.
TEST_F(PasswordManagerTest, UsernameFirstFlowFillingLocalPredictions) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUsernameFirstFlow);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(MakeSavedForm())));
manager()->OnPasswordFormsParsed(&driver_, {} /* observed */);
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
constexpr uint32_t kFieldRendererId = 1;
FormData non_password_form;
FormFieldData field;
field.form_control_type = "text";
field.unique_renderer_id = kFieldRendererId;
non_password_form.fields.push_back(field);
FormStructure form_structure(non_password_form);
MockFieldInfoManager mock_field_manager;
ON_CALL(client_, GetFieldInfoManager())
.WillByDefault(Return(&mock_field_manager));
EXPECT_CALL(mock_field_manager, GetFieldType(_, _))
.WillOnce(Return(autofill::SINGLE_USERNAME));
EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->ProcessAutofillPredictions(&driver_, {&form_structure});
}
TEST_F(PasswordManagerTest, FormSubmittedOnMainFrame) { TEST_F(PasswordManagerTest, FormSubmittedOnMainFrame) {
EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true)); EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
EXPECT_CALL(*store_, GetLogins(_, _)) EXPECT_CALL(*store_, GetLogins(_, _))
......
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