blob: 9b5a91a101b0bafd1b76469b90d9aa1df5e17adf [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.h"
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "components/autofill/core/common/signatures.h"
#include "components/autofill_assistant/browser/public/autofill_assistant.h"
#include "components/keyed_service/core/keyed_service.h"
#include "net/http/http_status_code.h"
#include "url/gurl.h"
#include "url/origin.h"
using autofill_assistant::AutofillAssistant;
using CapabilitiesInfo =
autofill_assistant::AutofillAssistant::CapabilitiesInfo;
namespace {
constexpr uint32_t kFastCheckoutHashPrefixSize = 15u;
constexpr char kFastCheckoutIntent[] = "CHROME_FAST_CHECKOUT";
constexpr char kUmaKeyHttpCode[] =
"Autofill.FastCheckout.CapabilitiesFetcher.HttpResponseCode";
constexpr char kUmaKeyCacheStateIsTriggerFormSupported[] =
"Autofill.FastCheckout.CapabilitiesFetcher."
"CacheStateForIsTriggerFormSupported";
} // namespace
FastCheckoutCapabilitiesFetcherImpl::FastCheckoutCapabilitiesFetcherImpl(
std::unique_ptr<AutofillAssistant> autofill_assistant)
: autofill_assistant_(std::move(autofill_assistant)) {}
FastCheckoutCapabilitiesFetcherImpl::~FastCheckoutCapabilitiesFetcherImpl() =
default;
void FastCheckoutCapabilitiesFetcherImpl::FetchAvailability(
const url::Origin& origin,
Callback callback) {
// If `origin` is already cached, no request needs to be made.
if (cache_.ContainsOrigin(origin)) {
// TODO(crbug.com/1350456): Record UMA.
std::move(callback).Run(/*success=*/true);
return;
}
// Check whether there is an ongoing request. If so, queue up the callback
// and return.
if (base::flat_map<url::Origin, std::vector<Callback>>::iterator it =
ongoing_requests_.find(origin);
it != ongoing_requests_.end()) {
// TODO(crbug.com/1350456): Record UMA.
it->second.emplace_back(std::move(callback));
return;
}
// Create a new request.
uint64_t hash_prefix =
AutofillAssistant::GetHashPrefix(kFastCheckoutHashPrefixSize, origin);
ongoing_requests_[origin].emplace_back(std::move(callback));
// Since `this` owns `autofill_assistant_` and `autofill_assistant_`,
// callbacks are only executed while `this` is alive.
autofill_assistant_->GetCapabilitiesByHashPrefix(
kFastCheckoutHashPrefixSize, {hash_prefix}, kFastCheckoutIntent,
base::BindOnce(&FastCheckoutCapabilitiesFetcherImpl::
OnGetCapabilitiesInformationReceived,
base::Unretained(this), origin));
// TODO(crbug.com/1350456): Record UMA.
}
bool FastCheckoutCapabilitiesFetcherImpl::IsTriggerFormSupported(
const url::Origin& origin,
autofill::FormSignature 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(
const url::Origin& origin,
int http_status,
const std::vector<CapabilitiesInfo>& capabilities) {
base::flat_map<url::Origin, std::vector<Callback>>::iterator request =
ongoing_requests_.find(origin);
if (request == ongoing_requests_.end()) {
// There should always be exactly one ongoing request per origin.
NOTREACHED();
return;
}
base::UmaHistogramSparse(kUmaKeyHttpCode, http_status);
// Short-hand for executing all callbacks.
auto inform_callers = [request](bool outcome) {
for (Callback& callback : request->second) {
std::move(callback).Run(outcome);
}
};
// If the request was unsuccessful, inform the callers, but do not update
// the cache.
if (http_status != net::HTTP_OK) {
inform_callers(false);
ongoing_requests_.erase(request);
return;
}
std::vector<CapabilitiesInfo>::const_iterator request_capabilities =
base::ranges::find_if(
capabilities, [&origin](const CapabilitiesInfo& info) {
return url::Origin::Create(GURL(info.url)) == origin;
});
if (request_capabilities != capabilities.end() &&
request_capabilities->bundle_capabilities_information.has_value()) {
cache_.AddToCache(
origin,
FastCheckoutCapabilitiesResult(
request_capabilities->bundle_capabilities_information.value()
.trigger_form_signatures));
} else {
// If no form signatures are supported, save that into the cache, too.
cache_.AddToCache(origin, FastCheckoutCapabilitiesResult());
}
inform_callers(true);
ongoing_requests_.erase(request);
}