Record if the smart bubble suppresses credentials.

In PasswordManager.FillingAssistance we record whether the user was
supported at logging into a site. One case is that this is not happening
because the user has no saved credentials. This CL introduces an enum to
indicate that there are no saved credentials and the user does not get offered
to save them anymore.

(cherry picked from commit 7cd82db22d43e7abd815b93eea13aae7dfa47fb4)

Bug: 918846
Change-Id: I2a041649e629ea38f859a4d88204db6b8fddfb2c
Reviewed-on: https://chromium-review.googlesource.com/c/1448164
Commit-Queue: Dominic Battré <battre@chromium.org>
Reviewed-by: Vadym Doroshenko <dvadym@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#627969}
Reviewed-on: https://chromium-review.googlesource.com/c/1451936
Reviewed-by: Dominic Battré <battre@chromium.org>
Cr-Commit-Position: refs/branch-heads/3683@{#156}
Cr-Branched-From: e51029943e0a38dd794b73caaf6373d5496ae783-refs/heads/master@{#625896}
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index 0f3e883..3e8a5ab 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -1065,7 +1065,8 @@
   saved_usernames.erase(base::string16());
 
   metrics_recorder_->CalculateFillingAssistanceMetric(
-      submitted_form, saved_usernames, saved_passwords);
+      submitted_form, saved_usernames, saved_passwords,
+      form_fetcher_->GetInteractionsStats());
 #endif
 }
 
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.cc b/components/password_manager/core/browser/password_form_metrics_recorder.cc
index cb67f45..8261074 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.cc
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.cc
@@ -13,7 +13,9 @@
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/password_generation_util.h"
 #include "components/password_manager/core/browser/form_fetcher.h"
+#include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 
 using autofill::FieldPropertiesFlags;
 using autofill::FormData;
@@ -121,6 +123,26 @@
   return result;
 }
 
+// Returns whether any value of |submitted_form| is listed in the
+// |interactions_stats| has having been prompted to save as a credential and
+// being ignored too often.
+bool BlacklistedBySmartBubble(
+    const FormData& submitted_form,
+    const std::vector<InteractionsStats>& interactions_stats) {
+  const int show_threshold =
+      password_bubble_experiment::GetSmartBubbleDismissalThreshold();
+  for (const FormFieldData& field : submitted_form.fields) {
+    const base::string16& value =
+        field.typed_value.empty() ? field.value : field.typed_value;
+    for (const InteractionsStats& stat : interactions_stats) {
+      if (stat.username_value == value &&
+          stat.dismissal_count >= show_threshold)
+        return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace
 
 PasswordFormMetricsRecorder::PasswordFormMetricsRecorder(
@@ -409,29 +431,28 @@
 void PasswordFormMetricsRecorder::CalculateFillingAssistanceMetric(
     const FormData& submitted_form,
     const std::set<base::string16>& saved_usernames,
-    const std::set<base::string16>& saved_passwords) {
+    const std::set<base::string16>& saved_passwords,
+    const std::vector<InteractionsStats>& interactions_stats) {
   // If the user asked to never save credentials on a domain, an entry with
   // empty password exists for that domain.
   bool blacklisted =
       saved_passwords.find(base::string16()) != saved_passwords.end();
   if (blacklisted && saved_passwords.size() == 1u) {
-    // Note that we miss two nuances here:
+    // Note that we miss a nuance here:
     //
-    // 1) It is possible that the user logs in to a.example.com but
-    // b.example.com is blacklisted. We would still report
-    // kNoSavedCredentialsAndBlacklisted even though the user has not
-    // blacklisted a.example.com and is asked whether they want to save the
-    // credentials.
-    //
-    // 2) It is possible that the user ignored the save bubble a number of times
-    // (default threshold is 3). In this case, no credential is stored and we
-    // report kNoSavedCredentials and not kNoSavedCredentialsAndBlacklisted.
+    // It is possible that the user logs in to a.example.com but b.example.com
+    // is blacklisted. We would still report kNoStoredCredentialsAndBlacklisted
+    // even though the user has not blacklisted a.example.com and is asked
+    // whether they want to save the credentials.
     filling_assistance_ = FillingAssistance::kNoSavedCredentialsAndBlacklisted;
     return;
   }
 
   if (saved_passwords.empty()) {
-    filling_assistance_ = FillingAssistance::kNoSavedCredentials;
+    filling_assistance_ =
+        BlacklistedBySmartBubble(submitted_form, interactions_stats)
+            ? FillingAssistance::kNoSavedCredentialsAndBlacklistedBySmartBubble
+            : FillingAssistance::kNoSavedCredentials;
     return;
   }
 
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.h b/components/password_manager/core/browser/password_form_metrics_recorder.h
index ae9658f..265a010 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.h
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.h
@@ -29,6 +29,7 @@
 namespace password_manager {
 
 class FormFetcher;
+struct InteractionsStats;
 
 // The pupose of this class is to record various types of metrics about the
 // behavior of the PasswordFormManager and its interaction with the user and
@@ -268,7 +269,10 @@
     kNoUserInputNoFillingInPasswordFields = 6,
     // Domain is blacklisted and no other credentials exist.
     kNoSavedCredentialsAndBlacklisted = 7,
-    kMaxValue = kNoSavedCredentialsAndBlacklisted,
+    // No credentials exist and the user has ignored the save bubble too often,
+    // meaning that they won't be asked to save credentials anymore.
+    kNoSavedCredentialsAndBlacklistedBySmartBubble = 8,
+    kMaxValue = kNoSavedCredentialsAndBlacklistedBySmartBubble,
   };
 
   // The maximum number of combinations of the ManagerAction, UserAction and
@@ -400,7 +404,8 @@
   void CalculateFillingAssistanceMetric(
       const autofill::FormData& submitted_form,
       const std::set<base::string16>& saved_usernames,
-      const std::set<base::string16>& saved_passwords);
+      const std::set<base::string16>& saved_passwords,
+      const std::vector<InteractionsStats>& interactions_stats);
 
  private:
   friend class base::RefCounted<PasswordFormMetricsRecorder>;
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc b/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
index 357b5b3..8d31f79 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
+++ b/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
@@ -712,6 +713,7 @@
   std::vector<FieldInfo> fields;
   std::vector<std::string> saved_usernames;
   std::vector<std::string> saved_passwords;
+  std::vector<InteractionsStats> interactions_stats;
 
   base::Optional<PasswordFormMetricsRecorder::FillingAssistance> expectation;
 };
@@ -769,9 +771,11 @@
 
     auto recorder =
         CreatePasswordFormMetricsRecorder(is_main_frame_secure, nullptr);
-    if (test_case.submission_detected)
+    if (test_case.submission_detected) {
       recorder->CalculateFillingAssistanceMetric(form_data, saved_usernames,
-                                                 saved_passwords);
+                                                 saved_passwords,
+                                                 test_case.interactions_stats);
+    }
 
     if (test_case.submission_is_successful)
       recorder->LogSubmitPassed();
@@ -1039,4 +1043,18 @@
            PasswordFormMetricsRecorder::FillingAssistance::kAutomatic});
 }
 
