| // 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 |