Commit 774332ae authored by Vasilii Sukhanov's avatar Vasilii Sukhanov Committed by Commit Bot

Create a separate structure that tracks the current generation status for a

field in PasswordGenerationAgent.

Currently |generation_element_| has a double meaning. It's an element that
should trigger the automatic generation. However, if a manual generation is
triggered then it's overwritten by a password field. That's confusing.
The CL decouples the field that should trigger the automatic generation from
whatever is happening right now on a random password field.
In the future that will allow us to have multiple generation elements
simultaneously. For now it fixes 870220 where the manual generation on the
ambiguous field was suppressing the automatic generation on it.

Bug: 870220,852309
Change-Id: I6a3edd351e615289f6271fda5a45f6ff47e653af
Reviewed-on: https://chromium-review.googlesource.com/c/1310713Reviewed-by: default avatarVadym Doroshenko <dvadym@chromium.org>
Commit-Queue: Vasilii Sukhanov <vasilii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605668}
parent e22b4b7d
...@@ -254,23 +254,28 @@ IN_PROC_BROWSER_TEST_F(PasswordGenerationInteractiveTest, ...@@ -254,23 +254,28 @@ IN_PROC_BROWSER_TEST_F(PasswordGenerationInteractiveTest,
PopupShownManuallyAndPasswordErased) { PopupShownManuallyAndPasswordErased) {
NavigateToFile("/password/password_form.html"); NavigateToFile("/password/password_form.html");
FocusPasswordField(); // Focus the field that is not the first password field. The first one is
// considered "automatic" generation field.
ASSERT_TRUE(content::ExecuteScript(
WebContents(), "document.getElementById('password_redirect').focus()"));
// The same flow happens when user generates a password from the context menu. // The same flow happens when user generates a password from the context menu.
password_manager_util::UserTriggeredManualGenerationFromContextMenu( password_manager_util::UserTriggeredManualGenerationFromContextMenu(
ChromePasswordManagerClient::FromWebContents(WebContents())); ChromePasswordManagerClient::FromWebContents(WebContents()));
WaitForPopupStatusChange();
EXPECT_TRUE(GenerationPopupShowing()); EXPECT_TRUE(GenerationPopupShowing());
SendKeyToPopup(ui::VKEY_DOWN); SendKeyToPopup(ui::VKEY_DOWN);
SendKeyToPopup(ui::VKEY_RETURN); SendKeyToPopup(ui::VKEY_RETURN);
// Wait until the password is filled. // Wait until the password is filled.
WaitForNonEmptyFieldValue("password_field"); WaitForNonEmptyFieldValue("password_redirect");
// Re-focusing the password field should show the editing popup. // Re-focusing the password field should show the editing popup.
FocusPasswordField(); ASSERT_TRUE(content::ExecuteScript(
WebContents(), "document.getElementById('password_redirect').focus()"));
EXPECT_TRUE(EditingPopupShowing()); EXPECT_TRUE(EditingPopupShowing());
// Delete the password. The generation prompt should not be visible. // Delete the password. The generation prompt should not be visible.
SimulateUserDeletingFieldContent("password_field"); SimulateUserDeletingFieldContent("password_redirect");
WaitForPopupStatusChange(); WaitForPopupStatusChange();
EXPECT_FALSE(EditingPopupShowing()); EXPECT_FALSE(EditingPopupShowing());
EXPECT_FALSE(GenerationPopupShowing()); EXPECT_FALSE(GenerationPopupShowing());
......
...@@ -789,6 +789,23 @@ TEST_F(PasswordGenerationAgentTest, ManualGenerationNoFormTest) { ...@@ -789,6 +789,23 @@ TEST_F(PasswordGenerationAgentTest, ManualGenerationNoFormTest) {
ExpectManualGenerationAvailable("second_password", false); ExpectManualGenerationAvailable("second_password", false);
} }
TEST_F(PasswordGenerationAgentTest, ManualGenerationDoesntSuppressAutomatic) {
LoadHTMLWithUserGesture(kAccountCreationFormHTML);
SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(password_generation_,
GetMainFrame()->GetDocument(), 0, 1);
ExpectAutomaticGenerationAvailable("first_password", true);
// The browser may show a standard password dropdown with the "Generate"
// option. In this case manual generation is triggered.
password_generation_->UserTriggeredGeneratePassword();
// Move the focus away to somewhere.
FocusField("address");
// Moving the focus back should trigger the automatic generation again.
ExpectAutomaticGenerationAvailable("first_password", true);
}
TEST_F(PasswordGenerationAgentTest, PresavingGeneratedPassword) { TEST_F(PasswordGenerationAgentTest, PresavingGeneratedPassword) {
const struct { const struct {
const char* form; const char* form;
......
...@@ -170,26 +170,81 @@ void CopyElementValueToOtherInputElements( ...@@ -170,26 +170,81 @@ void CopyElementValueToOtherInputElements(
} // namespace } // namespace
PasswordGenerationAgent::AccountCreationFormData::AccountCreationFormData( // Contains information about a form for which generation is possible.
linked_ptr<PasswordForm> password_form, struct PasswordGenerationAgent::AccountCreationFormData {
std::vector<WebInputElement> passwords) PasswordForm form;
: form(password_form), password_elements(std::move(passwords)) {} std::vector<blink::WebInputElement> password_elements;
PasswordGenerationAgent::AccountCreationFormData::AccountCreationFormData( AccountCreationFormData(PasswordForm password_form,
const AccountCreationFormData& other) = default; std::vector<blink::WebInputElement> passwords)
: form(std::move(password_form)),
PasswordGenerationAgent::AccountCreationFormData::~AccountCreationFormData() {} password_elements(std::move(passwords)) {}
AccountCreationFormData(AccountCreationFormData&& rhs) = default;
~AccountCreationFormData() = default;
DISALLOW_COPY_AND_ASSIGN(AccountCreationFormData);
};
// Contains information about generation status for an element for the
// lifetime of the possible interaction.
struct PasswordGenerationAgent::GenerationItemInfo {
GenerationItemInfo(const AccountCreationFormData& creation_form_data,
const blink::WebInputElement& generation_element)
: generation_element_(generation_element) {
form_ = creation_form_data.form;
password_elements_ = creation_form_data.password_elements;
}
GenerationItemInfo(blink::WebInputElement generation_element,
PasswordForm form,
std::vector<blink::WebInputElement> password_elements)
: generation_element_(std::move(generation_element)),
form_(std::move(form)),
password_elements_(std::move(password_elements)) {}
~GenerationItemInfo() = default;
// Element where we want to trigger password generation UI.
blink::WebInputElement generation_element_;
// Password form for the generation element.
PasswordForm form_;
// All the password elements in the form.
std::vector<blink::WebInputElement> password_elements_;
// If the password field at |generation_element_| contains a generated
// password.
bool password_is_generated_ = false;
// True if the last password generation was manually triggered.
bool is_manually_triggered_ = false;
// True if a password was generated and the user edited it. Used for UMA
// stats.
bool password_edited_ = false;
// True if the generation popup was shown during this navigation. Used to
// track UMA stats per page visit rather than per display, since the former
// is more interesting.
// TODO(crbug.com/845458): Remove this or change the description of the
// logged event as calling AutomaticgenerationStatusChanged will no longer
// imply that a popup is shown. This could instead be logged with the
// metrics collected on the browser process.
bool generation_popup_shown_ = false;
// True if the editing popup was shown during this navigation. Used to track
// UMA stats per page rather than per display, since the former is more
// interesting.
bool editing_popup_shown_ = false;
DISALLOW_COPY_AND_ASSIGN(GenerationItemInfo);
};
PasswordGenerationAgent::PasswordGenerationAgent( PasswordGenerationAgent::PasswordGenerationAgent(
content::RenderFrame* render_frame, content::RenderFrame* render_frame,
PasswordAutofillAgent* password_agent, PasswordAutofillAgent* password_agent,
blink::AssociatedInterfaceRegistry* registry) blink::AssociatedInterfaceRegistry* registry)
: content::RenderFrameObserver(render_frame), : content::RenderFrameObserver(render_frame),
password_is_generated_(false),
is_manually_triggered_(false),
password_edited_(false),
generation_popup_shown_(false),
editing_popup_shown_(false),
enabled_(password_generation::IsPasswordGenerationEnabled()), enabled_(password_generation::IsPasswordGenerationEnabled()),
mark_generation_element_( mark_generation_element_(
base::CommandLine::ForCurrentProcess()->HasSwitch( base::CommandLine::ForCurrentProcess()->HasSwitch(
...@@ -201,7 +256,8 @@ PasswordGenerationAgent::PasswordGenerationAgent( ...@@ -201,7 +256,8 @@ PasswordGenerationAgent::PasswordGenerationAgent(
&PasswordGenerationAgent::BindRequest, base::Unretained(this))); &PasswordGenerationAgent::BindRequest, base::Unretained(this)));
password_agent_->SetPasswordGenerationAgent(this); password_agent_->SetPasswordGenerationAgent(this);
} }
PasswordGenerationAgent::~PasswordGenerationAgent() {}
PasswordGenerationAgent::~PasswordGenerationAgent() = default;
void PasswordGenerationAgent::BindRequest( void PasswordGenerationAgent::BindRequest(
mojom::PasswordGenerationAgentAssociatedRequest request) { mojom::PasswordGenerationAgentAssociatedRequest request) {
...@@ -213,58 +269,42 @@ void PasswordGenerationAgent::DidCommitProvisionalLoad( ...@@ -213,58 +269,42 @@ void PasswordGenerationAgent::DidCommitProvisionalLoad(
ui::PageTransition transition) { ui::PageTransition transition) {
if (is_same_document_navigation) if (is_same_document_navigation)
return; return;
password_is_generated_ = false;
generation_element_.Reset();
last_focused_password_element_.Reset();
}
void PasswordGenerationAgent::DidFinishDocumentLoad() {
// Update stats for main frame navigation. // Update stats for main frame navigation.
if (!render_frame()->GetWebFrame()->Parent()) { if (!render_frame()->GetWebFrame()->Parent()) {
// Log statistics after navigation so that we only log once per page. // Log statistics after navigation so that we only log once per page.
if (enabled_ || generation_form_data_) { if (automatic_generation_form_data_ &&
if (generation_form_data_ && !automatic_generation_form_data_->password_elements.empty()) {
!generation_form_data_->password_elements.empty()) { password_generation::LogPasswordGenerationEvent(
password_generation::LogPasswordGenerationEvent( password_generation::SIGN_UP_DETECTED);
password_generation::SIGN_UP_DETECTED); } else {
} else { password_generation::LogPasswordGenerationEvent(
password_generation::LogPasswordGenerationEvent( password_generation::NO_SIGN_UP_DETECTED);
password_generation::NO_SIGN_UP_DETECTED); }
} if (current_generation_item_) {
if (password_edited_) { if (current_generation_item_->password_edited_) {
password_generation::LogPasswordGenerationEvent( password_generation::LogPasswordGenerationEvent(
password_generation::PASSWORD_EDITED); password_generation::PASSWORD_EDITED);
} }
if (generation_popup_shown_) { if (current_generation_item_->generation_popup_shown_) {
password_generation::LogPasswordGenerationEvent( password_generation::LogPasswordGenerationEvent(
password_generation::GENERATION_POPUP_SHOWN); password_generation::GENERATION_POPUP_SHOWN);
} }
if (editing_popup_shown_) { if (current_generation_item_->editing_popup_shown_) {
password_generation::LogPasswordGenerationEvent( password_generation::LogPasswordGenerationEvent(
password_generation::EDITING_POPUP_SHOWN); password_generation::EDITING_POPUP_SHOWN);
} }
} }
// In every navigation, the IPC message sent by the password autofill
// manager to query whether the current form is blacklisted or not happens
// when the document load finishes, so we need to clear previous states
// here before we hear back from the browser. We only clear this state on
// main frame load as we don't want subframe loads to clear state that we
// have received from the main frame. Note that we assume there is only one
// account creation form, but there could be multiple password forms in
// each frame.
not_blacklisted_password_form_origins_.clear();
generation_enabled_forms_.clear();
generation_element_.Reset();
possible_account_creation_forms_.clear();
generation_form_data_.reset();
password_is_generated_ = false;
password_edited_ = false;
generation_popup_shown_ = false;
editing_popup_shown_ = false;
is_manually_triggered_ = false;
} }
possible_account_creation_forms_.clear();
not_blacklisted_password_form_origins_.clear();
generation_enabled_forms_.clear();
automatic_generation_form_data_.reset();
automatic_generation_element_.Reset();
current_generation_item_.reset();
last_focused_password_element_.Reset();
}
void PasswordGenerationAgent::DidFinishDocumentLoad() {
FindPossibleGenerationForm(); FindPossibleGenerationForm();
} }
...@@ -286,11 +326,14 @@ void PasswordGenerationAgent::OnDynamicFormsSeen() { ...@@ -286,11 +326,14 @@ void PasswordGenerationAgent::OnDynamicFormsSeen() {
void PasswordGenerationAgent::OnFieldAutofilled( void PasswordGenerationAgent::OnFieldAutofilled(
const WebInputElement& password_element) { const WebInputElement& password_element) {
if (password_is_generated_ && generation_element_ == password_element) { if (current_generation_item_ &&
current_generation_item_->password_is_generated_ &&
current_generation_item_->generation_element_ == password_element) {
password_generation::LogPasswordGenerationEvent( password_generation::LogPasswordGenerationEvent(
password_generation::PASSWORD_DELETED_BY_AUTOFILLING); password_generation::PASSWORD_DELETED_BY_AUTOFILLING);
PasswordNoLongerGenerated(); PasswordNoLongerGenerated();
generation_element_.SetShouldRevealPassword(false); current_generation_item_->generation_element_.SetShouldRevealPassword(
false);
} }
} }
...@@ -304,7 +347,7 @@ void PasswordGenerationAgent::FindPossibleGenerationForm() { ...@@ -304,7 +347,7 @@ void PasswordGenerationAgent::FindPossibleGenerationForm() {
return; return;
// If we have already found a signup form for this page, no need to continue. // If we have already found a signup form for this page, no need to continue.
if (generation_form_data_) if (automatic_generation_form_data_)
return; return;
WebLocalFrame* web_frame = render_frame()->GetWebFrame(); WebLocalFrame* web_frame = render_frame()->GetWebFrame();
...@@ -334,8 +377,8 @@ void PasswordGenerationAgent::FindPossibleGenerationForm() { ...@@ -334,8 +377,8 @@ void PasswordGenerationAgent::FindPossibleGenerationForm() {
web_frame->GetDocument().All(), nullptr) web_frame->GetDocument().All(), nullptr)
: form_util::ExtractAutofillableElementsInForm(web_form), : form_util::ExtractAutofillableElementsInForm(web_form),
&passwords)) { &passwords)) {
possible_account_creation_forms_.emplace_back( possible_account_creation_forms_.emplace_back(std::move(*form.first),
make_linked_ptr(form.first.release()), std::move(passwords)); std::move(passwords));
} }
} }
...@@ -368,12 +411,13 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted( ...@@ -368,12 +411,13 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted(
// static cast is workaround for linker error. // static cast is workaround for linker error.
DCHECK_LE(static_cast<size_t>(kMinimumLengthForEditedPassword), DCHECK_LE(static_cast<size_t>(kMinimumLengthForEditedPassword),
password.size()); password.size());
password_is_generated_ = true; DCHECK(current_generation_item_);
password_edited_ = false; current_generation_item_->password_is_generated_ = true;
current_generation_item_->password_edited_ = false;
password_generation::LogPasswordGenerationEvent( password_generation::LogPasswordGenerationEvent(
password_generation::PASSWORD_ACCEPTED); password_generation::PASSWORD_ACCEPTED);
LogMessage(Logger::STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED); LogMessage(Logger::STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED);
for (auto& password_element : generation_form_data_->password_elements) { for (auto& password_element : current_generation_item_->password_elements_) {
password_element.SetAutofillValue(blink::WebString::FromUTF16(password)); password_element.SetAutofillValue(blink::WebString::FromUTF16(password));
// setAutofillValue() above may have resulted in JavaScript closing the // setAutofillValue() above may have resulted in JavaScript closing the
// frame. // frame.
...@@ -390,7 +434,7 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted( ...@@ -390,7 +434,7 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted(
// Call UpdateStateForTextChange after the corresponding PasswordFormManager // Call UpdateStateForTextChange after the corresponding PasswordFormManager
// is notified that the password was generated. // is notified that the password was generated.
for (auto& password_element : generation_form_data_->password_elements) { for (auto& password_element : current_generation_item_->password_elements_) {
// Needed to notify password_autofill_agent that the content of the field // Needed to notify password_autofill_agent that the content of the field
// has changed. Without this we will overwrite the generated // has changed. Without this we will overwrite the generated
// password with an Autofilled password when saving. // password with an Autofilled password when saving.
...@@ -401,20 +445,22 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted( ...@@ -401,20 +445,22 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted(
std::unique_ptr<PasswordForm> std::unique_ptr<PasswordForm>
PasswordGenerationAgent::CreatePasswordFormToPresave() { PasswordGenerationAgent::CreatePasswordFormToPresave() {
DCHECK(!generation_element_.IsNull()); DCHECK(current_generation_item_);
DCHECK(!current_generation_item_->generation_element_.IsNull());
// Since the form for presaving should match a form in the browser, create it // Since the form for presaving should match a form in the browser, create it
// with the same algorithm (to match html attributes, action, etc.), but // with the same algorithm (to match html attributes, action, etc.), but
// change username and password values. // change username and password values.
std::unique_ptr<PasswordForm> password_form; std::unique_ptr<PasswordForm> password_form;
if (!generation_element_.Form().IsNull()) { if (!current_generation_item_->generation_element_.Form().IsNull()) {
password_form = password_form = password_agent_->GetPasswordFormFromWebForm(
password_agent_->GetPasswordFormFromWebForm(generation_element_.Form()); current_generation_item_->generation_element_.Form());
} else { } else {
password_form = password_agent_->GetPasswordFormFromUnownedInputElements(); password_form = password_agent_->GetPasswordFormFromUnownedInputElements();
} }
if (password_form) { if (password_form) {
password_form->type = PasswordForm::TYPE_GENERATED; password_form->type = PasswordForm::TYPE_GENERATED;
password_form->password_value = generation_element_.Value().Utf16(); password_form->password_value =
current_generation_item_->generation_element_.Value().Utf16();
} }
return password_form; return password_form;
...@@ -433,21 +479,23 @@ void PasswordGenerationAgent::UserTriggeredGeneratePassword() { ...@@ -433,21 +479,23 @@ void PasswordGenerationAgent::UserTriggeredGeneratePassword() {
autofill::password_generation::PasswordGenerationUIData autofill::password_generation::PasswordGenerationUIData
password_generation_ui_data( password_generation_ui_data(
render_frame()->GetRenderView()->ElementBoundsInWindow( render_frame()->GetRenderView()->ElementBoundsInWindow(
generation_element_), current_generation_item_->generation_element_),
generation_element_.MaxLength(), current_generation_item_->generation_element_.MaxLength(),
generation_element_.NameForAutofill().Utf16(), current_generation_item_->generation_element_.NameForAutofill()
GetTextDirectionForElement(generation_element_), .Utf16(),
*generation_form_data_->form); GetTextDirectionForElement(
current_generation_item_->generation_element_),
current_generation_item_->form_);
// TODO(crbug.com/845458): remove it. The renderer should never invoke the // TODO(crbug.com/845458): remove it. The renderer should never invoke the
// prompt directly. // prompt directly.
GetPasswordManagerClient()->ShowManualPasswordGenerationPopup( GetPasswordManagerClient()->ShowManualPasswordGenerationPopup(
password_generation_ui_data); password_generation_ui_data);
generation_popup_shown_ = true; current_generation_item_->generation_popup_shown_ = true;
} }
} }
void PasswordGenerationAgent::DetermineGenerationElement() { void PasswordGenerationAgent::DetermineGenerationElement() {
if (generation_form_data_) { if (automatic_generation_form_data_) {
LogMessage(Logger::STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND); LogMessage(Logger::STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND);
return; return;
} }
...@@ -462,7 +510,7 @@ void PasswordGenerationAgent::DetermineGenerationElement() { ...@@ -462,7 +510,7 @@ void PasswordGenerationAgent::DetermineGenerationElement() {
// Note that no messages will be sent if this feature is disabled // Note that no messages will be sent if this feature is disabled
// (e.g. password saving is disabled). // (e.g. password saving is disabled).
for (auto& possible_form_data : possible_account_creation_forms_) { for (auto& possible_form_data : possible_account_creation_forms_) {
PasswordForm* possible_password_form = possible_form_data.form.get(); PasswordForm* possible_password_form = &possible_form_data.form;
const PasswordFormGenerationData* generation_data = nullptr; const PasswordFormGenerationData* generation_data = nullptr;
std::vector<WebInputElement> password_elements; std::vector<WebInputElement> password_elements;
...@@ -502,17 +550,25 @@ void PasswordGenerationAgent::DetermineGenerationElement() { ...@@ -502,17 +550,25 @@ void PasswordGenerationAgent::DetermineGenerationElement() {
return; return;
} }
generation_form_data_.reset(new AccountCreationFormData( automatic_generation_form_data_.reset(new AccountCreationFormData(
possible_form_data.form, std::move(password_elements))); possible_form_data.form, std::move(password_elements)));
generation_element_ = generation_form_data_->password_elements[0]; automatic_generation_element_ =
automatic_generation_form_data_->password_elements[0];
if (mark_generation_element_) if (mark_generation_element_)
generation_element_.SetAttribute("password_creation_field", "1"); automatic_generation_element_.SetAttribute("password_creation_field",
generation_element_.SetAttribute("aria-autocomplete", "list"); "1");
automatic_generation_element_.SetAttribute("aria-autocomplete", "list");
password_generation::LogPasswordGenerationEvent( password_generation::LogPasswordGenerationEvent(
password_generation::GENERATION_AVAILABLE); password_generation::GENERATION_AVAILABLE);
possible_account_creation_forms_.clear(); possible_account_creation_forms_.clear();
if (!current_generation_item_) {
// If the manual generation hasn't started, set
// |automatic_generation_element_| as the current generation field.
current_generation_item_.reset(new GenerationItemInfo(
*automatic_generation_form_data_, automatic_generation_element_));
}
GetPasswordManagerClient()->GenerationAvailableForForm( GetPasswordManagerClient()->GenerationAvailableForForm(
*generation_form_data_->form); automatic_generation_form_data_->form);
return; return;
} }
} }
...@@ -540,7 +596,6 @@ bool PasswordGenerationAgent::SetUpUserTriggeredGeneration() { ...@@ -540,7 +596,6 @@ bool PasswordGenerationAgent::SetUpUserTriggeredGeneration() {
if (!password_form) if (!password_form)
return false; return false;
generation_element_ = last_focused_password_element_;
std::vector<WebInputElement> password_elements; std::vector<WebInputElement> password_elements;
GetAccountCreationPasswordFields(control_elements, &password_elements); GetAccountCreationPasswordFields(control_elements, &password_elements);
password_elements = FindPasswordElementsForGeneration( password_elements = FindPasswordElementsForGeneration(
...@@ -551,9 +606,12 @@ bool PasswordGenerationAgent::SetUpUserTriggeredGeneration() { ...@@ -551,9 +606,12 @@ bool PasswordGenerationAgent::SetUpUserTriggeredGeneration() {
last_focused_password_element_.NameForAutofill().Utf16(), last_focused_password_element_.NameForAutofill().Utf16(),
last_focused_password_element_.FormControlTypeForAutofill() last_focused_password_element_.FormControlTypeForAutofill()
.Utf8()))); .Utf8())));
generation_form_data_.reset(new AccountCreationFormData( current_generation_item_.reset(new GenerationItemInfo(
make_linked_ptr(password_form.release()), password_elements)); last_focused_password_element_, std::move(*password_form),
is_manually_triggered_ = true; std::move(password_elements)));
// |automatic_generation_element_| should always generate the UI.
current_generation_item_->is_manually_triggered_ =
(last_focused_password_element_ != automatic_generation_element_);
return true; return true;
} }
...@@ -572,18 +630,22 @@ bool PasswordGenerationAgent::FocusedNodeHasChanged( ...@@ -572,18 +630,22 @@ bool PasswordGenerationAgent::FocusedNodeHasChanged(
const WebInputElement* element = ToWebInputElement(&web_element); const WebInputElement* element = ToWebInputElement(&web_element);
if (element && element->IsPasswordFieldForAutofill()) if (element && element->IsPasswordFieldForAutofill())
last_focused_password_element_ = *element; last_focused_password_element_ = *element;
if (!element || *element != generation_element_) {
if (!element || !current_generation_item_ ||
*element != current_generation_item_->generation_element_) {
return false; return false;
} }
if (password_is_generated_) { if (current_generation_item_->password_is_generated_) {
if (generation_element_.Value().length() < if (current_generation_item_->generation_element_.Value().length() <
kMinimumLengthForEditedPassword) { kMinimumLengthForEditedPassword) {
PasswordNoLongerGenerated(); PasswordNoLongerGenerated();
if (generation_element_.Value().IsEmpty()) if (current_generation_item_->generation_element_.Value().IsEmpty())
generation_element_.SetShouldRevealPassword(false); current_generation_item_->generation_element_.SetShouldRevealPassword(
false);
} else { } else {
generation_element_.SetShouldRevealPassword(true); current_generation_item_->generation_element_.SetShouldRevealPassword(
true);
ShowEditingPopup(); ShowEditingPopup();
} }
return true; return true;
...@@ -604,23 +666,23 @@ bool PasswordGenerationAgent::FocusedNodeHasChanged( ...@@ -604,23 +666,23 @@ bool PasswordGenerationAgent::FocusedNodeHasChanged(
void PasswordGenerationAgent::DidEndTextFieldEditing( void PasswordGenerationAgent::DidEndTextFieldEditing(
const blink::WebInputElement& element) { const blink::WebInputElement& element) {
if (!element.IsNull() && element == generation_element_) { if (!element.IsNull() && current_generation_item_ &&
element == current_generation_item_->generation_element_) {
AutomaticGenerationStatusChanged(false); AutomaticGenerationStatusChanged(false);
generation_element_.SetShouldRevealPassword(false); current_generation_item_->generation_element_.SetShouldRevealPassword(
false);
} }
} }
bool PasswordGenerationAgent::TextDidChangeInTextField( bool PasswordGenerationAgent::TextDidChangeInTextField(
const WebInputElement& element) { const WebInputElement& element) {
if (element != generation_element_) { if (!(current_generation_item_ &&
current_generation_item_->generation_element_ == element)) {
// Presave the username if it has been changed. // Presave the username if it has been changed.
// TODO(crbug.com/879713): investigate why the following DCHECKs can be if (current_generation_item_ &&
// triggered. current_generation_item_->password_is_generated_ && !element.IsNull() &&
DCHECK(!element.IsNull()); element.Form() ==
DCHECK(!password_is_generated_ || !generation_element_.IsNull()); current_generation_item_->generation_element_.Form()) {
if (password_is_generated_ && !element.IsNull() &&
!generation_element_.IsNull() &&
(element.Form() == generation_element_.Form())) {
std::unique_ptr<PasswordForm> presaved_form( std::unique_ptr<PasswordForm> presaved_form(
CreatePasswordFormToPresave()); CreatePasswordFormToPresave());
if (presaved_form) if (presaved_form)
...@@ -629,18 +691,21 @@ bool PasswordGenerationAgent::TextDidChangeInTextField( ...@@ -629,18 +691,21 @@ bool PasswordGenerationAgent::TextDidChangeInTextField(
return false; return false;
} }
if (element.Value().IsEmpty()) if (element.Value().IsEmpty()) {
generation_element_.SetShouldRevealPassword(false); current_generation_item_->generation_element_.SetShouldRevealPassword(
false);
}
if (!password_is_generated_ && if (!current_generation_item_->password_is_generated_ &&
element.Value().length() > kMaximumCharsForGenerationOffer) { element.Value().length() > kMaximumCharsForGenerationOffer) {
// User has rejected the feature and has started typing a password. // User has rejected the feature and has started typing a password.
GenerationRejectedByTyping(); GenerationRejectedByTyping();
} else { } else {
const bool leave_editing_state = const bool leave_editing_state =
password_is_generated_ && current_generation_item_->password_is_generated_ &&
element.Value().length() < kMinimumLengthForEditedPassword; element.Value().length() < kMinimumLengthForEditedPassword;
if (!password_is_generated_ || leave_editing_state) { if (!current_generation_item_->password_is_generated_ ||
leave_editing_state) {
// The call may pop up a generation prompt, replacing the editing prompt // The call may pop up a generation prompt, replacing the editing prompt
// if it was previously shown. // if it was previously shown.
MaybeOfferAutomaticGeneration(); MaybeOfferAutomaticGeneration();
...@@ -649,11 +714,11 @@ bool PasswordGenerationAgent::TextDidChangeInTextField( ...@@ -649,11 +714,11 @@ bool PasswordGenerationAgent::TextDidChangeInTextField(
// Tell the browser that the state isn't "editing" anymore. The browser // Tell the browser that the state isn't "editing" anymore. The browser
// should hide the editing prompt if it wasn't replaced above. // should hide the editing prompt if it wasn't replaced above.
PasswordNoLongerGenerated(); PasswordNoLongerGenerated();
} else if (password_is_generated_) { } else if (current_generation_item_->password_is_generated_) {
password_edited_ = true; current_generation_item_->password_edited_ = true;
// Mirror edits to any confirmation password fields. // Mirror edits to any confirmation password fields.
CopyElementValueToOtherInputElements( CopyElementValueToOtherInputElements(
&element, &generation_form_data_->password_elements); &element, &current_generation_item_->password_elements_);
std::unique_ptr<PasswordForm> presaved_form( std::unique_ptr<PasswordForm> presaved_form(
CreatePasswordFormToPresave()); CreatePasswordFormToPresave());
if (presaved_form) { if (presaved_form) {
...@@ -666,26 +731,30 @@ bool PasswordGenerationAgent::TextDidChangeInTextField( ...@@ -666,26 +731,30 @@ bool PasswordGenerationAgent::TextDidChangeInTextField(
void PasswordGenerationAgent::MaybeOfferAutomaticGeneration() { void PasswordGenerationAgent::MaybeOfferAutomaticGeneration() {
// TODO(crbug.com/852309): Add this check to the generation element class. // TODO(crbug.com/852309): Add this check to the generation element class.
if (!is_manually_triggered_) { if (!current_generation_item_->is_manually_triggered_) {
AutomaticGenerationStatusChanged(true /* available */); AutomaticGenerationStatusChanged(true /* available */);
} }
} }
void PasswordGenerationAgent::AutomaticGenerationStatusChanged(bool available) { void PasswordGenerationAgent::AutomaticGenerationStatusChanged(bool available) {
if (available) { if (available) {
if (!render_frame() || generation_element_.IsNull()) if (!render_frame())
return; return;
DCHECK(current_generation_item_);
DCHECK(!current_generation_item_->generation_element_.IsNull());
LogMessage( LogMessage(
Logger::STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE); Logger::STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE);
autofill::password_generation::PasswordGenerationUIData autofill::password_generation::PasswordGenerationUIData
password_generation_ui_data( password_generation_ui_data(
render_frame()->GetRenderView()->ElementBoundsInWindow( render_frame()->GetRenderView()->ElementBoundsInWindow(
generation_element_), current_generation_item_->generation_element_),
generation_element_.MaxLength(), current_generation_item_->generation_element_.MaxLength(),
generation_element_.NameForAutofill().Utf16(), current_generation_item_->generation_element_.NameForAutofill()
GetTextDirectionForElement(generation_element_), .Utf16(),
*generation_form_data_->form); GetTextDirectionForElement(
generation_popup_shown_ = true; current_generation_item_->generation_element_),
current_generation_item_->form_);
current_generation_item_->generation_popup_shown_ = true;
GetPasswordManagerClient()->AutomaticGenerationStatusChanged( GetPasswordManagerClient()->AutomaticGenerationStatusChanged(
true, password_generation_ui_data); true, password_generation_ui_data);
} else { } else {
...@@ -700,9 +769,9 @@ void PasswordGenerationAgent::ShowEditingPopup() { ...@@ -700,9 +769,9 @@ void PasswordGenerationAgent::ShowEditingPopup() {
return; return;
GetPasswordManagerClient()->ShowPasswordEditingPopup( GetPasswordManagerClient()->ShowPasswordEditingPopup(
render_frame()->GetRenderView()->ElementBoundsInWindow( render_frame()->GetRenderView()->ElementBoundsInWindow(
generation_element_), current_generation_item_->generation_element_),
*CreatePasswordFormToPresave()); *CreatePasswordFormToPresave());
editing_popup_shown_ = true; current_generation_item_->editing_popup_shown_ = true;
} }
void PasswordGenerationAgent::GenerationRejectedByTyping() { void PasswordGenerationAgent::GenerationRejectedByTyping() {
...@@ -710,17 +779,19 @@ void PasswordGenerationAgent::GenerationRejectedByTyping() { ...@@ -710,17 +779,19 @@ void PasswordGenerationAgent::GenerationRejectedByTyping() {
} }
void PasswordGenerationAgent::PasswordNoLongerGenerated() { void PasswordGenerationAgent::PasswordNoLongerGenerated() {
DCHECK(password_is_generated_); DCHECK(current_generation_item_);
DCHECK(current_generation_item_->password_is_generated_);
// Do not treat the password as generated, either here or in the browser. // Do not treat the password as generated, either here or in the browser.
password_is_generated_ = false; current_generation_item_->password_is_generated_ = false;
password_edited_ = false; current_generation_item_->password_edited_ = false;
for (WebInputElement& password : generation_form_data_->password_elements) for (WebInputElement& password : current_generation_item_->password_elements_)
password.SetAutofillState(WebAutofillState::kNotFilled); password.SetAutofillState(WebAutofillState::kNotFilled);
password_generation::LogPasswordGenerationEvent( password_generation::LogPasswordGenerationEvent(
password_generation::PASSWORD_DELETED); password_generation::PASSWORD_DELETED);
// Clear all other password fields. // Clear all other password fields.
for (WebInputElement& element : generation_form_data_->password_elements) { for (WebInputElement& element :
if (generation_element_ != element) current_generation_item_->password_elements_) {
if (current_generation_item_->generation_element_ != element)
element.SetAutofillValue(blink::WebString()); element.SetAutofillValue(blink::WebString());
} }
std::unique_ptr<PasswordForm> presaved_form(CreatePasswordFormToPresave()); std::unique_ptr<PasswordForm> presaved_form(CreatePasswordFormToPresave());
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include <vector> #include <vector>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/linked_ptr.h"
#include "components/autofill/content/common/autofill_agent.mojom.h" #include "components/autofill/content/common/autofill_agent.mojom.h"
#include "components/autofill/content/common/autofill_driver.mojom.h" #include "components/autofill/content/common/autofill_driver.mojom.h"
#include "components/autofill/content/renderer/renderer_save_password_progress_logger.h" #include "components/autofill/content/renderer/renderer_save_password_progress_logger.h"
...@@ -95,16 +94,11 @@ class PasswordGenerationAgent : public content::RenderFrameObserver, ...@@ -95,16 +94,11 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
void set_enabled(bool enabled) { enabled_ = enabled; } void set_enabled(bool enabled) { enabled_ = enabled; }
private: private:
struct AccountCreationFormData { // Contains information about a form for which generation is possible.
linked_ptr<PasswordForm> form; struct AccountCreationFormData;
std::vector<blink::WebInputElement> password_elements; // Contains information about generation status for an element for the
// lifetime of the possible interaction.
AccountCreationFormData( struct GenerationItemInfo;
linked_ptr<PasswordForm> form,
std::vector<blink::WebInputElement> password_elements);
AccountCreationFormData(const AccountCreationFormData& other);
~AccountCreationFormData();
};
typedef std::vector<AccountCreationFormData> AccountCreationFormDataList; typedef std::vector<AccountCreationFormData> AccountCreationFormDataList;
...@@ -123,9 +117,10 @@ class PasswordGenerationAgent : public content::RenderFrameObserver, ...@@ -123,9 +117,10 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
// |possible_account_creation_form_|. // |possible_account_creation_form_|.
void FindPossibleGenerationForm(); void FindPossibleGenerationForm();
// Helper function to decide if |passwords_| contains password fields for // Helper function to decide if |possible_account_creation_forms_| contains
// an account creation form. Sets |generation_element_| to the field that // password fields for an account creation form. Sets
// we want to trigger the generation UI on. // |automatic_generation_element_| to the field that we want to trigger the
// generation UI on.
void DetermineGenerationElement(); void DetermineGenerationElement();
// Helper function which takes care of the form processing and collecting the // Helper function which takes care of the form processing and collecting the
...@@ -186,48 +181,27 @@ class PasswordGenerationAgent : public content::RenderFrameObserver, ...@@ -186,48 +181,27 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
// forms will not be sent if the feature is disabled. // forms will not be sent if the feature is disabled.
std::vector<autofill::PasswordFormGenerationData> generation_enabled_forms_; std::vector<autofill::PasswordFormGenerationData> generation_enabled_forms_;
// Data for form which generation is allowed on. // Data for form which automatic generation is allowed on.
std::unique_ptr<AccountCreationFormData> generation_form_data_; std::unique_ptr<AccountCreationFormData> automatic_generation_form_data_;
// Element where we want to trigger automatic password generation UI on.
blink::WebInputElement automatic_generation_element_;
// Element where we want to trigger password generation UI. // Contains the current element where generation is offered at the moment. It
blink::WebInputElement generation_element_; // can be either automatic or manual password generation.
std::unique_ptr<GenerationItemInfo> current_generation_item_;
// Password element that had focus last. Since Javascript could change focused // Password element that had focus last. Since Javascript could change focused
// element after the user triggered a generation request, it is better to save // element after the user triggered a generation request, it is better to save
// the last focused password element. // the last focused password element.
blink::WebInputElement last_focused_password_element_; blink::WebInputElement last_focused_password_element_;
// If the password field at |generation_element_| contains a generated
// password.
bool password_is_generated_;
// True if the last password generation was manually triggered.
bool is_manually_triggered_;
// True if a password was generated and the user edited it. Used for UMA
// stats.
bool password_edited_;
// True if the generation popup was shown during this navigation. Used to
// track UMA stats per page visit rather than per display, since the former
// is more interesting.
// TODO(crbug.com/845458): Remove this or change the description of the
// logged event as calling AutomaticgenerationStatusChanged will no longer
// imply that a popup is shown. This could instead be logged with the
// metrics collected on the browser process.
bool generation_popup_shown_;
// True if the editing popup was shown during this navigation. Used to track
// UMA stats per page rather than per display, since the former is more
// interesting.
bool editing_popup_shown_;
// If this feature is enabled. Controlled by Finch. // If this feature is enabled. Controlled by Finch.
bool enabled_; bool enabled_;
// True iff the generation element should be marked with special HTML // True iff the generation element should be marked with special HTML
// attribute (only for experimental purposes). // attribute (only for experimental purposes).
bool mark_generation_element_; const bool mark_generation_element_;
// Unowned pointer. Used to notify PassowrdAutofillAgent when values // Unowned pointer. Used to notify PassowrdAutofillAgent when values
// in password fields are updated. // in password fields are updated.
......
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