#include "content/browser/loader/keep_alive_url_loader_service.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/trace_event/typed_macros.h"
#include "content/browser/renderer_host/policy_container_host.h"
#include "content/browser/url_loader_factory_getter.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/blink/public/common/features.h"
namespace content {
namespace {
struct FactoryContext {
FactoryContext(scoped_refptr<network::SharedURLLoaderFactory> factory,
scoped_refptr<PolicyContainerHost> frame_policy_container_host)
: factory(factory),
policy_container_host(std::move(frame_policy_container_host)) {
CHECK(policy_container_host);
}
explicit FactoryContext(const std::unique_ptr<FactoryContext>& other)
: FactoryContext(other->factory, other->policy_container_host) {}
~FactoryContext() = default;
FactoryContext(const FactoryContext&) = delete;
FactoryContext& operator=(const FactoryContext&) = delete;
scoped_refptr<network::SharedURLLoaderFactory> factory;
scoped_refptr<PolicyContainerHost> policy_container_host;
base::WeakPtrFactory<FactoryContext> weak_ptr_factory{this};
};
}
class KeepAliveURLLoaderService::KeepAliveURLLoaderFactory final
: public network::mojom::URLLoaderFactory {
public:
explicit KeepAliveURLLoaderFactory(KeepAliveURLLoaderService* service)
: service_(service) {
CHECK(service_);
}
~KeepAliveURLLoaderFactory() override = default;
KeepAliveURLLoaderFactory(const KeepAliveURLLoaderFactory&) = delete;
KeepAliveURLLoaderFactory& operator=(const KeepAliveURLLoaderFactory&) =
delete;
const std::unique_ptr<FactoryContext>& current_context() const;
void BindFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory,
scoped_refptr<PolicyContainerHost> policy_container_host);
void CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& resource_request_in,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
override;
void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
override;
private:
raw_ptr<KeepAliveURLLoaderService> service_;
mojo::ReceiverSet<network::mojom::URLLoaderFactory,
std::unique_ptr<FactoryContext>>
loader_factory_receivers_;
};
const std::unique_ptr<FactoryContext>&
KeepAliveURLLoaderService::KeepAliveURLLoaderFactory::current_context() const {
return loader_factory_receivers_.current_context();
}
void KeepAliveURLLoaderService::KeepAliveURLLoaderFactory::BindFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory,
scoped_refptr<PolicyContainerHost> policy_container_host) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CHECK(policy_container_host);
TRACE_EVENT("loading", "KeepAliveURLLoaderFactory::BindFactory");
auto factory_bundle =
network::SharedURLLoaderFactory::Create(std::move(pending_factory));
auto context = std::make_unique<FactoryContext>(
factory_bundle, std::move(policy_container_host));
loader_factory_receivers_.Add(this, std::move(receiver), std::move(context));
}
void KeepAliveURLLoaderService::KeepAliveURLLoaderFactory::CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& resource_request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT("loading", "KeepAliveURLLoaderFactory::CreateLoaderAndStart",
"request_id", request_id);
if (!resource_request.keepalive) {
loader_factory_receivers_.ReportBadMessage(
"Unexpected `resource_request` in "
"KeepAliveURLLoaderService::CreateLoaderAndStart(): "
"resource_request.keepalive must be true");
return;
}
if (resource_request.trusted_params) {
loader_factory_receivers_.ReportBadMessage(
"Unexpected `resource_request` in "
"KeepAliveURLLoaderService::CreateLoaderAndStart(): "
"resource_request.trusted_params must not be set");
return;
}
const std::unique_ptr<FactoryContext>& context = current_context();
CHECK(context->policy_container_host);
auto loader = std::make_unique<KeepAliveURLLoader>(
request_id, options, resource_request, std::move(client),
traffic_annotation, context->factory,
context->policy_container_host,
base::PassKey<KeepAliveURLLoaderService>());
auto* raw_loader = loader.get();
auto receiver_id = service_->loader_receivers_.Add(
raw_loader, std::move(receiver), std::move(loader));
raw_loader->set_on_delete_callback(
base::BindOnce(&KeepAliveURLLoaderService::RemoveLoader,
base::Unretained(service_), receiver_id));
if (service_->loader_test_observer_) {
raw_loader->SetObserverForTesting(
service_->loader_test_observer_);
}
}
void KeepAliveURLLoaderService::KeepAliveURLLoaderFactory::Clone(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
loader_factory_receivers_.Add(
this, std::move(receiver),
std::make_unique<FactoryContext>(current_context()));
}
KeepAliveURLLoaderService::KeepAliveURLLoaderService() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
factory_ =
std::make_unique<KeepAliveURLLoaderService::KeepAliveURLLoaderFactory>(
this);
loader_receivers_.set_disconnect_handler(
base::BindRepeating(&KeepAliveURLLoaderService::OnLoaderDisconnected,
base::Unretained(this)));
}
KeepAliveURLLoaderService::~KeepAliveURLLoaderService() = default;
void KeepAliveURLLoaderService::BindFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory,
scoped_refptr<PolicyContainerHost> policy_container_host) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CHECK(policy_container_host);
factory_->BindFactory(std::move(receiver), std::move(pending_factory),
std::move(policy_container_host));
}
void KeepAliveURLLoaderService::OnLoaderDisconnected() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto disconnected_loader_receiver_id = loader_receivers_.current_receiver();
TRACE_EVENT("loading", "KeepAliveURLLoaderService::OnLoaderDisconnected",
"loader_id", disconnected_loader_receiver_id);
disconnected_loaders_.emplace(disconnected_loader_receiver_id,
std::move(loader_receivers_.current_context()));
}
void KeepAliveURLLoaderService::RemoveLoader(
mojo::ReceiverId loader_receiver_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT("loading", "KeepAliveURLLoaderService::RemoveLoader", "loader_id",
loader_receiver_id);
loader_receivers_.Remove(loader_receiver_id);
disconnected_loaders_.erase(loader_receiver_id);
}
size_t KeepAliveURLLoaderService::NumLoadersForTesting() const {
return loader_receivers_.size() + disconnected_loaders_.size();
}
size_t KeepAliveURLLoaderService::NumDisconnectedLoadersForTesting() const {
return disconnected_loaders_.size();
}
void KeepAliveURLLoaderService::SetLoaderObserverForTesting(
scoped_refptr<KeepAliveURLLoader::TestObserver> observer) {
loader_test_observer_ = observer;
}
}