| // 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/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/web_contents.h" |
| #include "content/public/common/content_client.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=*/0, /*quota=*/0, /*success=*/false); |
| return; |
| } |
| std::move(callback).Run(usage, quota, /*success=*/true); |
| } |
| |
| } // namespace |
| |
| // static |
| void StorageAccessHandle::Create( |
| RenderFrameHost* host, |
| mojo::PendingReceiver<blink::mojom::StorageAccessHandle> receiver) { |
| CHECK(host); |
| if (!DoesFrameHaveStorageAccess(host)) { |
| #if DCHECK_IS_ON() |
| mojo::ReportBadMessage( |
| "Binding a StorageAccessHandle requires third-party cookie access or " |
| "permission access."); |
| #endif |
| return; |
| } |
| new StorageAccessHandle(*host, std::move(receiver)); |
| } |
| |
| // static |
| bool StorageAccessHandle::DoesFrameHaveStorageAccess(RenderFrameHost* host) { |
| bool has_full_cookie_access = |
| GetContentClient()->browser()->IsFullCookieAccessAllowed( |
| host->GetBrowserContext(), WebContents::FromRenderFrameHost(host), |
| host->GetLastCommittedURL(), host->GetStorageKey()); |
| if (has_full_cookie_access) { |
| return true; |
| } |
| return host->GetProcess() |
| ->GetBrowserContext() |
| ->GetPermissionController() |
| ->GetPermissionStatusForCurrentDocument( |
| blink::PermissionType::STORAGE_ACCESS_GRANT, host) == |
| blink::mojom::PermissionStatus::GRANTED; |
| } |
| |
| 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=*/0, /*quota=*/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=*/0, /*quota=*/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), base::DoNothing(), |
| /*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 |