+TEST(PasswordFormMetricsRecorder, FillingAssistanceBlacklistedBySmartBubble) {
+  CheckFillingAssistanceTestCase(
+      {.description_for_logging = "Submission without saved credentials while "
+                                  "smart bubble suppresses saving",
+       .fields = {{.value = "user1"},
+                  {.value = "password1", .is_password = true}},
+       .saved_usernames = {},
+       .saved_passwords = {},
+       .interactions_stats = {{.username_value = ASCIIToUTF16("user1"),
+                               .dismissal_count = 10}},
+       .expectation = PasswordFormMetricsRecorder::FillingAssistance::
+           kNoSavedCredentialsAndBlacklistedBySmartBubble});
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/statistics_table.cc b/components/password_manager/core/browser/statistics_table.cc
index da129d5..d9105d4 100644
--- a/components/password_manager/core/browser/statistics_table.cc
+++ b/components/password_manager/core/browser/statistics_table.cc
@@ -41,8 +41,6 @@
 
 }  // namespace
 
-InteractionsStats::InteractionsStats() = default;
-
 bool operator==(const InteractionsStats& lhs, const InteractionsStats& rhs) {
   return lhs.origin_domain == rhs.origin_domain &&
          lhs.username_value == rhs.username_value &&
diff --git a/components/password_manager/core/browser/statistics_table.h b/components/password_manager/core/browser/statistics_table.h
index af880c9..b24389e 100644
--- a/components/password_manager/core/browser/statistics_table.h
+++ b/components/password_manager/core/browser/statistics_table.h
@@ -21,8 +21,6 @@
 
 // The statistics containing user interactions with a site.
 struct InteractionsStats {
-  InteractionsStats();
-
   // The domain of the site.
   GURL origin_domain;
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 927471d..de60370 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -41243,6 +41243,9 @@
   <int value="7"
       label=":-X Unknown password typed, no saved credentials existed and
              site was explicitly blacklisted"/>
+  <int value="8"
+      label=":-X Unknown password typed, no saved credentials existed and
+             site is blacklisted by the smart bubble"/>
 </enum>
 
 <enum name="PasswordManagerFirstRendererFillingResult">