| // Copyright 2012 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_partition_impl.h" |
| |
| #include <stdint.h> |
| |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/barrier_callback.h" |
| #include "base/barrier_closure.h" |
| #include "base/command_line.h" |
| #include "base/containers/contains.h" |
| #include "base/dcheck_is_on.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/functional/concurrent_closures.h" |
| #include "base/location.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/observer_list.h" |
| #include "base/run_loop.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/task/thread_pool.h" |
| #include "base/threading/sequence_local_storage_slot.h" |
| #include "base/types/optional_util.h" |
| #include "build/build_config.h" |
| #include "components/attribution_reporting/features.h" |
| #include "components/leveldb_proto/public/proto_database_provider.h" |
| #include "components/services/storage/privileged/cpp/bucket_client_info.h" |
| #include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h" |
| #include "components/services/storage/public/cpp/buckets/bucket_locator.h" |
| #include "components/services/storage/public/cpp/constants.h" |
| #include "components/services/storage/public/cpp/filesystem/filesystem_impl.h" |
| #include "components/services/storage/public/mojom/filesystem/directory.mojom.h" |
| #include "components/services/storage/public/mojom/storage_service.mojom.h" |
| #include "components/services/storage/shared_storage/shared_storage_manager.h" |
| #include "components/services/storage/storage_service_impl.h" |
| #include "components/variations/net/variations_http_headers.h" |
| #include "content/browser/aggregation_service/aggregation_service.h" |
| #include "content/browser/aggregation_service/aggregation_service_impl.h" |
| #include "content/browser/attribution_reporting/attribution_manager_impl.h" |
| #include "content/browser/background_fetch/background_fetch_context.h" |
| #include "content/browser/blob_storage/blob_registry_wrapper.h" |
| #include "content/browser/blob_storage/chrome_blob_storage_context.h" |
| #include "content/browser/bluetooth/bluetooth_allowed_devices_map.h" |
| #include "content/browser/broadcast_channel/broadcast_channel_service.h" |
| #include "content/browser/browsing_data/clear_site_data_handler.h" |
| #include "content/browser/browsing_data/storage_partition_code_cache_data_remover.h" |
| #include "content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h" |
| #include "content/browser/buckets/bucket_manager.h" |
| #include "content/browser/cache_storage/cache_storage_control_wrapper.h" |
| #include "content/browser/code_cache/generated_code_cache.h" |
| #include "content/browser/code_cache/generated_code_cache_context.h" |
| #include "content/browser/cookie_deprecation_label/cookie_deprecation_label_manager_impl.h" |
| #include "content/browser/cookie_store/cookie_store_manager.h" |
| #include "content/browser/devtools/devtools_background_services_context_impl.h" |
| #include "content/browser/devtools/devtools_instrumentation.h" |
| #include "content/browser/devtools/devtools_url_loader_interceptor.h" |
| #include "content/browser/file_system/browser_file_system_helper.h" |
| #include "content/browser/file_system_access/file_system_access_manager_impl.h" |
| #include "content/browser/font_access/font_access_manager.h" |
| #include "content/browser/gpu/gpu_disk_cache_factory.h" |
| #include "content/browser/guest_page_holder_impl.h" |
| #include "content/browser/host_zoom_level_context.h" |
| #include "content/browser/indexed_db/indexed_db_control_wrapper.h" |
| #include "content/browser/interest_group/interest_group_manager_impl.h" |
| #include "content/browser/loader/keep_alive_url_loader_service.h" |
| #include "content/browser/loader/reconnectable_url_loader_factory.h" |
| #include "content/browser/loader/subresource_proxying_url_loader_service.h" |
| #include "content/browser/loader/url_loader_factory_utils.h" |
| #include "content/browser/locks/lock_manager.h" |
| #include "content/browser/navigation_or_document_handle.h" |
| #include "content/browser/network/shared_dictionary_util.h" |
| #include "content/browser/network_context_client_base_impl.h" |
| #include "content/browser/network_service_instance_impl.h" |
| #include "content/browser/notifications/platform_notification_context_impl.h" |
| #include "content/browser/payments/payment_app_context_impl.h" |
| #include "content/browser/preloading/prerender/prerender_final_status.h" |
| #include "content/browser/private_aggregation/private_aggregation_manager.h" |
| #include "content/browser/private_aggregation/private_aggregation_manager_impl.h" |
| #include "content/browser/push_messaging/push_messaging_context.h" |
| #include "content/browser/quota/quota_context.h" |
| #include "content/browser/renderer_host/frame_tree_node.h" |
| #include "content/browser/renderer_host/navigation_request.h" |
| #include "content/browser/renderer_host/navigation_state_keep_alive.h" |
| #include "content/browser/service_worker/service_worker_client.h" |
| #include "content/browser/service_worker/service_worker_context_wrapper.h" |
| #include "content/browser/shared_storage/shared_storage_header_observer.h" |
| #include "content/browser/shared_storage/shared_storage_runtime_manager.h" |
| #include "content/browser/ssl/ssl_client_auth_handler.h" |
| #include "content/browser/ssl/ssl_error_handler.h" |
| #include "content/browser/ssl_private_key_impl.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/browser/worker_host/shared_worker_service_impl.h" |
| #include "content/common/features.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/browsing_data_filter_builder.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/cookie_access_details.h" |
| #include "content/public/browser/dom_storage_context.h" |
| #include "content/public/browser/login_delegate.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/permission_controller.h" |
| #include "content/public/browser/permission_descriptor_util.h" |
| #include "content/public/browser/permission_result.h" |
| #include "content/public/browser/private_aggregation_data_model.h" |
| #include "content/public/browser/private_network_device_delegate.h" |
| #include "content/public/browser/runtime_feature_state/runtime_feature_state_document_data.h" |
| #include "content/public/browser/service_process_host.h" |
| #include "content/public/browser/session_storage_usage_info.h" |
| #include "content/public/browser/shared_cors_origin_access_list.h" |
| #include "content/public/browser/storage_notification_service.h" |
| #include "content/public/browser/storage_partition_config.h" |
| #include "content/public/browser/storage_usage_info.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_constants.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "mojo/public/cpp/bindings/callback_helpers.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "net/base/features.h" |
| #include "net/base/net_errors.h" |
| #include "net/cookies/cookie_setting_override.h" |
| #include "net/ssl/client_cert_store.h" |
| #include "ppapi/buildflags/buildflags.h" |
| #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h" |
| #include "services/network/public/cpp/cors/origin_access_list.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/cpp/ip_address_space_util.h" |
| #include "services/network/public/cpp/is_potentially_trustworthy.h" |
| #include "services/network/public/cpp/url_loader_factory_builder.h" |
| #include "services/network/public/mojom/clear_data_filter.mojom.h" |
| #include "services/network/public/mojom/cookie_access_observer.mojom.h" |
| #include "services/network/public/mojom/cookie_manager.mojom.h" |
| #include "services/network/public/mojom/device_bound_sessions.mojom.h" |
| #include "services/network/public/mojom/network_context.mojom.h" |
| #include "services/network/public/mojom/shared_dictionary_access_observer.mojom.h" |
| #include "services/network/public/mojom/trust_tokens.mojom.h" |
| #include "services/network/public/mojom/url_loader_network_service_observer.mojom.h" |
| #include "storage/browser/quota/quota_client_type.h" |
| #include "storage/browser/quota/quota_manager.h" |
| #include "storage/browser/quota/quota_manager_impl.h" |
| #include "storage/browser/quota/quota_settings.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/navigation/preloading_headers.h" |
| #include "third_party/blink/public/common/permissions/permission_utils.h" |
| #include "third_party/blink/public/common/storage_key/storage_key.h" |
| #include "third_party/blink/public/mojom/devtools/inspector_issue.mojom-shared.h" |
| #include "third_party/blink/public/mojom/private_network_device/private_network_device.mojom.h" |
| #include "url/scheme_host_port.h" |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include "content/public/browser/android/java_interfaces.h" |
| #include "net/android/http_auth_negotiate_android.h" |
| #include "services/service_manager/public/cpp/interface_provider.h" |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| #include "content/browser/media/cdm_storage_common.h" |
| #include "content/browser/media/cdm_storage_manager.h" |
| #include "content/public/browser/cdm_storage_data_model.h" |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| |
| using CookieDeletionFilter = network::mojom::CookieDeletionFilter; |
| using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr; |
| |
| namespace content { |
| |
| namespace { |
| |
| using Type = StoragePartitionImpl::ContextType; |
| |
| const storage::QuotaSettings* g_test_quota_settings; |
| |
| // Timeout after which the |
| // History.ClearBrowsingData.Duration.SlowTasks180sStoragePartition histogram is |
| // recorded. |
| const base::TimeDelta kSlowTaskTimeout = base::Seconds(180); |
| |
| // If true, Storage Service instances will always be started in-process. |
| bool g_force_in_process_storage_service = false; |
| |
| mojo::Remote<storage::mojom::StorageService>& GetStorageServiceRemoteStorage() { |
| // NOTE: This use of sequence-local storage is only to ensure that the Remote |
| // only lives as long as the UI-thread sequence, since the UI-thread sequence |
| // may be torn down and reinitialized e.g. between unit tests. |
| static base::SequenceLocalStorageSlot< |
| mojo::Remote<storage::mojom::StorageService>> |
| remote_slot; |
| return remote_slot.GetOrCreateValue(); |
| } |
| |
| void RunInProcessStorageService( |
| mojo::PendingReceiver<storage::mojom::StorageService> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| static base::SequenceLocalStorageSlot< |
| std::unique_ptr<storage::StorageServiceImpl>> |
| service_storage_slot; |
| service_storage_slot.GetOrCreateValue() = |
| std::make_unique<storage::StorageServiceImpl>(std::move(receiver), |
| /*io_task_runner=*/nullptr); |
| } |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| void BindStorageServiceFilesystemImpl( |
| const base::FilePath& directory_path, |
| mojo::PendingReceiver<storage::mojom::Directory> receiver) { |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<storage::FilesystemImpl>( |
| directory_path, storage::FilesystemImpl::ClientType::kUntrusted), |
| std::move(receiver)); |
| } |
| #endif |
| |
| mojo::Remote<storage::mojom::StorageService>& GetStorageServiceRemote() { |
| mojo::Remote<storage::mojom::StorageService>& remote = |
| GetStorageServiceRemoteStorage(); |
| if (!remote) { |
| #if !BUILDFLAG(IS_ANDROID) |
| const base::FilePath sandboxed_data_dir = |
| GetContentClient() |
| ->browser() |
| ->GetSandboxedStorageServiceDataDirectory(); |
| const bool single_process_mode = |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kSingleProcess); |
| const bool oop_storage_enabled = !sandboxed_data_dir.empty() && |
| !single_process_mode && |
| !g_force_in_process_storage_service; |
| if (oop_storage_enabled) { |
| DCHECK(sandboxed_data_dir.IsAbsolute()) |
| << "Storage Service data directory must be an absolute path, but \"" |
| << sandboxed_data_dir << "\" is not an absolute path."; |
| remote = ServiceProcessHost::Launch<storage::mojom::StorageService>( |
| ServiceProcessHost::Options() |
| .WithDisplayName("Storage Service") |
| .Pass()); |
| remote.reset_on_disconnect(); |
| |
| // Provide the service with an API it can use to access filesystem |
| // contents *only* within the embedder's specified data directory. |
| mojo::PendingRemote<storage::mojom::Directory> directory; |
| base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskPriority::USER_VISIBLE}) |
| ->PostTask(FROM_HERE, |
| base::BindOnce( |
| &BindStorageServiceFilesystemImpl, sandboxed_data_dir, |
| directory.InitWithNewPipeAndPassReceiver())); |
| remote->SetDataDirectory(sandboxed_data_dir, std::move(directory)); |
| } else |
| #endif // !BUILDFLAG(IS_ANDROID) |
| { |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&RunInProcessStorageService, |
| remote.BindNewPipeAndPassReceiver())); |
| } |
| |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableAggressiveDOMStorageFlushing)) { |
| remote->EnableAggressiveDomStorageFlushing(); |
| } |
| } |
| return remote; |
| } |
| |
| void OnClearedCookies(base::OnceClosure callback, uint32_t num_deleted) { |
| // The final callback needs to happen from UI thread. |
| if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&OnClearedCookies, std::move(callback), num_deleted)); |
| return; |
| } |
| |
| std::move(callback).Run(); |
| } |
| |
| void CheckQuotaManagedDataDeletionStatus(size_t* deletion_task_count, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (*deletion_task_count == 0) { |
| delete deletion_task_count; |
| std::move(callback).Run(); |
| } |
| } |
| |
| void OnQuotaManagedBucketDeleted(const storage::BucketLocator& bucket, |
| size_t* deletion_task_count, |
| base::OnceClosure callback, |
| blink::mojom::QuotaStatusCode status) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK_GT(*deletion_task_count, 0u); |
| if (status != blink::mojom::QuotaStatusCode::kOk) { |
| DLOG(ERROR) << "Couldn't remove data for bucket with storage key " |
| << bucket.storage_key.GetDebugString() << " is_default " |
| << bucket.is_default << " and bucket id " << bucket.id |
| << ". Status: " << static_cast<int>(status); |
| } |
| |
| (*deletion_task_count)--; |
| CheckQuotaManagedDataDeletionStatus(deletion_task_count, std::move(callback)); |
| } |
| |
| void PerformQuotaManagerStorageCleanup( |
| const scoped_refptr<storage::QuotaManager>& quota_manager, |
| storage::QuotaClientTypes quota_client_types, |
| base::OnceClosure callback) { |
| quota_manager->PerformStorageCleanup(std::move(quota_client_types), |
| std::move(callback)); |
| } |
| |
| void ClearedGpuCache(base::OnceClosure callback) { |
| if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&ClearedGpuCache, std::move(callback))); |
| return; |
| } |
| std::move(callback).Run(); |
| } |
| |
| void OnLocalStorageUsageInfo( |
| const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context, |
| const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| const base::Time delete_begin, |
| const base::Time delete_end, |
| base::OnceClosure callback, |
| const std::vector<StorageUsageInfo>& infos) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| base::OnceClosure done_callback = |
| perform_storage_cleanup |
| ? base::BindOnce( |
| &DOMStorageContextWrapper::PerformLocalStorageCleanup, |
| dom_storage_context, std::move(callback)) |
| : std::move(callback); |
| |
| base::ConcurrentClosures concurrent; |
| for (const StorageUsageInfo& info : infos) { |
| if (storage_key_matcher && |
| !storage_key_matcher.Run(info.storage_key, |
| special_storage_policy.get())) { |
| continue; |
| } |
| |
| if (info.last_modified >= delete_begin && |
| info.last_modified <= delete_end) { |
| dom_storage_context->DeleteLocalStorage(info.storage_key, |
| concurrent.CreateClosure()); |
| } |
| } |
| std::move(concurrent).Done(std::move(done_callback)); |
| } |
| |
| void OnSessionStorageUsageInfo( |
| const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context, |
| const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback, |
| const std::vector<SessionStorageUsageInfo>& infos) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| base::OnceClosure done_callback = |
| perform_storage_cleanup |
| ? base::BindOnce( |
| &DOMStorageContextWrapper::PerformSessionStorageCleanup, |
| dom_storage_context, std::move(callback)) |
| : std::move(callback); |
| |
| base::ConcurrentClosures concurrent; |
| for (const SessionStorageUsageInfo& info : infos) { |
| if (storage_key_matcher && |
| !storage_key_matcher.Run(info.storage_key, |
| special_storage_policy.get())) { |
| continue; |
| } |
| dom_storage_context->DeleteSessionStorage(info, concurrent.CreateClosure()); |
| } |
| std::move(concurrent).Done(std::move(done_callback)); |
| } |
| |
| void ClearLocalStorageOnUIThread( |
| const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context, |
| const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| const blink::StorageKey& storage_key, |
| bool perform_storage_cleanup, |
| const base::Time begin, |
| const base::Time end, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!storage_key.origin().opaque()) { |
| bool can_delete = |
| !storage_key_matcher || |
| storage_key_matcher.Run(storage_key, special_storage_policy.get()); |
| if (can_delete) { |
| dom_storage_context->DeleteLocalStorage(storage_key, std::move(callback)); |
| } else { |
| std::move(callback).Run(); |
| } |
| return; |
| } |
| |
| dom_storage_context->GetLocalStorageUsage( |
| base::BindOnce(&OnLocalStorageUsageInfo, dom_storage_context, |
| special_storage_policy, std::move(storage_key_matcher), |
| perform_storage_cleanup, begin, end, std::move(callback))); |
| } |
| |
| void ClearSessionStorageOnUIThread( |
| const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context, |
| const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| dom_storage_context->GetSessionStorageUsage( |
| base::BindOnce(&OnSessionStorageUsageInfo, dom_storage_context, |
| special_storage_policy, std::move(storage_key_matcher), |
| perform_storage_cleanup, std::move(callback))); |
| } |
| |
| // LoginHandlerDelegate manages HTTP auth. It is self-owning and deletes itself |
| // when the credentials are resolved or the AuthChallengeResponder is cancelled. |
| class LoginHandlerDelegate { |
| public: |
| LoginHandlerDelegate( |
| mojo::PendingRemote<network::mojom::AuthChallengeResponder> |
| auth_challenge_responder, |
| WebContents* web_contents, |
| content::BrowserContext* browser_context, |
| const net::AuthChallengeInfo& auth_info, |
| bool is_request_for_primary_main_frame_navigation, |
| bool is_request_for_navigation, |
| base::StrictNumeric<int32_t> process_id, |
| base::StrictNumeric<int32_t> request_id, |
| const GURL& url, |
| scoped_refptr<net::HttpResponseHeaders> response_headers, |
| bool first_auth_attempt, |
| FrameTreeNodeId frame_tree_node_id) |
| : auth_challenge_responder_(std::move(auth_challenge_responder)), |
| auth_info_(auth_info), |
| request_id_(process_id, request_id), |
| is_request_for_primary_main_frame_navigation_( |
| is_request_for_primary_main_frame_navigation), |
| is_request_for_navigation_(is_request_for_navigation), |
| creating_login_delegate_(false), |
| url_(url), |
| response_headers_(std::move(response_headers)), |
| first_auth_attempt_(first_auth_attempt), |
| web_contents_(web_contents ? web_contents->GetWeakPtr() : nullptr), |
| browser_context_(browser_context->GetWeakPtr()), |
| frame_tree_node_id_(frame_tree_node_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auth_challenge_responder_.set_disconnect_handler(base::BindOnce( |
| &LoginHandlerDelegate::OnRequestCancelled, base::Unretained(this))); |
| |
| DevToolsURLLoaderInterceptor::HandleAuthRequest( |
| request_id_, auth_info_, |
| base::BindOnce(&LoginHandlerDelegate::ContinueAfterInterceptor, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| private: |
| void OnRequestCancelled() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // This will destroy `login_handler_io_` on the IO thread and, if needed, |
| // inform the delegate. |
| delete this; |
| } |
| |
| void ContinueAfterInterceptor( |
| bool use_fallback, |
| const std::optional<net::AuthCredentials>& auth_credentials) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(!(use_fallback && auth_credentials.has_value())); |
| if (!use_fallback) { |
| OnAuthCredentials(auth_credentials); |
| return; |
| } |
| |
| if (!browser_context_) { |
| OnAuthCredentials(std::nullopt); |
| return; |
| } |
| |
| if (web_contents_) { |
| CHECK_EQ(web_contents_->GetBrowserContext(), browser_context_.get()); |
| } |
| |
| FrameTreeNode* frame_tree_node = |
| FrameTreeNode::GloballyFindByID(frame_tree_node_id_); |
| GuestPageHolder* guest = nullptr; |
| if (frame_tree_node) { |
| guest = GuestPageHolderImpl::FromRenderFrameHost( |
| *frame_tree_node->current_frame_host()); |
| } |
| |
| // WeakPtr is not strictly necessary here due to OnRequestCancelled. |
| creating_login_delegate_ = true; |
| login_delegate_ = GetContentClient()->browser()->CreateLoginDelegate( |
| auth_info_, web_contents_.get(), browser_context_.get(), request_id_, |
| is_request_for_primary_main_frame_navigation_, |
| is_request_for_navigation_, url_, response_headers_, |
| first_auth_attempt_, guest, |
| base::BindOnce(&LoginHandlerDelegate::OnAuthCredentials, |
| weak_factory_.GetWeakPtr())); |
| creating_login_delegate_ = false; |
| if (!login_delegate_) { |
| OnAuthCredentials(std::nullopt); |
| return; |
| } |
| } |
| |
| void OnAuthCredentials( |
| const std::optional<net::AuthCredentials>& auth_credentials) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // CreateLoginDelegate must not call the callback reentrantly. For |
| // robustness, detect this mistake. |
| CHECK(!creating_login_delegate_); |
| auth_challenge_responder_->OnAuthCredentials(auth_credentials); |
| delete this; |
| } |
| |
| mojo::Remote<network::mojom::AuthChallengeResponder> |
| auth_challenge_responder_; |
| net::AuthChallengeInfo auth_info_; |
| const content::GlobalRequestID request_id_; |
| bool is_request_for_primary_main_frame_navigation_; |
| bool is_request_for_navigation_; |
| bool creating_login_delegate_; |
| GURL url_; |
| const scoped_refptr<net::HttpResponseHeaders> response_headers_; |
| bool first_auth_attempt_; |
| base::WeakPtr<WebContents> web_contents_; |
| base::WeakPtr<BrowserContext> browser_context_; |
| std::unique_ptr<LoginDelegate> login_delegate_; |
| FrameTreeNodeId frame_tree_node_id_; |
| base::WeakPtrFactory<LoginHandlerDelegate> weak_factory_{this}; |
| }; |
| |
| // This class lives on the UI thread. It is self-owned and will delete itself |
| // after any of the SSLClientAuthHandler::Delegate methods are invoked (or when |
| // a mojo connection error occurs). |
| class SSLClientAuthDelegate : public SSLClientAuthHandler::Delegate { |
| public: |
| SSLClientAuthDelegate( |
| mojo::PendingRemote<network::mojom::ClientCertificateResponder> |
| client_cert_responder_remote, |
| BrowserContext* browser_context, |
| int process_id, |
| base::WeakPtr<WebContents> web_contents, |
| const scoped_refptr<net::SSLCertRequestInfo>& cert_info) |
| : client_cert_responder_(std::move(client_cert_responder_remote)), |
| ssl_client_auth_handler_(std::make_unique<SSLClientAuthHandler>( |
| GetContentClient()->browser()->CreateClientCertStore( |
| browser_context), |
| browser_context->GetWeakPtr(), |
| process_id, |
| web_contents, |
| std::move(cert_info.get()), |
| this)) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(client_cert_responder_); |
| client_cert_responder_.set_disconnect_handler(base::BindOnce( |
| &SSLClientAuthDelegate::DeleteSelf, base::Unretained(this))); |
| ssl_client_auth_handler_->SelectCertificate(); |
| } |
| |
| ~SSLClientAuthDelegate() override { DCHECK_CURRENTLY_ON(BrowserThread::UI); } |
| |
| void DeleteSelf() { delete this; } |
| |
| // SSLClientAuthHandler::Delegate: |
| void CancelCertificateSelection() override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| client_cert_responder_->CancelRequest(); |
| DeleteSelf(); |
| } |
| |
| // SSLClientAuthHandler::Delegate: |
| void ContinueWithCertificate( |
| scoped_refptr<net::X509Certificate> cert, |
| scoped_refptr<net::SSLPrivateKey> private_key) override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (cert && private_key) { |
| mojo::PendingRemote<network::mojom::SSLPrivateKey> ssl_private_key; |
| |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<SSLPrivateKeyImpl>(private_key), |
| ssl_private_key.InitWithNewPipeAndPassReceiver()); |
| |
| client_cert_responder_->ContinueWithCertificate( |
| cert, private_key->GetProviderName(), |
| private_key->GetAlgorithmPreferences(), std::move(ssl_private_key)); |
| } else { |
| client_cert_responder_->ContinueWithoutCertificate(); |
| } |
| |
| DeleteSelf(); |
| } |
| |
| private: |
| mojo::Remote<network::mojom::ClientCertificateResponder> |
| client_cert_responder_; |
| std::unique_ptr<SSLClientAuthHandler> ssl_client_auth_handler_; |
| }; |
| |
| void CallCancelRequest( |
| mojo::PendingRemote<network::mojom::ClientCertificateResponder> |
| client_cert_responder_remote) { |
| DCHECK(client_cert_responder_remote); |
| mojo::Remote<network::mojom::ClientCertificateResponder> |
| client_cert_responder(std::move(client_cert_responder_remote)); |
| client_cert_responder->CancelRequest(); |
| } |
| |
| // Cancels prerendering if `navigation_or_document` is in a prerendered frame |
| // tree, using `final_status` as the cancellation reason. Returns true if |
| // cancelled. |
| bool CancelIfPrerendering(NavigationOrDocumentHandle* navigation_or_document, |
| PrerenderFinalStatus final_status) { |
| FrameTreeNode* frame_tree_node = nullptr; |
| // `navigation_or_document` can be null for `kServiceWorkerContext`. |
| if (!navigation_or_document) { |
| return false; |
| } |
| auto* navigation_request = navigation_or_document->GetNavigationRequest(); |
| if (navigation_request) { |
| frame_tree_node = navigation_request->frame_tree_node(); |
| } |
| auto* render_frame_host = navigation_or_document->GetDocument(); |
| if (render_frame_host) { |
| frame_tree_node = FrameTreeNode::From(render_frame_host); |
| } |
| if (!frame_tree_node) { |
| return false; |
| } |
| |
| auto* web_contents = WebContentsImpl::FromFrameTreeNode(frame_tree_node); |
| return web_contents->CancelPrerendering(frame_tree_node, final_status); |
| } |
| |
| class SSLErrorDelegate : public SSLErrorHandler::Delegate { |
| public: |
| explicit SSLErrorDelegate(network::mojom::URLLoaderNetworkServiceObserver:: |
| OnSSLCertificateErrorCallback response) |
| : response_(std::move(response)) {} |
| ~SSLErrorDelegate() override = default; |
| void CancelSSLRequest(int error, const net::SSLInfo* ssl_info) override { |
| std::move(response_).Run(error); |
| delete this; |
| } |
| void ContinueSSLRequest() override { |
| std::move(response_).Run(net::OK); |
| delete this; |
| } |
| base::WeakPtr<SSLErrorDelegate> GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| private: |
| network::mojom::URLLoaderNetworkServiceObserver::OnSSLCertificateErrorCallback |
| response_; |
| base::WeakPtrFactory<SSLErrorDelegate> weak_factory_{this}; |
| }; |
| |
| #if BUILDFLAG(IS_ANDROID) |
| void FinishGenerateNegotiateAuthToken( |
| std::unique_ptr<net::android::HttpAuthNegotiateAndroid> auth_negotiate, |
| std::unique_ptr<std::string> auth_token, |
| std::unique_ptr<net::HttpAuthPreferences> prefs, |
| network::mojom::NetworkContextClient:: |
| OnGenerateHttpNegotiateAuthTokenCallback callback, |
| int result) { |
| std::move(callback).Run(result, *auth_token); |
| } |
| #endif |
| |
| // If both `storage_key_matcher` and `storage_key_policy_matcher` are null, this |
| // should return a null callback that indicates all StorageKeys should match. |
| // Otherwise, returns true if StorageKey matches both `storage_key_matcher` and |
| // `storage_key_policy_matcher` if not null. |
| StoragePartition::StorageKeyPolicyMatcherFunction |
| CombineStorageKeyMatcherFunctions( |
| StoragePartition::StorageKeyMatcherFunction storage_key_matcher, |
| StoragePartition::StorageKeyPolicyMatcherFunction |
| storage_key_policy_matcher) { |
| if (storage_key_matcher.is_null() && storage_key_policy_matcher.is_null()) { |
| return base::NullCallback(); |
| } |
| |
| return base::BindRepeating( |
| [](StoragePartition::StorageKeyMatcherFunction storage_key_matcher, |
| StoragePartition::StorageKeyPolicyMatcherFunction |
| storage_key_policy_matcher, |
| const blink::StorageKey& storage_key, |
| storage::SpecialStoragePolicy* policy) -> bool { |
| return (!storage_key_matcher || storage_key_matcher.Run(storage_key)) && |
| (!storage_key_policy_matcher || |
| storage_key_policy_matcher.Run(storage_key, policy)); |
| }, |
| std::move(storage_key_matcher), std::move(storage_key_policy_matcher)); |
| } |
| |
| // Conceptually, many downstream interfaces don't need to know about the |
| // complexity of callers into StoragePartition, so this function reduces the API |
| // surface to something simple and generic. It is designed to be used by |
| // callsites in ClearDataImpl. |
| // |
| // Precondition: `storage_key_matcher`/`storage_key_policy_matcher` and |
| // `storage_key` cannot both be set. If all of `storage_key_matcher`, |
| // `storage_key_policy_matcher` and `storage_key` are null/empty, this should |
| // return a null callback that indicates all StorageKeys should match. This is |
| // an optimization for backends to efficiently clear all data. |
| // |
| // TODO(csharrison, mek): Right now, this is only used by a small set of storage |
| // backends, e.g. aggregation service. We should consider moving some of the |
| // backends to use this if they can, and additionally we should consider |
| // rethinking this approach if / when storage backends move out of process |
| // (see crbug.com/1016065 for initial work here). |
| StoragePartition::StorageKeyMatcherFunction CreateGenericStorageKeyMatcher( |
| const blink::StorageKey& storage_key, |
| StoragePartition::StorageKeyMatcherFunction storage_key_matcher, |
| StoragePartition::StorageKeyPolicyMatcherFunction |
| storage_key_policy_matcher, |
| scoped_refptr<storage::SpecialStoragePolicy> policy) { |
| const bool storage_key_origin_empty = storage_key.origin().opaque(); |
| DCHECK(storage_key_origin_empty || storage_key_matcher.is_null()); |
| DCHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null()); |
| |
| if (storage_key_origin_empty && storage_key_matcher.is_null() && |
| storage_key_policy_matcher.is_null()) { |
| return base::NullCallback(); |
| } |
| |
| if (storage_key_matcher || storage_key_policy_matcher) { |
| return base::BindRepeating( |
| [](StoragePartition::StorageKeyPolicyMatcherFunction |
| storage_key_policy_matcher, |
| scoped_refptr<storage::SpecialStoragePolicy> policy, |
| const blink::StorageKey& storage_key) -> bool { |
| DCHECK(!storage_key_policy_matcher.is_null()); |
| return storage_key_policy_matcher.Run(storage_key, policy.get()); |
| }, |
| CombineStorageKeyMatcherFunctions( |
| std::move(storage_key_matcher), |
| std::move(storage_key_policy_matcher)), |
| std::move(policy)); |
| } |
| DCHECK(!storage_key_origin_empty); |
| return base::BindRepeating(std::equal_to<const blink::StorageKey&>(), |
| storage_key); |
| } |
| |
| std::vector<GlobalRenderFrameHostId> GetRoutingIdsForOrigin( |
| StoragePartitionImpl* storage_partition, |
| const url::Origin& origin) { |
| scoped_refptr<ServiceWorkerContextWrapper> service_worker_context = |
| storage_partition->GetServiceWorkerContext(); |
| |
| return *service_worker_context->GetWindowClientFrameRoutingIds( |
| blink::StorageKey::CreateFirstParty(origin)); |
| } |
| |
| } // namespace |
| |
| // Static. |
| storage::QuotaClientTypes StoragePartitionImpl::GenerateQuotaClientTypes( |
| uint32_t remove_mask) { |
| storage::QuotaClientTypes quota_client_types; |
| |
| if (remove_mask & StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS) { |
| quota_client_types.insert(storage::QuotaClientType::kFileSystem); |
| } |
| if (remove_mask & StoragePartition::REMOVE_DATA_MASK_WEBSQL) { |
| quota_client_types.insert(storage::QuotaClientType::kDatabase); |
| } |
| if (remove_mask & StoragePartition::REMOVE_DATA_MASK_INDEXEDDB) { |
| quota_client_types.insert(storage::QuotaClientType::kIndexedDatabase); |
| } |
| if (remove_mask & StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS) { |
| quota_client_types.insert(storage::QuotaClientType::kServiceWorker); |
| } |
| if (remove_mask & StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE) { |
| quota_client_types.insert(storage::QuotaClientType::kServiceWorkerCache); |
| } |
| if (remove_mask & StoragePartition::REMOVE_DATA_MASK_BACKGROUND_FETCH) { |
| quota_client_types.insert(storage::QuotaClientType::kBackgroundFetch); |
| } |
| return quota_client_types; |
| } |
| |
| // static |
| void StoragePartitionImpl::ForceInProcessStorageServiceForTesting() { |
| g_force_in_process_storage_service = true; |
| } |
| |
| // Helper for deleting quota managed data from a partition. |
| // |
| // Most of the operations in this class are done on IO thread. |
| class StoragePartitionImpl::QuotaManagedDataDeletionHelper { |
| public: |
| QuotaManagedDataDeletionHelper( |
| uint32_t remove_mask, |
| uint32_t quota_storage_remove_mask, |
| const std::optional<blink::StorageKey>& storage_key, |
| base::OnceClosure callback) |
| : remove_mask_(remove_mask), |
| quota_storage_remove_mask_(quota_storage_remove_mask), |
| storage_key_(storage_key), |
| callback_(std::move(callback)), |
| task_count_(0) { |
| DCHECK(!storage_key_.has_value() || !storage_key_->origin().opaque()); |
| } |
| |
| QuotaManagedDataDeletionHelper(const QuotaManagedDataDeletionHelper&) = |
| delete; |
| QuotaManagedDataDeletionHelper& operator=( |
| const QuotaManagedDataDeletionHelper&) = delete; |
| |
| void IncrementTaskCountOnIO(); |
| void DecrementTaskCountOnIO(); |
| |
| void ClearDataOnIOThread( |
| const scoped_refptr<storage::QuotaManager>& quota_manager, |
| const base::Time begin, |
| const base::Time end, |
| const scoped_refptr<storage::SpecialStoragePolicy>& |
| special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup); |
| |
| void ClearBucketsOnIOThread( |
| storage::QuotaManager* quota_manager, |
| const scoped_refptr<storage::SpecialStoragePolicy>& |
| special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback, |
| const std::set<storage::BucketLocator>& buckets); |
| |
| private: |
| // All of these data are accessed on IO thread. |
| uint32_t remove_mask_; |
| uint32_t quota_storage_remove_mask_; |
| std::optional<blink::StorageKey> storage_key_; |
| base::OnceClosure callback_; |
| int task_count_; |
| }; |
| |
| // Helper for deleting all sorts of data from a partition, keeps track of |
| // deletion status. |
| // |
| // StoragePartitionImpl creates an instance of this class to keep track of |
| // data deletion progress. Deletion requires deleting multiple bits of data |
| // (e.g. cookies, local storage, session storage etc.) and hopping between UI |
| // and IO thread. An instance of this class is created in the beginning of |
| // deletion process (StoragePartitionImpl::ClearDataImpl) and the instance is |
| // forwarded and updated on each (sub) deletion's callback. The instance is |
| // finally destroyed when deletion completes (and `callback` is invoked). |
| class StoragePartitionImpl::DataDeletionHelper { |
| public: |
| DataDeletionHelper(uint32_t remove_mask, |
| uint32_t quota_storage_remove_mask, |
| base::OnceClosure callback) |
| : remove_mask_(remove_mask), |
| quota_storage_remove_mask_(quota_storage_remove_mask), |
| callback_(std::move(callback)) {} |
| |
| DataDeletionHelper(const DataDeletionHelper&) = delete; |
| DataDeletionHelper& operator=(const DataDeletionHelper&) = delete; |
| |
| ~DataDeletionHelper() = default; |
| |
| void ClearDataOnUIThread( |
| const blink::StorageKey& storage_key, |
| BrowsingDataFilterBuilder* filter_builder, |
| StorageKeyPolicyMatcherFunction storage_key_policy_matcher, |
| CookieDeletionFilterPtr cookie_deletion_filter, |
| const base::FilePath& path, |
| DOMStorageContextWrapper* dom_storage_context, |
| storage::QuotaManager* quota_manager, |
| storage::SpecialStoragePolicy* special_storage_policy, |
| storage::FileSystemContext* filesystem_context, |
| network::mojom::CookieManager* cookie_manager, |
| InterestGroupManagerImpl* interest_group_manager, |
| AttributionManager* attribution_manager, |
| AggregationService* aggregation_service, |
| PrivateAggregationManagerImpl* private_aggregation_manager, |
| storage::SharedStorageManager* shared_storage_manager, |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| CdmStorageManager* cdm_storage_manager, |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| network::mojom::DeviceBoundSessionManager* device_bound_session_manager, |
| bool perform_storage_cleanup, |
| const base::Time begin, |
| const base::Time end); |
| |
| void ClearQuotaManagedDataOnIOThread( |
| const scoped_refptr<storage::QuotaManager>& quota_manager, |
| const base::Time begin, |
| const base::Time end, |
| const blink::StorageKey& storage_key, |
| const scoped_refptr<storage::SpecialStoragePolicy>& |
| special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback); |
| |
| private: |
| // For debugging purposes. Please add new deletion tasks at the end. |
| // This enum is recorded in a histogram, so don't change or reuse ids. |
| // LINT.IfChange(TracingDataType) |
| enum class TracingDataType { |
| kSynchronous = 1, |
| kCookies = 2, |
| kQuota = 3, |
| kLocalStorage = 4, |
| kSessionStorage = 5, |
| kShaderCache = 6, // Deprecated in favor of using kGpuCache. |
| kPluginPrivate = 7, // Deprecated. |
| kConversions = 8, |
| kAggregationService = 9, |
| kSharedStorage = 10, |
| kGpuCache = 11, |
| kPrivateAggregation = 12, |
| kInterestGroups = 13, |
| kCdmStorage = 14, |
| kDeviceBoundSessions = 15, |
| kMaxValue = kDeviceBoundSessions, |
| }; |
| // LINT.ThenChange(//tools/metrics/histograms/metadata/history/enums.xml:StoragePartitionRemoverTasks) |
| |
| base::OnceClosure CreateTaskCompletionClosure(TracingDataType data_type); |
| |
| void OnTaskComplete(TracingDataType data_type, |
| int tracing_id); // Callable on any thread. |
| void RecordUnfinishedSubTasks(); |
| |
| uint32_t remove_mask_; |
| uint32_t quota_storage_remove_mask_; |
| |
| // Accessed on UI thread. |
| base::OnceClosure callback_; |
| // Accessed on UI thread. |
| std::set<TracingDataType> pending_tasks_; |
| |
| base::WeakPtrFactory<StoragePartitionImpl::DataDeletionHelper> weak_factory_{ |
| this}; |
| }; |
| |
| void StoragePartitionImpl::DataDeletionHelper::ClearQuotaManagedDataOnIOThread( |
| const scoped_refptr<storage::QuotaManager>& quota_manager, |
| const base::Time begin, |
| const base::Time end, |
| const blink::StorageKey& storage_key, |
| const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| StoragePartitionImpl::QuotaManagedDataDeletionHelper* helper = |
| new StoragePartitionImpl::QuotaManagedDataDeletionHelper( |
| remove_mask_, quota_storage_remove_mask_, |
| storage_key.origin().opaque() ? std::nullopt |
| : std::make_optional(storage_key), |
| std::move(callback)); |
| helper->ClearDataOnIOThread(quota_manager, begin, end, special_storage_policy, |
| std::move(storage_key_matcher), |
| perform_storage_cleanup); |
| } |
| |
| class StoragePartitionImpl::ServiceWorkerCookieAccessObserver |
| : public network::mojom::CookieAccessObserver { |
| public: |
| explicit ServiceWorkerCookieAccessObserver( |
| StoragePartitionImpl* storage_partition) |
| : storage_partition_(storage_partition) {} |
| |
| private: |
| void Clone(mojo::PendingReceiver<network::mojom::CookieAccessObserver> |
| observer) override { |
| storage_partition_->service_worker_cookie_observers_.Add( |
| std::make_unique<ServiceWorkerCookieAccessObserver>(storage_partition_), |
| std::move(observer)); |
| } |
| |
| void OnCookiesAccessed(std::vector<network::mojom::CookieAccessDetailsPtr> |
| details_vector) override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| for (auto& details : details_vector) { |
| for (GlobalRenderFrameHostId frame_id : GetRoutingIdsForOrigin( |
| storage_partition_, url::Origin::Create(details->url))) { |
| if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) { |
| rfh->NotifyCookiesAccessed( |
| mojo::Clone(details_vector), |
| CookieAccessDetails::Source::kNonNavigation); |
| } |
| } |
| } |
| } |
| |
| // `storage_partition_` owns this object via UniqueReceiverSet |
| // (service_worker_cookie_observers_). |
| raw_ptr<StoragePartitionImpl> storage_partition_; |
| }; |
| |
| class StoragePartitionImpl::ServiceWorkerTrustTokenAccessObserver |
| : public network::mojom::TrustTokenAccessObserver { |
| public: |
| explicit ServiceWorkerTrustTokenAccessObserver( |
| StoragePartitionImpl* storage_partition) |
| : storage_partition_(storage_partition) {} |
| |
| private: |
| void Clone(mojo::PendingReceiver<network::mojom::TrustTokenAccessObserver> |
| observer) override { |
| storage_partition_->service_worker_trust_token_observers_.Add( |
| std::make_unique<ServiceWorkerTrustTokenAccessObserver>( |
| storage_partition_), |
| std::move(observer)); |
| } |
| |
| void OnTrustTokensAccessed( |
| network::mojom::TrustTokenAccessDetailsPtr details) override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| url::Origin origin; |
| switch (details->which()) { |
| case network::mojom::TrustTokenAccessDetails::Tag::kIssuance: |
| origin = details->get_issuance()->origin; |
| break; |
| case network::mojom::TrustTokenAccessDetails::Tag::kRedemption: |
| origin = details->get_redemption()->origin; |
| break; |
| case network::mojom::TrustTokenAccessDetails::Tag::kSigning: |
| origin = details->get_signing()->origin; |
| break; |
| } |
| |
| for (GlobalRenderFrameHostId frame_id : |
| GetRoutingIdsForOrigin(storage_partition_, origin)) { |
| if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) { |
| rfh->OnTrustTokensAccessed(mojo::Clone(details)); |
| } |
| } |
| } |
| |
| // `storage_partition_` owns this object via UniqueReceiverSet |
| // (service_worker_trust_token_observers_). |
| raw_ptr<StoragePartitionImpl> storage_partition_; |
| }; |
| |
| class StoragePartitionImpl::ServiceWorkerSharedDictionaryAccessObserver |
| : public network::mojom::SharedDictionaryAccessObserver { |
| public: |
| explicit ServiceWorkerSharedDictionaryAccessObserver( |
| StoragePartitionImpl* storage_partition) |
| : storage_partition_(storage_partition) {} |
| |
| private: |
| void Clone( |
| mojo::PendingReceiver<network::mojom::SharedDictionaryAccessObserver> |
| observer) override { |
| storage_partition_->service_worker_shared_dictionary_observers_.Add( |
| std::make_unique<ServiceWorkerSharedDictionaryAccessObserver>( |
| storage_partition_), |
| std::move(observer)); |
| } |
| |
| void OnSharedDictionaryAccessed( |
| network::mojom::SharedDictionaryAccessDetailsPtr details) override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| for (GlobalRenderFrameHostId frame_id : GetRoutingIdsForOrigin( |
| storage_partition_, details->isolation_key.frame_origin())) { |
| if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) { |
| rfh->OnSharedDictionaryAccessed(mojo::Clone(details)); |
| } |
| } |
| } |
| |
| // `storage_partition_` owns this object via UniqueReceiverSet |
| // (service_worker_shared_dictionary_observers_). |
| raw_ptr<StoragePartitionImpl> storage_partition_; |
| }; |
| |
| class StoragePartitionImpl::ServiceWorkerDeviceBoundSessionAccessObserver |
| : public network::mojom::DeviceBoundSessionAccessObserver { |
| public: |
| explicit ServiceWorkerDeviceBoundSessionAccessObserver( |
| StoragePartitionImpl* storage_partition) |
| : storage_partition_(storage_partition) {} |
| |
| private: |
| void Clone( |
| mojo::PendingReceiver<network::mojom::DeviceBoundSessionAccessObserver> |
| observer) override { |
| storage_partition_->service_worker_device_bound_session_observers_.Add( |
| std::make_unique<ServiceWorkerDeviceBoundSessionAccessObserver>( |
| storage_partition_), |
| std::move(observer)); |
| } |
| |
| void OnDeviceBoundSessionAccessed( |
| const net::device_bound_sessions::SessionAccess& access) override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| for (GlobalRenderFrameHostId frame_id : GetRoutingIdsForOrigin( |
| storage_partition_, |
| url::Origin::Create(access.session_key.site.GetURL()))) { |
| if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) { |
| rfh->OnDeviceBoundSessionAccessed(access); |
| } |
| } |
| } |
| |
| // `storage_partition_` owns this object via UniqueReceiverSet |
| // (service_worker_device_bound_session_observers_). |
| raw_ptr<StoragePartitionImpl> storage_partition_; |
| }; |
| |
| struct StoragePartitionImpl::NetworkContextOwner { |
| mojo::Remote<network::mojom::NetworkContext> network_context; |
| }; |
| |
| StoragePartitionImpl::StoragePartitionImpl( |
| BrowserContext* browser_context, |
| const StoragePartitionConfig& config, |
| const base::FilePath& partition_path, |
| const base::FilePath& relative_partition_path, |
| storage::SpecialStoragePolicy* special_storage_policy) |
| : browser_context_(browser_context), |
| partition_path_(partition_path), |
| config_(config), |
| relative_partition_path_(relative_partition_path), |
| special_storage_policy_(special_storage_policy), |
| network_context_owner_(std::make_unique<NetworkContextOwner>()), |
| deletion_helpers_running_(0) {} |
| |
| StoragePartitionImpl::~StoragePartitionImpl() { |
| #if DCHECK_IS_ON() |
| DCHECK(on_browser_context_will_be_destroyed_called_); |
| #endif |
| browser_context_ = nullptr; |
| |
| if (GetFileSystemContext()) { |
| GetFileSystemContext()->Shutdown(); |
| } |
| |
| if (GetDOMStorageContext()) { |
| GetDOMStorageContext()->Shutdown(); |
| } |
| |
| if (GetServiceWorkerContext()) { |
| GetServiceWorkerContext()->Shutdown(); |
| } |
| |
| if (GetBackgroundSyncContext()) { |
| GetBackgroundSyncContext()->Shutdown(); |
| } |
| |
| if (GetBackgroundFetchContext()) { |
| GetBackgroundFetchContext()->Shutdown(); |
| } |
| |
| if (GetGeneratedCodeCacheContext()) { |
| GetGeneratedCodeCacheContext()->Shutdown(); |
| } |
| } |
| |
| void StoragePartitionImpl::OnBrowserContextWillBeDestroyed() { |
| #if DCHECK_IS_ON() |
| on_browser_context_will_be_destroyed_called_ = true; |
| #endif |
| |
| // Shut down service worker and shared worker machinery because these can keep |
| // RenderProcessHosts and SiteInstances alive, and the codebase assumes these |
| // are destroyed before the BrowserContext is destroyed. |
| GetServiceWorkerContext()->Shutdown(); |
| GetSharedWorkerService()->Shutdown(); |
| |
| // These hold raw pointers to objects that are about to be destroyed, before |
| // this object is destroyed. Shut them down now to avoid dangling pointers. |
| if (GetFileSystemAccessManager()) { |
| GetFileSystemAccessManager()->Shutdown(); |
| } |
| |
| if (GetContentIndexContext()) { |
| GetContentIndexContext()->Shutdown(); |
| } |
| |
| if (GetPlatformNotificationContext()) { |
| GetPlatformNotificationContext()->Shutdown(); |
| } |
| |
| if (keep_alive_url_loader_service_) { |
| keep_alive_url_loader_service_->Shutdown(); |
| } |
| } |
| |
| void StoragePartitionImpl::RegisterKeepAliveHandle( |
| mojo::PendingReceiver<blink::mojom::NavigationStateKeepAliveHandle> |
| receiver, |
| std::unique_ptr<NavigationStateKeepAlive> handle) { |
| navigation_state_keep_alive_map_.erase(handle->frame_token()); |
| navigation_state_keep_alive_map_.insert( |
| std::make_pair(handle->frame_token(), handle.get())); |
| |
| keep_alive_handles_receiver_set_.Add(std::move(handle), std::move(receiver)); |
| } |
| |
| void StoragePartitionImpl::RevokeNetworkForNoncesInNetworkContext( |
| const std::vector<base::UnguessableToken>& nonces, |
| network::mojom::NetworkContext::RevokeNetworkForNoncesCallback callback) { |
| GetNetworkContext()->RevokeNetworkForNonces(nonces, std::move(callback)); |
| |
| // Save nonces in `StoragePartitionImpl`. When there is a crash of |
| // `NetworkService`, the network revocation nonces of `NetworkContext` will be |
| // restored using this. |
| network_revocation_nonces_.insert(std::begin(nonces), std::end(nonces)); |
| } |
| |
| void StoragePartitionImpl::ClearNoncesInNetworkContextAfterDelay( |
| const std::vector<base::UnguessableToken>& nonces) { |
| GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &StoragePartitionImpl::ClearNoncesInNetworkContextAfterDelayCallback, |
| weak_factory_.GetWeakPtr(), nonces), |
| clear_nonces_in_network_context_delay_); |
| } |
| |
| void StoragePartitionImpl::ClearNoncesInNetworkContextAfterDelayCallback( |
| const std::vector<base::UnguessableToken>& nonces) { |
| GetNetworkContext()->ClearNonces(nonces); |
| |
| for (const auto& nonce : nonces) { |
| network_revocation_nonces_.erase(nonce); |
| } |
| |
| clear_nonces_in_network_context_callback_for_testing_.Run(); |
| } |
| |
| void StoragePartitionImpl::RemoveKeepAliveHandleFromMap( |
| blink::LocalFrameToken frame_token, |
| NavigationStateKeepAlive* keep_alive) { |
| // The NavigationStateKeepAlive associated with `frame_token` may have |
| // changed. Make sure the specified one is removed from the map. |
| auto it = navigation_state_keep_alive_map_.find(frame_token); |
| if (it != navigation_state_keep_alive_map_.end() && |
| it->second == keep_alive) { |
| navigation_state_keep_alive_map_.erase(frame_token); |
| } |
| } |
| |
| NavigationStateKeepAlive* StoragePartitionImpl::GetNavigationStateKeepAlive( |
| blink::LocalFrameToken frame_token) { |
| auto it = navigation_state_keep_alive_map_.find(frame_token); |
| if (it == navigation_state_keep_alive_map_.end()) { |
| return nullptr; |
| } |
| return it->second; |
| } |
| |
| // static |
| std::unique_ptr<StoragePartitionImpl> StoragePartitionImpl::Create( |
| BrowserContext* context, |
| const StoragePartitionConfig& config, |
| const base::FilePath& relative_partition_path) { |
| // Ensure that these methods are called on the UI thread, except for |
| // unittests where a UI thread might not have been created. |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| !BrowserThread::IsThreadInitialized(BrowserThread::UI)); |
| |
| base::FilePath partition_path = |
| context->GetPath().Append(relative_partition_path); |
| |
| return base::WrapUnique(new StoragePartitionImpl( |
| context, config, partition_path, relative_partition_path, |
| context->GetSpecialStoragePolicy())); |
| } |
| |
| void StoragePartitionImpl::Initialize( |
| StoragePartitionImpl* fallback_for_blob_urls) { |
| // Ensure that these methods are called on the UI thread, except for |
| // unittests where a UI thread might not have been created. |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| !BrowserThread::IsThreadInitialized(BrowserThread::UI)); |
| DCHECK(!initialized_); |
| initialized_ = true; |
| |
| // All of the clients have to be created and registered with the |
| // QuotaManager prior to the QuotaManager being used. We do them |
| // all together here prior to handing out a reference to anything |
| // that utilizes the QuotaManager. |
| quota_context_ = base::MakeRefCounted<QuotaContext>( |
| is_in_memory(), partition_path_, |
| browser_context_->GetSpecialStoragePolicy(), |
| base::BindRepeating(&StoragePartitionImpl::GetQuotaSettings, |
| weak_factory_.GetWeakPtr())); |
| quota_manager_ = quota_context_->quota_manager(); |
| scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy = |
| quota_manager_->proxy(); |
| |
| StorageNotificationService* storage_notification_service = |
| browser_context_->GetStorageNotificationService(); |
| if (storage_notification_service) { |
| // The weak ptr associated with the pressure notification callback will be |
| // created and evaluated by a task runner on the UI thread, as confirmed by |
| // the DCHECK's above, ensuring that the task runner does not attempt to run |
| // the callback in the case that the storage notification service is already |
| // destructed. |
| quota_manager_->SetStoragePressureCallback( |
| storage_notification_service |
| ->CreateThreadSafePressureNotificationCallback()); |
| } |
| |
| // Each consumer is responsible for registering its QuotaClient during |
| // its construction. |
| filesystem_context_ = CreateFileSystemContext( |
| browser_context_, partition_path_, is_in_memory(), quota_manager_proxy); |
| |
| dom_storage_context_ = DOMStorageContextWrapper::Create( |
| this, browser_context_->GetSpecialStoragePolicy()); |
| |
| lock_manager_ = std::make_unique<LockManager<storage::BucketId>>(); |
| |
| shared_storage_runtime_manager_ = |
| std::make_unique<SharedStorageRuntimeManager>(*this); |
| |
| scoped_refptr<ChromeBlobStorageContext> blob_context = |
| ChromeBlobStorageContext::GetFor(browser_context_); |
| |
| file_system_access_manager_ = |
| base::MakeRefCounted<FileSystemAccessManagerImpl>( |
| filesystem_context_, blob_context, |
| browser_context_->GetFileSystemAccessPermissionContext(), |
| browser_context_->IsOffTheRecord()); |
| |
| mojo::PendingRemote<storage::mojom::FileSystemAccessContext> |
| file_system_access_context; |
| file_system_access_manager_->BindInternalsReceiver( |
| file_system_access_context.InitWithNewPipeAndPassReceiver()); |
| base::FilePath path = is_in_memory() ? base::FilePath() : partition_path_; |
| indexed_db_control_wrapper_ = |
| std::make_unique<indexed_db::IndexedDBControlWrapper>( |
| path, browser_context_->GetSpecialStoragePolicy(), |
| quota_manager_proxy, |
| ChromeBlobStorageContext::GetRemoteFor(browser_context_), |
| std::move(file_system_access_context), GetIOThreadTaskRunner({})); |
| |
| cache_storage_control_wrapper_ = std::make_unique<CacheStorageControlWrapper>( |
| GetIOThreadTaskRunner({}), path, |
| browser_context_->GetSpecialStoragePolicy(), quota_manager_proxy, |
| ChromeBlobStorageContext::GetRemoteFor(browser_context_)); |
| |
| service_worker_context_ = new ServiceWorkerContextWrapper(browser_context_); |
| service_worker_context_->set_storage_partition(this); |
| |
| dedicated_worker_service_ = std::make_unique<DedicatedWorkerServiceImpl>(); |
| |
| shared_worker_service_ = |
| std::make_unique<SharedWorkerServiceImpl>(this, service_worker_context_); |
| |
| push_messaging_context_ = std::make_unique<PushMessagingContext>( |
| browser_context_, service_worker_context_); |
| |
| host_zoom_level_context_.reset(new HostZoomLevelContext( |
| browser_context_->CreateZoomLevelDelegate(partition_path_))); |
| |
| platform_notification_context_ = new PlatformNotificationContextImpl( |
| path, browser_context_, service_worker_context_); |
| platform_notification_context_->Initialize(); |
| |
| devtools_background_services_context_ = |
| std::make_unique<DevToolsBackgroundServicesContextImpl>( |
| browser_context_, service_worker_context_); |
| |
| content_index_context_ = base::MakeRefCounted<ContentIndexContextImpl>( |
| browser_context_, service_worker_context_); |
| |
| background_fetch_context_ = base::MakeRefCounted<BackgroundFetchContext>( |
| weak_factory_.GetWeakPtr(), service_worker_context_, quota_manager_proxy, |
| *devtools_background_services_context_.get()); |
| |
| background_sync_context_ = base::MakeRefCounted<BackgroundSyncContextImpl>(); |
| background_sync_context_->Init(service_worker_context_, |
| *devtools_background_services_context_.get()); |
| |
| payment_app_context_ = new PaymentAppContextImpl(); |
| payment_app_context_->Init(service_worker_context_); |
| |
| broadcast_channel_service_ = std::make_unique<BroadcastChannelService>(); |
| |
| bluetooth_allowed_devices_map_ = |
| std::make_unique<BluetoothAllowedDevicesMap>(); |
| |
| // Must be initialized before the |
| // `shared_url_loader_factory_for_browser_process_`. Cookie deprecation |
| // traffic labels should not be sent for off-the-record profiles, unless the |
| // "enable_otr_profiles" feature parameter is true. |
| if (base::FeatureList::IsEnabled( |
| features::kCookieDeprecationFacilitatedTesting) && |
| (!is_in_memory() || |
| features::kCookieDeprecationFacilitatedTestingEnableOTRProfiles.Get())) { |
| cookie_deprecation_label_manager_ = |
| std::make_unique<CookieDeprecationLabelManagerImpl>(browser_context_); |
| } |
| |
| shared_url_loader_factory_for_browser_process_ = std::make_unique< |
| ReconnectableURLLoaderFactoryForIOThreadWrapper>(base::BindRepeating( |
| &StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal, |
| GetWeakPtr())); |
| shared_url_loader_factory_for_browser_process_->factory_for_io_thread() |
| ->Initialize(); |
| |
| service_worker_context_->Init(path, quota_manager_proxy.get(), |
| browser_context_->GetSpecialStoragePolicy(), |
| blob_context.get()); |
| |
| blob_url_registry_ = std::make_unique<storage::BlobUrlRegistry>( |
| fallback_for_blob_urls |
| ? fallback_for_blob_urls->GetBlobUrlRegistry()->AsWeakPtr() |
| : nullptr); |
| |
| blob_registry_ = BlobRegistryWrapper::Create(blob_context); |
| |
| subresource_proxying_url_loader_service_ = |
| std::make_unique<SubresourceProxyingURLLoaderService>(browser_context_); |
| |
| if (blink::features::IsKeepAliveURLLoaderServiceEnabled()) { |
| keep_alive_url_loader_service_ = |
| std::make_unique<KeepAliveURLLoaderService>(browser_context_); |
| } |
| |
| cookie_store_manager_ = |
| std::make_unique<CookieStoreManager>(service_worker_context_); |
| // Unit tests use the LoadAllSubscriptions() callback to crash early if |
| // restoring the CookieManagerStore's state from ServiceWorkerStorage fails. |
| // Production and browser tests rely on CookieStoreManager's well-defined |
| // behavior when restoring the state fails. |
| cookie_store_manager_->LoadAllSubscriptions(base::DoNothing()); |
| |
| bucket_manager_ = std::make_unique<BucketManager>(this); |
| |
| if (base::FeatureList::IsEnabled( |
| attribution_reporting::features::kConversionMeasurement)) { |
| // The Conversion Measurement API is not available in Incognito mode, but |
| // this is enforced by the `AttributionManagerImpl` itself for better error |
| // reporting and metrics. |
| attribution_manager_ = std::make_unique<AttributionManagerImpl>( |
| this, path, special_storage_policy_); |
| } |
| |
| if (base::FeatureList::IsEnabled(network::features::kInterestGroupStorage)) { |
| // Auction worklets on non-Android use dedicated processes; on Android due |
| // to high cost of process launch they try to reuse renderers. |
| interest_group_manager_ = std::make_unique<InterestGroupManagerImpl>( |
| path, is_in_memory(), |
| #if BUILDFLAG(IS_ANDROID) |
| InterestGroupManagerImpl::ProcessMode::kInRenderer, |
| #else |
| InterestGroupManagerImpl::ProcessMode::kDedicated, |
| #endif |
| GetURLLoaderFactoryForBrowserProcess(), |
| base::BindRepeating(&BrowserContext::GetKAnonymityServiceDelegate, |
| // This use of Unretained is safe since the browser |
| // context owns this storage partition. |
| base::Unretained(browser_context_))); |
| } |
| |
| // The Topics API is not available in Incognito mode. |
| if (!is_in_memory() && |
| base::FeatureList::IsEnabled(network::features::kBrowsingTopics)) { |
| browsing_topics_site_data_manager_ = |
| std::make_unique<BrowsingTopicsSiteDataManagerImpl>(path); |
| } |
| |
| GeneratedCodeCacheSettings settings = |
| GetContentClient()->browser()->GetGeneratedCodeCacheSettings( |
| browser_context_); |
| |
| // For Incognito mode, we should not persist anything on the disk so |
| // we do not create a code cache. Caching the generated code in memory |
| // is not useful, since V8 already maintains one copy in memory. |
| if (!is_in_memory() && settings.enabled()) { |
| generated_code_cache_context_ = |
| base::MakeRefCounted<GeneratedCodeCacheContext>(); |
| |
| base::FilePath code_cache_path; |
| if (config_.partition_domain().empty()) { |
| code_cache_path = settings.path().AppendASCII("Code Cache"); |
| } else { |
| // For site isolated partitions use the config directory. |
| code_cache_path = settings.path() |
| .Append(relative_partition_path_) |
| .AppendASCII("Code Cache"); |
| } |
| DCHECK_GE(settings.size_in_bytes(), 0); |
| GetGeneratedCodeCacheContext()->Initialize(code_cache_path, |
| settings.size_in_bytes()); |
| } |
| |
| font_access_manager_ = FontAccessManager::Create(); |
| |
| aggregation_service_ = |
| std::make_unique<AggregationServiceImpl>(is_in_memory(), path, this); |
| |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| if (is_in_memory()) { |
| // Pass an empty path if in_memory so that CdmStorage.db is not stored on |
| // disk. |
| cdm_storage_manager_ = |
| std::make_unique<CdmStorageManager>(base::FilePath()); |
| } else { |
| cdm_storage_manager_ = std::make_unique<CdmStorageManager>( |
| partition_path_.Append(kCdmStorageDatabaseFileName)); |
| } |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| |
| if (base::FeatureList::IsEnabled(network::features::kSharedStorageAPI)) { |
| base::FilePath shared_storage_path = |
| is_in_memory() ? base::FilePath() |
| : path.Append(storage::kSharedStoragePath); |
| shared_storage_manager_ = std::make_unique<storage::SharedStorageManager>( |
| shared_storage_path, special_storage_policy_); |
| shared_storage_header_observer_ = |
| std::make_unique<SharedStorageHeaderObserver>(this); |
| } |
| |
| if (base::FeatureList::IsEnabled(blink::features::kPrivateAggregationApi)) { |
| private_aggregation_manager_ = |
| std::make_unique<PrivateAggregationManagerImpl>(is_in_memory(), path, |
| this); |
| } |
| } |
| |
| void StoragePartitionImpl::OnStorageServiceDisconnected() { |
| // This will be lazily re-bound on next use. |
| remote_partition_.reset(); |
| |
| dom_storage_context_->RecoverFromStorageServiceCrash(); |
| for (const auto& client : dom_storage_clients_) { |
| client.second->ResetStorageAreaAndNamespaceConnections(); |
| } |
| } |
| |
| const StoragePartitionConfig& StoragePartitionImpl::GetConfig() const { |
| return config_; |
| } |
| |
| const base::FilePath& StoragePartitionImpl::GetPath() const { |
| return partition_path_; |
| } |
| |
| const std::string& StoragePartitionImpl::GetPartitionDomain() const { |
| return config_.partition_domain(); |
| } |
| |
| network::mojom::NetworkContext* StoragePartitionImpl::GetNetworkContext() { |
| DCHECK(initialized_); |
| if (!network_context_owner_->network_context.is_bound()) { |
| InitNetworkContext(); |
| } |
| return network_context_owner_->network_context.get(); |
| } |
| |
| cert_verifier::mojom::CertVerifierServiceUpdater* |
| StoragePartitionImpl::GetCertVerifierServiceUpdater() { |
| DCHECK(initialized_); |
| if (!cert_verifier_service_updater_.is_bound()) { |
| InitNetworkContext(); |
| } |
| return cert_verifier_service_updater_.get(); |
| } |
| |
| scoped_refptr<network::SharedURLLoaderFactory> |
| StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcess() { |
| CHECK(shared_url_loader_factory_for_browser_process_); |
| return shared_url_loader_factory_for_browser_process_->factory(); |
| } |
| |
| std::unique_ptr<network::PendingSharedURLLoaderFactory> |
| StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcessIOThread() { |
| CHECK(shared_url_loader_factory_for_browser_process_); |
| return shared_url_loader_factory_for_browser_process_->factory_for_io_thread() |
| ->CloneForIOThread(); |
| } |
| |
| network::mojom::CookieManager* |
| StoragePartitionImpl::GetCookieManagerForBrowserProcess() { |
| DCHECK(initialized_); |
| // Create the CookieManager as needed. |
| if (!cookie_manager_for_browser_process_ || |
| !cookie_manager_for_browser_process_.is_connected()) { |
| // Reset `cookie_manager_for_browser_process_` before binding it again. |
| cookie_manager_for_browser_process_.reset(); |
| GetNetworkContext()->GetCookieManager( |
| cookie_manager_for_browser_process_.BindNewPipeAndPassReceiver()); |
| } |
| return cookie_manager_for_browser_process_.get(); |
| } |
| |
| void StoragePartitionImpl::CreateRestrictedCookieManager( |
| network::mojom::RestrictedCookieManagerRole role, |
| const url::Origin& origin, |
| const net::IsolationInfo& isolation_info, |
| bool is_service_worker, |
| int process_id, |
| int routing_id, |
| net::CookieSettingOverrides cookie_setting_overrides, |
| net::CookieSettingOverrides devtools_cookie_setting_overrides, |
| mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver, |
| mojo::PendingRemote<network::mojom::CookieAccessObserver> cookie_observer) { |
| DCHECK(initialized_); |
| if (!GetContentClient()->browser()->WillCreateRestrictedCookieManager( |
| role, browser_context_, origin, isolation_info, is_service_worker, |
| process_id, routing_id, &receiver)) { |
| GetNetworkContext()->GetRestrictedCookieManager( |
| std::move(receiver), role, origin, isolation_info, |
| cookie_setting_overrides, devtools_cookie_setting_overrides, |
| std::move(cookie_observer)); |
| } |
| } |
| |
| void StoragePartitionImpl::CreateTrustTokenQueryAnswerer( |
| mojo::PendingReceiver<network::mojom::TrustTokenQueryAnswerer> receiver, |
| const url::Origin& top_frame_origin) { |
| DCHECK(initialized_); |
| GetNetworkContext()->GetTrustTokenQueryAnswerer(std::move(receiver), |
| top_frame_origin); |
| } |
| |
| storage::QuotaManager* StoragePartitionImpl::GetQuotaManager() { |
| DCHECK(initialized_); |
| return quota_manager_.get(); |
| } |
| |
| storage::QuotaManagerProxy* StoragePartitionImpl::GetQuotaManagerProxy() { |
| DCHECK(initialized_); |
| return quota_manager_->proxy(); |
| } |
| |
| BackgroundSyncContextImpl* StoragePartitionImpl::GetBackgroundSyncContext() { |
| DCHECK(initialized_); |
| return background_sync_context_.get(); |
| } |
| |
| storage::FileSystemContext* StoragePartitionImpl::GetFileSystemContext() { |
| DCHECK(initialized_); |
| return filesystem_context_.get(); |
| } |
| |
| DOMStorageContextWrapper* StoragePartitionImpl::GetDOMStorageContext() { |
| DCHECK(initialized_); |
| return dom_storage_context_.get(); |
| } |
| |
| storage::mojom::LocalStorageControl* |
| StoragePartitionImpl::GetLocalStorageControl() { |
| DCHECK(initialized_); |
| return GetDOMStorageContext()->GetLocalStorageControl(); |
| } |
| |
| LockManager<storage::BucketId>* StoragePartitionImpl::GetLockManager() { |
| DCHECK(initialized_); |
| return lock_manager_.get(); |
| } |
| |
| SharedStorageRuntimeManager* |
| StoragePartitionImpl::GetSharedStorageRuntimeManager() { |
| DCHECK(initialized_); |
| return shared_storage_runtime_manager_.get(); |
| } |
| |
| storage::mojom::IndexedDBControl& StoragePartitionImpl::GetIndexedDBControl() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| return indexed_db_control_wrapper_->GetIndexedDBControl(); |
| } |
| |
| FileSystemAccessEntryFactory* |
| StoragePartitionImpl::GetFileSystemAccessEntryFactory() { |
| DCHECK(initialized_); |
| return file_system_access_manager_.get(); |
| } |
| |
| QuotaContext* StoragePartitionImpl::GetQuotaContext() { |
| DCHECK(initialized_); |
| return quota_context_.get(); |
| } |
| |
| storage::mojom::CacheStorageControl* |
| StoragePartitionImpl::GetCacheStorageControl() { |
| DCHECK(initialized_); |
| return cache_storage_control_wrapper_.get(); |
| } |
| |
| ServiceWorkerContextWrapper* StoragePartitionImpl::GetServiceWorkerContext() { |
| DCHECK(initialized_); |
| return service_worker_context_.get(); |
| } |
| |
| DedicatedWorkerServiceImpl* StoragePartitionImpl::GetDedicatedWorkerService() { |
| DCHECK(initialized_); |
| return dedicated_worker_service_.get(); |
| } |
| |
| SharedWorkerService* StoragePartitionImpl::GetSharedWorkerService() { |
| DCHECK(initialized_); |
| return shared_worker_service_.get(); |
| } |
| |
| HostZoomMap* StoragePartitionImpl::GetHostZoomMap() { |
| DCHECK(initialized_); |
| DCHECK(host_zoom_level_context_.get()); |
| return host_zoom_level_context_->GetHostZoomMap(); |
| } |
| |
| HostZoomLevelContext* StoragePartitionImpl::GetHostZoomLevelContext() { |
| DCHECK(initialized_); |
| return host_zoom_level_context_.get(); |
| } |
| |
| ZoomLevelDelegate* StoragePartitionImpl::GetZoomLevelDelegate() { |
| DCHECK(initialized_); |
| DCHECK(host_zoom_level_context_.get()); |
| return host_zoom_level_context_->GetZoomLevelDelegate(); |
| } |
| |
| PlatformNotificationContextImpl* |
| StoragePartitionImpl::GetPlatformNotificationContext() { |
| DCHECK(initialized_); |
| return platform_notification_context_.get(); |
| } |
| |
| BackgroundFetchContext* StoragePartitionImpl::GetBackgroundFetchContext() { |
| DCHECK(initialized_); |
| return background_fetch_context_.get(); |
| } |
| |
| PaymentAppContextImpl* StoragePartitionImpl::GetPaymentAppContext() { |
| DCHECK(initialized_); |
| return payment_app_context_.get(); |
| } |
| |
| BroadcastChannelService* StoragePartitionImpl::GetBroadcastChannelService() { |
| DCHECK(initialized_); |
| return broadcast_channel_service_.get(); |
| } |
| |
| BluetoothAllowedDevicesMap* |
| StoragePartitionImpl::GetBluetoothAllowedDevicesMap() { |
| DCHECK(initialized_); |
| return bluetooth_allowed_devices_map_.get(); |
| } |
| |
| BlobRegistryWrapper* StoragePartitionImpl::GetBlobRegistry() { |
| DCHECK(initialized_); |
| return blob_registry_.get(); |
| } |
| |
| storage::BlobUrlRegistry* StoragePartitionImpl::GetBlobUrlRegistry() { |
| DCHECK(initialized_); |
| return blob_url_registry_.get(); |
| } |
| |
| SubresourceProxyingURLLoaderService* |
| StoragePartitionImpl::GetSubresourceProxyingURLLoaderService() { |
| DCHECK(initialized_); |
| return subresource_proxying_url_loader_service_.get(); |
| } |
| |
| KeepAliveURLLoaderService* |
| StoragePartitionImpl::GetKeepAliveURLLoaderService() { |
| DCHECK(initialized_); |
| return keep_alive_url_loader_service_.get(); |
| } |
| |
| CookieStoreManager* StoragePartitionImpl::GetCookieStoreManager() { |
| DCHECK(initialized_); |
| return cookie_store_manager_.get(); |
| } |
| |
| BucketManager* StoragePartitionImpl::GetBucketManager() { |
| DCHECK(initialized_); |
| return bucket_manager_.get(); |
| } |
| |
| GeneratedCodeCacheContext* |
| StoragePartitionImpl::GetGeneratedCodeCacheContext() { |
| DCHECK(initialized_); |
| return generated_code_cache_context_.get(); |
| } |
| |
| DevToolsBackgroundServicesContext* |
| StoragePartitionImpl::GetDevToolsBackgroundServicesContext() { |
| DCHECK(initialized_); |
| return devtools_background_services_context_.get(); |
| } |
| |
| FileSystemAccessManagerImpl* |
| StoragePartitionImpl::GetFileSystemAccessManager() { |
| DCHECK(initialized_); |
| return file_system_access_manager_.get(); |
| } |
| |
| AttributionManager* StoragePartitionImpl::GetAttributionManager() { |
| DCHECK(initialized_); |
| return attribution_manager_.get(); |
| } |
| |
| AttributionDataModel* StoragePartitionImpl::GetAttributionDataModel() { |
| DCHECK(initialized_); |
| return attribution_manager_.get(); |
| } |
| |
| FontAccessManager* StoragePartitionImpl::GetFontAccessManager() { |
| DCHECK(initialized_); |
| return font_access_manager_.get(); |
| } |
| |
| void StoragePartitionImpl::SetFontAccessManagerForTesting( |
| std::unique_ptr<FontAccessManager> font_access_manager) { |
| DCHECK(initialized_); |
| DCHECK(font_access_manager); |
| font_access_manager_ = std::move(font_access_manager); |
| } |
| |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| CdmStorageDataModel* StoragePartitionImpl::GetCdmStorageDataModel() { |
| DCHECK(initialized_); |
| return cdm_storage_manager_.get(); |
| } |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| |
| network::mojom::DeviceBoundSessionManager* |
| StoragePartitionImpl::GetDeviceBoundSessionManager() { |
| DCHECK(initialized_); |
| #if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS) |
| if (!device_bound_session_manager_ || |
| !device_bound_session_manager_.is_connected()) { |
| // Reset `device_bound_session_manager_` before binding it again. |
| device_bound_session_manager_.reset(); |
| GetNetworkContext()->GetDeviceBoundSessionManager( |
| device_bound_session_manager_.BindNewPipeAndPassReceiver()); |
| } |
| |
| return device_bound_session_manager_.get(); |
| #else |
| return nullptr; |
| #endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS) |
| } |
| |
| InterestGroupManager* StoragePartitionImpl::GetInterestGroupManager() { |
| DCHECK(initialized_); |
| return interest_group_manager_.get(); |
| } |
| |
| BrowsingTopicsSiteDataManager* |
| StoragePartitionImpl::GetBrowsingTopicsSiteDataManager() { |
| DCHECK(initialized_); |
| return browsing_topics_site_data_manager_.get(); |
| } |
| |
| ContentIndexContextImpl* StoragePartitionImpl::GetContentIndexContext() { |
| DCHECK(initialized_); |
| return content_index_context_.get(); |
| } |
| |
| AggregationService* StoragePartitionImpl::GetAggregationService() { |
| DCHECK(initialized_); |
| return aggregation_service_.get(); |
| } |
| |
| leveldb_proto::ProtoDatabaseProvider* |
| StoragePartitionImpl::GetProtoDatabaseProvider() { |
| if (!proto_database_provider_) { |
| proto_database_provider_ = |
| std::make_unique<leveldb_proto::ProtoDatabaseProvider>(partition_path_, |
| is_in_memory()); |
| } |
| return proto_database_provider_.get(); |
| } |
| |
| void StoragePartitionImpl::SetProtoDatabaseProvider( |
| std::unique_ptr<leveldb_proto::ProtoDatabaseProvider> proto_db_provider) { |
| DCHECK(!proto_database_provider_); |
| proto_database_provider_ = std::move(proto_db_provider); |
| } |
| |
| leveldb_proto::ProtoDatabaseProvider* |
| StoragePartitionImpl::GetProtoDatabaseProviderForTesting() { |
| return proto_database_provider_.get(); |
| } |
| |
| storage::SharedStorageManager* StoragePartitionImpl::GetSharedStorageManager() { |
| return shared_storage_manager_.get(); |
| } |
| |
| PrivateAggregationManager* |
| StoragePartitionImpl::GetPrivateAggregationManager() { |
| DCHECK(initialized_); |
| return private_aggregation_manager_.get(); |
| } |
| |
| PrivateAggregationDataModel* |
| StoragePartitionImpl::GetPrivateAggregationDataModel() { |
| DCHECK(initialized_); |
| return private_aggregation_manager_.get(); |
| } |
| |
| CookieDeprecationLabelManager* |
| StoragePartitionImpl::GetCookieDeprecationLabelManager() { |
| CHECK(initialized_); |
| return cookie_deprecation_label_manager_.get(); |
| } |
| |
| void StoragePartitionImpl::DeleteStaleSessionData() { |
| GetDOMStorageContext()->StartScavengingUnusedSessionStorage(); |
| // We need to delay deleting stale session cookies until after the cookie db |
| // has initialized, otherwise we will bypass lazy loading and block. |
| // See crbug.com/40285083 for more info. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &StoragePartitionImpl::DeleteStaleSessionOnlyCookiesAfterDelay, |
| weak_factory_.GetWeakPtr()), |
| delete_stale_session_only_cookies_delay_); |
| } |
| |
| void StoragePartitionImpl::DeleteStaleSessionOnlyCookiesAfterDelay() { |
| GetCookieManagerForBrowserProcess()->DeleteStaleSessionOnlyCookies( |
| base::BindOnce([](const uint32_t num_deleted) { |
| base::UmaHistogramCounts10M( |
| "Cookie.StaleSessionCookiesDeletedOnStartup", num_deleted); |
| })); |
| } |
| |
| void StoragePartitionImpl::OpenLocalStorage( |
| const blink::StorageKey& storage_key, |
| const blink::LocalFrameToken& local_frame_token, |
| mojo::PendingReceiver<blink::mojom::StorageArea> receiver) { |
| DCHECK(initialized_); |
| ChildProcessSecurityPolicyImpl::Handle security_policy_handle = |
| dom_storage_receivers_.current_context()->Duplicate(); |
| dom_storage_context_->OpenLocalStorage( |
| storage_key, local_frame_token, std::move(receiver), |
| std::move(security_policy_handle), |
| dom_storage_receivers_.GetBadMessageCallback()); |
| } |
| |
| void StoragePartitionImpl::BindSessionStorageNamespace( |
| const std::string& namespace_id, |
| mojo::PendingReceiver<blink::mojom::SessionStorageNamespace> receiver) { |
| DCHECK(initialized_); |
| dom_storage_context_->BindNamespace( |
| namespace_id, dom_storage_receivers_.GetBadMessageCallback(), |
| std::move(receiver)); |
| } |
| |
| void StoragePartitionImpl::BindSessionStorageArea( |
| const blink::StorageKey& storage_key, |
| const blink::LocalFrameToken& local_frame_token, |
| const std::string& namespace_id, |
| mojo::PendingReceiver<blink::mojom::StorageArea> receiver) { |
| DCHECK(initialized_); |
| ChildProcessSecurityPolicyImpl::Handle security_policy_handle = |
| dom_storage_receivers_.current_context()->Duplicate(); |
| dom_storage_context_->BindStorageArea( |
| storage_key, local_frame_token, namespace_id, std::move(receiver), |
| std::move(security_policy_handle), |
| dom_storage_receivers_.GetBadMessageCallback()); |
| } |
| |
| void StoragePartitionImpl::OnAuthRequired( |
| const std::optional<base::UnguessableToken>& window_id, |
| int32_t request_id, |
| const GURL& url, |
| bool first_auth_attempt, |
| const net::AuthChallengeInfo& auth_info, |
| const scoped_refptr<net::HttpResponseHeaders>& head_headers, |
| mojo::PendingRemote<network::mojom::AuthChallengeResponder> |
| auth_challenge_responder) { |
| URLLoaderNetworkContext context = |
| url_loader_network_observers_.current_context(); |
| std::optional<bool> is_primary_main_frame_navigation; |
| std::optional<bool> is_navigation_request; |
| |
| if (window_id) { |
| // Use `window_id` if it is provided, because this request was sent by a |
| // service worker; service workers use `window_id` to identify the frame |
| // that sends the request since a worker is shared among multiple frames. |
| // TODO(crbug.com/40194275): Add a DCHECK here that process_id and |
| // routing_id are invalid. It can't be added yet because somehow routing_id |
| // is valid here. |
| if (service_worker_context_->context()) { |
| auto* service_worker_client = |
| service_worker_context_->context() |
| ->service_worker_client_owner() |
| .GetServiceWorkerClientByWindowId(*window_id); |
| if (service_worker_client) { |
| if (service_worker_client->GetRenderFrameHostId()) { |
| // Use ServiceWorkerClient's GlobalRenderFrameHostId when the |
| // navigation commit has already started. |
| GlobalRenderFrameHostId render_frame_host_id = |
| service_worker_client->GetRenderFrameHostId(); |
| context = URLLoaderNetworkContext::CreateForRenderFrameHost( |
| render_frame_host_id); |
| |
| // Only the request for a sub resource intercepted by a service worker |
| // reaches here. |
| is_primary_main_frame_navigation = false; |
| is_navigation_request = false; |
| } else if (NavigationRequest* ongoing_navigation = |
| service_worker_client |
| ->GetOngoingNavigationRequestBeforeCommit( |
| base::PassKey<StoragePartitionImpl>())) { |
| // This auth request is for an ongoing navigation controlled |
| // by service worker. The navigation request can be nullptr if user |
| // has closed the WebContents. |
| // Overwrite the context; set `type` to kNavigationRequestContext. |
| // TODO(crbug.com/40784852): Optimize locating logic. |
| context = |
| URLLoaderNetworkContext::CreateForNavigation(*ongoing_navigation); |
| } |
| } |
| } |
| } |
| |
| // If the request is for a prerendering page, prerendering should be cancelled |
| // because the embedder may show UI for auth requests, and it's unsuitable for |
| // a hidden page. |
| if (CancelIfPrerendering(context.navigation_or_document(), |
| PrerenderFinalStatus::kLoginAuthRequested)) { |
| return; |
| } |
| |
| if (!is_primary_main_frame_navigation.has_value()) { |
| is_primary_main_frame_navigation = context.IsPrimaryMainFrameRequest(); |
| } |
| if (!is_navigation_request.has_value()) { |
| is_navigation_request = context.IsNavigationRequestContext(); |
| } |
| int process_id = network::mojom::kBrowserProcessId; |
| if (context.type() == ContextType::kRenderFrameHostContext) { |
| // Set `process_id` to `kInvalidProcessId` considering `render_frame_host` |
| // can be null when it's destroyed already. `process_id` is updated only if |
| // `render_frame_host` is not null. If `render_frame_host` is null, |
| // later logic will call OnAuthCredentials() with a nullopt that triggers |
| // CancelAuth(). |
| process_id = network::mojom::kInvalidProcessId; |
| |
| // `navigation_or_document_` can be null when `context` is created with |
| // an invalid RenderFrameHost after a page is destroyed. |
| // It is currently possible for the ServiceWorker case above to use |
| // kRenderFrameHostContext for the auth request, after the RenderFrameHost |
| // has been deleted. Treating this as an invalid process ID will cancel the |
| // auth, which is the same outcome as if the ServiceWorker's process were |
| // used. |
| // TODO(crbug.com/40224422): Update the ServiceWorker code to |
| // recognize when the RenderFrameHost goes away and not use |
| // CreateForRenderFrameHost above. |
| if (context.navigation_or_document()) { |
| auto* render_frame_host = context.navigation_or_document()->GetDocument(); |
| if (render_frame_host) { |
| process_id = render_frame_host->GetGlobalId().child_id; |
| } |
| } |
| } else if (context.type() == ContextType::kServiceWorkerContext) { |
| process_id = context.process_id(); |
| } |
| |
| FrameTreeNodeId frame_tree_node_id; |
| if (auto* navigation_or_document = context.navigation_or_document()) { |
| if (auto* frame_tree_node = navigation_or_document->GetFrameTreeNode()) { |
| frame_tree_node_id = frame_tree_node->frame_tree_node_id(); |
| } |
| } |
| |
| WebContents* current_web_contents = context.GetWebContents(); |
| if (current_web_contents) { |
| // Evict all the BFCache entries that |
| // 1): are stored in the same BrowserContext |
| // 2): were loaded with the "Cache-control: no-store" header |
| // 3): match the challenger information of the page that requires HTTP |
| // authentication. |
| for (WebContentsImpl* web_contents : WebContentsImpl::GetAllWebContents()) { |
| if (web_contents->GetBrowserContext()->UniqueId() == |
| current_web_contents->GetBrowserContext()->UniqueId()) { |
| for (const std::unique_ptr<BackForwardCacheImpl::Entry>& entry : |
| web_contents->GetController().GetBackForwardCache().GetEntries()) { |
| RenderFrameHostImpl* rfh = entry->render_frame_host(); |
| const GURL& last_committed_url = rfh->GetLastCommittedURL(); |
| if (rfh->LoadedWithCacheControlNoStoreHeader() && |
| auth_info.challenger == |
| url::SchemeHostPort(last_committed_url.scheme(), |
| last_committed_url.host(), |
| last_committed_url.IntPort())) { |
| BackForwardCacheCanStoreDocumentResult flattened_reasons; |
| flattened_reasons.No(BackForwardCacheMetrics::NotRestoredReason:: |
| kCacheControlNoStore); |
| flattened_reasons.No( |
| BackForwardCacheMetrics::NotRestoredReason::kHTTPAuthRequired); |
| rfh->EvictFromBackForwardCacheWithFlattenedReasons( |
| flattened_reasons); |
| } |
| } |
| } |
| } |
| } |
| |
| new LoginHandlerDelegate( |
| std::move(auth_challenge_responder), current_web_contents, |
| browser_context_, auth_info, *is_primary_main_frame_navigation, |
| *is_navigation_request, process_id, request_id, url, head_headers, |
| first_auth_attempt, frame_tree_node_id); // deletes self |
| } |
| |
| void StoragePartitionImpl::OnPrivateNetworkAccessPermissionRequired( |
| const GURL& url, |
| const net::IPAddress& ip_address, |
| const std::optional<std::string>& private_network_device_id, |
| const std::optional<std::string>& private_network_device_name, |
| OnPrivateNetworkAccessPermissionRequiredCallback callback) { |
| if (!base::FeatureList::IsEnabled( |
| network::features::kPrivateNetworkAccessPermissionPrompt)) { |
| std::move(callback).Run(false); |
| return; |
| } |
| |
| if (url_loader_network_observers_.empty()) { |
| std::move(callback).Run(false); |
| return; |
| } |
| const URLLoaderNetworkContext& context = |
| url_loader_network_observers_.current_context(); |
| |
| if (context.type() != ContextType::kRenderFrameHostContext || |
| !context.navigation_or_document()) { |
| std::move(callback).Run(false); |
| return; |
| } |
| RenderFrameHost* render_frame_host = |
| context.navigation_or_document()->GetDocument(); |
| if (!render_frame_host) { |
| std::move(callback).Run(false); |
| return; |
| } |
| auto device = blink::mojom::PrivateNetworkDevice::New( |
| private_network_device_id, private_network_device_name, ip_address); |
| |
| PrivateNetworkDeviceDelegate* delegate = |
| GetContentClient()->browser()->GetPrivateNetworkDeviceDelegate(); |
| if (!delegate) { |
| std::move(callback).Run(false); |
| return; |
| } |
| |
| delegate->RequestPermission(*render_frame_host, std::move(device), |
| std::move(callback)); |
| } |
| |
| void StoragePartitionImpl::OnLocalNetworkAccessPermissionRequired( |
| OnLocalNetworkAccessPermissionRequiredCallback callback) { |
| if (!base::FeatureList::IsEnabled( |
| network::features::kLocalNetworkAccessChecks) && |
| !network::features::kLocalNetworkAccessChecksWarn.Get()) { |
| // If LNA checks are not enabled, just allow the request by default. |
| std::move(callback).Run(true); |
| return; |
| } |
| |
| if (url_loader_network_observers_.empty()) { |
| std::move(callback).Run(false); |
| return; |
| } |
| const URLLoaderNetworkContext& context = |
| url_loader_network_observers_.current_context(); |
| |
| // Three different cases are handled here depending on the request context: |
| // 1. Document context (ContextType::kRenderFrameHostContext) covers fetch() |
| // and subresource requests. These should check for existing permission |
| // state, and if the state is ASK trigger the permission prompt. These |
| // should also handle being delegated into subframe documents. |
| // 2. Navigation context (ContextType::kNavigationRequestContext) covers |
| // subframe navigations. These should check for existing permission |
| // state, and if the state is ASK trigger the permission prompt. Nested |
| // subframes should be allowed iff permission policy delegated the |
| // permission into the embedding frame. |
| // 3. Worker context (ContextType::kServiceWorkerContext) covers requests |
| // from workers. These may not have an existing document around. These |
| // should check for the permission state, but NOT trigger the permission |
| // prompt. |
| |
| // Currently requesting the Local Network Access permission is restricted to |
| // subresource requests and subframe navigation requests. |
| // TODO(crbug.com/404887285): Denying permission for a subframe navigation |
| // results in an error page with text that isn't quite true anymore: "The |
| // connection is blocked because it was initiated by a public page to connect |
| // to devices or servers on your private network. Reload this page to allow |
| // the connection." The last sentence should be removed. |
| |
| // Handle document (Case 1) and navigation (Case 2) contexts. |
| if (context.navigation_or_document()) { |
| RenderFrameHost* rfh = nullptr; |
| if (context.navigation_or_document()->GetDocument()) { |
| // Get the document that is making the request. |
| rfh = context.navigation_or_document()->GetDocument(); |
| } else if (context.navigation_or_document()->GetNavigationRequest()) { |
| // Get the document that is embedding the frame being navigated. |
| rfh = context.navigation_or_document() |
| ->GetNavigationRequest() |
| ->GetParentFrameOrOuterDocument(); |
| } |
| if (!rfh) { |
| std::move(callback).Run(false); |
| return; |
| } |
| |
| PermissionController* permission_controller = |
| browser_context_->GetPermissionController(); |
| DCHECK(permission_controller); |
| auto status = permission_controller->GetPermissionStatusForCurrentDocument( |
| content::PermissionDescriptorUtil:: |
| CreatePermissionDescriptorForPermissionType( |
| blink::PermissionType::LOCAL_NETWORK_ACCESS), |
| rfh); |
| if (status == blink::mojom::PermissionStatus::GRANTED) { |
| std::move(callback).Run(true); |
| return; |
| } else if (status == blink::mojom::PermissionStatus::DENIED) { |
| std::move(callback).Run(false); |
| return; |
| } else { |
| // PermissionStatus is ASK, so request the permission. Converts the result |
| // into a boolean to pass back to `callback`, capturing whether the |
| // permission is granted or not. |
| permission_controller->RequestPermissionFromCurrentDocument( |
| rfh, |
| PermissionRequestDescription( |
| content::PermissionDescriptorUtil:: |
| CreatePermissionDescriptorForPermissionType( |
| blink::PermissionType::LOCAL_NETWORK_ACCESS)), |
| base::BindOnce( |
| [](OnLocalNetworkAccessPermissionRequiredCallback cb, |
| PermissionStatus status) { |
| std::move(cb).Run(status == |
| blink::mojom::PermissionStatus::GRANTED); |
| }, |
| std::move(callback))); |
| return; |
| } |
| } else if (context.type() == ContextType::kServiceWorkerContext) { |
| // TODO(crbug.com/404887282): Add support for gating requests from workers |
| // on whether the user previously granted the permission. This will require |
| // plumbing through the `window_id` for the fetch to identify the worker |
| // context and then using that to get the worker origin to use when calling |
| // PermissionController::GetPermissionStatusForWorker(). |
| std::move(callback).Run(true); |
| return; |
| } |
| |
| // Otherwise default to denying local network access. |
| std::move(callback).Run(false); |
| return; |
| } |
| |
| void StoragePartitionImpl::OnCertificateRequested( |
| const std::optional<base::UnguessableToken>& window_id, |
| const scoped_refptr<net::SSLCertRequestInfo>& cert_info, |
| mojo::PendingRemote<network::mojom::ClientCertificateResponder> |
| cert_responder) { |
| URLLoaderNetworkContext context = |
| url_loader_network_observers_.current_context(); |
| |
| if (window_id) { |
| // Use `window_id` if it is provided, because this request was sent by a |
| // service worker; service workers use `window_id` to identify the frame |
| // that sends the request since a worker is shared among multiple frames. |
| // TODO(crbug.com/40194275): Add a DCHECK here that process_id and |
| // routing_id are invalid. It can't be added yet because somehow routing_id |
| // is valid here. |
| if (service_worker_context_->context()) { |
| auto* service_worker_client = |
| service_worker_context_->context() |
| ->service_worker_client_owner() |
| .GetServiceWorkerClientByWindowId(*window_id); |
| if (service_worker_client) { |
| if (service_worker_client->GetRenderFrameHostId()) { |
| // Use ServiceWorkerClient's GlobalRenderFrameHostId when the |
| // navigation commit has already started. |
| GlobalRenderFrameHostId render_frame_host_id = |
| service_worker_client->GetRenderFrameHostId(); |
| context = URLLoaderNetworkContext::CreateForRenderFrameHost( |
| render_frame_host_id); |
| } else if (NavigationRequest* ongoing_navigation = |
| service_worker_client |
| ->GetOngoingNavigationRequestBeforeCommit( |
| base::PassKey<StoragePartitionImpl>())) { |
| // This certification request is for an ongoing navigation. |
| // Overwrite the context; set `type` to kNavigationRequestContext. |
| // TODO(crbug.com/40784852): Optimize locating logic. |
| context = |
| URLLoaderNetworkContext::CreateForNavigation(*ongoing_navigation); |
| } else { |
| // The navigation request was canceled since the WebContents was |
| // discarded, so it is meaningless to continue the certification |
| // request. |
| CallCancelRequest(std::move(cert_responder)); |
| return; |
| } |
| } |
| } |
| } |
| |
| // If the request is for a prerendering page, prerendering should be cancelled |
| // because the embedder may show a dialog and ask users to select client |
| // certificates, and it's unsuitable for a hidden page. |
| if (CancelIfPrerendering(context.navigation_or_document(), |
| PrerenderFinalStatus::kClientCertRequested)) { |
| CallCancelRequest(std::move(cert_responder)); |
| return; |
| } |
| |
| base::WeakPtr<WebContents> web_contents_weak; |
| int process_id = network::mojom::kInvalidProcessId; |
| if (context.type() == ContextType::kServiceWorkerContext) { |
| process_id = context.process_id(); |
| } else { |
| WebContents* web_contents = context.GetWebContents(); |
| // The WebContents is already invalid. Bail. |
| if (!web_contents) { |
| CallCancelRequest(std::move(cert_responder)); |
| return; |
| } |
| CHECK_EQ(web_contents->GetBrowserContext(), browser_context_.get()); |
| web_contents_weak = web_contents->GetWeakPtr(); |
| |
| if (context.navigation_or_document()) { |
| auto* render_frame_host = context.navigation_or_document()->GetDocument(); |
| if (render_frame_host) { |
| process_id = render_frame_host->GetProcess()->GetDeprecatedID(); |
| } |
| } |
| } |
| |
| // SSLClientAuthDelegate handles its own lifetime. |
| new SSLClientAuthDelegate(std::move(cert_responder), browser_context(), |
| process_id, web_contents_weak, cert_info); |
| } |
| |
| void StoragePartitionImpl::OnSSLCertificateError( |
| const GURL& url, |
| int net_error, |
| const net::SSLInfo& ssl_info, |
| bool fatal, |
| OnSSLCertificateErrorCallback response) { |
| URLLoaderNetworkContext context = |
| url_loader_network_observers_.current_context(); |
| |
| // Cancel this request and the prerendering if the request is for a |
| // prerendering page, because prerendering pages are invisible and browser |
| // cannot show errors on invisible pages. |
| if (CancelIfPrerendering(context.navigation_or_document(), |
| PrerenderFinalStatus::kSslCertificateError)) { |
| std::move(response).Run(net_error); |
| return; |
| } |
| |
| SSLErrorDelegate* delegate = |
| new SSLErrorDelegate(std::move(response)); // deletes self |
| bool is_primary_main_frame_request = context.IsPrimaryMainFrameRequest(); |
| SSLManager::OnSSLCertificateError( |
| delegate->GetWeakPtr(), is_primary_main_frame_request, url, |
| context.navigation_or_document(), net_error, ssl_info, fatal); |
| } |
| |
| void StoragePartitionImpl::OnLoadingStateUpdate( |
| network::mojom::LoadInfoPtr info, |
| OnLoadingStateUpdateCallback callback) { |
| auto* web_contents = |
| url_loader_network_observers_.current_context().GetWebContents(); |
| if (web_contents) { |
| static_cast<WebContentsImpl*>(web_contents) |
| ->LoadStateChanged(std::move(info)); |
| } |
| std::move(callback).Run(); |
| } |
| |
| void StoragePartitionImpl::OnDataUseUpdate( |
| int32_t network_traffic_annotation_id_hash, |
| int64_t recv_bytes, |
| int64_t sent_bytes) { |
| GlobalRenderFrameHostId render_frame_host_id = |
| GetRenderFrameHostIdFromNetworkContext(); |
| GetContentClient()->browser()->OnNetworkServiceDataUseUpdate( |
| render_frame_host_id, network_traffic_annotation_id_hash, recv_bytes, |
| sent_bytes); |
| } |
| |
| void StoragePartitionImpl::OnSharedStorageHeaderReceived( |
| const url::Origin& request_origin, |
| std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr> |
| methods_with_options, |
| const std::optional<std::string>& with_lock, |
| OnSharedStorageHeaderReceivedCallback callback) { |
| if (!shared_storage_header_observer_) { |
| std::move(callback).Run(); |
| return; |
| } |
| |
| // Currently, shared-storage-writable headers aren't available for requests |
| // initiated by service workers, so `navigation_or_document` should be |
| // non-null. |
| // |
| // TODO(cammie): If we handle the service worker case by allowing service |
| // workers to initiate shared-storage-writable requests, the assumption that |
| // `navigation_or_document` must be non-null may become incorrect. |
| auto* navigation_or_document = |
| url_loader_network_observers_.current_context().navigation_or_document(); |
| DCHECK(navigation_or_document); |
| |
| shared_storage_header_observer_->HeaderReceived( |
| request_origin, url_loader_network_observers_.current_context().type(), |
| navigation_or_document, std::move(methods_with_options), with_lock, |
| std::move(callback), mojo::GetBadMessageCallback(), /*can_defer=*/true); |
| } |
| |
| void StoragePartitionImpl::OnAdAuctionEventRecordHeaderReceived( |
| network::AdAuctionEventRecord event_record, |
| const std::optional<url::Origin>& top_frame_origin) { |
| DCHECK(browser_context()); |
| interest_group_manager_->RecordViewClick( |
| *browser_context(), |
| url_loader_network_observers_.current_context().navigation_or_document(), |
| top_frame_origin, std::move(event_record)); |
| } |
| |
| void StoragePartitionImpl::Clone( |
| mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver> |
| observer) { |
| url_loader_network_observers_.Add( |
| this, std::move(observer), |
| url_loader_network_observers_.current_context()); |
| } |
| |
| void StoragePartitionImpl::OnWebSocketConnectedToPrivateNetwork( |
| network::mojom::IPAddressSpace ip_address_space) { |
| RenderFrameHostImpl* render_frame_host_impl = |
| RenderFrameHostImpl::FromID(GetRenderFrameHostIdFromNetworkContext()); |
| |
| if (render_frame_host_impl && |
| network::IsLessPublicAddressSpace( |
| ip_address_space, render_frame_host_impl->BuildClientSecurityState() |
| ->ip_address_space)) { |
| GetContentClient()->browser()->LogWebFeatureForCurrentPage( |
| render_frame_host_impl, |
| blink::mojom::WebFeature::kPrivateNetworkAccessWebSocketConnected); |
| } |
| } |
| |
| void StoragePartitionImpl::OnUrlLoaderConnectedToPrivateNetwork( |
| const GURL& request_url, |
| network::mojom::IPAddressSpace response_address_space, |
| network::mojom::IPAddressSpace client_address_space, |
| network::mojom::IPAddressSpace target_address_space) { |
| RenderFrameHostImpl* render_frame_host_impl = |
| RenderFrameHostImpl::FromID(GetRenderFrameHostIdFromNetworkContext()); |
| if (!render_frame_host_impl) { |
| return; |
| } |
| // Log a UseCounter for potential PNA 2.0 breakage. We are interested in the |
| // case where post-PNA 2.0 this request would fail due to mixed content |
| // blocking: |
| // 1. The request was to a private or local address space, |
| // 2. The request was from non-secure context, |
| // 3. The request was to a URL that is not potentially trustworthy, |
| // 4. The request is to a less public address space than the client making the |
| // request, and |
| // 5. The request was _not_ a priori private. We a priori know a request is |
| // private (and thus can trigger permission prompts _before_ mixed content |
| // checks) if the target address space is specified, if the request is to a |
| // `.local` domain, or if the request is to a private IP address literal. |
| // |
| // (1) is checked by the caller in the network service. (2) is implied because |
| // if it was from a secure context then the request to a not potentially |
| // trustworthy URL would have been blocked as mixed content. (3-5) are checked |
| // below. |
| // |
| // This may be called multiple times over the lifetime of resource load (due |
| // to redirect hops), as we want to consider every redirect hop for this |
| // metric as breakage may occur at the initial request or at any hop in the |
| // redirect chain. This is logged as a UseCounter for the current page, which |
| // "deduplicates" these repeated calls. |
| if (!network::IsUrlPotentiallyTrustworthy(request_url) && |
| network::IsLessPublicAddressSpace(response_address_space, |
| client_address_space) && |
| // Not a priori known to be private. |
| target_address_space == network::mojom::IPAddressSpace::kUnknown && |
| !request_url.HostIsIPAddress() && !request_url.DomainIs("local")) { |
| GetContentClient()->browser()->LogWebFeatureForCurrentPage( |
| render_frame_host_impl, |
| blink::mojom::WebFeature:: |
| kPrivateNetworkAccessInsecureResourceNotKnownPrivate); |
| } |
| } |
| |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> |
| StoragePartitionImpl::CreateURLLoaderNetworkObserverForFrame(int process_id, |
| int routing_id) { |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote; |
| url_loader_network_observers_.Add( |
| this, remote.InitWithNewPipeAndPassReceiver(), |
| URLLoaderNetworkContext::CreateForRenderFrameHost( |
| GlobalRenderFrameHostId(process_id, routing_id))); |
| return remote; |
| } |
| |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> |
| StoragePartitionImpl::CreateURLLoaderNetworkObserverForNavigationRequest( |
| NavigationRequest& navigation_request) { |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote; |
| url_loader_network_observers_.Add( |
| this, remote.InitWithNewPipeAndPassReceiver(), |
| URLLoaderNetworkContext::CreateForNavigation(navigation_request)); |
| return remote; |
| } |
| |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> |
| StoragePartitionImpl::CreateAuthCertObserverForServiceWorker(int process_id) { |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote; |
| url_loader_network_observers_.Add(this, |
| remote.InitWithNewPipeAndPassReceiver(), |
| URLLoaderNetworkContext(process_id)); |
| return remote; |
| } |
| |
| void StoragePartitionImpl::OnFileUploadRequested( |
| int32_t process_id, |
| bool async, |
| const std::vector<base::FilePath>& file_paths, |
| const GURL& destination_url, |
| OnFileUploadRequestedCallback callback) { |
| NetworkContextOnFileUploadRequested(process_id, async, file_paths, |
| destination_url, std::move(callback)); |
| } |
| |
| void StoragePartitionImpl::OnCanSendReportingReports( |
| const std::vector<url::Origin>& origins, |
| OnCanSendReportingReportsCallback callback) { |
| DCHECK(initialized_); |
| PermissionController* permission_controller = |
| browser_context_->GetPermissionController(); |
| DCHECK(permission_controller); |
| |
| std::vector<url::Origin> origins_out; |
| for (auto& origin : origins) { |
| bool allowed = permission_controller |
| ->GetPermissionResultForOriginWithoutContext( |
| content::PermissionDescriptorUtil:: |
| CreatePermissionDescriptorForPermissionType( |
| blink::PermissionType::BACKGROUND_SYNC), |
| origin) |
| .status == blink::mojom::PermissionStatus::GRANTED; |
| if (allowed) { |
| origins_out.push_back(origin); |
| } |
| } |
| |
| std::move(callback).Run(origins_out); |
| } |
| |
| void StoragePartitionImpl::OnCanSendDomainReliabilityUpload( |
| const url::Origin& origin, |
| OnCanSendDomainReliabilityUploadCallback callback) { |
| DCHECK(initialized_); |
| PermissionController* permission_controller = |
| browser_context_->GetPermissionController(); |
| std::move(callback).Run( |
| permission_controller |
| ->GetPermissionResultForOriginWithoutContext( |
| content::PermissionDescriptorUtil:: |
| CreatePermissionDescriptorForPermissionType( |
| blink::PermissionType::BACKGROUND_SYNC), |
| origin) |
| .status == blink::mojom::PermissionStatus::GRANTED); |
| } |
| |
| void StoragePartitionImpl::OnClearSiteData( |
| const GURL& url, |
| const std::string& header_value, |
| int load_flags, |
| const std::optional<net::CookiePartitionKey>& cookie_partition_key, |
| bool partitioned_state_allowed_only, |
| OnClearSiteDataCallback callback) { |
| DCHECK(initialized_); |
| |
| base::WeakPtr<WebContents> weak_web_contents; |
| WebContents* web_contents = |
| url_loader_network_observers_.current_context().GetWebContents(); |
| if (web_contents) { |
| weak_web_contents = web_contents->GetWeakPtr(); |
| } |
| |
| std::optional<blink::StorageKey> storage_key = CalculateStorageKey( |
| url::Origin::Create(url), |
| cookie_partition_key.has_value() |
| ? base::OptionalToPtr(cookie_partition_key.value().nonce()) |
| : nullptr); |
| |
| ClearSiteDataHandler::HandleHeader( |
| browser_context()->GetWeakPtr(), weak_web_contents, GetConfig(), url, |
| header_value, load_flags, cookie_partition_key, storage_key, |
| partitioned_state_allowed_only, std::move(callback)); |
| } |
| |
| #if BUILDFLAG(IS_ANDROID) |
| void StoragePartitionImpl::OnGenerateHttpNegotiateAuthToken( |
| const std::string& server_auth_token, |
| bool can_delegate, |
| const std::string& auth_negotiate_android_account_type, |
| const std::string& spn, |
| OnGenerateHttpNegotiateAuthTokenCallback callback) { |
| // The callback takes ownership of these unique_ptrs and destroys them when |
| // run. |
| auto prefs = std::make_unique<net::HttpAuthPreferences>(); |
| prefs->set_auth_android_negotiate_account_type( |
| auth_negotiate_android_account_type); |
| |
| auto auth_negotiate = |
| std::make_unique<net::android::HttpAuthNegotiateAndroid>(prefs.get()); |
| net::android::HttpAuthNegotiateAndroid* auth_negotiate_raw = |
| auth_negotiate.get(); |
| auth_negotiate->set_server_auth_token(server_auth_token); |
| auth_negotiate->set_can_delegate(can_delegate); |
| |
| auto auth_token = std::make_unique<std::string>(); |
| auth_negotiate_raw->GenerateAuthTokenAndroid( |
| nullptr, spn, std::string(), auth_token.get(), |
| base::BindOnce(&FinishGenerateNegotiateAuthToken, |
| std::move(auth_negotiate), std::move(auth_token), |
| std::move(prefs), std::move(callback))); |
| } |
| #endif |
| |
| #if BUILDFLAG(IS_CT_SUPPORTED) |
| void StoragePartitionImpl::OnCanSendSCTAuditingReport( |
| OnCanSendSCTAuditingReportCallback callback) { |
| bool allowed = |
| GetContentClient()->browser()->CanSendSCTAuditingReport(browser_context_); |
| std::move(callback).Run(allowed); |
| } |
| |
| void StoragePartitionImpl::OnNewSCTAuditingReportSent() { |
| GetContentClient()->browser()->OnNewSCTAuditingReportSent(browser_context_); |
| } |
| #endif |
| |
| void StoragePartitionImpl::ClearDataImpl( |
| uint32_t remove_mask, |
| uint32_t quota_storage_remove_mask, |
| const blink::StorageKey& storage_key, |
| BrowsingDataFilterBuilder* filter_builder, |
| StorageKeyPolicyMatcherFunction storage_key_policy_matcher, |
| CookieDeletionFilterPtr cookie_deletion_filter, |
| bool perform_storage_cleanup, |
| const base::Time begin, |
| const base::Time end, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| bool storage_key_origin_empty = storage_key.origin().opaque(); |
| DCHECK(storage_key_origin_empty || !filter_builder); |
| DCHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null()); |
| |
| StorageKeyMatcherFunction storage_key_matcher = |
| filter_builder ? filter_builder->BuildStorageKeyFilter() |
| : StorageKeyMatcherFunction(); |
| |
| for (auto& observer : data_removal_observers_) { |
| auto filter = CreateGenericStorageKeyMatcher( |
| storage_key, storage_key_matcher, storage_key_policy_matcher, |
| special_storage_policy_); |
| observer.OnStorageKeyDataCleared(remove_mask, std::move(filter), begin, |
| end); |
| } |
| |
| DataDeletionHelper* helper = new DataDeletionHelper( |
| remove_mask, quota_storage_remove_mask, |
| base::BindOnce(&StoragePartitionImpl::DeletionHelperDone, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| // `helper` deletes itself when done in |
| // DataDeletionHelper::DecrementTaskCount(). |
| deletion_helpers_running_++; |
| helper->ClearDataOnUIThread( |
| storage_key, filter_builder, std::move(storage_key_policy_matcher), |
| std::move(cookie_deletion_filter), GetPath(), dom_storage_context_.get(), |
| quota_manager_.get(), special_storage_policy_.get(), |
| filesystem_context_.get(), GetCookieManagerForBrowserProcess(), |
| interest_group_manager_.get(), attribution_manager_.get(), |
| aggregation_service_.get(), private_aggregation_manager_.get(), |
| shared_storage_manager_.get(), |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| cdm_storage_manager_.get(), |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| GetDeviceBoundSessionManager(), perform_storage_cleanup, begin, end); |
| } |
| |
| void StoragePartitionImpl::DeletionHelperDone(base::OnceClosure callback) { |
| std::move(callback).Run(); |
| deletion_helpers_running_--; |
| if (on_deletion_helpers_done_callback_ && deletion_helpers_running_ == 0) { |
| // Notify tests that storage partition is done with all deletion tasks. |
| std::move(on_deletion_helpers_done_callback_).Run(); |
| } |
| } |
| |
| void StoragePartitionImpl::QuotaManagedDataDeletionHelper:: |
| IncrementTaskCountOnIO() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| ++task_count_; |
| } |
| |
| void StoragePartitionImpl::QuotaManagedDataDeletionHelper:: |
| DecrementTaskCountOnIO() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK_GT(task_count_, 0); |
| --task_count_; |
| if (task_count_) { |
| return; |
| } |
| |
| std::move(callback_).Run(); |
| delete this; |
| } |
| |
| void StoragePartitionImpl::QuotaManagedDataDeletionHelper::ClearDataOnIOThread( |
| const scoped_refptr<storage::QuotaManager>& quota_manager, |
| const base::Time begin, |
| const base::Time end, |
| const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup) { |
| IncrementTaskCountOnIO(); |
| base::RepeatingClosure decrement_callback = base::BindRepeating( |
| &QuotaManagedDataDeletionHelper::DecrementTaskCountOnIO, |
| base::Unretained(this)); |
| |
| // Ask the QuotaManager for all buckets modified within the user-specified |
| // timeframe, and deal with the resulting set in ClearBucketsOnIOThread(). |
| if (quota_storage_remove_mask_ & QUOTA_MANAGED_STORAGE_MASK_TEMPORARY) { |
| IncrementTaskCountOnIO(); |
| quota_manager->GetBucketsModifiedBetween( |
| begin, end, |
| base::BindOnce(&QuotaManagedDataDeletionHelper::ClearBucketsOnIOThread, |
| base::Unretained(this), base::RetainedRef(quota_manager), |
| special_storage_policy, storage_key_matcher, |
| perform_storage_cleanup, decrement_callback)); |
| } |
| |
| DecrementTaskCountOnIO(); |
| } |
| |
| void StoragePartitionImpl::QuotaManagedDataDeletionHelper:: |
| ClearBucketsOnIOThread( |
| storage::QuotaManager* quota_manager, |
| const scoped_refptr<storage::SpecialStoragePolicy>& |
| special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback, |
| const std::set<storage::BucketLocator>& buckets) { |
| // The QuotaManager manages all storage other than cookies, LocalStorage, |
| // and SessionStorage. This loop wipes out most HTML5 storage for the given |
| // storage keys. |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (buckets.empty()) { |
| std::move(callback).Run(); |
| return; |
| } |
| |
| storage::QuotaClientTypes quota_client_types = |
| StoragePartitionImpl::GenerateQuotaClientTypes(remove_mask_); |
| |
| // The logic below (via CheckQuotaManagedDataDeletionStatus) only |
| // invokes the callback when all processing is complete. |
| base::OnceClosure done_callback = |
| perform_storage_cleanup |
| ? base::BindOnce(&PerformQuotaManagerStorageCleanup, |
| base::WrapRefCounted(quota_manager), |
| quota_client_types, std::move(callback)) |
| : std::move(callback); |
| |
| size_t* deletion_task_count = new size_t(0u); |
| (*deletion_task_count)++; |
| for (const auto& bucket : buckets) { |
| // TODO(mkwst): Clean this up, it's slow. http://crbug.com/130746 |
| if (storage_key_.has_value() && bucket.storage_key != *storage_key_) { |
| continue; |
| } |
| |
| if (storage_key_matcher && |
| !storage_key_matcher.Run(bucket.storage_key, |
| special_storage_policy.get())) { |
| continue; |
| } |
| |
| auto split_callback = base::SplitOnceCallback(std::move(done_callback)); |
| done_callback = std::move(split_callback.first); |
| |
| (*deletion_task_count)++; |
| quota_manager->DeleteBucketData( |
| bucket, quota_client_types, |
| base::BindOnce(&OnQuotaManagedBucketDeleted, bucket, |
| deletion_task_count, std::move(split_callback.second))); |
| } |
| (*deletion_task_count)--; |
| |
| CheckQuotaManagedDataDeletionStatus(deletion_task_count, |
| std::move(done_callback)); |
| } |
| |
| base::OnceClosure |
| StoragePartitionImpl::DataDeletionHelper::CreateTaskCompletionClosure( |
| TracingDataType data_type) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto result = pending_tasks_.insert(data_type); |
| DCHECK(result.second) << "Task already started: " |
| << static_cast<int>(data_type); |
| |
| static int tracing_id = 0; |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( |
| "browsing_data", "StoragePartitionImpl", |
| TRACE_ID_WITH_SCOPE("StoragePartitionImpl", ++tracing_id), "data_type", |
| static_cast<int>(data_type)); |
| return base::BindOnce( |
| &StoragePartitionImpl::DataDeletionHelper::OnTaskComplete, |
| base::Unretained(this), data_type, tracing_id); |
| } |
| |
| void StoragePartitionImpl::DataDeletionHelper::OnTaskComplete( |
| TracingDataType data_type, |
| int tracing_id) { |
| if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&DataDeletionHelper::OnTaskComplete, |
| base::Unretained(this), data_type, tracing_id)); |
| return; |
| } |
| size_t num_erased = pending_tasks_.erase(data_type); |
| DCHECK_EQ(num_erased, 1U) << static_cast<int>(data_type); |
| TRACE_EVENT_NESTABLE_ASYNC_END0( |
| "browsing_data", "StoragePartitionImpl", |
| TRACE_ID_WITH_SCOPE("StoragePartitionImpl", tracing_id)); |
| |
| if (pending_tasks_.empty()) { |
| std::move(callback_).Run(); |
| delete this; |
| } |
| } |
| |
| void StoragePartitionImpl::DataDeletionHelper::RecordUnfinishedSubTasks() { |
| DCHECK(!pending_tasks_.empty()); |
| for (TracingDataType task : pending_tasks_) { |
| base::UmaHistogramEnumeration( |
| "History.ClearBrowsingData.Duration.SlowTasks180sStoragePartition", |
| task); |
| } |
| } |
| |
| void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread( |
| const blink::StorageKey& storage_key, |
| BrowsingDataFilterBuilder* filter_builder, |
| StorageKeyPolicyMatcherFunction storage_key_policy_matcher, |
| CookieDeletionFilterPtr cookie_deletion_filter, |
| const base::FilePath& path, |
| DOMStorageContextWrapper* dom_storage_context, |
| storage::QuotaManager* quota_manager, |
| storage::SpecialStoragePolicy* special_storage_policy, |
| storage::FileSystemContext* filesystem_context, |
| network::mojom::CookieManager* cookie_manager, |
| InterestGroupManagerImpl* interest_group_manager, |
| AttributionManager* attribution_manager, |
| AggregationService* aggregation_service, |
| PrivateAggregationManagerImpl* private_aggregation_manager, |
| storage::SharedStorageManager* shared_storage_manager, |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| CdmStorageManager* cdm_storage_manager, |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| network::mojom::DeviceBoundSessionManager* device_bound_session_manager, |
| bool perform_storage_cleanup, |
| const base::Time begin, |
| const base::Time end) { |
| DCHECK_NE(remove_mask_, 0u); |
| DCHECK(callback_); |
| |
| // Only one of `storage_key`'s origin and |
| // `filter_builder`/`storage_key_policy_matcher` can be set. |
| const bool storage_key_origin_empty = storage_key.origin().opaque(); |
| DCHECK(storage_key_origin_empty || !filter_builder); |
| DCHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null()); |
| |
| GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &StoragePartitionImpl::DataDeletionHelper::RecordUnfinishedSubTasks, |
| weak_factory_.GetWeakPtr()), |
| kSlowTaskTimeout); |
| |
| base::ScopedClosureRunner synchronous_clear_operations( |
| CreateTaskCompletionClosure(TracingDataType::kSynchronous)); |
| |
| scoped_refptr<storage::SpecialStoragePolicy> storage_policy_ref = |
| base::WrapRefCounted(special_storage_policy); |
| |
| StorageKeyMatcherFunction storage_key_matcher = |
| filter_builder ? filter_builder->BuildStorageKeyFilter() |
| : StorageKeyMatcherFunction(); |
| |
| // This is preferred for new storage APIs to reduce the complexity. |
| auto generic_filter = CreateGenericStorageKeyMatcher( |
| storage_key, storage_key_matcher, storage_key_policy_matcher, |
| storage_policy_ref); |
| |
| auto combined_storage_key_matcher = CombineStorageKeyMatcherFunctions( |
| storage_key_matcher, storage_key_policy_matcher); |
| |
| if (remove_mask_ & REMOVE_DATA_MASK_COOKIES) { |
| // The CookieDeletionFilter has a redundant time interval to `begin` and |
| // `end`. Ensure that the filter has no time interval specified to help |
| // callers detect when they are using the wrong interval values. |
| DCHECK(!cookie_deletion_filter->created_after_time.has_value()); |
| DCHECK(!cookie_deletion_filter->created_before_time.has_value()); |
| |
| if (!begin.is_null()) { |
| cookie_deletion_filter->created_after_time = begin; |
| } |
| if (!end.is_null()) { |
| cookie_deletion_filter->created_before_time = end; |
| } |
| if (!storage_key_origin_empty) { |
| cookie_deletion_filter->cookie_partition_key_collection = |
| net::CookiePartitionKeyCollection(storage_key.ToCookiePartitionKey()); |
| } |
| |
| cookie_manager->DeleteCookies( |
| std::move(cookie_deletion_filter), |
| base::BindOnce( |
| &OnClearedCookies, |
| // Handle the cookie store being destroyed and the callback thus not |
| // being called. |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kCookies)))); |
| } |
| |
| // It is not expected to only delete internal interest group data, or to |
| // request interest group removal to be extra-thorough w/o asking for |
| // interest group removal. |
| DCHECK(!(remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS_INTERNAL) || |
| remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS); |
| DCHECK(!(remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS_USER_CLEAR) || |
| remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS); |
| if (remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS) { |
| if (interest_group_manager) { |
| // The internal interest group data is not specific to a site so it only |
| // makes sense to delete it for all sites (i.e. when |
| // generic_filter.is_null()). |
| if ((remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS_INTERNAL) && |
| generic_filter.is_null()) { |
| interest_group_manager->DeleteAllInterestGroupData( |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kInterestGroups))); |
| } else { |
| interest_group_manager->DeleteInterestGroupData( |
| generic_filter, |
| remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS_USER_CLEAR, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kInterestGroups))); |
| } |
| } |
| } |
| |
| if (remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUP_PERMISSIONS_CACHE) { |
| if (interest_group_manager) { |
| interest_group_manager->ClearPermissionsCache(); |
| } |
| } |
| |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| if ((remove_mask_ & REMOVE_DATA_MASK_MEDIA_LICENSES)) { |
| auto cdm_deletion_callback = base::BindOnce( |
| base::IgnoreArgs<bool>(mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kCdmStorage)))); |
| |
| cdm_storage_manager->DeleteData(generic_filter, storage_key, begin, end, |
| std::move(cdm_deletion_callback)); |
| } |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| |
| if (remove_mask_ & REMOVE_DATA_MASK_INDEXEDDB || |
| remove_mask_ & REMOVE_DATA_MASK_WEBSQL || |
| remove_mask_ & REMOVE_DATA_MASK_FILE_SYSTEMS || |
| remove_mask_ & REMOVE_DATA_MASK_SERVICE_WORKERS || |
| remove_mask_ & REMOVE_DATA_MASK_CACHE_STORAGE) { |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&DataDeletionHelper::ClearQuotaManagedDataOnIOThread, |
| base::Unretained(this), |
| base::WrapRefCounted(quota_manager), begin, end, |
| storage_key, storage_policy_ref, |
| combined_storage_key_matcher, perform_storage_cleanup, |
| CreateTaskCompletionClosure(TracingDataType::kQuota))); |
| } |
| |
| if (remove_mask_ & REMOVE_DATA_MASK_LOCAL_STORAGE) { |
| ClearLocalStorageOnUIThread( |
| base::WrapRefCounted(dom_storage_context), storage_policy_ref, |
| combined_storage_key_matcher, storage_key, perform_storage_cleanup, |
| begin, end, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kLocalStorage))); |
| |
| // ClearDataImpl cannot clear session storage data when a particular origin |
| // is specified. Therefore we ignore clearing session storage in this case. |
| // TODO(lazyboy): Fix. |
| if (storage_key_origin_empty) { |
| // TODO(crbug.com/41457196): Sometimes SessionStorage fails to call its |
| // callback. Figure out why. |
| ClearSessionStorageOnUIThread( |
| base::WrapRefCounted(dom_storage_context), storage_policy_ref, |
| combined_storage_key_matcher, perform_storage_cleanup, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kSessionStorage))); |
| } |
| } |
| |
| if (remove_mask_ & REMOVE_DATA_MASK_SHADER_CACHE) { |
| gpu::GpuDiskCacheFactory* gpu_cache_factory = |
| GetGpuDiskCacheFactorySingleton(); |
| // May be null in tests where it is difficult to plumb through a test |
| // storage partition. |
| if (!path.empty() && gpu_cache_factory) { |
| // Clear the path for all the different GPU cache sub-types. |
| base::RepeatingClosure barrier = base::BarrierClosure( |
| gpu::kGpuDiskCacheTypes.size(), |
| CreateTaskCompletionClosure(TracingDataType::kGpuCache)); |
| for (gpu::GpuDiskCacheType type : gpu::kGpuDiskCacheTypes) { |
| gpu_cache_factory->ClearByPath( |
| path.Append(gpu::GetGpuDiskCacheSubdir(type)), begin, end, |
| base::BindOnce(&ClearedGpuCache, barrier)); |
| } |
| } |
| } |
| |
| // It is not expected to only delete internal attribution reporting data. |
| DCHECK(!(remove_mask_ & REMOVE_DATA_MASK_ATTRIBUTION_REPORTING_INTERNAL) || |
| remove_mask_ & REMOVE_DATA_MASK_ATTRIBUTION_REPORTING_SITE_CREATED); |
| if (attribution_manager && |
| (remove_mask_ & REMOVE_DATA_MASK_ATTRIBUTION_REPORTING_SITE_CREATED)) { |
| if (storage_key_origin_empty) { |
| attribution_manager->ClearData( |
| begin, end, generic_filter, filter_builder, |
| remove_mask_ & REMOVE_DATA_MASK_ATTRIBUTION_REPORTING_INTERNAL, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kConversions))); |
| } else if (storage_key.IsFirstPartyContext()) { |
| // Attribution Reporting API doesn't support cross-site data deletion. |
| std::unique_ptr<BrowsingDataFilterBuilder> effective_filter_builder = |
| BrowsingDataFilterBuilder::Create( |
| BrowsingDataFilterBuilder::Mode::kDelete); |
| effective_filter_builder->AddOrigin(storage_key.origin()); |
| attribution_manager->ClearData( |
| begin, end, generic_filter, effective_filter_builder.get(), |
| remove_mask_ & REMOVE_DATA_MASK_ATTRIBUTION_REPORTING_INTERNAL, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kConversions))); |
| } |
| } |
| |
| if (aggregation_service && |
| (remove_mask_ & REMOVE_DATA_MASK_AGGREGATION_SERVICE)) { |
| // Currently the aggregation service only stores public keys and we don't |
| // have information on the page/context that uses the public key origin, |
| // therefore we don't check origins and instead just delete all rows in the |
| // given time range. |
| // TODO(crbug.com/40210305): Consider fine-grained deletion of public keys. |
| // TODO(crbug.com/40815455): Consider adding aggregation service origins to |
| // `CookiesTreeModel`. |
| aggregation_service->ClearData( |
| begin, end, generic_filter, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kAggregationService))); |
| } |
| |
| if (private_aggregation_manager && |
| (remove_mask_ & REMOVE_DATA_MASK_PRIVATE_AGGREGATION_INTERNAL)) { |
| private_aggregation_manager->ClearBudgetData( |
| begin, end, generic_filter, |
| |
| // Wrapping the callback ensures that the callback is still run in the |
| // case that the storage partition is deleted before the task is posted. |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kPrivateAggregation))); |
| } |
| |
| if (base::FeatureList::IsEnabled(network::features::kSharedStorageAPI) && |
| shared_storage_manager && |
| (remove_mask_ & REMOVE_DATA_MASK_SHARED_STORAGE)) { |
| auto shared_storage_purge_callback = base::BindOnce( |
| [](base::WeakPtr<storage::SharedStorageManager> manager, |
| base::OnceClosure callback, |
| storage::SharedStorageDatabase::OperationResult result) { |
| if (manager) { |
| manager->OnOperationResult(result); |
| } |
| std::move(callback).Run(); |
| }, |
| shared_storage_manager->GetWeakPtr(), |
| CreateTaskCompletionClosure(TracingDataType::kSharedStorage)); |
| |
| shared_storage_manager->PurgeMatchingOrigins( |
| combined_storage_key_matcher, begin, end, |
| std::move(shared_storage_purge_callback), perform_storage_cleanup); |
| } |
| |
| if (remove_mask_ & REMOVE_DATA_MASK_DEVICE_BOUND_SESSIONS && |
| device_bound_session_manager) { |
| device_bound_session_manager->DeleteAllSessions( |
| begin, end, |
| filter_builder ? filter_builder->BuildNetworkServiceFilter() : nullptr, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun(CreateTaskCompletionClosure( |
| TracingDataType::kDeviceBoundSessions))); |
| } |
| } |
| |
| void StoragePartitionImpl::ClearDataForOrigin( |
| uint32_t remove_mask, |
| uint32_t quota_storage_remove_mask, |
| const GURL& storage_origin, |
| base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(initialized_); |
| CookieDeletionFilterPtr deletion_filter = CookieDeletionFilter::New(); |
| if (!storage_origin.host().empty()) { |
| deletion_filter->host_name = storage_origin.host(); |
| } |
| // Construct a |BrowsingDataFilterBuilder| instead of just passing a storage |
| // key based on the origin directly. This is needed to be able to delete the |
| // associated 3P data embedded on the origin. |
| auto filter_builder = BrowsingDataFilterBuilder::Create( |
| content::BrowsingDataFilterBuilder::Mode::kDelete); |
| filter_builder->AddOrigin(url::Origin::Create(storage_origin)); |
| ClearDataImpl(remove_mask, quota_storage_remove_mask, blink::StorageKey(), |
| filter_builder.get(), StorageKeyPolicyMatcherFunction(), |
| std::move(deletion_filter), false, base::Time(), |
| base::Time::Max(), std::move(callback)); |
| } |
| |
| void StoragePartitionImpl::ClearDataForBuckets( |
| const blink::StorageKey& storage_key, |
| const std::set<std::string>& storage_buckets, |
| base::OnceClosure callback) { |
| DCHECK(initialized_); |
| |
| const auto remove_buckets_done = |
| base::BarrierCallback<blink::mojom::QuotaStatusCode>( |
| storage_buckets.size(), |
| BindPostTaskToCurrentDefault( |
| base::BindOnce(&StoragePartitionImpl::ClearDataForBucketsDone, |
| base::Unretained(this), storage_key, |
| storage_buckets, std::move(callback)))); |
| |
| storage::QuotaManagerProxy* quota_manager_proxy = GetQuotaManagerProxy(); |
| |
| for (const auto& bucket : storage_buckets) { |
| quota_manager_proxy->DeleteBucket( |
| storage_key, bucket, base::SequencedTaskRunner::GetCurrentDefault(), |
| remove_buckets_done); |
| } |
| } |
| |
| void StoragePartitionImpl::ClearDataForBucketsDone( |
| const blink::StorageKey& storage_key, |
| const std::set<std::string>& storage_buckets, |
| base::OnceClosure callback, |
| const std::vector<blink::mojom::QuotaStatusCode>& status_codes) { |
| auto bucket_iterator = storage_buckets.begin(); |
| |
| for (const auto status_code : status_codes) { |
| if (bucket_iterator == storage_buckets.end()) { |
| break; |
| } |
| |
| const std::string bucket_name = *bucket_iterator; |
| |
| if (status_code != blink::mojom::QuotaStatusCode::kOk) { |
| DLOG(ERROR) << "Couldn't remove bucket with name" << bucket_name |
| << " with storage key " << storage_key.GetDebugString() |
| << ". Status: " << static_cast<int>(status_code); |
| } |
| |
| ++bucket_iterator; |
| } |
| |
| std::move(callback).Run(); |
| } |
| |
| void StoragePartitionImpl::ClearData(uint32_t remove_mask, |
| uint32_t quota_storage_remove_mask, |
| const blink::StorageKey& storage_key, |
| const base::Time begin, |
| const base::Time end, |
| base::OnceClosure callback) { |
| DCHECK(initialized_); |
| CookieDeletionFilterPtr deletion_filter = CookieDeletionFilter::New(); |
| if (!storage_key.origin().host().empty()) { |
| deletion_filter->host_name = storage_key.origin().host(); |
| } |
| bool perform_storage_cleanup = |
| begin.is_null() && end.is_max() && storage_key.origin().opaque(); |
| ClearDataImpl(remove_mask, quota_storage_remove_mask, storage_key, |
| /*filter_builder=*/nullptr, StorageKeyPolicyMatcherFunction(), |
| std::move(deletion_filter), perform_storage_cleanup, begin, end, |
| std::move(callback)); |
| } |
| |
| void StoragePartitionImpl::ClearData( |
| uint32_t remove_mask, |
| uint32_t quota_storage_remove_mask, |
| BrowsingDataFilterBuilder* filter_builder, |
| StorageKeyPolicyMatcherFunction storage_key_policy_matcher, |
| network::mojom::CookieDeletionFilterPtr cookie_deletion_filter, |
| bool perform_storage_cleanup, |
| const base::Time begin, |
| const base::Time end, |
| base::OnceClosure callback) { |
| DCHECK(initialized_); |
| ClearDataImpl(remove_mask, quota_storage_remove_mask, blink::StorageKey(), |
| filter_builder, std::move(storage_key_policy_matcher), |
| std::move(cookie_deletion_filter), perform_storage_cleanup, |
| begin, end, std::move(callback)); |
| } |
| |
| void StoragePartitionImpl::ClearCodeCaches( |
| const base::Time begin, |
| const base::Time end, |
| const base::RepeatingCallback<bool(const GURL&)>& url_matcher, |
| base::OnceClosure callback) { |
| DCHECK(initialized_); |
| // StoragePartitionCodeCacheDataRemover deletes itself when it is done. |
| StoragePartitionCodeCacheDataRemover::Create(this, url_matcher, begin, end) |
| ->Remove(std::move(callback)); |
| } |
| |
| void StoragePartitionImpl::Flush() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(initialized_); |
| if (GetDOMStorageContext()) { |
| GetDOMStorageContext()->Flush(); |
| } |
| } |
| |
| void StoragePartitionImpl::ResetURLLoaderFactories() { |
| CHECK(initialized_); |
| GetNetworkContext()->ResetURLLoaderFactories(); |
| shared_url_loader_factory_for_browser_process_->factory()->Reset(); |
| shared_url_loader_factory_for_browser_process_->factory_for_io_thread() |
| ->Reset(); |
| } |
| |
| void StoragePartitionImpl::ClearBluetoothAllowedDevicesMapForTesting() { |
| DCHECK(initialized_); |
| bluetooth_allowed_devices_map_->Clear(); |
| } |
| |
| void StoragePartitionImpl::AddObserver(DataRemovalObserver* observer) { |
| data_removal_observers_.AddObserver(observer); |
| } |
| |
| void StoragePartitionImpl::RemoveObserver(DataRemovalObserver* observer) { |
| data_removal_observers_.RemoveObserver(observer); |
| } |
| |
| void StoragePartitionImpl::FlushNetworkInterfaceForTesting() { |
| CHECK(initialized_); |
| DCHECK(network_context_owner_->network_context); |
| network_context_owner_->network_context.FlushForTesting(); // IN-TEST |
| shared_url_loader_factory_for_browser_process_->factory() |
| ->FlushForTesting(); // IN-TEST |
| if (cookie_manager_for_browser_process_) { |
| cookie_manager_for_browser_process_.FlushForTesting(); // IN-TEST |
| } |
| } |
| |
| void StoragePartitionImpl::FlushNetworkInterfaceOnIOThreadForTesting() { |
| CHECK(initialized_); |
| CHECK(shared_url_loader_factory_for_browser_process_); |
| shared_url_loader_factory_for_browser_process_->factory_for_io_thread() |
| ->FlushForTesting(); // IN-TEST |
| } |
| |
| void StoragePartitionImpl::FlushCertVerifierInterfaceForTesting() { |
| DCHECK(initialized_); |
| DCHECK(cert_verifier_service_updater_); |
| cert_verifier_service_updater_.FlushForTesting(); // IN-TEST |
| } |
| |
| void StoragePartitionImpl::WaitForDeletionTasksForTesting() { |
| DCHECK(initialized_); |
| if (deletion_helpers_running_) { |
| base::RunLoop loop; |
| on_deletion_helpers_done_callback_ = loop.QuitClosure(); |
| loop.Run(); |
| } |
| } |
| |
| void StoragePartitionImpl::WaitForCodeCacheShutdownForTesting() { |
| DCHECK(initialized_); |
| if (generated_code_cache_context_) { |
| // If this is still running its initialization task it may check |
| // enabled features on a sequenced worker pool which could race |
| // between ScopedFeatureList destruction. |
| base::RunLoop loop; |
| GeneratedCodeCacheContext::RunOrPostTask( |
| generated_code_cache_context_, FROM_HERE, |
| base::BindOnce( |
| [](scoped_refptr<GeneratedCodeCacheContext> context, |
| base::OnceClosure quit) { |
| context->generated_js_code_cache()->GetBackend(base::BindOnce( |
| [](base::OnceClosure quit, disk_cache::Backend*) { |
| std::move(quit).Run(); |
| }, |
| std::move(quit))); |
| }, |
| generated_code_cache_context_, loop.QuitClosure())); |
| loop.Run(); |
| generated_code_cache_context_->Shutdown(); |
| } |
| } |
| |
| void StoragePartitionImpl::SetNetworkContextForTesting( |
| mojo::PendingRemote<network::mojom::NetworkContext> |
| network_context_remote) { |
| network_context_owner_->network_context.reset(); |
| network_context_owner_->network_context.Bind( |
| std::move(network_context_remote)); |
| } |
| |
| void StoragePartitionImpl::OverrideDeleteStaleSessionOnlyCookiesDelayForTesting( |
| const base::TimeDelta& delay) { |
| delete_stale_session_only_cookies_delay_ = delay; |
| } |
| |
| void StoragePartitionImpl::SetClearNoncesInNetworkContextParamsForTesting( |
| const base::TimeDelta& delay, |
| base::RepeatingClosure callback) { |
| clear_nonces_in_network_context_delay_ = delay; |
| clear_nonces_in_network_context_callback_for_testing_ = callback; |
| } |
| |
| base::WeakPtr<StoragePartitionImpl> StoragePartitionImpl::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| BrowserContext* StoragePartitionImpl::browser_context() const { |
| return browser_context_; |
| } |
| |
| storage::mojom::Partition* StoragePartitionImpl::GetStorageServicePartition() { |
| if (!remote_partition_) { |
| std::optional<base::FilePath> storage_path; |
| if (!is_in_memory()) { |
| storage_path = |
| browser_context_->GetPath().Append(relative_partition_path_); |
| } |
| GetStorageServiceRemote()->BindPartition( |
| storage_path, remote_partition_.BindNewPipeAndPassReceiver()); |
| remote_partition_.set_disconnect_handler( |
| base::BindOnce(&StoragePartitionImpl::OnStorageServiceDisconnected, |
| base::Unretained(this))); |
| } |
| return remote_partition_.get(); |
| } |
| |
| // static |
| mojo::Remote<storage::mojom::StorageService>& |
| StoragePartitionImpl::GetStorageServiceForTesting() { |
| return GetStorageServiceRemote(); |
| } |
| |
| void StoragePartitionImpl::BindIndexedDB( |
| const storage::BucketLocator& bucket_locator, |
| const storage::BucketClientInfo& client_info, |
| mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker> |
| client_state_checker_remote, |
| mojo::PendingReceiver<blink::mojom::IDBFactory> receiver) { |
| indexed_db_control_wrapper_->BindIndexedDB( |
| bucket_locator, client_info, std::move(client_state_checker_remote), |
| std::move(receiver)); |
| } |
| |
| mojo::ReceiverId StoragePartitionImpl::BindDomStorage( |
| int process_id, |
| mojo::PendingReceiver<blink::mojom::DomStorage> receiver, |
| mojo::PendingRemote<blink::mojom::DomStorageClient> client) { |
| DCHECK(initialized_); |
| auto handle = |
| ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(process_id); |
| mojo::ReceiverId id = dom_storage_receivers_.Add( |
| this, std::move(receiver), |
| std::make_unique<SecurityPolicyHandle>(std::move(handle))); |
| dom_storage_clients_[id].Bind(std::move(client)); |
| return id; |
| } |
| |
| void StoragePartitionImpl::UnbindDomStorage(mojo::ReceiverId receiver_id) { |
| DCHECK(initialized_); |
| dom_storage_receivers_.Remove(receiver_id); |
| dom_storage_clients_.erase(receiver_id); |
| } |
| |
| void StoragePartitionImpl::OverrideQuotaManagerForTesting( |
| storage::QuotaManager* quota_manager) { |
| DCHECK(initialized_); |
| quota_manager_ = quota_manager; |
| } |
| |
| void StoragePartitionImpl::OverrideSpecialStoragePolicyForTesting( |
| storage::SpecialStoragePolicy* special_storage_policy) { |
| DCHECK(initialized_); |
| special_storage_policy_ = special_storage_policy; |
| } |
| |
| void StoragePartitionImpl::ShutdownBackgroundSyncContextForTesting() { |
| DCHECK(initialized_); |
| if (GetBackgroundSyncContext()) { |
| GetBackgroundSyncContext()->Shutdown(); |
| } |
| } |
| |
| void StoragePartitionImpl::OverrideBackgroundSyncContextForTesting( |
| BackgroundSyncContextImpl* background_sync_context) { |
| DCHECK(initialized_); |
| DCHECK(!GetBackgroundSyncContext() || |
| !GetBackgroundSyncContext()->background_sync_manager()); |
| background_sync_context_ = background_sync_context; |
| } |
| |
| void StoragePartitionImpl::OverrideSharedWorkerServiceForTesting( |
| std::unique_ptr<SharedWorkerServiceImpl> shared_worker_service) { |
| DCHECK(initialized_); |
| shared_worker_service_ = std::move(shared_worker_service); |
| } |
| |
| void StoragePartitionImpl::OverrideSharedStorageRuntimeManagerForTesting( |
| std::unique_ptr<SharedStorageRuntimeManager> |
| shared_storage_runtime_manager) { |
| DCHECK(initialized_); |
| shared_storage_runtime_manager_ = std::move(shared_storage_runtime_manager); |
| } |
| |
| void StoragePartitionImpl::OverrideSharedStorageHeaderObserverForTesting( |
| std::unique_ptr<SharedStorageHeaderObserver> |
| shared_storage_header_observer) { |
| DCHECK(initialized_); |
| shared_storage_header_observer_ = std::move(shared_storage_header_observer); |
| } |
| |
| void StoragePartitionImpl::OverrideAggregationServiceForTesting( |
| std::unique_ptr<AggregationService> aggregation_service) { |
| DCHECK(initialized_); |
| aggregation_service_ = std::move(aggregation_service); |
| } |
| |
| void StoragePartitionImpl::OverrideAttributionManagerForTesting( |
| std::unique_ptr<AttributionManager> attribution_manager) { |
| DCHECK(initialized_); |
| attribution_manager_ = std::move(attribution_manager); |
| } |
| |
| void StoragePartitionImpl::OverridePrivateAggregationManagerForTesting( |
| std::unique_ptr<PrivateAggregationManagerImpl> |
| private_aggregation_manager) { |
| DCHECK(initialized_); |
| private_aggregation_manager_ = std::move(private_aggregation_manager); |
| } |
| |
| void StoragePartitionImpl::OverrideDeviceBoundSessionManagerForTesting( |
| std::unique_ptr<network::mojom::DeviceBoundSessionManager> |
| device_bound_session_manager) { |
| DCHECK(initialized_); |
| mojo::MakeSelfOwnedReceiver( |
| std::move(device_bound_session_manager), |
| device_bound_session_manager_.BindNewPipeAndPassReceiver()); |
| } |
| |
| void StoragePartitionImpl::GetQuotaSettings( |
| storage::OptionalQuotaSettingsCallback callback) { |
| if (g_test_quota_settings) { |
| // For debugging tests harness can inject settings. |
| std::move(callback).Run(*g_test_quota_settings); |
| return; |
| } |
| |
| storage::GetNominalDynamicSettings( |
| GetPath(), browser_context_->IsOffTheRecord(), |
| storage::GetDefaultDeviceInfoHelper(), std::move(callback)); |
| } |
| |
| void StoragePartitionImpl::InitNetworkContext() { |
| network::mojom::NetworkContextParamsPtr context_params = |
| network::mojom::NetworkContextParams::New(); |
| cert_verifier::mojom::CertVerifierCreationParamsPtr |
| cert_verifier_creation_params = |
| cert_verifier::mojom::CertVerifierCreationParams::New(); |
| GetContentClient()->browser()->ConfigureNetworkContextParams( |
| browser_context_, is_in_memory(), relative_partition_path_, |
| context_params.get(), cert_verifier_creation_params.get()); |
| // Should be initialized with existing per-profile CORS access lists. |
| DCHECK(context_params->cors_origin_access_list.empty()) |
| << "NetworkContextParams::cors_origin_access_list should be populated " |
| "via SharedCorsOriginAccessList"; |
| context_params->cors_origin_access_list = |
| browser_context_->GetSharedCorsOriginAccessList() |
| ->GetOriginAccessList() |
| .CreateCorsOriginAccessPatternsList(); |
| devtools_instrumentation::ApplyNetworkContextParamsOverrides( |
| browser_context_, context_params.get()); |
| DCHECK(!context_params->cert_verifier_params) |
| << "`cert_verifier_params` should not be set in the " |
| "NetworkContextParams, as they will be replaced with a new pipe to " |
| "the CertVerifierService."; |
| |
| cert_verifier_service_updater_.reset(); |
| context_params->cert_verifier_params = GetCertVerifierParamsWithUpdater( |
| std::move(cert_verifier_creation_params), |
| cert_verifier_service_updater_.BindNewPipeAndPassReceiver()); |
| |
| // This mechanisms should be used only for legacy internal headers. You can |
| // find a recommended alternative approach on URLRequest::cors_exempt_headers |
| // at services/network/public/mojom/url_loader.mojom. |
| context_params->cors_exempt_header_list.push_back(blink::kPurposeHeaderName); |
| context_params->cors_exempt_header_list.push_back( |
| GetCorsExemptRequestedWithHeaderName()); |
| variations::UpdateCorsExemptHeaderForVariations(context_params.get()); |
| |
| cors_exempt_header_list_ = context_params->cors_exempt_header_list; |
| |
| if (base::FeatureList::IsEnabled( |
| network::features::kCompressionDictionaryTransportBackend) && |
| GetContentClient()->browser()->AllowCompressionDictionaryTransport( |
| browser_context_)) { |
| context_params->shared_dictionary_enabled = true; |
| if (!is_in_memory()) { |
| // Some callers may already initialize NetworkContextFilePaths, and we |
| // don't want to overwrite them. |
| if (!context_params->file_paths) { |
| context_params->file_paths = |
| network::mojom::NetworkContextFilePaths::New(); |
| } |
| context_params->file_paths->shared_dictionary_directory = |
| partition_path_.Append(FILE_PATH_LITERAL("Shared Dictionary")); |
| } |
| if (context_params->shared_dictionary_cache_max_size == 0u) { |
| CalculateAndSetSharedDictionaryCacheMaxSize( |
| GetWeakPtr(), is_in_memory() ? base::FilePath() : partition_path_); |
| } |
| } |
| |
| if (cookie_deprecation_label_manager_) { |
| context_params->cookie_deprecation_label = |
| cookie_deprecation_label_manager_->GetValue().value_or(""); |
| } |
| |
| network_context_owner_->network_context.reset(); |
| CreateNetworkContextInNetworkService( |
| network_context_owner_->network_context.BindNewPipeAndPassReceiver(), |
| std::move(context_params)); |
| DCHECK(network_context_owner_->network_context); |
| |
| // Restore the saved network revocation nonces. This allows fenced frames' |
| // untrusted network access states to be persisted in case of a |
| // `NetworkService` crash. |
| std::vector<base::UnguessableToken> nonces( |
| std::begin(network_revocation_nonces_), |
| std::end(network_revocation_nonces_)); |
| network_context_owner_->network_context->RevokeNetworkForNonces( |
| nonces, base::NullCallback()); |
| |
| network_context_client_receiver_.reset(); |
| network_context_owner_->network_context->SetClient( |
| network_context_client_receiver_.BindNewPipeAndPassRemote()); |
| network_context_owner_->network_context.set_disconnect_handler(base::BindOnce( |
| &StoragePartitionImpl::InitNetworkContext, weak_factory_.GetWeakPtr())); |
| } |
| |
| network::mojom::URLLoaderFactoryParamsPtr |
| StoragePartitionImpl::CreateURLLoaderFactoryParams() { |
| network::mojom::URLLoaderFactoryParamsPtr params = |
| network::mojom::URLLoaderFactoryParams::New(); |
| // This method is used for browser-process initiated requests for which there |
| // is no corresponding RenderProcessHost. |
| params->process_id = network::mojom::kBrowserProcessId; |
| params->automatically_assign_isolation_info = true; |
| params->is_orb_enabled = false; |
| params->is_trusted = true; |
| params->url_loader_network_observer = |
| CreateAuthCertObserverForServiceWorker(params->process_id); |
| params->disable_web_security = |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableWebSecurity); |
| return params; |
| } |
| |
| void StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal( |
| mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) { |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| network::URLLoaderFactoryBuilder factory_builder; |
| if (url_loader_factory::GetTestingInterceptor()) { |
| url_loader_factory::GetTestingInterceptor().Run( |
| network::mojom::kBrowserProcessId, factory_builder); |
| } |
| *out_factory = |
| std::move(factory_builder) |
| .Finish<mojo::PendingRemote<network::mojom::URLLoaderFactory>>( |
| GetNetworkContext(), CreateURLLoaderFactoryParams()); |
| } |
| |
| void StoragePartition::SetDefaultQuotaSettingsForTesting( |
| const storage::QuotaSettings* settings) { |
| g_test_quota_settings = settings; |
| } |
| |
| mojo::PendingRemote<network::mojom::CookieAccessObserver> |
| StoragePartitionImpl::CreateCookieAccessObserverForServiceWorker() { |
| mojo::PendingRemote<network::mojom::CookieAccessObserver> remote; |
| service_worker_cookie_observers_.Add( |
| std::make_unique<ServiceWorkerCookieAccessObserver>(this), |
| remote.InitWithNewPipeAndPassReceiver()); |
| return remote; |
| } |
| |
| mojo::PendingRemote<network::mojom::TrustTokenAccessObserver> |
| StoragePartitionImpl::CreateTrustTokenAccessObserverForServiceWorker() { |
| mojo::PendingRemote<network::mojom::TrustTokenAccessObserver> remote; |
| service_worker_trust_token_observers_.Add( |
| std::make_unique<ServiceWorkerTrustTokenAccessObserver>(this), |
| remote.InitWithNewPipeAndPassReceiver()); |
| return remote; |
| } |
| |
| mojo::PendingRemote<network::mojom::SharedDictionaryAccessObserver> |
| StoragePartitionImpl::CreateSharedDictionaryAccessObserverForServiceWorker() { |
| mojo::PendingRemote<network::mojom::SharedDictionaryAccessObserver> remote; |
| service_worker_shared_dictionary_observers_.Add( |
| std::make_unique<ServiceWorkerSharedDictionaryAccessObserver>(this), |
| remote.InitWithNewPipeAndPassReceiver()); |
| return remote; |
| } |
| |
| mojo::PendingRemote<network::mojom::DeviceBoundSessionAccessObserver> |
| StoragePartitionImpl::CreateDeviceBoundSessionObserverForServiceWorker() { |
| mojo::PendingRemote<network::mojom::DeviceBoundSessionAccessObserver> remote; |
| service_worker_device_bound_session_observers_.Add( |
| std::make_unique<ServiceWorkerDeviceBoundSessionAccessObserver>(this), |
| remote.InitWithNewPipeAndPassReceiver()); |
| return remote; |
| } |
| |
| void StoragePartitionImpl::OpenLocalStorageForProcess( |
| int process_id, |
| const blink::StorageKey& storage_key, |
| mojo::PendingReceiver<blink::mojom::StorageArea> receiver) { |
| DCHECK(initialized_); |
| auto handle = |
| ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(process_id); |
| dom_storage_context_->OpenLocalStorage(storage_key, std::nullopt, |
| std::move(receiver), std::move(handle), |
| base::DoNothing()); |
| } |
| |
| void StoragePartitionImpl::BindSessionStorageAreaForProcess( |
| int process_id, |
| const blink::StorageKey& storage_key, |
| const std::string& namespace_id, |
| mojo::PendingReceiver<blink::mojom::StorageArea> receiver) { |
| DCHECK(initialized_); |
| auto handle = |
| ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(process_id); |
| dom_storage_context_->BindStorageArea(storage_key, std::nullopt, namespace_id, |
| std::move(receiver), std::move(handle), |
| base::DoNothing()); |
| } |
| |
| std::optional<blink::StorageKey> StoragePartitionImpl::CalculateStorageKey( |
| const url::Origin& origin, |
| const base::UnguessableToken* nonce) { |
| if (!blink::StorageKey::IsThirdPartyStoragePartitioningEnabled()) { |
| return std::nullopt; |
| } |
| |
| NavigationOrDocumentHandle* handle = |
| url_loader_network_observers_.current_context().navigation_or_document(); |
| if (!handle) { |
| return std::nullopt; |
| } |
| FrameTreeNode* node = handle->GetFrameTreeNode(); |
| if (!node) { |
| return std::nullopt; |
| } |
| RenderFrameHostImpl* frame_host = node->current_frame_host(); |
| if (!frame_host) { |
| return std::nullopt; |
| } |
| |
| return frame_host->CalculateStorageKey(origin, nonce); |
| } |
| |
| GlobalRenderFrameHostId |
| StoragePartitionImpl::GetRenderFrameHostIdFromNetworkContext() { |
| URLLoaderNetworkContext context = |
| url_loader_network_observers_.current_context(); |
| |
| // `navigation_or_document()` can be null for `kServiceWorkerContext`. |
| RenderFrameHost* render_frame_host = |
| context.navigation_or_document() |
| ? context.navigation_or_document()->GetDocument() |
| : nullptr; |
| |
| // It can pass empty GlobalRenderFrameHostId() when the context type is |
| // not `kRenderFrameHostContext`. |
| return render_frame_host ? render_frame_host->GetGlobalId() |
| : GlobalRenderFrameHostId(); |
| } |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext( |
| GlobalRenderFrameHostId render_frame_host_id) |
| : type_(Type::kRenderFrameHostContext) { |
| auto* render_frame_host = RenderFrameHostImpl::FromID(render_frame_host_id); |
| if (!render_frame_host) { |
| return; |
| } |
| |
| navigation_or_document_ = render_frame_host->GetNavigationOrDocumentHandle(); |
| } |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext( |
| NavigationRequest& navigation_request) |
| : type_(Type::kNavigationRequestContext) { |
| navigation_or_document_ = navigation_request.navigation_or_document_handle(); |
| } |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext( |
| int process_id) |
| : type_(Type::kServiceWorkerContext), process_id_(process_id) {} |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext( |
| const URLLoaderNetworkContext& other) = default; |
| |
| StoragePartitionImpl::URLLoaderNetworkContext& |
| StoragePartitionImpl::URLLoaderNetworkContext::operator=( |
| const URLLoaderNetworkContext& other) = default; |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::~URLLoaderNetworkContext() = |
| default; |
| |
| StoragePartitionImpl::URLLoaderNetworkContext |
| StoragePartitionImpl::URLLoaderNetworkContext::CreateForRenderFrameHost( |
| GlobalRenderFrameHostId render_frame_host_id) { |
| return StoragePartitionImpl::URLLoaderNetworkContext( |
| render_frame_host_id); |
| } |
| |
| StoragePartitionImpl::URLLoaderNetworkContext |
| StoragePartitionImpl::URLLoaderNetworkContext::CreateForNavigation( |
| NavigationRequest& navigation_request) { |
| return StoragePartitionImpl::URLLoaderNetworkContext(navigation_request); |
| } |
| |
| bool StoragePartitionImpl::URLLoaderNetworkContext::IsNavigationRequestContext() |
| const { |
| return type_ == ContextType::kNavigationRequestContext; |
| } |
| |
| // Returns the WebContents corresponding to `context`. |
| WebContents* StoragePartitionImpl::URLLoaderNetworkContext::GetWebContents() { |
| if (!navigation_or_document()) { |
| return nullptr; |
| } |
| return navigation_or_document()->GetWebContents(); |
| } |
| |
| // Returns true if the request is the primary main frame navigation. |
| bool StoragePartitionImpl::URLLoaderNetworkContext:: |
| IsPrimaryMainFrameRequest() { |
| if (!IsNavigationRequestContext()) { |
| return false; |
| } |
| |
| return navigation_or_document()->IsInPrimaryMainFrame(); |
| } |
| |
| } // namespace content |