Refactor automatic generation UI flow

This aims to make the flow easier to use by the new password generation
Android UI by decoupling the renderer logic in PasswordGenerationAgent
from the UI display logic.

A follow-up CL will refactor the manual generation flow as well.

Bug:845458

Change-Id: I441546003cf3afcaa2de6ff84ccbcee6f131d150
Reviewed-on: https://chromium-review.googlesource.com/1044372
Reviewed-by: Mike West <mkwst@chromium.org>
Reviewed-by: Vaclav Brozek <vabr@chromium.org>
Reviewed-by: Vasilii Sukhanov <vasilii@chromium.org>
Commit-Queue: Ioana Pandele <ioanap@chromium.org>
Cr-Commit-Position: refs/heads/master@{#564494}
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 51ec2129..9d661b0e 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -32,6 +32,7 @@
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/common/password_form.h"
+#include "components/autofill/core/common/password_generation_util.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
 #include "components/password_manager/content/browser/form_submission_tracker_util.h"
@@ -652,27 +653,23 @@
   return bounds + client_area.OffsetFromOrigin();
 }
 
-void ChromePasswordManagerClient::ShowPasswordGenerationPopup(
-    const gfx::RectF& bounds,
-    int max_length,
-    const base::string16& generation_element,
-    bool is_manually_triggered,
-    const autofill::PasswordForm& form) {
-  // TODO(gcasto): Validate data in PasswordForm.
+void ChromePasswordManagerClient::AutomaticGenerationStatusChanged(
+    bool available,
+    const base::Optional<
+        autofill::password_generation::PasswordGenerationUIData>&
+        password_generation_ui_data) {
+  // TODO(crbug.com/835234): Use the case when available is false to
+  // remove the option from the keyboard accessory on Android and maybe
+  // to hide the popup on desktop.
+  if (available) {
+    ShowPasswordGenerationPopup(password_generation_ui_data.value(),
+                                false /* is_manually_triggered */);
+  }
+}
 
-  auto* driver = driver_factory_->GetDriverForFrame(
-      password_manager_client_bindings_.GetCurrentTargetFrame());
-  DCHECK(driver);
-  password_manager_.SetGenerationElementAndReasonForForm(
-      driver, form, generation_element, is_manually_triggered);
-  gfx::RectF element_bounds_in_screen_space = GetBoundsInScreenSpace(bounds);
-
-  popup_controller_ =
-      autofill::PasswordGenerationPopupControllerImpl::GetOrCreate(
-          popup_controller_, element_bounds_in_screen_space, form, max_length,
-          &password_manager_, driver->AsWeakPtr(), observer_, web_contents(),
-          web_contents()->GetNativeView());
-  popup_controller_->Show(true /* display_password */);
+void ChromePasswordManagerClient::ShowManualPasswordGenerationPopup(
+    const autofill::password_generation::PasswordGenerationUIData& ui_data) {
+  ShowPasswordGenerationPopup(ui_data, true /* is_manually_triggered */);
 }
 
 void ChromePasswordManagerClient::ShowPasswordEditingPopup(
@@ -817,6 +814,27 @@
 #endif
 }
 
+void ChromePasswordManagerClient::ShowPasswordGenerationPopup(
+    const autofill::password_generation::PasswordGenerationUIData& ui_data,
+    bool is_manually_triggered) {
+  auto* driver = driver_factory_->GetDriverForFrame(
+      password_manager_client_bindings_.GetCurrentTargetFrame());
+  DCHECK(driver);
+  password_manager_.SetGenerationElementAndReasonForForm(
+      driver, ui_data.password_form, ui_data.generation_element,
+      is_manually_triggered);
+  gfx::RectF element_bounds_in_screen_space =
+      GetBoundsInScreenSpace(ui_data.bounds);
+
+  popup_controller_ =
+      autofill::PasswordGenerationPopupControllerImpl::GetOrCreate(
+          popup_controller_, element_bounds_in_screen_space,
+          ui_data.password_form, ui_data.max_length, &password_manager_,
+          driver->AsWeakPtr(), observer_, web_contents(),
+          web_contents()->GetNativeView());
+  popup_controller_->Show(true /* display_password */);
+}
+
 password_manager::PasswordManager*
 ChromePasswordManagerClient::GetPasswordManager() {
   return &password_manager_;
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index 9b38639..b951d1f 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -33,6 +33,10 @@
 namespace autofill {
 class PasswordGenerationPopupObserver;
 class PasswordGenerationPopupControllerImpl;
+
+namespace password_generation {
+struct PasswordGenerationUIData;
+}
 }
 
 namespace content {
@@ -105,11 +109,14 @@
   const password_manager::LogManager* GetLogManager() const override;
 
   // autofill::mojom::PasswordManagerClient overrides.
-  void ShowPasswordGenerationPopup(const gfx::RectF& bounds,
-                                   int max_length,
-                                   const base::string16& generation_element,
-                                   bool is_manually_triggered,
-                                   const autofill::PasswordForm& form) override;
+  void AutomaticGenerationStatusChanged(
+      bool available,
+      const base::Optional<
+          autofill::password_generation::PasswordGenerationUIData>& ui_data)
+      override;
+  void ShowManualPasswordGenerationPopup(
+      const autofill::password_generation::PasswordGenerationUIData& ui_data)
+      override;
   void ShowPasswordEditingPopup(const gfx::RectF& bounds,
                                 const autofill::PasswordForm& form) override;
   void GenerationAvailableForForm(const autofill::PasswordForm& form) override;
@@ -192,6 +199,10 @@
   void PromptUserToEnableAutosignin() override;
   password_manager::PasswordManager* GetPasswordManager() override;
 
+  void ShowPasswordGenerationPopup(
+      const autofill::password_generation::PasswordGenerationUIData& ui_data,
+      bool is_manually_triggered);
+
   Profile* const profile_;
 
   password_manager::PasswordManager password_manager_;
diff --git a/chrome/renderer/autofill/fake_password_manager_client.cc b/chrome/renderer/autofill/fake_password_manager_client.cc
index 3d19856..b8a8a6a 100644
--- a/chrome/renderer/autofill/fake_password_manager_client.cc
+++ b/chrome/renderer/autofill/fake_password_manager_client.cc
@@ -19,13 +19,18 @@
 }
 
 // autofill::mojom::PasswordManagerClient:
-void FakePasswordManagerClient::ShowPasswordGenerationPopup(
-    const gfx::RectF& bounds,
-    int max_length,
-    const base::string16& generation_element,
-    bool is_manually_triggered,
-    const autofill::PasswordForm& form) {
-  called_show_pw_generation_popup_ = true;
+void FakePasswordManagerClient::AutomaticGenerationStatusChanged(
+    bool available,
+    const base::Optional<
+        autofill::password_generation::PasswordGenerationUIData>& ui_data) {
+  if (available) {
+    called_automatic_generation_status_changed_true_ = true;
+  }
+}
+
+void FakePasswordManagerClient::ShowManualPasswordGenerationPopup(
+    const autofill::password_generation::PasswordGenerationUIData& ui_data) {
+  called_show_manual_pw_generation_popup_ = true;
 }
 
 void FakePasswordManagerClient::ShowPasswordEditingPopup(
diff --git a/chrome/renderer/autofill/fake_password_manager_client.h b/chrome/renderer/autofill/fake_password_manager_client.h
index 9bdd8ae..b653ac3 100644
--- a/chrome/renderer/autofill/fake_password_manager_client.h
+++ b/chrome/renderer/autofill/fake_password_manager_client.h
@@ -12,6 +12,7 @@
 #include "base/strings/string16.h"
 #include "components/autofill/content/common/autofill_driver.mojom.h"
 #include "components/autofill/core/common/password_form.h"
+#include "components/autofill/core/common/password_generation_util.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 
 class FakePasswordManagerClient
@@ -26,8 +27,12 @@
 
   void Flush();
 
-  bool called_show_pw_generation_popup() const {
-    return called_show_pw_generation_popup_;
+  bool called_automatic_generation_status_changed_true() const {
+    return called_automatic_generation_status_changed_true_;
+  }
+
+  bool called_show_manual_pw_generation_popup() const {
+    return called_show_manual_pw_generation_popup_;
   }
 
   bool called_generation_available_for_form() const {
@@ -38,8 +43,12 @@
     return called_hide_pw_generation_popup_;
   }
 
-  void reset_called_show_pw_generation_popup() {
-    called_show_pw_generation_popup_ = false;
+  void reset_called_automatic_generation_status_changed_true() {
+    called_automatic_generation_status_changed_true_ = false;
+  }
+
+  void reset_called_show_manual_pw_generation_popup() {
+    called_show_manual_pw_generation_popup_ = false;
   }
 
   void reset_called_generation_available_for_form() {
@@ -52,11 +61,15 @@
 
  private:
   // autofill::mojom::PasswordManagerClient:
-  void ShowPasswordGenerationPopup(const gfx::RectF& bounds,
-                                   int max_length,
-                                   const base::string16& generation_element,
-                                   bool is_manually_triggered,
-                                   const autofill::PasswordForm& form) override;
+  void AutomaticGenerationStatusChanged(
+      bool available,
+      const base::Optional<
+          autofill::password_generation::PasswordGenerationUIData>& ui_data)
+      override;
+
+  void ShowManualPasswordGenerationPopup(
+      const autofill::password_generation::PasswordGenerationUIData& ui_data)
+      override;
 
   void ShowPasswordEditingPopup(const gfx::RectF& bounds,
                                 const autofill::PasswordForm& form) override;
@@ -65,8 +78,11 @@
 
   void HidePasswordGenerationPopup() override;
 
+  // Records whether AutomaticGenerationStatusChanged(true) gets called.
+  bool called_automatic_generation_status_changed_true_ = false;
+
   // Records whether ShowPasswordGenerationPopup() gets called.
-  bool called_show_pw_generation_popup_ = false;
+  bool called_show_manual_pw_generation_popup_ = false;
 
   // Records whether GenerationAvailableForForm() gets called.
   bool called_generation_available_for_form_ = false;
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index ba8bcf7..32fd57a8 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -639,9 +639,9 @@
     }
   }
 
-  bool GetCalledShowPasswordGenerationPopup() {
+  bool GetCalledAutomaticGenerationStatusChangedTrue() {
     fake_pw_client_.Flush();
-    return fake_pw_client_.called_show_pw_generation_popup();
+    return fake_pw_client_.called_automatic_generation_status_changed_true();
   }
 
   void BindPasswordManagerDriver(mojo::ScopedMessagePipeHandle handle) {
@@ -2413,7 +2413,7 @@
   SimulateElementClick("new_password");
   EXPECT_FALSE(GetCalledShowPasswordSuggestions());
   EXPECT_FALSE(GetCalledShowManualFallbackSuggestion());
-  EXPECT_TRUE(GetCalledShowPasswordGenerationPopup());
+  EXPECT_TRUE(GetCalledAutomaticGenerationStatusChangedTrue());
 }
 
 // Tests that a password change form is properly filled with the username and
diff --git a/chrome/renderer/autofill/password_generation_agent_browsertest.cc b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
index 6d55709..9dc5a4c 100644
--- a/chrome/renderer/autofill/password_generation_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
@@ -90,12 +90,19 @@
                            element_id).c_str());
   }
 
-  void ExpectGenerationAvailable(const char* element_id,
-                                 bool available) {
+  void ExpectAutomaticGenerationAvailable(const char* element_id,
+                                          bool available) {
     FocusField(element_id);
     base::RunLoop().RunUntilIdle();
-    ASSERT_EQ(available, GetCalledShowPasswordGenerationPopup());
-    fake_pw_client_.reset_called_show_pw_generation_popup();
+    ASSERT_EQ(available, GetCalledAutomaticGenerationStatusChangedTrue());
+    fake_pw_client_.reset_called_automatic_generation_status_changed_true();
+  }
+
+  void ExpectManualGenerationAvailable(const char* element_id, bool available) {
+    FocusField(element_id);
+    base::RunLoop().RunUntilIdle();
+    ASSERT_EQ(available, GetCalledShowManualPasswordGenerationPopup());
+    fake_pw_client_.reset_called_show_manual_pw_generation_popup();
   }
 
   void AllowToRunFormClassifier() {
@@ -117,9 +124,14 @@
     fake_driver_.reset_save_generation_field();
   }
 
-  bool GetCalledShowPasswordGenerationPopup() {
+  bool GetCalledAutomaticGenerationStatusChangedTrue() {
     fake_pw_client_.Flush();
-    return fake_pw_client_.called_show_pw_generation_popup();
+    return fake_pw_client_.called_automatic_generation_status_changed_true();
+  }
+
+  bool GetCalledShowManualPasswordGenerationPopup() {
+    fake_pw_client_.Flush();
+    return fake_pw_client_.called_show_manual_pw_generation_popup();
   }
 
   void SelectGenerationFallbackInContextMenu(const char* element_id) {
@@ -299,13 +311,13 @@
     "<SPAN id='span'>Text to click on</SPAN>";
 
 TEST_F(PasswordGenerationAgentTest, DetectionTest) {
-  // Don't shown the icon for non account creation forms.
+  // Don't show the icon for non account creation forms.
   LoadHTMLWithUserGesture(kSigninFormHTML);
-  ExpectGenerationAvailable("password", false);
+  ExpectAutomaticGenerationAvailable("password", false);
 
   // We don't show the decoration yet because the feature isn't enabled.
   LoadHTMLWithUserGesture(kAccountCreationFormHTML);
-  ExpectGenerationAvailable("first_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", false);
 
   // Pretend like We have received message indicating site is not blacklisted,
   // and we have received message indicating the form is classified as
@@ -314,7 +326,7 @@
   SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   // Hidden fields are not treated differently.
   LoadHTMLWithUserGesture(kHiddenPasswordAccountCreationFormHTML);
@@ -322,7 +334,7 @@
                            kHiddenPasswordAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   // This doesn't trigger because the form action is invalid.
   LoadHTMLWithUserGesture(kInvalidActionAccountCreationFormHTML);
@@ -330,7 +342,7 @@
                            kInvalidActionAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", false);
 }
 
 TEST_F(PasswordGenerationAgentTest, DetectionTestNoForm) {
@@ -351,8 +363,8 @@
       CalculateFieldSignatureForField(form_data.fields[1])});
   password_generation_->FoundFormsEligibleForGeneration(forms);
 
-  ExpectGenerationAvailable("first_password", true);
-  ExpectGenerationAvailable("second_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("second_password", false);
 }
 
 TEST_F(PasswordGenerationAgentTest, FillTest) {
@@ -450,7 +462,7 @@
   // and trigger generation again.
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(fake_driver_.called_password_no_longer_generated());
-  EXPECT_TRUE(GetCalledShowPasswordGenerationPopup());
+  EXPECT_TRUE(GetCalledAutomaticGenerationStatusChangedTrue());
 }
 
 TEST_F(PasswordGenerationAgentTest, BlacklistedTest) {
@@ -459,7 +471,7 @@
   LoadHTMLWithUserGesture(kAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", false);
 
   // Receive one not blacklisted message for non account creation form. Don't
   // show password generation icon.
@@ -467,7 +479,7 @@
   SetNotBlacklistedMessage(password_generation_, kSigninFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", false);
 
   // Receive one not blacklisted message for account creation form. Show
   // password generation icon.
@@ -475,7 +487,7 @@
   SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   // Receive two not blacklisted messages, one is for account creation form and
   // the other is not. Show password generation icon.
@@ -484,7 +496,7 @@
   SetNotBlacklistedMessage(password_generation_, kSigninFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 }
 
 TEST_F(PasswordGenerationAgentTest, AccountCreationFormsDetectedTest) {
@@ -492,7 +504,7 @@
   // password generation icon.
   LoadHTMLWithUserGesture(kAccountCreationFormHTML);
   SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
-  ExpectGenerationAvailable("first_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", false);
 
   // Receive the account creation forms detected message. Show password
   // generation icon.
@@ -500,7 +512,7 @@
   SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 }
 
 TEST_F(PasswordGenerationAgentTest, MaximumOfferSize) {
@@ -510,7 +522,7 @@
   SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   WebDocument document = GetMainFrame()->GetDocument();
   WebElement element =
@@ -523,8 +535,8 @@
       &first_password_element,
       std::string(password_generation_->kMaximumOfferSize - 1, 'a'));
   // There should now be a message to show the UI.
-  EXPECT_TRUE(GetCalledShowPasswordGenerationPopup());
-  fake_pw_client_.reset_called_show_pw_generation_popup();
+  EXPECT_TRUE(GetCalledAutomaticGenerationStatusChangedTrue());
+  fake_pw_client_.reset_called_automatic_generation_status_changed_true();
 
   fake_pw_client_.reset_called_hide_pw_generation_popup();
   // Simulate a user typing a password just over maximum offer size.
@@ -533,29 +545,28 @@
   // There should now be a message to hide the UI.
   fake_pw_client_.Flush();
   EXPECT_TRUE(fake_pw_client_.called_hide_pw_generation_popup());
-  fake_pw_client_.reset_called_show_pw_generation_popup();
+  fake_pw_client_.reset_called_show_manual_pw_generation_popup();
 
   // Simulate the user deleting characters. The generation popup should be shown
   // again.
   SimulateUserTypingASCIICharacter(ui::VKEY_BACK, true);
   // There should now be a message to show the UI.
-  EXPECT_TRUE(GetCalledShowPasswordGenerationPopup());
-  fake_pw_client_.reset_called_show_pw_generation_popup();
+  EXPECT_TRUE(GetCalledAutomaticGenerationStatusChangedTrue());
+  fake_pw_client_.reset_called_automatic_generation_status_changed_true();
 
   // Change focus. Bubble should be hidden, but that is handled by AutofilAgent,
   // so no messages are sent.
   ExecuteJavaScriptForTests("document.getElementById('username').focus();");
-  EXPECT_FALSE(GetCalledShowPasswordGenerationPopup());
-  fake_pw_client_.reset_called_show_pw_generation_popup();
+  EXPECT_FALSE(GetCalledAutomaticGenerationStatusChangedTrue());
 
   // Focusing the password field will bring up the generation UI again.
   ExecuteJavaScriptForTests(
       "document.getElementById('first_password').focus();");
-  EXPECT_TRUE(GetCalledShowPasswordGenerationPopup());
-  fake_pw_client_.reset_called_show_pw_generation_popup();
+  EXPECT_TRUE(GetCalledAutomaticGenerationStatusChangedTrue());
+  fake_pw_client_.reset_called_automatic_generation_status_changed_true();
 
   // Loading a different page triggers UMA stat upload. Verify that only one
-  // display event is sent even though
+  // display event is sent.
   LoadHTMLWithUserGesture(kSigninFormHTML);
 
   histogram_tester.ExpectBucketCount(
@@ -596,7 +607,7 @@
   // didAssociateFormControls() isn't called. If this turns out to be a problem
   // adding a call to OnDynamicFormsSeen(GetMainFrame()) will fix it, though
   // it will weaken the test.
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 }
 
 TEST_F(PasswordGenerationAgentTest, MultiplePasswordFormsTest) {
@@ -610,8 +621,8 @@
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 1, 1);
 
-  ExpectGenerationAvailable("password", false);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("password", false);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 }
 
 TEST_F(PasswordGenerationAgentTest, MessagesAfterAccountSignupFormFound) {
@@ -621,15 +632,15 @@
                                          GetMainFrame()->GetDocument(), 0, 1);
 
   // Generation should be enabled.
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   // Extra not blacklisted messages can be sent. Make sure that they are handled
   // correctly (generation should still be available).
   SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
 
   // Need to focus another field first for verification to work.
-  ExpectGenerationAvailable("second_password", false);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("second_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 }
 
 // Losing focus should not trigger a password generation popup.
@@ -642,7 +653,7 @@
 
   // Focus on the first password field: password generation popup should show
   // up.
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   fake_pw_client_.reset_called_generation_available_for_form();
   // Remove focus from everywhere by clicking an unfocusable element: password
@@ -650,7 +661,7 @@
   EXPECT_TRUE(SimulateElementClick("disabled"));
   fake_pw_client_.Flush();
   EXPECT_FALSE(fake_pw_client_.called_generation_available_for_form());
-  EXPECT_FALSE(GetCalledShowPasswordGenerationPopup());
+  EXPECT_FALSE(GetCalledAutomaticGenerationStatusChangedTrue());
 }
 
 TEST_F(PasswordGenerationAgentTest, AutocompleteAttributesTest) {
@@ -659,56 +670,56 @@
   LoadHTMLWithUserGesture(kBothAutocompleteAttributesFormHTML);
   SetNotBlacklistedMessage(password_generation_,
                            kBothAutocompleteAttributesFormHTML);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   // Only username autocomplete attribute enabled doesn't trigger generation.
   LoadHTMLWithUserGesture(kUsernameAutocompleteAttributeFormHTML);
   SetNotBlacklistedMessage(password_generation_,
                            kUsernameAutocompleteAttributeFormHTML);
-  ExpectGenerationAvailable("first_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", false);
 
   // Only new-password autocomplete attribute enabled does trigger generation.
   LoadHTMLWithUserGesture(kNewPasswordAutocompleteAttributeFormHTML);
   SetNotBlacklistedMessage(password_generation_,
                            kNewPasswordAutocompleteAttributeFormHTML);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   // Generation is triggered if the form has only password fields.
   LoadHTMLWithUserGesture(kCurrentAndNewPasswordAutocompleteAttributeFormHTML);
   SetNotBlacklistedMessage(password_generation_,
                            kCurrentAndNewPasswordAutocompleteAttributeFormHTML);
-  ExpectGenerationAvailable("old_password", false);
-  ExpectGenerationAvailable("new_password", true);
-  ExpectGenerationAvailable("confirm_password", false);
+  ExpectAutomaticGenerationAvailable("old_password", false);
+  ExpectAutomaticGenerationAvailable("new_password", true);
+  ExpectAutomaticGenerationAvailable("confirm_password", false);
 }
 
 TEST_F(PasswordGenerationAgentTest, ChangePasswordFormDetectionTest) {
   // Verify that generation is shown on correct field after message receiving.
   LoadHTMLWithUserGesture(kPasswordChangeFormHTML);
   SetNotBlacklistedMessage(password_generation_, kPasswordChangeFormHTML);
-  ExpectGenerationAvailable("password", false);
-  ExpectGenerationAvailable("newpassword", false);
-  ExpectGenerationAvailable("confirmpassword", false);
+  ExpectAutomaticGenerationAvailable("password", false);
+  ExpectAutomaticGenerationAvailable("newpassword", false);
+  ExpectAutomaticGenerationAvailable("confirmpassword", false);
 
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 2);
-  ExpectGenerationAvailable("password", false);
-  ExpectGenerationAvailable("newpassword", true);
-  ExpectGenerationAvailable("confirmpassword", false);
+  ExpectAutomaticGenerationAvailable("password", false);
+  ExpectAutomaticGenerationAvailable("newpassword", true);
+  ExpectAutomaticGenerationAvailable("confirmpassword", false);
 }
 
 TEST_F(PasswordGenerationAgentTest, ManualGenerationInFormTest) {
   LoadHTMLWithUserGesture(kAccountCreationFormHTML);
   SelectGenerationFallbackInContextMenu("first_password");
-  ExpectGenerationAvailable("first_password", true);
-  ExpectGenerationAvailable("second_password", false);
+  ExpectManualGenerationAvailable("first_password", true);
+  ExpectManualGenerationAvailable("second_password", false);
 }
 
 TEST_F(PasswordGenerationAgentTest, ManualGenerationNoFormTest) {
   LoadHTMLWithUserGesture(kAccountCreationNoForm);
   SelectGenerationFallbackInContextMenu("first_password");
-  ExpectGenerationAvailable("first_password", true);
-  ExpectGenerationAvailable("second_password", false);
+  ExpectManualGenerationAvailable("first_password", true);
+  ExpectManualGenerationAvailable("second_password", false);
 }
 
 TEST_F(PasswordGenerationAgentTest, ManualGenerationChangeFocusTest) {
@@ -718,8 +729,8 @@
   LoadHTMLWithUserGesture(kAccountCreationFormHTML);
   FocusField("first_password");
   SelectGenerationFallbackInContextMenu("username" /* current focus */);
-  ExpectGenerationAvailable("first_password", true);
-  ExpectGenerationAvailable("second_password", false);
+  ExpectAutomaticGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("second_password", false);
 }
 
 TEST_F(PasswordGenerationAgentTest, PresavingGeneratedPassword) {
@@ -735,7 +746,7 @@
     // To be able to work with input elements outside <form>'s, use manual
     // generation.
     SelectGenerationFallbackInContextMenu(test_case.generation_element);
-    ExpectGenerationAvailable(test_case.generation_element, true);
+    ExpectManualGenerationAvailable(test_case.generation_element, true);
 
     base::string16 password = base::ASCIIToUTF16("random_password");
     password_generation_->GeneratedPasswordAccepted(password);
@@ -761,7 +772,7 @@
 TEST_F(PasswordGenerationAgentTest, FallbackForSaving) {
   LoadHTMLWithUserGesture(kAccountCreationFormHTML);
   SelectGenerationFallbackInContextMenu("first_password");
-  ExpectGenerationAvailable("first_password", true);
+  ExpectManualGenerationAvailable("first_password", true);
   EXPECT_EQ(0, fake_driver_.called_show_manual_fallback_for_saving_count());
   password_generation_->GeneratedPasswordAccepted(
       base::ASCIIToUTF16("random_password"));
@@ -851,7 +862,7 @@
   const char* kSpanId = "span";
   const char* kTextFieldId = "username";
 
-  ExpectGenerationAvailable(kGenerationElementId, true);
+  ExpectAutomaticGenerationAvailable(kGenerationElementId, true);
   password_generation_->GeneratedPasswordAccepted(base::ASCIIToUTF16("pwd"));
 
   const bool kFalseTrue[] = {false, true};
@@ -882,7 +893,7 @@
                                          GetMainFrame()->GetDocument(), 0, 1);
 
   const char kGenerationElementId[] = "first_password";
-  ExpectGenerationAvailable(kGenerationElementId, true);
+  ExpectAutomaticGenerationAvailable(kGenerationElementId, true);
   password_generation_->GeneratedPasswordAccepted(base::ASCIIToUTF16("pwd"));
   ExecuteJavaScriptForTests(
       "document.getElementById('first_password').value = '';");
@@ -915,7 +926,7 @@
   SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
 
   WebDocument document = GetMainFrame()->GetDocument();
   WebElement element =
@@ -933,7 +944,7 @@
   SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
   SetAccountCreationFormsDetectedMessage(password_generation_,
                                          GetMainFrame()->GetDocument(), 0, 1);
-  ExpectGenerationAvailable("first_password", true);
+  ExpectAutomaticGenerationAvailable("first_password", true);
   WebDocument document = GetMainFrame()->GetDocument();
 
   // Check the form signature is set.
diff --git a/components/autofill/content/common/BUILD.gn b/components/autofill/content/common/BUILD.gn
index 1b2628c7..04379f4 100644
--- a/components/autofill/content/common/BUILD.gn
+++ b/components/autofill/content/common/BUILD.gn
@@ -25,6 +25,7 @@
 
   public_deps = [
     "//mojo/public/mojom/base",
+    "//ui/gfx/geometry/mojo",
     "//url/mojom:url_mojom_gurl",
     "//url/mojom:url_mojom_origin",
   ]
diff --git a/components/autofill/content/common/autofill_driver.mojom b/components/autofill/content/common/autofill_driver.mojom
index c15cb9fa..5f99e594 100644
--- a/components/autofill/content/common/autofill_driver.mojom
+++ b/components/autofill/content/common/autofill_driver.mojom
@@ -154,18 +154,18 @@
   // form. This is used for UMA stats.
   GenerationAvailableForForm(PasswordForm password_form);
 
-  // Instructs the browser to show the password generation popup at the
-  // specified location. This location should be specified in the renderers
-  // coordinate system. Form is the form associated with the password field.
-  // The popup will be anchored at |bounds|. The generated password
-  // will be no longer than |max_length|. |generation_element| should contain a
-  // name of a password field at which generation popup is attached.
-  // |is_manually_triggered| informs whether it is automatically or manually
-  // triggered generation.
-  ShowPasswordGenerationPopup(
-      gfx.mojom.RectF bounds, int32 max_length,
-      mojo_base.mojom.String16 generation_element, bool is_manually_triggered,
-      PasswordForm password_form);
+  // Notifies the browser when automatic generation becomes available or
+  // unavailable and provides data needed by the UI.
+  AutomaticGenerationStatusChanged(
+      bool available, PasswordGenerationUIData? password_generation_ui_data);
+
+  // Instructs the browser to show the password generation popup for manual
+  // generation and provides the data necessary to display it.
+  // TODO(crbug.com/845458): Replace this with a method called from the browser
+  // when user triggers generation manually which returns a boolean signaling
+  // whether the state for generation could be saved or not.
+  ShowManualPasswordGenerationPopup(
+      PasswordGenerationUIData password_generation_ui_data);
 
   // Instructs the browser to show the popup for editing a generated password.
   // The location should be specified in the renderers coordinate system. Form
diff --git a/components/autofill/content/common/autofill_types.mojom b/components/autofill/content/common/autofill_types.mojom
index a3a63cd..c1be977 100644
--- a/components/autofill/content/common/autofill_types.mojom
+++ b/components/autofill/content/common/autofill_types.mojom
@@ -7,6 +7,7 @@
 import "mojo/public/mojom/base/text_direction.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/string16.mojom";
+import "ui/gfx/geometry/mojo/geometry.mojom";
 import "url/mojom/origin.mojom";
 import "url/mojom/url.mojom";
 
@@ -189,6 +190,14 @@
   uint32 confirmation_field_signature;
 };
 
+// autofill::password_generation::PasswordGenerationUIData
+struct PasswordGenerationUIData {
+  gfx.mojom.RectF bounds;
+  int32 max_length;
+  mojo_base.mojom.String16 generation_element;
+  PasswordForm password_form;
+};
+
 // autofill::ValueElementPair
 struct ValueElementPair {
   mojo_base.mojom.String16 value;
diff --git a/components/autofill/content/common/autofill_types.typemap b/components/autofill/content/common/autofill_types.typemap
index 44e88ba..1c85cc7 100644
--- a/components/autofill/content/common/autofill_types.typemap
+++ b/components/autofill/content/common/autofill_types.typemap
@@ -12,6 +12,7 @@
   "//components/autofill/core/common/password_form_field_prediction_map.h",
   "//components/autofill/core/common/password_form_fill_data.h",
   "//components/autofill/core/common/password_form_generation_data.h",
+  "//components/autofill/core/common/password_generation_util.h",
   "//components/autofill/core/common/submission_source.h",
 ]
 traits_headers =
@@ -23,6 +24,7 @@
   "//base",
   "//base:i18n",
   "//components/autofill/core/common",
+  "//ui/gfx/geometry/mojo:struct_traits",
 ]
 
 type_mappings = [
@@ -42,6 +44,7 @@
   "autofill.mojom.PasswordFormLayout=autofill::PasswordForm::Layout",
   "autofill.mojom.PasswordFormScheme=autofill::PasswordForm::Scheme",
   "autofill.mojom.PasswordFormType=autofill::PasswordForm::Type",
+  "autofill.mojom.PasswordGenerationUIData=autofill::password_generation::PasswordGenerationUIData",
   "autofill.mojom.RoleAttribute=autofill::FormFieldData::RoleAttribute",
   "autofill.mojom.ValueElementPair=autofill::ValueElementPair",
   "autofill.mojom.PasswordFormSubmissionIndicatorEvent=autofill::PasswordForm::SubmissionIndicatorEvent",
diff --git a/components/autofill/content/common/autofill_types_struct_traits.cc b/components/autofill/content/common/autofill_types_struct_traits.cc
index 51ac34a9..0394d70 100644
--- a/components/autofill/content/common/autofill_types_struct_traits.cc
+++ b/components/autofill/content/common/autofill_types_struct_traits.cc
@@ -8,6 +8,7 @@
 #include "mojo/public/cpp/base/string16_mojom_traits.h"
 #include "mojo/public/cpp/base/text_direction_mojom_traits.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
+#include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
 #include "url/mojom/origin_mojom_traits.h"
 #include "url/mojom/url_gurl_mojom_traits.h"
 
@@ -705,6 +706,23 @@
 }
 
 // static
+bool StructTraits<autofill::mojom::PasswordGenerationUIDataDataView,
+                  autofill::password_generation::PasswordGenerationUIData>::
+    Read(autofill::mojom::PasswordGenerationUIDataDataView data,
+         autofill::password_generation::PasswordGenerationUIData* out) {
+  if (!data.ReadBounds(&out->bounds))
+    return false;
+
+  out->max_length = data.max_length();
+
+  if (!data.ReadGenerationElement(&out->generation_element) ||
+      !data.ReadPasswordForm(&out->password_form))
+    return false;
+
+  return true;
+}
+
+// static
 bool StructTraits<
     autofill::mojom::PasswordFormDataView,
     autofill::PasswordForm>::Read(autofill::mojom::PasswordFormDataView data,
diff --git a/components/autofill/content/common/autofill_types_struct_traits.h b/components/autofill/content/common/autofill_types_struct_traits.h
index 7b80e54..6811541 100644
--- a/components/autofill/content/common/autofill_types_struct_traits.h
+++ b/components/autofill/content/common/autofill_types_struct_traits.h
@@ -21,8 +21,10 @@
 #include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/autofill/core/common/password_form_generation_data.h"
+#include "components/autofill/core/common/password_generation_util.h"
 #include "components/autofill/core/common/submission_source.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
+#include "ui/gfx/geometry/rect_f.h"
 #include "url/origin.h"
 
 namespace mojo {
@@ -421,6 +423,34 @@
 };
 
 template <>
+struct StructTraits<autofill::mojom::PasswordGenerationUIDataDataView,
+                    autofill::password_generation::PasswordGenerationUIData> {
+  static const gfx::RectF& bounds(
+      const autofill::password_generation::PasswordGenerationUIData& r) {
+    return r.bounds;
+  }
+
+  static int max_length(
+      const autofill::password_generation::PasswordGenerationUIData& r) {
+    return r.max_length;
+  }
+
+  static const base::string16& generation_element(
+      const autofill::password_generation::PasswordGenerationUIData& r) {
+    return r.generation_element;
+  }
+
+  static const autofill::PasswordForm& password_form(
+      const autofill::password_generation::PasswordGenerationUIData& r) {
+    return r.password_form;
+  }
+
+  static bool Read(
+      autofill::mojom::PasswordGenerationUIDataDataView data,
+      autofill::password_generation::PasswordGenerationUIData* out);
+};
+
+template <>
 struct StructTraits<autofill::mojom::PasswordFormDataView,
                     autofill::PasswordForm> {
   static autofill::PasswordForm::Scheme scheme(
diff --git a/components/autofill/content/common/autofill_types_struct_traits_unittest.cc b/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
index 6c0b146..7d5fd34 100644
--- a/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
+++ b/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
@@ -11,6 +11,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_generation_util.h"
 #include "components/autofill/core/common/signatures_util.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
@@ -231,6 +232,12 @@
     std::move(callback).Run(s);
   }
 
+  void PassPasswordGenerationUIData(
+      const password_generation::PasswordGenerationUIData& s,
+      PassPasswordGenerationUIDataCallback callback) override {
+    std::move(callback).Run(s);
+  }
+
   void PassPasswordForm(const PasswordForm& s,
                         PassPasswordFormCallback callback) override {
     std::move(callback).Run(s);
diff --git a/components/autofill/content/common/test_autofill_types.mojom b/components/autofill/content/common/test_autofill_types.mojom
index b578eb4..a0a0234 100644
--- a/components/autofill/content/common/test_autofill_types.mojom
+++ b/components/autofill/content/common/test_autofill_types.mojom
@@ -15,4 +15,5 @@
   PassPasswordFormFillData(PasswordFormFillData s) => (PasswordFormFillData passed);
   PassFormsPredictionsMap(FormsPredictionsMap s) => (FormsPredictionsMap passed);
   PassPasswordFormGenerationData(PasswordFormGenerationData s) => (PasswordFormGenerationData passed);
+  PassPasswordGenerationUIData(PasswordGenerationUIData s) => (PasswordGenerationUIData passed);
 };
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index 4b1d4c1..1c8a33d19 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -181,7 +181,6 @@
     service_manager::BinderRegistry* registry)
     : content::RenderFrameObserver(render_frame),
       password_is_generated_(false),
-      is_manually_triggered_(false),
       password_edited_(false),
       generation_popup_shown_(false),
       editing_popup_shown_(false),
@@ -540,7 +539,6 @@
                   .Utf8())));
   generation_form_data_.reset(new AccountCreationFormData(
       make_linked_ptr(password_form.release()), password_elements));
-  is_manually_triggered_ = true;
   return true;
 }
 
@@ -577,7 +575,7 @@
   // the password suggestion.
   if (!element->IsReadOnly() && element->IsEnabled() &&
       element->Value().length() <= kMaximumOfferSize) {
-    ShowGenerationPopup();
+    AutomaticGenerationStatusChanged(true);
     return true;
   }
 
@@ -596,7 +594,7 @@
     }
 
     // Offer generation again.
-    ShowGenerationPopup();
+    AutomaticGenerationStatusChanged(true);
   } else if (password_is_generated_) {
     password_edited_ = true;
     // Mirror edits to any confirmation password fields.
@@ -613,22 +611,47 @@
     // Password isn't generated and there are fewer than kMaximumOfferSize
     // characters typed, so keep offering the password. Note this function
     // will just keep the previous popup if one is already showing.
-    ShowGenerationPopup();
+    AutomaticGenerationStatusChanged(true);
   }
 
   return true;
 }
 
