blob: a0495aca6d7620c7b96e40cafad805e51697f685 [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 "services/network/web_bundle_manager.h"
#include "base/bind.h"
#include "components/web_package/web_bundle_utils.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/network_context.h"
#include "services/network/public/mojom/web_bundle_handle.mojom.h"
#include "services/network/web_bundle_memory_quota_consumer.h"
#include "services/network/web_bundle_url_loader_factory.h"
namespace network {
// Represents a pending subresource request.
struct WebBundlePendingSubresourceRequest {
WebBundlePendingSubresourceRequest(
mojo::PendingReceiver<mojom::URLLoader> receiver,
const ResourceRequest& url_request,
mojo::PendingRemote<mojom::URLLoaderClient> client,
mojo::Remote<mojom::TrustedHeaderClient> trusted_header_client)
: receiver(std::move(receiver)),
url_request(url_request),
client(std::move(client)),
trusted_header_client(std::move(trusted_header_client)) {}
~WebBundlePendingSubresourceRequest() = default;
WebBundlePendingSubresourceRequest(
const WebBundlePendingSubresourceRequest&) = delete;
WebBundlePendingSubresourceRequest& operator=(
const WebBundlePendingSubresourceRequest&) = delete;
mojo::PendingReceiver<mojom::URLLoader> receiver;
const ResourceRequest url_request;
mojo::PendingRemote<mojom::URLLoaderClient> client;
mojo::Remote<mojom::TrustedHeaderClient> trusted_header_client;
};
class WebBundleManager::MemoryQuotaConsumer
: public WebBundleMemoryQuotaConsumer {
public:
MemoryQuotaConsumer(base::WeakPtr<WebBundleManager> manager,
int32_t process_id)
: manager_(std::move(manager)), process_id_(process_id) {}
MemoryQuotaConsumer(const MemoryQuotaConsumer&) = delete;
MemoryQuotaConsumer& operator=(const MemoryQuotaConsumer&) = delete;
~MemoryQuotaConsumer() override {
if (!manager_)
return;
manager_->ReleaseMemoryForProcess(process_id_, allocated_bytes_);
}
bool AllocateMemory(uint64_t num_bytes) override {
if (!manager_)
return false;
if (!manager_->AllocateMemoryForProcess(process_id_, num_bytes))
return false;
allocated_bytes_ += num_bytes;
return true;
}
private:
base::WeakPtr<WebBundleManager> manager_;
const int32_t process_id_;
uint64_t allocated_bytes_ = 0;
};
WebBundleManager::WebBundleManager()
: max_memory_per_process_(web_package::kDefaultMaxMemoryPerProcess) {}
WebBundleManager::~WebBundleManager() = default;
base::WeakPtr<WebBundleURLLoaderFactory>
WebBundleManager::CreateWebBundleURLLoaderFactory(
const GURL& bundle_url,
const ResourceRequest::WebBundleTokenParams& web_bundle_token_params,
int32_t process_id,
const absl::optional<url::Origin>& request_initiator_origin_lock,
mojo::PendingRemote<mojom::DevToolsObserver> devtools_observer,
absl::optional<std::string> devtools_request_id) {
DCHECK(factories_.find({process_id, web_bundle_token_params.token}) ==
factories_.end());
mojo::Remote<mojom::WebBundleHandle> remote(
web_bundle_token_params.CloneHandle());
// Set a disconnect handler to remove a WebBundleURLLoaderFactory from this
// WebBundleManager when the corresponding endpoint in the renderer is
// removed.
remote.set_disconnect_handler(base::BindOnce(
&WebBundleManager::DisconnectHandler,
// |this| outlives |remote|.
base::Unretained(this), web_bundle_token_params.token, process_id));
auto factory = std::make_unique<WebBundleURLLoaderFactory>(
bundle_url, std::move(remote), request_initiator_origin_lock,
std::make_unique<MemoryQuotaConsumer>(weak_ptr_factory_.GetWeakPtr(),
process_id),
std::move(devtools_observer), std::move(devtools_request_id));
// Process pending subresource requests if there are.
// These subresource requests arrived earlier than the request for the bundle.
auto it = pending_requests_.find({process_id, web_bundle_token_params.token});
if (it != pending_requests_.end()) {
for (auto& pending_request : it->second) {
factory->StartSubresourceRequest(
std::move(pending_request->receiver), pending_request->url_request,
std::move(pending_request->client),
std::move(pending_request->trusted_header_client));
}
pending_requests_.erase(it);
}
auto weak_factory = factory->GetWeakPtr();
factories_.insert({std::make_pair(process_id, web_bundle_token_params.token),
std::move(factory)});
return weak_factory;
}
base::WeakPtr<WebBundleURLLoaderFactory>
WebBundleManager::GetWebBundleURLLoaderFactory(
const ResourceRequest::WebBundleTokenParams& token_params,
int32_t process_id) {
// If the request is from the browser process, use
// WebBundleTokenParams::render_process_id for matching.
if (process_id == mojom::kBrowserProcessId)
process_id = token_params.render_process_id;
auto it = factories_.find({process_id, token_params.token});
if (it == factories_.end()) {
return nullptr;
}
return it->second->GetWeakPtr();
}
void WebBundleManager::StartSubresourceRequest(
mojo::PendingReceiver<mojom::URLLoader> receiver,
const ResourceRequest& url_request,
mojo::PendingRemote<mojom::URLLoaderClient> client,
int32_t process_id,
mojo::Remote<mojom::TrustedHeaderClient> trusted_header_client) {
DCHECK(url_request.web_bundle_token_params.has_value());
base::WeakPtr<WebBundleURLLoaderFactory> web_bundle_url_loader_factory =
GetWebBundleURLLoaderFactory(*url_request.web_bundle_token_params,
process_id);
if (web_bundle_url_loader_factory) {
web_bundle_url_loader_factory->StartSubresourceRequest(
std::move(receiver), url_request, std::move(client),
std::move(trusted_header_client));
return;
}
// A request for subresource arrives earlier than a request for a webbundle.
pending_requests_[{process_id, url_request.web_bundle_token_params->token}]
.push_back(std::make_unique<WebBundlePendingSubresourceRequest>(
std::move(receiver), url_request, std::move(client),
std::move(trusted_header_client)));
}
void WebBundleManager::DisconnectHandler(
base::UnguessableToken web_bundle_token,
int32_t process_id) {
factories_.erase({process_id, web_bundle_token});
pending_requests_.erase({process_id, web_bundle_token});
}
bool WebBundleManager::AllocateMemoryForProcess(int32_t process_id,
uint64_t num_bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (memory_usage_per_process_[process_id] + num_bytes >
max_memory_per_process_) {
return false;
}
memory_usage_per_process_[process_id] += num_bytes;
if (max_memory_usage_per_process_[process_id] <
memory_usage_per_process_[process_id]) {
max_memory_usage_per_process_[process_id] =
memory_usage_per_process_[process_id];
}
return true;
}
void WebBundleManager::ReleaseMemoryForProcess(int32_t process_id,
uint64_t num_bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GE(memory_usage_per_process_[process_id], num_bytes);
memory_usage_per_process_[process_id] -= num_bytes;
if (memory_usage_per_process_[process_id] == 0) {
memory_usage_per_process_.erase(process_id);
base::UmaHistogramCustomCounts(
"SubresourceWebBundles.MaxMemoryUsagePerProcess",
max_memory_usage_per_process_[process_id], 1, 50000000, 50);
max_memory_usage_per_process_.erase(process_id);
}
}
} // namespace network