blob: 83025ae38916136466023f251731f1e425cd6e1e [file] [log] [blame]
// Copyright 2020 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 "components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher.h"
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/json/json_reader.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "components/autofill_assistant/browser/features.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"
namespace autofill_assistant {
namespace {
bool ExtractStrings(const base::Value& json,
AutofillAssistantOnboardingFetcher::StringMap& string_map) {
for (const auto& intent_it : json.DictItems()) {
const auto& intent = intent_it.first;
if (!intent_it.second.is_dict()) {
return false;
}
base::flat_map<std::string, std::string> strings;
for (const auto& string_it : intent_it.second.DictItems()) {
const auto& string_id = string_it.first;
if (!string_it.second.is_string()) {
return false;
}
strings[string_id] = string_it.second.GetString();
}
string_map[intent] = strings;
}
return true;
}
} // namespace
constexpr char kDefaultOnboardingDataUrlPattern[] =
"https://www.gstatic.com/autofill_assistant/$1/onboarding_definition.json";
constexpr int kMaxDownloadSizeInBytes = 10 * 1024;
constexpr char kTrafficAnnotationId[] = "gstatic_onboarding_definition";
constexpr char kTrafficAnnotationDefinition[] = R"(
semantics {
sender: "Autofill Assistant"
description:
"A JSON file hosted by gstatic containing a definition of "
"content for onboarding."
trigger:
"When Autofill Assistant starts for a user that has not previously "
"accepted the onboarding."
data:
"The request body is empty. No user data is included."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
})";
AutofillAssistantOnboardingFetcher::AutofillAssistantOnboardingFetcher(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: url_loader_factory_(std::move(url_loader_factory)) {}
AutofillAssistantOnboardingFetcher::~AutofillAssistantOnboardingFetcher() =
default;
void AutofillAssistantOnboardingFetcher::FetchOnboardingDefinition(
const std::string& intent,
const std::string& locale,
int timeout_ms,
ResponseCallback callback) {
pending_callbacks_.emplace_back(
base::BindOnce(&AutofillAssistantOnboardingFetcher::RunCallback,
base::Unretained(this), intent, std::move(callback)));
StartFetch(locale, timeout_ms);
}
void AutofillAssistantOnboardingFetcher::StartFetch(const std::string& locale,
int timeout_ms) {
static const base::NoDestructor<base::TimeDelta> kFetchTimeout(
base::TimeDelta::FromMilliseconds(timeout_ms));
if (url_loader_) {
return;
}
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = GURL(base::ReplaceStringPlaceholders(
kDefaultOnboardingDataUrlPattern, {locale}, /* offset= */ nullptr));
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation(kTrafficAnnotationId,
kTrafficAnnotationDefinition);
url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
traffic_annotation);
url_loader_->SetTimeoutDuration(*kFetchTimeout);
url_loader_->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&AutofillAssistantOnboardingFetcher::OnFetchComplete,
base::Unretained(this)),
kMaxDownloadSizeInBytes);
}
void AutofillAssistantOnboardingFetcher::OnFetchComplete(
std::unique_ptr<std::string> response_body) {
url_loader_.reset();
ResultStatus result_status = ParseResponse(std::move(response_body));
base::UmaHistogramEnumeration(
"AutofillAssistant.AutofillAssistantOnboardingFetcher.ResultStatus",
result_status);
for (auto& callback : pending_callbacks_) {
std::move(callback).Run();
}
pending_callbacks_.clear();
}
AutofillAssistantOnboardingFetcher::ResultStatus
AutofillAssistantOnboardingFetcher::ParseResponse(
std::unique_ptr<std::string> response_body) {
onboarding_strings_.clear();
if (!response_body) {
return ResultStatus::kNoBody;
}
base::JSONReader::ValueWithError data =
base::JSONReader::ReadAndReturnValueWithError(*response_body);
if (data.value == base::nullopt) {
DVLOG(1) << "Parse error: " << data.error_message;
return ResultStatus::kInvalidJson;
}
if (!data.value->is_dict()) {
return ResultStatus::kInvalidData;
}
return ExtractStrings(*data.value, onboarding_strings_)
? ResultStatus::kOk
: ResultStatus::kInvalidData;
}
void AutofillAssistantOnboardingFetcher::RunCallback(
const std::string& intent,
ResponseCallback callback) {
std::move(callback).Run(onboarding_strings_[intent]);
}
} // namespace autofill_assistant