-void PasswordGenerationAgent::ShowGenerationPopup() {
+void PasswordGenerationAgent::AutomaticGenerationStatusChanged(bool available) {
+  if (available) {
+    if (!render_frame() || generation_element_.IsNull())
+      return;
+    LogMessage(
+        Logger::STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE);
+    autofill::password_generation::PasswordGenerationUIData
+        password_generation_ui_data(
+            render_frame()->GetRenderView()->ElementBoundsInWindow(
+                generation_element_),
+            generation_element_.MaxLength(),
+            generation_element_.NameForAutofill().Utf16(),
+            *generation_form_data_->form);
+    GetPasswordManagerClient()->AutomaticGenerationStatusChanged(
+        true, password_generation_ui_data);
+    generation_popup_shown_ = true;
+  } else {
+    GetPasswordManagerClient()->AutomaticGenerationStatusChanged(false,
+                                                                 base::nullopt);
+  }
+}
+
+void PasswordGenerationAgent::ShowManualGenerationPopup() {
   if (!render_frame() || generation_element_.IsNull())
     return;
-  LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP);
-  GetPasswordManagerClient()->ShowPasswordGenerationPopup(
-      render_frame()->GetRenderView()->ElementBoundsInWindow(
-          generation_element_),
-      generation_element_.MaxLength(),
-      generation_element_.NameForAutofill().Utf16(), is_manually_triggered_,
-      *generation_form_data_->form);
+  LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP);
+  autofill::password_generation::PasswordGenerationUIData
+      password_generation_ui_data(
+          render_frame()->GetRenderView()->ElementBoundsInWindow(
+              generation_element_),
+          generation_element_.MaxLength(),
+          generation_element_.NameForAutofill().Utf16(),
+          *generation_form_data_->form);
+  GetPasswordManagerClient()->ShowManualPasswordGenerationPopup(
+      password_generation_ui_data);
   generation_popup_shown_ = true;
 }
 
