0

[Passwords] Fix for showing the Save prompt after deleting the password

Bug: 1069376
Change-Id: Ied89b907d20859ea44ea4611f0705a9cd722a395
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2148602
Reviewed-by: Colin Blundell <blundell@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Vadym Doroshenko  <dvadym@chromium.org>
Commit-Queue: Mohamed Amir Yosef <mamir@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805789}
This commit is contained in:
Mohamed Amir Yosef
2020-09-10 16:37:55 +00:00
committed by Commit Bot
parent 9c6535819d
commit 090e52a01b
19 changed files with 185 additions and 172 deletions

@ -67,16 +67,12 @@ void FakeMojoPasswordManagerDriver::CheckSafeBrowsingReputation(
called_check_safe_browsing_reputation_cnt_++;
}
void FakeMojoPasswordManagerDriver::ShowManualFallbackForSaving(
void FakeMojoPasswordManagerDriver::InformAboutUserInput(
const autofill::FormData& form_data) {
called_show_manual_fallback_for_saving_count_++;
called_inform_about_user_input_count_++;
form_data_maybe_submitted_ = form_data;
}
void FakeMojoPasswordManagerDriver::HideManualFallbackForSaving() {
called_show_manual_fallback_for_saving_count_ = 0;
}
void FakeMojoPasswordManagerDriver::FocusedInputChanged(
autofill::mojom::FocusedFieldType focused_field_type) {
last_focused_field_type_ = focused_field_type;

@ -111,8 +111,8 @@ class FakeMojoPasswordManagerDriver
return called_check_safe_browsing_reputation_cnt_;
}
int called_show_manual_fallback_for_saving_count() const {
return called_show_manual_fallback_for_saving_count_;
int called_inform_about_user_input_count() const {
return called_inform_about_user_input_count_;
}
autofill::mojom::FocusedFieldType last_focused_field_type() const {
@ -143,9 +143,8 @@ class FakeMojoPasswordManagerDriver
void CheckSafeBrowsingReputation(const GURL& form_action,
const GURL& frame_url) override;
void ShowManualFallbackForSaving(
const autofill::FormData& form_data) override;
void HideManualFallbackForSaving() override;
void InformAboutUserInput(const autofill::FormData& form_data) override;
void FocusedInputChanged(
autofill::mojom::FocusedFieldType focused_field_type) override;
void LogFirstFillingResult(autofill::FormRendererId form_renderer_id,
@ -181,9 +180,8 @@ class FakeMojoPasswordManagerDriver
// Records number of times CheckSafeBrowsingReputation() gets called.
int called_check_safe_browsing_reputation_cnt_ = 0;
// Records the number of request to show manual fallback for password saving.
// If it is zero, the fallback is not available.
int called_show_manual_fallback_for_saving_count_ = 0;
// Records the number of request to inform about user input.
int called_inform_about_user_input_count_ = 0;
// Records the last focused field type that FocusedInputChanged() was called
// with.

@ -56,6 +56,7 @@
#include "third_party/blink/public/web/win/web_font_rendering.h"
#endif
using autofill::FormRendererId;
using autofill::FormTracker;
using autofill::mojom::FocusedFieldType;
using autofill::mojom::PasswordFormFieldPredictionType;
@ -461,8 +462,8 @@ class PasswordAutofillAgentTest : public ChromeRenderViewTest {
password_element_.UniqueRendererFormControlId());
WebFormElement form = password_element_.Form();
fill_data_.form_renderer_id =
form.IsNull() ? autofill::FormRendererId()
: autofill::FormRendererId(form.UniqueRendererFormId());
form.IsNull() ? FormRendererId()
: FormRendererId(form.UniqueRendererFormId());
}
void UpdateUsernameAndPasswordElements() {
@ -674,18 +675,18 @@ class PasswordAutofillAgentTest : public ChromeRenderViewTest {
<< "Some expected masks are missed in FormData";
}
autofill::FormRendererId GetFormUniqueRendererId(const WebString& form_id) {
FormRendererId GetFormUniqueRendererId(const WebString& form_id) {
WebLocalFrame* frame = GetMainFrame();
if (!frame)
return autofill::FormRendererId();
return FormRendererId();
WebFormElement web_form =
frame->GetDocument().GetElementById(form_id).To<WebFormElement>();
return autofill::FormRendererId(web_form.UniqueRendererFormId());
return FormRendererId(web_form.UniqueRendererFormId());
}
void ExpectFormDataWithUsernameAndPasswordsAndEvent(
const autofill::FormData& form_data,
autofill::FormRendererId form_renderer_id,
FormRendererId form_renderer_id,
const std::string& username_value,
const std::string& password_value,
const std::string& new_password_value,
@ -698,7 +699,7 @@ class PasswordAutofillAgentTest : public ChromeRenderViewTest {
}
void ExpectFormSubmittedWithUsernameAndPasswords(
autofill::FormRendererId form_renderer_id,
FormRendererId form_renderer_id,
const std::string& username_value,
const std::string& password_value,
const std::string& new_password_value) {
@ -712,7 +713,7 @@ class PasswordAutofillAgentTest : public ChromeRenderViewTest {
}
void ExpectSameDocumentNavigationWithUsernameAndPasswords(
autofill::FormRendererId form_renderer_id,
FormRendererId form_renderer_id,
const std::string& username_value,
const std::string& password_value,
const std::string& new_password_value,
@ -959,7 +960,7 @@ TEST_F(PasswordAutofillAgentTest, NoFillingOnSignupForm_NoMetrics) {
WebFormElement form_element =
document.GetElementById("LoginTestForm").To<WebFormElement>();
fill_data_.form_renderer_id =
autofill::FormRendererId(form_element.UniqueRendererFormId());
FormRendererId(form_element.UniqueRendererFormId());
SimulateOnFillPasswordForm(fill_data_);
histogram_tester_.ExpectTotalCount(
@ -1482,7 +1483,7 @@ TEST_F(PasswordAutofillAgentTest,
// Neither field should have been autocompleted.
CheckTextFieldsDOMState("user1", false, std::string(), false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(0, fake_driver_.called_inform_about_user_input_count());
// Only password field should be autocompleted.
EXPECT_TRUE(password_autofill_agent_->FillSuggestion(
@ -1490,7 +1491,7 @@ TEST_F(PasswordAutofillAgentTest,
ASCIIToUTF16(kAlicePassword)));
CheckTextFieldsDOMState("user1", false, kAlicePassword, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(1, fake_driver_.called_inform_about_user_input_count());
// Try Filling with a different password. Only password should be changed.
EXPECT_TRUE(password_autofill_agent_->FillSuggestion(
@ -1498,7 +1499,7 @@ TEST_F(PasswordAutofillAgentTest,
ASCIIToUTF16(kCarolPassword)));
CheckTextFieldsDOMState("user1", false, kCarolPassword, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(2, fake_driver_.called_inform_about_user_input_count());
}
// Tests that |FillSuggestion| properly fills the password if the username field
@ -1807,7 +1808,7 @@ TEST_F(PasswordAutofillAgentTest, DontTryToShowTouchToFillSignUpForm) {
WebFormElement form_element =
document.GetElementById("LoginTestForm").To<WebFormElement>();
fill_data_.form_renderer_id =
autofill::FormRendererId(form_element.UniqueRendererFormId());
FormRendererId(form_element.UniqueRendererFormId());
SimulateOnFillPasswordForm(fill_data_);
@ -2100,7 +2101,7 @@ TEST_F(PasswordAutofillAgentTest, NoCredentialsOnPasswordClick) {
// PasswordAutofillAgent can still remember the username and the password
// typed by the user.
TEST_F(PasswordAutofillAgentTest,
DISABLED_RememberLastNonEmptyUsernameAndPasswordOnSubmit_ScriptCleared) {
RememberLastNonEmptyUsernameAndPasswordOnSubmit_ScriptCleared) {
LoadHTML(kSignupFormHTML);
WebInputElement username_element = GetInputElementByID("random_info");
ASSERT_FALSE(username_element.IsNull());
@ -2128,8 +2129,9 @@ TEST_F(PasswordAutofillAgentTest,
// Observe that the PasswordAutofillAgent still remembered the last non-empty
// username and password and sent that to the browser.
ExpectFormSubmittedWithUsernameAndPasswords(GetFormUniqueRendererId("form"),
"username", "", "random");
ExpectFormSubmittedWithUsernameAndPasswords(
FormRendererId(username_element.Form().UniqueRendererFormId()),
"username", "", "random");
fake_driver_.form_data_submitted();
}
@ -2157,7 +2159,7 @@ TEST_F(PasswordAutofillAgentTest,
// Similar to RememberLastNonEmptyPasswordOnSubmit_ScriptCleared, but uses the
// new password instead of the current password.
TEST_F(PasswordAutofillAgentTest,
DISABLED_RememberLastNonEmptyUsernameAndPasswordOnSubmit_New) {
RememberLastNonEmptyUsernameAndPasswordOnSubmit_New) {
const char kNewPasswordFormHTML[] =
"<FORM name='LoginTestForm' action='http://www.bidule.com'>"
" <INPUT type='text' id='username' autocomplete='username'/>"
@ -2180,7 +2182,8 @@ TEST_F(PasswordAutofillAgentTest,
// Observe that the PasswordAutofillAgent still remembered the last non-empty
// password and sent that to the browser.
ExpectFormSubmittedWithUsernameAndPasswords(
GetFormUniqueRendererId("LoginTestForm"), "temp", "", "random");
FormRendererId(username_element_.Form().UniqueRendererFormId()), "temp",
"", "random");
}
// The user first accepts a suggestion, but then overwrites the password. This
@ -2812,7 +2815,7 @@ TEST_F(PasswordAutofillAgentTest, NoForm_PromptForAJAXSubmitWithoutNavigation) {
FireAjaxSucceeded();
ExpectSameDocumentNavigationWithUsernameAndPasswords(
autofill::FormRendererId(), "Bob", "mypassword", "",
FormRendererId(), "Bob", "mypassword", "",
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
@ -2836,7 +2839,7 @@ TEST_F(PasswordAutofillAgentTest,
base::RunLoop().RunUntilIdle();
ExpectSameDocumentNavigationWithUsernameAndPasswords(
autofill::FormRendererId(), "Bob", "mypassword", "",
FormRendererId(), "Bob", "mypassword", "",
SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR);
}
@ -2871,7 +2874,7 @@ TEST_F(PasswordAutofillAgentTest,
PromptForAJAXSubmitAfterDeletingParentElement) {
LoadHTML(kDivWrappedFormHTML);
UpdateUsernameAndPasswordElements();
autofill::FormRendererId renderer_id = GetFormUniqueRendererId("form");
FormRendererId renderer_id = GetFormUniqueRendererId("form");
SimulateUsernameTyping("Bob");
SimulatePasswordTyping("mypassword");
@ -3304,7 +3307,7 @@ TEST_F(PasswordAutofillAgentTest,
FireAjaxSucceeded();
ExpectSameDocumentNavigationWithUsernameAndPasswords(
autofill::FormRendererId(), "Alice", "mypassword", "",
FormRendererId(), "Alice", "mypassword", "",
SubmissionIndicatorEvent::XHR_SUCCEEDED);
}
}
@ -3330,7 +3333,7 @@ TEST_F(PasswordAutofillAgentTest,
base::RunLoop().RunUntilIdle();
ExpectSameDocumentNavigationWithUsernameAndPasswords(
autofill::FormRendererId(), "Alice", "mypassword", "",
FormRendererId(), "Alice", "mypassword", "",
SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR);
}
@ -3468,8 +3471,7 @@ TEST_F(PasswordAutofillAgentTest,
SameDocumentNavigationSubmissionUsernameIsEmpty) {
username_element_.SetValue(WebString());
SimulatePasswordTyping("random");
autofill::FormRendererId renderer_id =
GetFormUniqueRendererId("LoginTestForm");
FormRendererId renderer_id = GetFormUniqueRendererId("LoginTestForm");
// Simulate that JavaScript removes the submitted form from DOM. That means
// that a submission was successful.
@ -3557,42 +3559,41 @@ TEST_F(PasswordAutofillAgentTest, NoForm_MultipleAJAXEventsWithoutSubmission) {
TEST_F(PasswordAutofillAgentTest, ManualFallbackForSaving) {
EXPECT_CALL(fake_pw_client_, PresaveGeneratedPassword(_, _)).Times(0);
// The users enters a username. No password - no fallback.
// The users enters a username. Inform the driver regardless.
SimulateUsernameTyping(kUsernameName);
EXPECT_EQ(0, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(1, fake_driver_.called_inform_about_user_input_count());
// The user enters a password.
SimulatePasswordTyping(kPasswordName);
// SimulateUsernameTyping/SimulatePasswordTyping calls
// PasswordAutofillAgent::UpdateStateForTextChange only once.
EXPECT_EQ(1, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(2, fake_driver_.called_inform_about_user_input_count());
// Remove one character from the password value.
SimulateUserTypingASCIICharacter(ui::VKEY_BACK, true);
EXPECT_EQ(2, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(3, fake_driver_.called_inform_about_user_input_count());
// Add one character to the username value.
SetFocused(username_element_);
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(3, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(4, fake_driver_.called_inform_about_user_input_count());
// Remove username value.
SimulateUsernameTyping("");
EXPECT_EQ(4, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(5, fake_driver_.called_inform_about_user_input_count());
// Change the password. Despite of empty username the fallback is still
// there.
// Change the password.
SetFocused(password_element_);
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(5, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(6, fake_driver_.called_inform_about_user_input_count());
// Remove password value. The fallback should be disabled.
// Remove password value. Inform the driver too.
SimulatePasswordTyping("");
EXPECT_EQ(0, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(7, fake_driver_.called_inform_about_user_input_count());
// The user enters new password. Show the fallback again.
// The user enters new password.
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(1, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(8, fake_driver_.called_inform_about_user_input_count());
}
TEST_F(PasswordAutofillAgentTest, ManualFallbackForSaving_PasswordChangeForm) {
@ -3600,37 +3601,37 @@ TEST_F(PasswordAutofillAgentTest, ManualFallbackForSaving_PasswordChangeForm) {
UpdateUrlForHTML(kPasswordChangeFormHTML);
UpdateUsernameAndPasswordElements();
// No password to save yet - no fallback.
// No password to save yet - still we should inform the driver.
SimulateUsernameTyping(kUsernameName);
EXPECT_EQ(0, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(1, fake_driver_.called_inform_about_user_input_count());
// The user enters in the current password field. The fallback should be
// available to save the entered value.
SimulatePasswordTyping(kPasswordName);
// SimulateUsernameTyping/SimulatePasswordTyping calls
// PasswordAutofillAgent::UpdateStateForTextChange only once.
EXPECT_EQ(1, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(2, fake_driver_.called_inform_about_user_input_count());
// The user types into the new password field. The fallback should be updated.
// The user types into the new password field. Inform the driver.
WebInputElement new_password = GetInputElementByID("newpassword");
ASSERT_FALSE(new_password.IsNull());
SetFocused(new_password);
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(2, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(3, fake_driver_.called_inform_about_user_input_count());
// Edits of the confirmation password field trigger fallback updates.
// Edits of the confirmation password field trigger informing the driver.
WebInputElement confirmation_password =
GetInputElementByID("confirmpassword");
ASSERT_FALSE(confirmation_password.IsNull());
SetFocused(confirmation_password);
SimulateUserTypingASCIICharacter('a', true);
EXPECT_EQ(3, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(4, fake_driver_.called_inform_about_user_input_count());
// Clear all password fields. The fallback should be disabled.
// Clear all password fields. The driver should be informed.
SimulatePasswordTyping("");
SimulateUserInputChangeForElement(&new_password, "");
SimulateUserInputChangeForElement(&confirmation_password, "");
EXPECT_EQ(0, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(5, fake_driver_.called_inform_about_user_input_count());
}
// Tests that information about Gaia reauthentication form is sent to the

@ -895,21 +895,20 @@ TEST_F(PasswordGenerationAgentTest, FallbackForSaving) {
LoadHTMLWithUserGesture(kAccountCreationFormHTML);
SimulateElementRightClick("first_password");
SelectGenerationFallbackAndExpect(true);
EXPECT_EQ(0, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(0, fake_driver_.called_inform_about_user_input_count());
base::string16 password = base::ASCIIToUTF16("random_password");
EXPECT_CALL(fake_pw_client_, PresaveGeneratedPassword(_, Eq(password)))
.WillOnce(testing::InvokeWithoutArgs([this]() {
// Make sure that generation event was propagated to the browser before
// the fallback showing. Otherwise, the fallback for saving provides a
// save bubble instead of a confirmation bubble.
EXPECT_EQ(0,
fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(0, fake_driver_.called_inform_about_user_input_count());
}));
password_generation_->GeneratedPasswordAccepted(password);
fake_driver_.Flush();
// Two fallback requests are expected because generation changes either new
// password and confirmation fields.
EXPECT_EQ(2, fake_driver_.called_show_manual_fallback_for_saving_count());
EXPECT_EQ(2, fake_driver_.called_inform_about_user_input_count());
}
TEST_F(PasswordGenerationAgentTest, FormClassifierDisabled) {

@ -96,13 +96,10 @@ interface PasswordManagerDriver {
// Notification that this password form was submitted by the user.
PasswordFormSubmitted(FormData form_data);
// Notification that a user starts typing in password fields and the omnibox
// icon with anchored save/update prompt should be available.
ShowManualFallbackForSaving(FormData form_data);
// Notification that there is no user input in password fields and the
// save/update prompt anchored to the omnibox icon should be removed.
HideManualFallbackForSaving();
// Notification that a user has modified a password field. This is fired both
// when typing new characters and deleting characters, so the password field
// in `form_data` may or may not be empty
InformAboutUserInput(FormData form_data);
// Notification that same-document navigation happened. We use this as a
// signal for successful login.

@ -33,6 +33,7 @@ class FormTracker : public content::RenderFrameObserver {
SELECT_CHANGED,
};
// TODO(crbug.com/1126017): Find a better name for this method.
// Invoked when form needs to be saved because of |source|, |element| is
// valid if the callback caused by source other than
// WILL_SEND_SUBMIT_EVENT, |form| is valid for the callback caused by

@ -356,17 +356,6 @@ bool FormHasPasswordField(const FormData& form) {
return false;
}
// Whether any of the fields in |form| is a non-empty password field.
bool FormHasNonEmptyPasswordField(const FormData& form) {
for (const auto& field : form.fields) {
if (field.IsPasswordInputElement()) {
if (!field.value.empty() || !field.typed_value.empty())
return true;
}
}
return false;
}
void AnnotateFieldWithParsingResult(WebDocument doc,
FieldRendererId renderer_id,
const std::string& text) {
@ -564,7 +553,7 @@ void PasswordAutofillAgent::UpdateStateForTextChange(
field_data_manager_->UpdateFieldDataMap(element_id, element_value,
FieldPropertiesFlags::kUserTyped);
ProvisionallySavePassword(element.Form(), element, RESTRICTION_NONE);
InformBrowserAboutUserInput(element.Form(), element);
if (element.IsPasswordFieldForAutofill()) {
auto iter = password_to_username_.find(element);
@ -659,8 +648,7 @@ void PasswordAutofillAgent::FillPasswordFieldAndSave(
DCHECK(password_input);
DCHECK(password_input->IsPasswordFieldForAutofill());
FillField(password_input, credential);
ProvisionallySavePassword(password_input->Form(), *password_input,
RESTRICTION_NONE);
InformBrowserAboutUserInput(password_input->Form(), *password_input);
}
bool PasswordAutofillAgent::PreviewSuggestion(
@ -1454,29 +1442,23 @@ void PasswordAutofillAgent::ClearPreview(WebInputElement* username,
password->SetAutofillState(password_autofill_state_);
}
}
void PasswordAutofillAgent::ProvisionallySavePassword(
void PasswordAutofillAgent::InformBrowserAboutUserInput(
const WebFormElement& form,
const WebInputElement& element,
ProvisionallySaveRestriction restriction) {
const WebInputElement& element) {
DCHECK(!form.IsNull() || !element.IsNull());
if (!FrameCanAccessPasswordManager())
return;
SetLastUpdatedFormAndField(form, element);
std::unique_ptr<FormData> form_data =
(form.IsNull() ? GetFormDataFromUnownedInputElements()
: GetFormDataFromWebForm(form));
form.IsNull() ? GetFormDataFromUnownedInputElements()
: GetFormDataFromWebForm(form);
if (!form_data)
return;
bool has_password = FormHasNonEmptyPasswordField(*form_data);
if (restriction == RESTRICTION_NON_EMPTY_PASSWORD && !has_password)
if (!FormHasPasswordField(*form_data))
return;
if (!FrameCanAccessPasswordManager())
return;
if (has_password)
GetPasswordManagerDriver()->ShowManualFallbackForSaving(*form_data);
else
GetPasswordManagerDriver()->HideManualFallbackForSaving();
GetPasswordManagerDriver()->InformAboutUserInput(*form_data);
browser_has_form_to_process_ = true;
}
@ -1639,8 +1621,7 @@ void PasswordAutofillAgent::OnProvisionallySaveForm(
}
DCHECK_EQ(ElementChangeSource::WILL_SEND_SUBMIT_EVENT, source);
ProvisionallySavePassword(form, input_element,
RESTRICTION_NON_EMPTY_PASSWORD);
InformBrowserAboutUserInput(form, input_element);
}
void PasswordAutofillAgent::OnFormSubmitted(const WebFormElement& form) {

@ -243,12 +243,6 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
private:
using OnPasswordField = util::StrongAlias<class OnPasswordFieldTag, bool>;
// Ways to restrict which passwords are saved in ProvisionallySavePassword.
enum ProvisionallySaveRestriction {
RESTRICTION_NONE,
RESTRICTION_NON_EMPTY_PASSWORD
};
// Enumeration representing possible Touch To Fill states. This is used to
// make sure that Touch To Fill will only be shown in response to the first
// password form focus during a frame's life time and to suppress the soft
@ -397,14 +391,12 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
void FillPasswordFieldAndSave(blink::WebInputElement* password_input,
const base::string16& credential);
// Saves |form| and |input| in |provisionally_saved_form_|, as long as it
// satisfies |restriction|. |form| and |input| are the elements user has just
// been interacting with before the form save. |form| or |input| can be null
// but not both at the same time. For example: if the form is unowned, |form|
// will be null; if the user has submitted the form, |input| will be null.
void ProvisionallySavePassword(const blink::WebFormElement& form,
const blink::WebInputElement& input,
ProvisionallySaveRestriction restriction);
// |form| and |input| are the elements user has just been interacting with
// before the form save. |form| or |input| can be null but not both at the
// same time. For example: if the form is unowned, |form| will be null; if the
// user has submitted the form, |input| will be null.
void InformBrowserAboutUserInput(const blink::WebFormElement& form,
const blink::WebInputElement& input);
// This function attempts to fill |username_element| and |password_element|
// with values from |fill_data|. The |username_element| and |password_element|

@ -50,10 +50,7 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver {
void PasswordFormSubmitted(const autofill::FormData& form_data) override {}
void ShowManualFallbackForSaving(
const autofill::FormData& form_data) override {}
void HideManualFallbackForSaving() override {}
void InformAboutUserInput(const autofill::FormData& form_data) override {}
void SameDocumentNavigation(autofill::mojom::SubmissionIndicatorEvent
submission_indication_event) override {}

@ -142,6 +142,16 @@ bool FormData::IdentityComparator::operator()(const FormData& a,
FormFieldData::IdentityComparator());
}
bool FormHasNonEmptyPasswordField(const FormData& form) {
for (const auto& field : form.fields) {
if (field.IsPasswordInputElement()) {
if (!field.value.empty() || !field.typed_value.empty())
return true;
}
}
return false;
}
std::ostream& operator<<(std::ostream& os, const FormData& form) {
os << base::UTF16ToUTF8(form.name) << " " << form.url << " " << form.action
<< " " << form.main_frame_origin << " " << form.is_form_tag << " "

@ -120,6 +120,9 @@ struct FormData {
#endif
};
// Whether any of the fields in |form| is a non-empty password field.
bool FormHasNonEmptyPasswordField(const FormData& form);
// For testing.
std::ostream& operator<<(std::ostream& os, const FormData& form);

@ -34,7 +34,7 @@ enum class BadMessageReason {
CPMD_BAD_ORIGIN_PASSWORD_NO_LONGER_GENERATED = 6,
CPMD_BAD_ORIGIN_PRESAVE_GENERATED_PASSWORD = 7,
CPMD_BAD_ORIGIN_SAVE_GENERATION_FIELD_DETECTED_BY_CLASSIFIER = 8,
CPMD_BAD_ORIGIN_SHOW_FALLBACK_FOR_SAVING = 9,
CPMD_BAD_ORIGIN_UPON_USER_INPUT_CHANGE = 9,
CPMD_BAD_ORIGIN_AUTOMATIC_GENERATION_STATUS_CHANGED = 10,
CPMD_BAD_ORIGIN_SHOW_MANUAL_PASSWORD_GENERATION_POPUP = 11,
CPMD_BAD_ORIGIN_SHOW_PASSWORD_EDITING_POPUP = 12,

@ -246,15 +246,16 @@ void ContentPasswordManagerDriver::PasswordFormSubmitted(
LogSiteIsolationMetricsForSubmittedForm(render_frame_host_);
}
void ContentPasswordManagerDriver::ShowManualFallbackForSaving(
void ContentPasswordManagerDriver::InformAboutUserInput(
const autofill::FormData& form_data) {
if (!password_manager::bad_message::CheckChildProcessSecurityPolicyForURL(
render_frame_host_, form_data.url,
BadMessageReason::CPMD_BAD_ORIGIN_SHOW_FALLBACK_FOR_SAVING))
BadMessageReason::CPMD_BAD_ORIGIN_UPON_USER_INPUT_CHANGE))
return;
GetPasswordManager()->ShowManualFallbackForSaving(this, form_data);
GetPasswordManager()->OnInformAboutUserInput(this, form_data);
if (client_->IsIsolationForPasswordSitesEnabled()) {
if (FormHasNonEmptyPasswordField(form_data) &&
client_->IsIsolationForPasswordSitesEnabled()) {
// This function signals that a password field has been filled (whether by
// the user, JS, autofill, or some other means) or a password form has been
// submitted. Use this as a heuristic to start site-isolating the form's
@ -266,10 +267,6 @@ void ContentPasswordManagerDriver::ShowManualFallbackForSaving(
}
}
void ContentPasswordManagerDriver::HideManualFallbackForSaving() {
GetPasswordManager()->HideManualFallbackForSaving();
}
void ContentPasswordManagerDriver::SameDocumentNavigation(
autofill::mojom::SubmissionIndicatorEvent submission_indication_event) {
GetPasswordManager()->OnPasswordFormSubmittedNoChecks(

@ -100,9 +100,7 @@ class ContentPasswordManagerDriver
const std::vector<autofill::FormData>& visible_forms_data,
bool did_stop_loading) override;
void PasswordFormSubmitted(const autofill::FormData& form_data) override;
void ShowManualFallbackForSaving(
const autofill::FormData& form_data) override;
void HideManualFallbackForSaving() override;
void InformAboutUserInput(const autofill::FormData& form_data) override;
void SameDocumentNavigation(autofill::mojom::SubmissionIndicatorEvent
submission_indication_event) override;
void RecordSavePasswordProgress(const std::string& log) override;

@ -488,8 +488,8 @@ void PasswordManager::OnUserModifiedNonPasswordField(
renderer_id, value, base::Time::Now(), driver_id);
}
void PasswordManager::ShowManualFallbackForSaving(PasswordManagerDriver* driver,
const FormData& form_data) {
void PasswordManager::OnInformAboutUserInput(PasswordManagerDriver* driver,
const FormData& form_data) {
PasswordFormManager* manager = ProvisionallySaveForm(form_data, driver, true);
if (manager && form_data.is_gaia_with_skip_save_password_form) {
@ -504,7 +504,7 @@ void PasswordManager::ShowManualFallbackForSaving(PasswordManagerDriver* driver,
if (client_ && client_->GetMetricsRecorder())
client_->GetMetricsRecorder()->RecordFormManagerAvailable(availability);
ShowManualFallbackForSavingImpl(manager, form_data);
ShowManualFallbackForSaving(manager, form_data);
}
void PasswordManager::HideManualFallbackForSaving() {
@ -724,8 +724,7 @@ void PasswordManager::UpdateStateOnUserInput(
if (manager->UpdateStateOnUserInput(form_id, field_id, field_value)) {
ProvisionallySaveForm(*manager->observed_form(), driver, true);
if (manager->is_submitted() && !manager->HasGeneratedPassword()) {
ShowManualFallbackForSavingImpl(manager.get(),
*manager->observed_form());
ShowManualFallbackForSaving(manager.get(), *manager->observed_form());
} else {
HideManualFallbackForSaving();
}
@ -1197,10 +1196,19 @@ void PasswordManager::TryToFindPredictionsToPossibleUsernameData() {
}
}
void PasswordManager::ShowManualFallbackForSavingImpl(
void PasswordManager::ShowManualFallbackForSaving(
PasswordFormManager* form_manager,
const FormData& form_data) {
if (!form_manager || !form_manager->is_submitted())
// Where `form_manager` is nullptr, make sure the manual fallback isn't
// shown. One scenario where this is relevant is when the user inputs some
// password and then removes it. Upon removing the password, the
// `form_manager` will become nullptr.
if (!form_manager) {
HideManualFallbackForSaving();
return;
}
if (!form_manager->is_submitted())
return;
if (!client_->GetProfilePasswordStore()->IsAbleToSavePasswords() ||

@ -166,10 +166,10 @@ class PasswordManager : public PasswordManagerInterface {
autofill::FieldRendererId renderer_id,
const base::string16& value);
// Handles a request to show manual fallback for password saving, i.e. the
// omnibox icon with the anchored hidden prompt.
void ShowManualFallbackForSaving(PasswordManagerDriver* driver,
const autofill::FormData& form_data);
// Handles user input and decides whether to show manual fallback for password
// saving, i.e. the omnibox icon with the anchored hidden prompt.
void OnInformAboutUserInput(PasswordManagerDriver* driver,
const autofill::FormData& form_data);
// Handles a request to hide manual fallback for password saving.
void HideManualFallbackForSaving();
@ -320,8 +320,8 @@ class PasswordManager : public PasswordManagerInterface {
// Handles a request to show manual fallback for password saving, i.e. the
// omnibox icon with the anchored hidden prompt. todo
void ShowManualFallbackForSavingImpl(PasswordFormManager* form_manager,
const autofill::FormData& form_data);
void ShowManualFallbackForSaving(PasswordFormManager* form_manager,
const autofill::FormData& form_data);
// Returns the timeout for the disabling Password Manager's prompts.
base::TimeDelta GetTimeoutForDisablingPrompts();

@ -913,7 +913,7 @@ TEST_P(PasswordManagerTest, DontSaveAlreadySavedCredential) {
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, incomplete_match.form_data);
manager()->OnInformAboutUserInput(&driver_, incomplete_match.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(incomplete_match));
@ -924,7 +924,7 @@ TEST_P(PasswordManagerTest, DontSaveAlreadySavedCredential) {
// because the credential is already in the store.
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true)).Times(0);
EXPECT_CALL(client_, HideManualFallbackForSaving());
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
// The user submits the form. No prompt should pop up. The credential is
// updated in background.
@ -939,6 +939,42 @@ TEST_P(PasswordManagerTest, DontSaveAlreadySavedCredential) {
user_action_tester.GetActionCount("PasswordManager_LoginPassed"));
}
TEST_P(PasswordManagerTest, DoNotSaveWhenUserDeletesPassword) {
PasswordForm form(MakeSimpleForm());
PasswordForm stored_form = form;
stored_form.password_value = ASCIIToUTF16("old_password");
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(store_.get(), stored_form)));
ON_CALL(client_, IsSavingAndFillingEnabled(form.url))
.WillByDefault(Return(true));
std::vector<FormData> observed = {form.form_data};
manager()->OnPasswordFormsParsed(&driver_, observed);
// The user is typing a credential manually, the fallback should be available.
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnInformAboutUserInput(&driver_, form.form_data);
ASSERT_TRUE(form_manager_to_save);
// The user deletes the password, no manuall fallback should be shown.
PasswordForm empty_password_form(form);
empty_password_form.password_value.clear();
empty_password_form.form_data.fields[1].value.clear();
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr).Times(0);
EXPECT_CALL(client_, HideManualFallbackForSaving());
manager()->OnInformAboutUserInput(&driver_, empty_password_form.form_data);
// The user submits the form. No prompt should pop up.
OnPasswordFormSubmitted(empty_password_form.form_data);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr).Times(0);
observed.clear();
manager()->DidNavigateMainFrame(true);
manager()->OnPasswordFormsParsed(&driver_, observed);
}
// Tests that on Chrome sign-in form credentials are not saved.
TEST_P(PasswordManagerTest, DoNotSaveOnChromeSignInForm) {
FormData form_data(MakeSimpleFormData());
@ -957,7 +993,7 @@ TEST_P(PasswordManagerTest, DoNotSaveOnChromeSignInForm) {
FormData typed_credentials(form_data);
typed_credentials.fields[1].value = ASCIIToUTF16("pw");
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _)).Times(0);
manager()->ShowManualFallbackForSaving(&driver_, form_data);
manager()->OnInformAboutUserInput(&driver_, form_data);
// The user submits the form. No prompt should pop up.
OnPasswordFormSubmitted(form_data);
@ -992,7 +1028,7 @@ TEST_P(PasswordManagerTest,
EXPECT_CALL(client_, HideManualFallbackForSaving());
// The call to manual fallback with |form| equal to already saved should close
// the fallback, but it should not prevent sending metrics.
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, UpdateLogin(_));
@ -1812,7 +1848,7 @@ TEST_P(PasswordManagerTest, SameDocumentNavigation) {
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
@ -1843,7 +1879,7 @@ TEST_P(PasswordManagerTest, SameDocumentBlockedSite) {
.WillRepeatedly(Return(true));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
@ -2466,7 +2502,7 @@ TEST_P(PasswordManagerTest, ManualFallbackForSaving) {
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
@ -2476,7 +2512,7 @@ TEST_P(PasswordManagerTest, ManualFallbackForSaving) {
new_form.form_data.fields[0].value = new_form.username_value;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, new_form.form_data);
manager()->OnInformAboutUserInput(&driver_, new_form.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(new_form));
@ -2516,7 +2552,7 @@ TEST_P(PasswordManagerTest, ManualFallbackForSaving_SlowBackend) {
// There is no response from the store. Don't show the fallback.
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _)).Times(0);
manager()->ShowManualFallbackForSaving(&driver_, form_data);
manager()->OnInformAboutUserInput(&driver_, form_data);
// The storage responded. The fallback can be shown.
ASSERT_TRUE(store_consumer);
@ -2525,7 +2561,7 @@ TEST_P(PasswordManagerTest, ManualFallbackForSaving_SlowBackend) {
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, form_data);
manager()->OnInformAboutUserInput(&driver_, form_data);
}
TEST_P(PasswordManagerTest, ManualFallbackForSaving_GeneratedPassword) {
@ -2549,7 +2585,7 @@ TEST_P(PasswordManagerTest, ManualFallbackForSaving_GeneratedPassword) {
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPresaveGeneratedPassword(&driver_, form.form_data,
form.password_value);
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
@ -2560,7 +2596,7 @@ TEST_P(PasswordManagerTest, ManualFallbackForSaving_GeneratedPassword) {
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnPresaveGeneratedPassword(&driver_, form.form_data,
form.password_value);
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
// A user removes the generated password. The presaved password is removed,
// the fallback is disabled.
@ -2829,7 +2865,7 @@ TEST_P(PasswordManagerTest, ProcessingOtherSubmissionTypes) {
observed.push_back(form_data);
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
manager()->ShowManualFallbackForSaving(&driver_, form_data);
manager()->OnInformAboutUserInput(&driver_, form_data);
auto submitted_form_data = form_data;
submitted_form_data.fields[0].value = ASCIIToUTF16("username");
@ -2915,7 +2951,7 @@ TEST_P(PasswordManagerTest, ManualFallbackForSavingNewParser) {
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, true))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
@ -2925,7 +2961,7 @@ TEST_P(PasswordManagerTest, ManualFallbackForSavingNewParser) {
new_form.form_data.fields[0].value = new_form.username_value;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, new_form.form_data);
manager()->OnInformAboutUserInput(&driver_, new_form.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(new_form));
@ -3066,7 +3102,7 @@ TEST_P(PasswordManagerTest, SavingAfterUserTypingAndNavigation) {
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(form));
@ -3245,7 +3281,7 @@ TEST_P(PasswordManagerTest, ReportMissingFormManager) {
manager()->OnPasswordFormSubmitted(nullptr, form_data);
break;
case MissingFormManagerTestCase::Signal::Manual:
manager()->ShowManualFallbackForSaving(nullptr, form_data);
manager()->OnInformAboutUserInput(nullptr, form_data);
break;
case MissingFormManagerTestCase::Signal::None:
break;
@ -3355,7 +3391,7 @@ TEST_P(PasswordManagerTest, FillingAndSavingFallbacksOnNonPasswordForm) {
std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->ShowManualFallbackForSaving(&driver_, credit_card_form.form_data);
manager()->OnInformAboutUserInput(&driver_, credit_card_form.form_data);
ASSERT_TRUE(form_manager_to_save);
EXPECT_THAT(form_manager_to_save->GetPendingCredentials(),
FormMatches(credit_card_form));
@ -3677,7 +3713,7 @@ TEST_P(PasswordManagerTest, NoPromptAutofillAssistantManuallyCuratedScript) {
for (size_t i = 0; i < 2; i++) {
PasswordForm form(MakeSimpleForm());
manager()->OnPasswordFormsParsed(&driver_, {form.form_data});
manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
manager()->OnInformAboutUserInput(&driver_, form.form_data);
manager()->DidNavigateMainFrame(true /* form_may_be_submitted */);
manager()->OnPasswordFormsRendered(&driver_, {} /* observed */,
@ -3708,8 +3744,8 @@ TEST_P(PasswordManagerTest,
PasswordForm form2(MakeSimpleForm());
manager()->OnPasswordFormsParsed(&driver_,
{form1.form_data, form2.form_data});
manager()->ShowManualFallbackForSaving(&driver_, form1.form_data);
manager()->ShowManualFallbackForSaving(&driver_, form2.form_data);
manager()->OnInformAboutUserInput(&driver_, form1.form_data);
manager()->OnInformAboutUserInput(&driver_, form2.form_data);
// Simulate submission in different ways depending on whether
// |owned_submitted_form_manager_| should be set and |form_managers_| should

@ -6432,7 +6432,7 @@ Called by update_bad_message_reasons.py.-->
<int value="7" label="CPMD_BAD_ORIGIN_PRESAVE_GENERATED_PASSWORD"/>
<int value="8"
label="CPMD_BAD_ORIGIN_SAVE_GENERATION_FIELD_DETECTED_BY_CLASSIFIER"/>
<int value="9" label="CPMD_BAD_ORIGIN_SHOW_FALLBACK_FOR_SAVING"/>
<int value="9" label="CPMD_BAD_ORIGIN_UPON_USER_INPUT_CHANGE"/>
<int value="10" label="CPMD_BAD_ORIGIN_AUTOMATIC_GENERATION_STATUS_CHANGED"/>
<int value="11"
label="CPMD_BAD_ORIGIN_SHOW_MANUAL_PASSWORD_GENERATION_POPUP"/>

@ -38,16 +38,16 @@ class PasswordManagerDriverFactory::PasswordManagerDriver
const std::vector<autofill::FormData>& visible_forms_data,
bool did_stop_loading) override {}
void PasswordFormSubmitted(const autofill::FormData& form_data) override {}
void ShowManualFallbackForSaving(
const autofill::FormData& form_data) override {
void InformAboutUserInput(const autofill::FormData& form_data) override {
if (!password_manager::bad_message::CheckChildProcessSecurityPolicyForURL(
render_frame_host_, form_data.url,
password_manager::BadMessageReason::
CPMD_BAD_ORIGIN_SHOW_FALLBACK_FOR_SAVING)) {
CPMD_BAD_ORIGIN_UPON_USER_INPUT_CHANGE)) {
return;
}
if (site_isolation::SiteIsolationPolicy::
if (FormHasNonEmptyPasswordField(form_data) &&
site_isolation::SiteIsolationPolicy::
IsIsolationForPasswordSitesEnabled()) {
// This function signals that a password field has been filled (whether by
// the user, JS, autofill, or some other means) or a password form has
@ -59,7 +59,6 @@ class PasswordManagerDriverFactory::PasswordManagerDriver
form_data.url);
}
}
void HideManualFallbackForSaving() override {}
void SameDocumentNavigation(autofill::mojom::SubmissionIndicatorEvent
submission_indication_event) override {}
void RecordSavePasswordProgress(const std::string& log) override {}