blob: 341a3460ddcfb18aea5b2abf91dfb48df27448d8 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/pending_beacon_service.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "content/browser/renderer_host/pending_beacon_host.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/data_element.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
constexpr net::NetworkTrafficAnnotationTag kPendingBeaconNetworkTag =
net::DefineNetworkTrafficAnnotation("pending_beacon_api",
R"(
semantics {
sender: "Pending Beacon API"
description:
"This request sends out a pending beacon data as single HTTP POST "
" or GET request. This is used similarly to javascript "
"`navigator.sendBeacon`, but can be sent either manually by a "
"developer using the `sendNow` method, or automatically by the "
"browser when a document is being discarded."
trigger:
"On document destruction or `PendingBeacon.sendNow` is called."
data:
"Data sent by the beacon is set by javascript on the page."
destination: WEBSITE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting: "This feature cannot be fully disabled. Only for the beacon "
"requests sent on document discarded, they can be disabled via the "
"`Background sync` section under the `Privacy and security` tab "
"in chrome://settings. This feature is enabled by default."
policy_exception_justification: "The policy for Background sync is "
"not yet implemented."
}
)");
namespace content {
class Beacon;
PendingBeaconService* PendingBeaconService::GetInstance() {
return base::Singleton<PendingBeaconService>::get();
}
PendingBeaconService::PendingBeaconService() = default;
PendingBeaconService::~PendingBeaconService() = default;
void PendingBeaconService::SendBeacons(
const std::vector<std::unique_ptr<Beacon>>& beacons,
network::SharedURLLoaderFactory* shared_url_loader_factory) {
for (const auto& beacon : beacons) {
auto resource_request = beacon->GenerateResourceRequest();
// SimpleURLLoader doesn't support bytes and file request body. We need to
// call AttachStringForUpload and AttachFileForUpload instead in such cases.
absl::optional<network::DataElement> element;
if (resource_request->request_body) {
auto& elements = *resource_request->request_body->elements_mutable();
DCHECK_EQ(elements.size(), 1u);
if (elements[0].type() == network::DataElement::Tag::kBytes ||
elements[0].type() == network::DataElement::Tag::kFile) {
element = std::move(elements[0]);
resource_request->request_body = nullptr;
}
}
std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
network::SimpleURLLoader::Create(std::move(resource_request),
kPendingBeaconNetworkTag);
if (element.has_value()) {
const auto& content_type = beacon->content_type();
if (element->type() == network::DataElement::Tag::kBytes) {
const auto& bytes = element->As<network::DataElementBytes>();
if (content_type.empty()) {
simple_url_loader->AttachStringForUpload(
std::string(bytes.AsStringPiece()));
} else {
simple_url_loader->AttachStringForUpload(
std::string(bytes.AsStringPiece()), content_type);
}
} else if (element->type() == network::DataElement::Tag::kFile) {
const auto& file = element->As<network::DataElementFile>();
if (content_type.empty()) {
simple_url_loader->AttachFileForUpload(file.path(), file.offset(),
file.length());
} else {
simple_url_loader->AttachFileForUpload(file.path(), content_type,
file.offset(), file.length());
}
} else {
NOTREACHED();
}
}
network::SimpleURLLoader* simple_url_loader_ptr = simple_url_loader.get();
// Send out the |beacon|.
// The PendingBeaconService is a singleton with a lifetime the same as the
// browser process', so it's safe to capture it here.
UMA_HISTOGRAM_ENUMERATION("PendingBeaconHost.Action",
PendingBeaconHost::Action::kNetworkSend);
simple_url_loader_ptr->DownloadHeadersOnly(
shared_url_loader_factory,
base::BindOnce(
[](std::unique_ptr<network::SimpleURLLoader> loader,
scoped_refptr<net::HttpResponseHeaders> headers) {
// Intentionally left empty, this callback captures the |loader|
// so it stays alive until the beacon request, i.e.
// |DownloadHeadersOnly|, completes.
UMA_HISTOGRAM_ENUMERATION(
"PendingBeaconHost.Action",
PendingBeaconHost::Action::kNetworkComplete);
},
std::move(simple_url_loader)));
}
}
} // namespace content