@@ -666,7 +689,7 @@
 
 void PasswordGenerationAgent::UserTriggeredGeneratePassword() {
   if (SetUpUserTriggeredGeneration())
-    ShowGenerationPopup();
+    ShowManualGenerationPopup();
 }
 
 void PasswordGenerationAgent::UserSelectedManualGenerationOption() {
@@ -674,7 +697,7 @@
     last_focused_password_element_.SetAutofillValue(blink::WebString());
     last_focused_password_element_.SetAutofillState(
         WebAutofillState::kNotFilled);
-    ShowGenerationPopup();
+    ShowManualGenerationPopup();
   }
 }
 
diff --git a/components/autofill/content/renderer/password_generation_agent.h b/components/autofill/content/renderer/password_generation_agent.h
index a669c07..ad2534a 100644
--- a/components/autofill/content/renderer/password_generation_agent.h
+++ b/components/autofill/content/renderer/password_generation_agent.h
@@ -118,8 +118,14 @@
   // all required information is collected.
   bool SetUpUserTriggeredGeneration();
 
-  // Show password generation UI anchored at |generation_element_|.
-  void ShowGenerationPopup();
+  // Signals the browser that it should offer or rescind automatic password
+  // generation depending whether the user has just focused a form field
+  // suitable for generation or has changed focus from such a field.
+  void AutomaticGenerationStatusChanged(bool available);
+
+  // Show password generation UI anchored at |generation_element_|. This is
+  // only called for manual password generation.
+  void ShowManualGenerationPopup();
 
   // Show UI for editing a generated password at |generation_element_|.
   void ShowEditingPopup();
