blob: 51e8983fe58af194ef184b3874002b1edc43feee [file] [log] [blame]
// Copyright 2023 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/storage_access/storage_access_handle.h"
#include "base/byte_count.h"
#include "base/functional/callback_helpers.h"
#include "base/types/pass_key.h"
#include "content/browser/broadcast_channel/broadcast_channel_provider.h"
#include "content/browser/broadcast_channel/broadcast_channel_service.h"
#include "content/browser/file_system_access/file_system_access_manager_impl.h"
#include "content/browser/network/cross_origin_embedder_policy_reporter.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/worker_host/shared_worker_connector_impl.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "storage/browser/blob/blob_url_registry.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
namespace content {
using PassKey = base::PassKey<StorageAccessHandle>;
namespace {
void EstimateImplAfterGetBucketUsageAndQuota(
StorageAccessHandle::EstimateCallback callback,
blink::mojom::QuotaStatusCode code,
int64_t usage,
int64_t quota) {
if (code != blink::mojom::QuotaStatusCode::kOk) {
std::move(callback).Run(/*usage=*/base::ByteCount(0),
/*quota=*/base::ByteCount(0), /*success=*/false);
return;
}
std::move(callback).Run(base::ByteCount(usage), base::ByteCount(quota),
/*success=*/true);
}
} // namespace
// static
void StorageAccessHandle::Create(
RenderFrameHost* host,
mojo::PendingReceiver<blink::mojom::StorageAccessHandle> receiver) {
CHECK(host);
if (!host->IsFullCookieAccessAllowed()) {
#if DCHECK_IS_ON()
mojo::ReportBadMessage(
"Binding a StorageAccessHandle requires third-party cookie access.");
#endif
return;
}
new StorageAccessHandle(*host, std::move(receiver));
}
void StorageAccessHandle::BindIndexedDB(
mojo::PendingReceiver<blink::mojom::IDBFactory> receiver) {
render_frame_host().GetProcess()->BindIndexedDB(
blink::StorageKey::CreateFirstParty(
render_frame_host().GetStorageKey().origin()),
static_cast<RenderFrameHostImpl&>(render_frame_host()),
std::move(receiver));
}
void StorageAccessHandle::BindLocks(
mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
render_frame_host().GetProcess()->CreateLockManager(
blink::StorageKey::CreateFirstParty(
render_frame_host().GetStorageKey().origin()),
std::move(receiver));
}
void StorageAccessHandle::BindCaches(
mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
RenderFrameHostImpl& host =
static_cast<RenderFrameHostImpl&>(render_frame_host());
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_remote;
if (host.coep_reporter()) {
host.coep_reporter()->Clone(
coep_reporter_remote.InitWithNewPipeAndPassReceiver());
}
mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter>
dip_reporter_remote;
if (host.dip_reporter()) {
host.dip_reporter()->Clone(
dip_reporter_remote.InitWithNewPipeAndPassReceiver());
}
host.GetProcess()->BindCacheStorage(
host.cross_origin_embedder_policy(), std::move(coep_reporter_remote),
host.policy_container_host()->policies().document_isolation_policy,
std::move(dip_reporter_remote),
storage::BucketLocator::ForDefaultBucket(
blink::StorageKey::CreateFirstParty(host.GetStorageKey().origin())),
std::move(receiver));
}
void StorageAccessHandle::GetDirectory(GetDirectoryCallback callback) {
static_cast<RenderFrameHostImpl&>(render_frame_host())
.GetStoragePartition()
->GetFileSystemAccessManager()
->GetSandboxedFileSystem(
FileSystemAccessManagerImpl::BindingContext(
blink::StorageKey::CreateFirstParty(
render_frame_host().GetStorageKey().origin()),
render_frame_host().GetLastCommittedURL(),
render_frame_host().GetGlobalId()),
/*bucket=*/std::nullopt,
/*directory_path_components=*/{}, std::move(callback));
}
void StorageAccessHandle::Estimate(EstimateCallback callback) {
static_cast<RenderFrameHostImpl&>(render_frame_host())
.GetStoragePartition()
->GetQuotaManagerProxy()
->GetBucketsForStorageKey(
blink::StorageKey::CreateFirstParty(
render_frame_host().GetStorageKey().origin()),
/*delete_expired=*/false,
base::SequencedTaskRunner::GetCurrentDefault(),
base::BindOnce(&StorageAccessHandle::EstimateImpl,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void StorageAccessHandle::EstimateImpl(
EstimateCallback callback,
storage::QuotaErrorOr<std::set<storage::BucketInfo>> bucket_set) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!bucket_set.has_value()) {
std::move(callback).Run(/*usage=*/base::ByteCount(0),
/*quota=*/base::ByteCount(0), /*success=*/false);
return;
}
storage::BucketInfo bucket_info;
for (const storage::BucketInfo& info : *bucket_set) {
if (info.is_default()) {
bucket_info = info;
break;
}
}
if (bucket_info.is_null()) {
std::move(callback).Run(/*usage=*/base::ByteCount(0),
/*quota=*/base::ByteCount(0), /*success=*/true);
return;
}
static_cast<RenderFrameHostImpl&>(render_frame_host())
.GetStoragePartition()
->GetQuotaManagerProxy()
->GetBucketUsageAndReportedQuota(
bucket_info.id, base::SequencedTaskRunner::GetCurrentDefault(),
base::BindOnce(&EstimateImplAfterGetBucketUsageAndQuota,
std::move(callback)));
}
void StorageAccessHandle::BindBlobStorage(
mojo::PendingAssociatedReceiver<blink::mojom::BlobURLStore> receiver) {
static_cast<RenderFrameHostImpl&>(render_frame_host())
.GetStoragePartition()
->GetBlobUrlRegistry()
->AddReceiver(
blink::StorageKey::CreateFirstParty(
render_frame_host().GetStorageKey().origin()),
render_frame_host().GetLastCommittedOrigin(),
render_frame_host().GetProcess()->GetDeprecatedID(),
std::move(receiver),
/*partitioning_blob_url_closure=*/base::DoNothing(),
// In the case that a context is granted storage access, the
// StorageAccessHandle context still shouldn't bypass the partitioning
// check (e.g. using a Blob URL created with URL.createObjectURL in
// the third-party context with the StorageAccessHandle's SharedWorker
// constructor.)
/*storage_access_check_callback=*/
base::BindRepeating([]() -> bool { return false; }),
// A StorageAccessHandle is not a top-level blob document, so always
// pass std::nullopt here.
/*top_level_blob_document_url=*/std::nullopt,
/*context_type_for_debugging=*/"StorageAccessHandle",
base::BindRepeating(
[](base::WeakPtr<StorageAccessHandle> handle) -> std::string {
if (!handle) {
return "destroyed StorageAccessHandle";
}
return handle->render_frame_host()
.GetStorageKey()
.GetDebugString();
},
weak_factory_.GetWeakPtr()),
/*partitioning_disabled_by_policy=*/false);
}
void StorageAccessHandle::BindBroadcastChannel(
mojo::PendingAssociatedReceiver<blink::mojom::BroadcastChannelProvider>
receiver) {
BroadcastChannelService* service =
static_cast<RenderFrameHostImpl&>(render_frame_host())
.GetStoragePartition()
->GetBroadcastChannelService();
service->AddAssociatedReceiver(
std::make_unique<BroadcastChannelProvider>(
service, blink::StorageKey::CreateFirstParty(
render_frame_host().GetStorageKey().origin())),
std::move(receiver));
}
void StorageAccessHandle::BindSharedWorker(
mojo::PendingReceiver<blink::mojom::SharedWorkerConnector> receiver) {
SharedWorkerConnectorImpl::Create(
PassKey(), render_frame_host().GetGlobalId(),
blink::StorageKey::CreateFirstParty(
render_frame_host().GetStorageKey().origin()),
std::move(receiver));
}
StorageAccessHandle::StorageAccessHandle(
RenderFrameHost& host,
mojo::PendingReceiver<blink::mojom::StorageAccessHandle> receiver)
: DocumentService(host, std::move(receiver)) {}
StorageAccessHandle::~StorageAccessHandle() = default;
} // namespace content