[FastCheckout] Add form cache state UMA in CapabilitiesFetcher.

This CL adds support for recording the cache state at the point
where availability for a given form signature on a given origin
is looked up.

Bug: 1350456, 1334642
Change-Id: Ib7318aee1eac2b4161fed7205609d1bb1de7249f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3829265
Commit-Queue: Jan Keitel <jkeitel@google.com>
Reviewed-by: Norge Vizcay <vizcay@google.com>
Reviewed-by: Vidhan Jain <vidhanj@google.com>
Cr-Commit-Position: refs/heads/main@{#1036577}
diff --git a/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.cc b/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.cc
index 4f73784..9b5a91a 100644
--- a/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.cc
+++ b/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.cc
@@ -27,6 +27,9 @@
 constexpr char kFastCheckoutIntent[] = "CHROME_FAST_CHECKOUT";
 constexpr char kUmaKeyHttpCode[] =
     "Autofill.FastCheckout.CapabilitiesFetcher.HttpResponseCode";
+constexpr char kUmaKeyCacheStateIsTriggerFormSupported[] =
+    "Autofill.FastCheckout.CapabilitiesFetcher."
+    "CacheStateForIsTriggerFormSupported";
 }  // namespace
 
 FastCheckoutCapabilitiesFetcherImpl::FastCheckoutCapabilitiesFetcherImpl(
@@ -73,9 +76,26 @@
 bool FastCheckoutCapabilitiesFetcherImpl::IsTriggerFormSupported(
     const url::Origin& origin,
     autofill::FormSignature form_signature) {
-  // TODO(crbug.com/1350456): Check whether there is an ongoing request. If so,
-  // record a UMA to indicate that the request resolution was not fast enough.
-  return cache_.ContainsTriggerForm(origin, form_signature);
+  if (cache_.ContainsTriggerForm(origin, form_signature)) {
+    base::UmaHistogramEnumeration(
+        kUmaKeyCacheStateIsTriggerFormSupported,
+        CacheStateForIsTriggerFormSupported::kEntryAvailableAndFormSupported);
+    return true;
+  }
+
+  // Analyze why the result is `false` to record the correct metric.
+  if (cache_.ContainsOrigin(origin)) {
+    base::UmaHistogramEnumeration(kUmaKeyCacheStateIsTriggerFormSupported,
+                                  CacheStateForIsTriggerFormSupported::
+                                      kEntryAvailableAndFormNotSupported);
+  } else {
+    base::UmaHistogramEnumeration(
+        kUmaKeyCacheStateIsTriggerFormSupported,
+        ongoing_requests_.contains(origin)
+            ? CacheStateForIsTriggerFormSupported::kFetchOngoing
+            : CacheStateForIsTriggerFormSupported::kNeverFetched);
+  }
+  return false;
 }
 
 void FastCheckoutCapabilitiesFetcherImpl::OnGetCapabilitiesInformationReceived(
diff --git a/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.h b/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.h
index c8d12bb..594bfa8 100644
--- a/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.h
+++ b/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.h
@@ -22,6 +22,31 @@
 class FastCheckoutCapabilitiesFetcherImpl
     : public FastCheckoutCapabilitiesFetcher {
  public:
+  // Possible different cache states that `FastCheckoutCapabilitiesFetcherImpl`
+  // can encounter when `IsTriggerFormSupported` is called.
+  //
+  // Do not remove or renumber entries in this enum. It needs to be kept in
+  // sync with `FastCheckoutCacheStateForIsTriggerFormSupported` in `enums.xml`.
+  enum class CacheStateForIsTriggerFormSupported {
+    // Availability is currently being fetched for this entry, but the request
+    // has not completed yet.
+    kFetchOngoing = 0,
+
+    // There is a valid cache entry for this origin and the form signature that
+    // is being checked is not supported.
+    kEntryAvailableAndFormNotSupported = 1,
+
+    // There is a valid cache entry for this origin and the form signature that
+    // is being checked is supported.
+    kEntryAvailableAndFormSupported = 2,
+
+    // No availability was fetched for this origin within the lifetime of the
+    // cache.
+    kNeverFetched = 3,
+
+    kMaxValue = kNeverFetched
+  };
+
   explicit FastCheckoutCapabilitiesFetcherImpl(
       std::unique_ptr<autofill_assistant::AutofillAssistant>
           autofill_assistant);
diff --git a/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl_unittest.cc b/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl_unittest.cc
index b5be2350..c8ce02f7 100644
--- a/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl_unittest.cc
+++ b/chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl_unittest.cc
@@ -27,13 +27,18 @@
 using CapabilitiesInfo =
     autofill_assistant::AutofillAssistant::CapabilitiesInfo;
 using autofill_assistant::MockAutofillAssistant;
-using ::base::test::RunOnceCallback;
-using ::testing::_;
+using base::test::RunOnceCallback;
+using CacheStateForIsTriggerFormSupported =
+    FastCheckoutCapabilitiesFetcherImpl::CacheStateForIsTriggerFormSupported;
+using testing::_;
 
 constexpr uint32_t kHashPrefixSize = 15u;
 constexpr char kIntent[] = "CHROME_FAST_CHECKOUT";
 constexpr char kUmaKeyHttpCode[] =
     "Autofill.FastCheckout.CapabilitiesFetcher.HttpResponseCode";
+constexpr char kUmaKeyCacheStateIsTriggerFormSupported[] =
+    "Autofill.FastCheckout.CapabilitiesFetcher."
+    "CacheStateForIsTriggerFormSupported";
 
 constexpr char kUrl1[] = "https://wwww.firstpage.com/";
 constexpr char kUrl2[] = "https://wwww.another-domain.co.uk/";
@@ -276,4 +281,61 @@
   EXPECT_TRUE(fetcher()->IsTriggerFormSupported(origin2, kFormSignature3));
 }
 
+TEST_F(FastCheckoutCapabilitiesFetcherImplTest,
+       IsTriggerFormSupportedRecordsUmaMetrics) {
+  url::Origin origin1 = url::Origin::Create(GURL(kUrl1));
+  uint64_t hash1 = AutofillAssistant::GetHashPrefix(kHashPrefixSize, origin1);
+
+  // The cache is empty.
+  EXPECT_FALSE(fetcher()->IsTriggerFormSupported(origin1, kFormSignature1));
+  EXPECT_FALSE(fetcher()->IsTriggerFormSupported(origin1, kFormSignature2));
+  histogram_tester().ExpectUniqueSample(
+      kUmaKeyCacheStateIsTriggerFormSupported,
+      CacheStateForIsTriggerFormSupported::kNeverFetched, 2u);
+
+  AutofillAssistant::GetCapabilitiesResponseCallback response_callback1;
+  EXPECT_CALL(*autofill_assistant(),
+              GetCapabilitiesByHashPrefix(
+                  kHashPrefixSize, std::vector<uint64_t>{hash1}, kIntent, _))
+      .Times(1)
+      .WillOnce(MoveArg<3>(&response_callback1));
+
+  base::MockCallback<FastCheckoutCapabilitiesFetcher::Callback> callback1;
+  fetcher()->FetchAvailability(origin1, callback1.Get());
+
+  // While the fetch is still ongoing, there is no availability yet.
+  EXPECT_FALSE(fetcher()->IsTriggerFormSupported(origin1, kFormSignature1));
+  histogram_tester().ExpectTotalCount(kUmaKeyCacheStateIsTriggerFormSupported,
+                                      3u);
+  histogram_tester().ExpectBucketCount(
+      kUmaKeyCacheStateIsTriggerFormSupported,
+      CacheStateForIsTriggerFormSupported::kNeverFetched, 2u);
+  histogram_tester().ExpectBucketCount(
+      kUmaKeyCacheStateIsTriggerFormSupported,
+      CacheStateForIsTriggerFormSupported::kFetchOngoing, 1u);
+
+  EXPECT_CALL(callback1, Run(true));
+  BundleCapabilitiesInformation capabilities;
+  capabilities.trigger_form_signatures.push_back(kFormSignature1);
+  CapabilitiesInfo info1{kUrl1, {}, capabilities};
+  std::move(response_callback1)
+      .Run(net::HttpStatusCode::HTTP_OK, std::vector<CapabilitiesInfo>{info1});
+
+  // The cache contains information for the first domain.
+  EXPECT_TRUE(fetcher()->IsTriggerFormSupported(origin1, kFormSignature1));
+  histogram_tester().ExpectTotalCount(kUmaKeyCacheStateIsTriggerFormSupported,
+                                      4u);
+  histogram_tester().ExpectBucketCount(
+      kUmaKeyCacheStateIsTriggerFormSupported,
+      CacheStateForIsTriggerFormSupported::kEntryAvailableAndFormSupported, 1u);
+
+  EXPECT_FALSE(fetcher()->IsTriggerFormSupported(origin1, kFormSignature2));
+  histogram_tester().ExpectTotalCount(kUmaKeyCacheStateIsTriggerFormSupported,
+                                      5u);
+  histogram_tester().ExpectBucketCount(
+      kUmaKeyCacheStateIsTriggerFormSupported,
+      CacheStateForIsTriggerFormSupported::kEntryAvailableAndFormNotSupported,
+      1u);
+}
+
 }  // namespace
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a0e884f..169d18c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -36294,6 +36294,17 @@
   <int value="4" label="Regular User"/>
 </enum>
 
+<enum name="FastCheckoutCacheStateForIsTriggerFormSupported">
+  <summary>
+    Classifies the cache state of the FastCheckout capabilities fetcher at the
+    time of checking whether a form signature on a domain is supported.
+  </summary>
+  <int value="0" label="Cache not available and request ongoing"/>
+  <int value="1" label="Cache available and form signature not supported"/>
+  <int value="2" label="Cache available and form signature supported"/>
+  <int value="3" label="Cache not available and no request made"/>
+</enum>
+
 <enum name="FastPairAccountKeyFailure">
   <int value="0" label="Failed to find the Account Key GATT characteristic"/>
   <int value="1"
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index d176de4..d4626bd 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -1202,6 +1202,19 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Autofill.FastCheckout.CapabilitiesFetcher.CacheStateForIsTriggerFormSupported"
+    enum="FastCheckoutCacheStateForIsTriggerFormSupported"
+    expires_after="2023-08-15">
+  <owner>bwolfgang@google.com</owner>
+  <owner>jkeitel@google.com</owner>
+  <owner>vizcay@google.com</owner>
+  <summary>
+    The state of the cache of the Fast Checkout capabilities fetcher at the
+    point of checking whether a given form signature on an origin is supported.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.FastCheckout.CapabilitiesFetcher.HttpResponseCode"
     enum="HttpResponseCode" expires_after="2023-08-15">
   <owner>bwolfgang@google.com</owner>