@@ -176,9 +182,6 @@
   // password.
   bool password_is_generated_;
 
-  // True if 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_;
@@ -186,6 +189,10 @@
   // 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
diff --git a/components/autofill/core/common/BUILD.gn b/components/autofill/core/common/BUILD.gn
index 8e15cd7..3c2d7d6 100644
--- a/components/autofill/core/common/BUILD.gn
+++ b/components/autofill/core/common/BUILD.gn
@@ -52,6 +52,7 @@
     "//base",
     "//base:i18n",
     "//components/variations",
+    "//ui/gfx/geometry",
     "//url",
   ]
 
diff --git a/components/autofill/core/common/password_generation_util.cc b/components/autofill/core/common/password_generation_util.cc
index 530f75f..710fd602 100644
--- a/components/autofill/core/common/password_generation_util.cc
+++ b/components/autofill/core/common/password_generation_util.cc
@@ -22,6 +22,20 @@
 PasswordGenerationActions::~PasswordGenerationActions() {
 }
 
+PasswordGenerationUIData::PasswordGenerationUIData(
+    const gfx::RectF& bounds,
+    int max_length,
+    const base::string16& generation_element,
+    const autofill::PasswordForm& password_form)
+    : bounds(bounds),
+      max_length(max_length),
+      generation_element(generation_element),
+      password_form(password_form) {}
+
+PasswordGenerationUIData::PasswordGenerationUIData() = default;
+
+PasswordGenerationUIData::~PasswordGenerationUIData() = default;
+
 void LogUserActions(PasswordGenerationActions actions) {
   UserAction action = IGNORE_FEATURE;
   if (actions.password_accepted) {
diff --git a/components/autofill/core/common/password_generation_util.h b/components/autofill/core/common/password_generation_util.h
index 0d1547a..e1fed68 100644
--- a/components/autofill/core/common/password_generation_util.h
+++ b/components/autofill/core/common/password_generation_util.h
@@ -5,7 +5,11 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_
 #define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_
 
+#include "components/autofill/core/common/password_form.h"
+#include "ui/gfx/geometry/rect_f.h"
+
 namespace autofill {
+
 namespace password_generation {
 
 // Enumerates various events related to the password generation process.
@@ -90,6 +94,29 @@
   ~PasswordGenerationActions();
 };
 
+struct PasswordGenerationUIData {
+  PasswordGenerationUIData(const gfx::RectF& bounds,
+                           int max_length,
+                           const base::string16& generation_element,
+                           const autofill::PasswordForm& password_form);
+  PasswordGenerationUIData();
+  ~PasswordGenerationUIData();
+
+  // Location at which to display a popup if needed. This location is specified
+  // in the renderer's coordinate system. The popup will be anchored at
+  // |bounds|.
+  gfx::RectF bounds;
+
+  // Maximum length of the generated password.
+  int max_length;
+
+  // Name of the password field to which the generation popup is attached.
+  base::string16 generation_element;
+
+  // The form associated with the password field.
+  autofill::PasswordForm password_form;
+};
+
 void LogUserActions(PasswordGenerationActions actions);
 
 void LogPasswordGenerationEvent(PasswordGenerationEvent event);
diff --git a/components/autofill/core/common/save_password_progress_logger.cc b/components/autofill/core/common/save_password_progress_logger.cc
index 17f6da45b..859ab7041 100644
--- a/components/autofill/core/common/save_password_progress_logger.cc
+++ b/components/autofill/core/common/save_password_progress_logger.cc
@@ -410,8 +410,10 @@
       return "Generation: eligible form found";
     case STRING_GENERATION_RENDERER_NO_FIELD_FOUND:
       return "Generation: fields for generation are not found";
-    case STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP:
-      return "Show generation popup";
+    case STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE:
+      return "Generation: automatic generation is available";
+    case STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP:
+      return "Show generation popup triggered manually";
     case STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED:
       return "Generated password accepted";
     case STRING_SUCCESSFUL_SUBMISSION_INDICATOR_EVENT:
diff --git a/components/autofill/core/common/save_password_progress_logger.h b/components/autofill/core/common/save_password_progress_logger.h
index cb0523f..65389f4 100644
--- a/components/autofill/core/common/save_password_progress_logger.h
+++ b/components/autofill/core/common/save_password_progress_logger.h
@@ -160,7 +160,8 @@
     STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL,
     STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND,
     STRING_GENERATION_RENDERER_NO_FIELD_FOUND,
-    STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP,
+    STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE,
+    STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP,
     STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED,
     STRING_SUCCESSFUL_SUBMISSION_INDICATOR_EVENT,
     STRING_MAIN_FRAME_ORIGIN,