| // 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 <variant> |
| #include <vector> |
| |
| #include "base/barrier_callback.h" |
| #include "base/barrier_closure.h" |
| #include "base/check_deref.h" |
| #include "base/command_line.h" |
| #include "base/dcheck_is_on.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/functional/concurrent_closures.h" |
| #include "base/location.h" |
| #include "base/logging.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/metrics/histogram_macros.h" |
| #include "base/not_fatal_until.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/timer/elapsed_timer.h" |
| #include "base/types/optional_util.h" |
| #include "base/unguessable_token.h" |
| #include "build/build_config.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/storage_service_impl.h" |
| #include "components/variations/net/variations_http_headers.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/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_store/cookie_store_manager.h" |
| #include "content/browser/declarative_performance_observer/declarative_performance_observer_store.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/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/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/indexed_db_client_state_checker_factory.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_core.h" |
| #include "content/browser/service_worker/service_worker_context_wrapper.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/render_frame_host.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/browser/web_contents.h" |
| #include "content/public/common/child_process_id_util.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/disk_cache/backend_experiment.h" |
| #include "net/disk_cache/buildflags.h" |
| #include "net/ssl/client_cert_store.h" |
| #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h" |
| #include "services/network/public/cpp/constants.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 "sql/database.h" |
| #include "storage/browser/blob/blob_url_registry.h" |
| #include "storage/browser/quota/quota_client_type.h" |
| #include "storage/browser/quota/quota_features.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/use_counter/metrics/web_feature.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) { |
| CHECK_CURRENTLY_ON(BrowserThread::IO, base::NotFatalUntil::M159); |
| 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 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) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| |
| 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, |
| StoragePartition::StorageKeyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback, |
| const std::vector<SessionStorageUsageInfo>& infos) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| |
| 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)) { |
| continue; |
| } |
| dom_storage_context->DeleteSessionStorage(info, concurrent.CreateClosure()); |
| } |
| std::move(concurrent).Done(std::move(done_callback)); |
| } |
| |
| void ClearQuotaManagedData( |
| scoped_refptr<storage::QuotaManager> quota_manager, |
| const base::Time begin, |
| const base::Time end, |
| storage::QuotaClientTypes quota_client_types, |
| const blink::StorageKey& storage_key, |
| scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| |
| auto on_got_modified_buckets = base::BindOnce( |
| [](scoped_refptr<storage::QuotaManager> quota_manager, |
| scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy, |
| StoragePartition::StorageKeyPolicyMatcherFunction storage_key_matcher, |
| std::optional<blink::StorageKey> storage_key, |
| storage::QuotaClientTypes quota_client_types, |
| bool perform_storage_cleanup, base::OnceClosure callback, |
| const std::set<storage::BucketLocator>& buckets) { |
| CHECK_CURRENTLY_ON(BrowserThread::IO, base::NotFatalUntil::M159); |
| if (buckets.empty()) { |
| std::move(callback).Run(); |
| return; |
| } |
| |
| // Collect buckets that pass the filters. |
| std::vector<storage::BucketLocator> buckets_to_delete; |
| for (const storage::BucketLocator& bucket : buckets) { |
| 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; |
| } |
| |
| buckets_to_delete.push_back(bucket); |
| } |
| |
| base::OnceClosure done_callback = |
| perform_storage_cleanup |
| ? base::BindOnce(&storage::QuotaManager::PerformStorageCleanup, |
| quota_manager, quota_client_types, |
| std::move(callback)) |
| : std::move(callback); |
| |
| base::RepeatingClosure barrier = base::BarrierClosure( |
| buckets_to_delete.size(), std::move(done_callback)); |
| |
| for (const storage::BucketLocator& bucket : buckets_to_delete) { |
| quota_manager->DeleteBucketData( |
| bucket, quota_client_types, |
| base::IgnoreArgs<blink::mojom::QuotaStatusCode>(barrier)); |
| } |
| }, |
| quota_manager, std::move(special_storage_policy), |
| std::move(storage_key_matcher), |
| storage_key.origin().opaque() ? std::nullopt |
| : std::make_optional(storage_key), |
| std::move(quota_client_types), perform_storage_cleanup, |
| std::move(callback)); |
| |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&storage::QuotaManager::GetBucketsModifiedBetween, |
| std::move(quota_manager), begin, end, |
| std::move(on_got_modified_buckets))); |
| } |
| |
| void ClearLocalStorage( |
| 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) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| |
| 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 ClearSessionStorage( |
| const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context, |
| StoragePartition::StorageKeyMatcherFunction storage_key_matcher, |
| bool perform_storage_cleanup, |
| base::OnceClosure callback) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| |
| dom_storage_context->GetSessionStorageUsage( |
| base::BindOnce(&OnSessionStorageUsageInfo, dom_storage_context, |
| 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, |
| const network::OriginatingProcessId& 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) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| 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() { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| // 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) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| CHECK(!(use_fallback && auth_credentials.has_value()), |
| base::NotFatalUntil::M159); |
| 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) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| // 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)) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| CHECK(client_cert_responder_, base::NotFatalUntil::M159); |
| 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 { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| client_cert_responder_->CancelRequest(); |
| DeleteSelf(); |
| } |
| |
| // SSLClientAuthHandler::Delegate: |
| void ContinueWithCertificate( |
| scoped_refptr<net::X509Certificate> cert, |
| scoped_refptr<net::SSLPrivateKey> private_key) override { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| |
| 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) { |
| CHECK(client_cert_responder_remote, base::NotFatalUntil::M159); |
| 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 `kSharedOrServiceWorkerContext`. |
| 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 { |
| base::UmaHistogramTimes("Content.SslError.CallbackDuration", |
| base::TimeTicks::Now() - startTime_); |
| std::move(response_).Run(error); |
| delete this; |
| } |
| void ContinueSSLRequest() override { |
| base::UmaHistogramTimes("Content.SslError.CallbackDuration", |
| base::TimeTicks::Now() - startTime_); |
| std::move(response_).Run(net::OK); |
| delete this; |
| } |
| base::WeakPtr<SSLErrorDelegate> GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| private: |
| base::TimeTicks startTime_ = base::TimeTicks::Now(); |
| 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(); |
| CHECK(storage_key_origin_empty || storage_key_matcher.is_null(), |
| base::NotFatalUntil::M159); |
| CHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null(), |
| base::NotFatalUntil::M159); |
| |
| 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 { |
| CHECK(!storage_key_policy_matcher.is_null(), |
| base::NotFatalUntil::M159); |
| 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)); |
| } |
| CHECK(!storage_key_origin_empty, base::NotFatalUntil::M159); |
| 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)); |
| } |
| |
| // Resulting decision for a local network access permission check. |
| enum class LocalNetworkAccessDecision { |
| // The request should be blocked immediately (e.g., in fenced frames or |
| // prerendering main frames). |
| kBlock, |
| // The request is default-allowed without checking permissions (e.g., primary |
| // main frame navigations). |
| kAllow, |
| // The request should proceed to permission checking using the resolved `rfh` |
| // (if non-null) or worker context. |
| kProceed, |
| }; |
| |
| // Outcome of resolving local network access context for a network request. |
| struct LocalNetworkAccessResolution { |
| LocalNetworkAccessDecision decision; |
| // The RenderFrameHost associated with the request context, if available. |
| // Null for worker contexts or when no specific frame applies. |
| raw_ptr<RenderFrameHost> rfh = nullptr; |
| }; |
| |
| // Resolves the frame context and initial decision for local network access |
| // requests. Shared helper between Web LNA |
| // (OnLocalNetworkAccessPermissionRequired) and Platform LNA |
| // (OnPlatformLocalNetworkPermissionRequired). |
| LocalNetworkAccessResolution ResolveLocalNetworkAccess( |
| const StoragePartitionImpl::URLLoaderNetworkContext& 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 |
| // all navigations. If the navigation is in a subframe, 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::kSharedOrServiceWorkerContext) covers |
| // requests from service workers and shared 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. |
| |
| if (!context.navigation_or_document()) { |
| // Workers and background tasks do not resolve to a RenderFrameHost, but we |
| // return kProceed here to let downstream handlers apply their own rules: |
| // - Web LNA: Evaluates worker permissions via PermissionController. |
| // - Platform LNA: Requires a UI tab (WebContents) to display dialogs. Since |
| // background workers lack a UI tab, requests are safely denied early. |
| // (See TODO below for potential future support via frame window IDs). |
| return {LocalNetworkAccessDecision::kProceed, nullptr}; |
| } |
| |
| // Case 1: Document context (covers fetch() and subresource requests). |
| if (RenderFrameHost* rfh = context.navigation_or_document()->GetDocument()) { |
| // Get the document that is making the request. |
| return {LocalNetworkAccessDecision::kProceed, rfh}; |
| } |
| |
| // Case 2: Navigation context. |
| if (auto* request = |
| context.navigation_or_document()->GetNavigationRequest()) { |
| // Currently the LNA permission only applies to subframe navigations. |
| // See content/browser/renderer_host/local_network_access_util.cc for |
| // current feature state to policy mapping logic. |
| // |
| // For other types of navigation, we either default-allow or default-block |
| // local network requests: |
| // - Primary main frame: default-allow. |
| // - Guest view main frame: default-allow. |
| // - Prerender: default-block. |
| // - Fenced frame: default-block. (See crbug.com/409303581.) |
| switch (request->GetNavigatingFrameType()) { |
| case FrameType::kPrimaryMainFrame: |
| case FrameType::kGuestMainFrame: |
| return {LocalNetworkAccessDecision::kAllow}; |
| case FrameType::kFencedFrameRoot: |
| case FrameType::kPrerenderMainFrame: |
| return {LocalNetworkAccessDecision::kBlock}; |
| case FrameType::kSubframe: { |
| // Get the document that initiated the navigation. Can be nullptr if |
| // the initiator has gone away, in which case we should just block the |
| // navigation. |
| if (!request->GetInitiatorFrameToken().has_value()) { |
| return {LocalNetworkAccessDecision::kBlock}; |
| } |
| |
| RenderFrameHost* initiator_rfh = |
| RenderFrameHost::FromFrameToken(GlobalRenderFrameHostToken( |
| request->GetInitiatorProcessId(), |
| request->GetInitiatorFrameToken().value())); |
| |
| if (!initiator_rfh) { |
| return {LocalNetworkAccessDecision::kBlock}; |
| } |
| |
| return {LocalNetworkAccessDecision::kProceed, initiator_rfh}; |
| } |
| } |
| } |
| |
| // Fallback if neither document nor navigation request is available. |
| return {LocalNetworkAccessDecision::kBlock}; |
| } |
| |
| } // 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_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 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, base::OnceClosure callback) |
| : remove_mask_(remove_mask), callback_(std::move(callback)) {} |
| |
| DataDeletionHelper(const DataDeletionHelper&) = delete; |
| DataDeletionHelper& operator=(const DataDeletionHelper&) = delete; |
| |
| ~DataDeletionHelper() = default; |
| |
| void ClearData( |
| 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, |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| CdmStorageManager* cdm_storage_manager, |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| network::mojom::DeviceBoundSessionManager* device_bound_session_manager, |
| KeepAliveURLLoaderService* keep_alive_url_loader_service, |
| DeclarativePerformanceObserverStore* |
| declarative_performance_observer_store, |
| bool perform_storage_cleanup, |
| const base::Time begin, |
| const base::Time end); |
| |
| 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, // Deprecated. |
| kAggregationService = 9, // Deprecated. |
| kSharedStorage = 10, |
| kGpuCache = 11, |
| kPrivateAggregation = 12, // Deprecated. |
| kInterestGroups = 13, // Deprecated |
| kCdmStorage = 14, |
| kDeviceBoundSessions = 15, |
| kDeclarativePerformanceObserver = 16, |
| kMaxValue = kDeclarativePerformanceObserver, |
| }; |
| // 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_; |
| |
| // Accessed on UI thread. |
| base::OnceClosure callback_; |
| // Accessed on UI thread. |
| std::set<TracingDataType> pending_tasks_; |
| |
| base::WeakPtrFactory<StoragePartitionImpl::DataDeletionHelper> weak_factory_{ |
| this}; |
| }; |
| |
| 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 { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| // TODO(https://crbug.com/423472677): Group multiple cookie access details |
| // with the same RenderFrameHost. This UMA metrics is to decide whether to |
| // optimize this method or not. |
| base::UmaHistogramCounts1M( |
| "Cookie.OnCookiesAccessed.BatchSize.CookieAccessObserver", |
| details_vector.size()); |
| std::optional<base::ElapsedTimer> timer; |
| if (base::ShouldRecordSubsampledMetric(0.01)) { |
| timer.emplace(); |
| } |
| for (auto& details : details_vector) { |
| for (GlobalRenderFrameHostId frame_id : GetRoutingIdsForOrigin( |
| storage_partition_, url::Origin::Create(details->url))) { |
| if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(frame_id)) { |
| std::vector<network::mojom::CookieAccessDetailsPtr> details_list; |
| details_list.reserve(1); |
| details_list.push_back(mojo::Clone(details)); |
| rfh->NotifyCookiesAccessed( |
| std::move(details_list), |
| CookieAccessDetails::Source::kNonNavigation); |
| } |
| } |
| } |
| if (timer) { |
| base::UmaHistogramCustomMicrosecondsTimes( |
| "Browser.CookieAccessObserver.StoragePartitionImpl.Duration." |
| "Subsampled", |
| timer->Elapsed(), base::Microseconds(1), base::Seconds(1), 100); |
| } |
| } |
| |
| // `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 { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| |
| 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 { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| 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 { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| 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) |
| : performance_scenarios::MatchingScenarioObserver( |
| performance_scenarios::kDefaultIdleScenarios), |
| 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) { |
| #if BUILDFLAG(ENABLE_DISK_CACHE_SQL_BACKEND) |
| // To run a database checkpoint when the browser is idle, the SQL disk cache |
| // uses PerformanceScenarioObserverList to detect the idle state. |
| if (base::FeatureList::IsEnabled( |
| net::features::kDiskCacheBackendExperiment) && |
| net::features::kDiskCacheBackendParam.Get() == |
| net::features::DiskCacheBackend::kSql) { |
| // `observer_list` may be null in tests. |
| if (auto observer_list = |
| performance_scenarios::PerformanceScenarioObserverList::GetForScope( |
| performance_scenarios::ScenarioScope::kGlobal)) { |
| performance_scenario_observation_.Observe(observer_list.get()); |
| } |
| } |
| #endif // ENABLE_DISK_CACHE_SQL_BACKEND |
| } |
| |
| bool StoragePartitionImpl::ShouldClearSessionStorageOnStartup() const { |
| return browser_context_->ShouldClearSessionStorageOnStartup(); |
| } |
| |
| StoragePartitionImpl::~StoragePartitionImpl() { |
| #if DCHECK_IS_ON() |
| CHECK(on_browser_context_will_be_destroyed_called_, |
| base::NotFatalUntil::M159); |
| #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(); |
| } |
| |
| if (declarative_performance_observer_store_) { |
| declarative_performance_observer_store_->Close(); |
| } |
| } |
| |
| void StoragePartitionImpl::RestrictNetworkForIdsInNetworkContext( |
| const std::map<base::UnguessableToken, network::ConnectionAllowlists>& |
| ids_to_allowlists, |
| base::OnceClosure callback) { |
| std::vector<network::mojom::IdAndAllowlistedPatternsPtr> dest_vector; |
| for (const auto& pair : ids_to_allowlists) { |
| CHECK(pair.first != network::GetNoOpNetworkRestrictionsId(), |
| base::NotFatalUntil::M165); |
| CHECK(pair.first != network::GetTODONetworkRestrictionsId(), |
| base::NotFatalUntil::M165); |
| |
| // Create a new Mojo struct pointer for each map entry. |
| auto id_and_patterns = network::mojom::IdAndAllowlistedPatterns::New(); |
| id_and_patterns->network_restrictions_id = pair.first; |
| id_and_patterns->allowlists = pair.second; |
| dest_vector.push_back(std::move(id_and_patterns)); |
| } |
| GetNetworkContext()->RestrictNetworkForIds(std::move(dest_vector), |
| std::move(callback)); |
| |
| // Save IDs and allowlisted URLs in `StoragePartitionImpl`. When there is a |
| // crash of `NetworkService`, the network restriction IDs of |
| // `NetworkContext` will be restored using this. |
| for (const auto& pair : ids_to_allowlists) { |
| network_restrictions_ids_.insert_or_assign(pair.first, pair.second); |
| } |
| } |
| |
| void StoragePartitionImpl::ClearNetworkRestrictionsAfterDelay( |
| const std::vector<base::UnguessableToken>& network_restrictions_ids) { |
| GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &StoragePartitionImpl::ClearNetworkRestrictionsAfterDelayCallback, |
| weak_factory_.GetWeakPtr(), network_restrictions_ids), |
| clear_network_restrictions_delay_); |
| } |
| |
| void StoragePartitionImpl::ClearNetworkRestrictionsAfterDelayCallback( |
| const std::vector<base::UnguessableToken>& network_restrictions_ids) { |
| GetNetworkContext()->ClearNetworkRestrictions(network_restrictions_ids); |
| |
| for (const auto& network_restrictions_id : network_restrictions_ids) { |
| network_restrictions_ids_.erase(network_restrictions_id); |
| } |
| |
| clear_network_restrictions_callback_for_testing_.Run(); // IN-TEST |
| } |
| |
| // 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. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| !BrowserThread::IsThreadInitialized(BrowserThread::UI), |
| base::NotFatalUntil::M159); |
| |
| 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. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| !BrowserThread::IsThreadInitialized(BrowserThread::UI), |
| base::NotFatalUntil::M159); |
| CHECK(!initialized_, base::NotFatalUntil::M159); |
| initialized_ = true; |
| SCOPED_UMA_HISTOGRAM_TIMER("Storage.StoragePartition.InitializeDuration"); |
| base::ElapsedTimer step_timer; |
| |
| // 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. |
| const bool report_static_storage_quota = |
| GetContentClient() |
| ->browser() |
| ->GetOverrideValueForStaticStorageQuota(browser_context_) |
| .value_or(base::FeatureList::IsEnabled( |
| storage::features::kStaticStorageQuota)); |
| |
| quota_context_ = base::MakeRefCounted<QuotaContext>( |
| is_in_memory(), partition_path_, |
| browser_context_->GetSpecialStoragePolicy(), |
| base::BindRepeating(&StoragePartitionImpl::GetQuotaSettings, |
| weak_factory_.GetWeakPtr()), |
| report_static_storage_quota); |
| 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); |
| base::UmaHistogramTimes( |
| "Storage.StoragePartition.InitializeDuration.QuotaAndFileSystem", |
| step_timer.Elapsed()); |
| step_timer = base::ElapsedTimer(); |
| |
| dom_storage_context_ = DOMStorageContextWrapper::Create( |
| this, browser_context_->GetSpecialStoragePolicy()); |
| |
| lock_manager_ = std::make_unique<LockManager<storage::BucketId>>(); |
| |
| |
| 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({}), |
| IndexedDBClientStateCheckerFactory::GetClientStateCheckerCallback()); |
| |
| cache_storage_control_wrapper_ = std::make_unique<CacheStorageControlWrapper>( |
| GetIOThreadTaskRunner({}), path, |
| browser_context_->GetSpecialStoragePolicy(), quota_manager_proxy, |
| ChromeBlobStorageContext::GetRemoteFor(browser_context_)); |
| base::UmaHistogramTimes( |
| "Storage.StoragePartition.InitializeDuration.DOMStorageAndIndexedDB", |
| step_timer.Elapsed()); |
| step_timer = base::ElapsedTimer(); |
| |
| 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>(); |
| |
| 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>(this); |
| } |
| |
| 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()); |
| base::UmaHistogramTimes( |
| "Storage.StoragePartition.InitializeDuration.ServiceWorkerContext", |
| step_timer.Elapsed()); |
| step_timer = base::ElapsedTimer(); |
| |
| bucket_manager_ = std::make_unique<BucketManager>(this); |
| |
| base::UmaHistogramTimes( |
| "Storage.StoragePartition.InitializeDuration.BackgroundTasks", |
| step_timer.Elapsed()); |
| step_timer = base::ElapsedTimer(); |
| |
| 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"); |
| } |
| // TODO(crbug.com/562462615): CHECK-exclusion: Convert to a CHECK once we |
| // are confident it won't be triggered. |
| DCHECK_GE(settings.size_in_bytes(), 0); |
| GetGeneratedCodeCacheContext()->Initialize(code_cache_path, |
| settings.size_in_bytes()); |
| } |
| |
| font_access_manager_ = FontAccessManager::Create(); |
| |
| #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( |
| blink::features::kDeclarativePerformanceObserver)) { |
| declarative_performance_observer_store_ = |
| std::make_unique<DeclarativePerformanceObserverStore>(is_in_memory(), |
| path); |
| } |
| base::UmaHistogramTimes( |
| "Storage.StoragePartition.InitializeDuration.CodeCacheAndInterestGroup", |
| step_timer.Elapsed()); |
| } |
| |
| void StoragePartitionImpl::ResetSessionStorageConnections() { |
| for (const auto& client : dom_storage_clients_) { |
| client.second->ResetSessionStorageConnections(); |
| } |
| } |
| |
| void StoragePartitionImpl::ResetLocalStorageConnections() { |
| for (const auto& client : dom_storage_clients_) { |
| client.second->ResetLocalStorageConnections(); |
| } |
| } |
| |
| 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() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| if (!network_context_owner_->network_context.is_bound()) { |
| InitNetworkContext(); |
| } |
| return network_context_owner_->network_context.get(); |
| } |
| |
| bool StoragePartitionImpl::IsNetworkContextInitialized() { |
| return network_context_owner_->network_context.is_bound(); |
| } |
| |
| cert_verifier::mojom::CertVerifierServiceUpdater* |
| StoragePartitionImpl::GetCertVerifierServiceUpdater() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| // 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, |
| bool prefer_bound_cookie_context, |
| net::CookieSettingOverrides cookie_setting_overrides, |
| net::CookieSettingOverrides devtools_cookie_setting_overrides, |
| mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver, |
| mojo::PendingRemote<network::mojom::CookieAccessObserver> cookie_observer) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| if (!GetContentClient()->browser()->WillCreateRestrictedCookieManager( |
| role, browser_context_, origin, isolation_info, is_service_worker, |
| process_id, routing_id, prefer_bound_cookie_context, &receiver)) { |
| GetNetworkContext()->GetRestrictedCookieManager( |
| std::move(receiver), role, origin, isolation_info, |
| cookie_setting_overrides, devtools_cookie_setting_overrides, |
| prefer_bound_cookie_context, std::move(cookie_observer)); |
| } |
| } |
| |
| void StoragePartitionImpl::CreateTrustTokenQueryAnswerer( |
| mojo::PendingReceiver<network::mojom::TrustTokenQueryAnswerer> receiver, |
| const url::Origin& top_frame_origin) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| GetNetworkContext()->GetTrustTokenQueryAnswerer(std::move(receiver), |
| top_frame_origin); |
| } |
| |
| storage::QuotaManager* StoragePartitionImpl::GetQuotaManager() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return quota_manager_.get(); |
| } |
| |
| storage::QuotaManagerProxy* StoragePartitionImpl::GetQuotaManagerProxy() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return quota_manager_->proxy(); |
| } |
| |
| BackgroundSyncContextImpl* StoragePartitionImpl::GetBackgroundSyncContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return background_sync_context_.get(); |
| } |
| |
| storage::FileSystemContext* StoragePartitionImpl::GetFileSystemContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return filesystem_context_.get(); |
| } |
| |
| DOMStorageContextWrapper* StoragePartitionImpl::GetDOMStorageContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return dom_storage_context_.get(); |
| } |
| |
| storage::mojom::LocalStorageControl* |
| StoragePartitionImpl::GetLocalStorageControl() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return GetDOMStorageContext()->GetLocalStorageControl(); |
| } |
| |
| LockManager<storage::BucketId>* StoragePartitionImpl::GetLockManager() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return lock_manager_.get(); |
| } |
| |
| |
| storage::mojom::IndexedDBControl& StoragePartitionImpl::GetIndexedDBControl() { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| return indexed_db_control_wrapper_->GetIndexedDBControl(); |
| } |
| |
| FileSystemAccessEntryFactory* |
| StoragePartitionImpl::GetFileSystemAccessEntryFactory() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return file_system_access_manager_.get(); |
| } |
| |
| QuotaContext* StoragePartitionImpl::GetQuotaContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return quota_context_.get(); |
| } |
| |
| storage::mojom::CacheStorageControl* |
| StoragePartitionImpl::GetCacheStorageControl() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return cache_storage_control_wrapper_.get(); |
| } |
| |
| ServiceWorkerContextWrapper* StoragePartitionImpl::GetServiceWorkerContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return service_worker_context_.get(); |
| } |
| |
| DedicatedWorkerServiceImpl* StoragePartitionImpl::GetDedicatedWorkerService() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return dedicated_worker_service_.get(); |
| } |
| |
| SharedWorkerService* StoragePartitionImpl::GetSharedWorkerService() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return shared_worker_service_.get(); |
| } |
| |
| HostZoomMap* StoragePartitionImpl::GetHostZoomMap() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| CHECK(host_zoom_level_context_.get(), base::NotFatalUntil::M159); |
| return host_zoom_level_context_->GetHostZoomMap(); |
| } |
| |
| HostZoomLevelContext* StoragePartitionImpl::GetHostZoomLevelContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return host_zoom_level_context_.get(); |
| } |
| |
| ZoomLevelDelegate* StoragePartitionImpl::GetZoomLevelDelegate() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| CHECK(host_zoom_level_context_.get(), base::NotFatalUntil::M159); |
| return host_zoom_level_context_->GetZoomLevelDelegate(); |
| } |
| |
| PlatformNotificationContextImpl* |
| StoragePartitionImpl::GetPlatformNotificationContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return platform_notification_context_.get(); |
| } |
| |
| BackgroundFetchContext* StoragePartitionImpl::GetBackgroundFetchContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return background_fetch_context_.get(); |
| } |
| |
| PaymentAppContextImpl* StoragePartitionImpl::GetPaymentAppContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return payment_app_context_.get(); |
| } |
| |
| BroadcastChannelService* StoragePartitionImpl::GetBroadcastChannelService() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return broadcast_channel_service_.get(); |
| } |
| |
| BluetoothAllowedDevicesMap* |
| StoragePartitionImpl::GetBluetoothAllowedDevicesMap() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return bluetooth_allowed_devices_map_.get(); |
| } |
| |
| BlobRegistryWrapper* StoragePartitionImpl::GetBlobRegistry() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return blob_registry_.get(); |
| } |
| |
| storage::BlobUrlRegistry* StoragePartitionImpl::GetBlobUrlRegistry() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return blob_url_registry_.get(); |
| } |
| |
| SubresourceProxyingURLLoaderService* |
| StoragePartitionImpl::GetSubresourceProxyingURLLoaderService() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return subresource_proxying_url_loader_service_.get(); |
| } |
| |
| KeepAliveURLLoaderService* |
| StoragePartitionImpl::GetKeepAliveURLLoaderService() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return keep_alive_url_loader_service_.get(); |
| } |
| |
| CookieStoreManager* StoragePartitionImpl::GetCookieStoreManager() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return cookie_store_manager_.get(); |
| } |
| |
| BucketManager* StoragePartitionImpl::GetBucketManager() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return bucket_manager_.get(); |
| } |
| |
| GeneratedCodeCacheContext* |
| StoragePartitionImpl::GetGeneratedCodeCacheContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return generated_code_cache_context_.get(); |
| } |
| |
| DevToolsBackgroundServicesContext* |
| StoragePartitionImpl::GetDevToolsBackgroundServicesContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return devtools_background_services_context_.get(); |
| } |
| |
| FileSystemAccessManagerImpl* |
| StoragePartitionImpl::GetFileSystemAccessManager() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return file_system_access_manager_.get(); |
| } |
| |
| FontAccessManager* StoragePartitionImpl::GetFontAccessManager() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return font_access_manager_.get(); |
| } |
| |
| void StoragePartitionImpl::SetFontAccessManagerForTesting( |
| std::unique_ptr<FontAccessManager> font_access_manager) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| CHECK(font_access_manager, base::NotFatalUntil::M159); |
| font_access_manager_ = std::move(font_access_manager); |
| } |
| |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| CdmStorageDataModel* StoragePartitionImpl::GetCdmStorageDataModel() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return cdm_storage_manager_.get(); |
| } |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| |
| network::mojom::DeviceBoundSessionManager* |
| StoragePartitionImpl::GetDeviceBoundSessionManager() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| #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) |
| } |
| |
| ContentIndexContextImpl* StoragePartitionImpl::GetContentIndexContext() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| return content_index_context_.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) { |
| CHECK(!proto_database_provider_, base::NotFatalUntil::M159); |
| proto_database_provider_ = std::move(proto_db_provider); |
| } |
| |
| leveldb_proto::ProtoDatabaseProvider* |
| StoragePartitionImpl::GetProtoDatabaseProviderForTesting() { |
| return proto_database_provider_.get(); |
| } |
| |
| |
| void StoragePartitionImpl::DeleteStaleSessionData() { |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (base::FeatureList::IsEnabled( |
| features::kDeferSessionStorageScavengingOnStartup)) { |
| // Defer session storage scavenging to avoid LevelDB initialization |
| // blocking the critical path of startup. We reuse the cookie delay |
| // to batch these cleanup tasks. |
| GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&StoragePartitionImpl::DeleteStaleSessionDataAfterDelay, |
| weak_factory_.GetWeakPtr()), |
| stale_session_cleanup_delay_); |
| } else { |
| 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. |
| GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &StoragePartitionImpl::DeleteStaleSessionOnlyCookiesAfterDelay, |
| weak_factory_.GetWeakPtr()), |
| stale_session_cleanup_delay_); |
| } |
| } |
| |
| void StoragePartitionImpl::DeleteStaleSessionOnlyCookiesAfterDelay() { |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| GetCookieManagerForBrowserProcess()->DeleteStaleSessionOnlyCookies( |
| base::BindOnce([](const uint32_t num_deleted) { |
| base::UmaHistogramCounts10M( |
| "Cookie.StaleSessionCookiesDeletedOnStartup", num_deleted); |
| })); |
| } |
| |
| void StoragePartitionImpl::DeleteStaleSessionDataAfterDelay() { |
| GetDOMStorageContext()->StartScavengingUnusedSessionStorage(); |
| DeleteStaleSessionOnlyCookiesAfterDelay(); |
| } |
| |
| void StoragePartitionImpl::OpenLocalStorage( |
| const blink::StorageKey& storage_key, |
| const blink::LocalFrameToken& local_frame_token, |
| mojo::PendingReceiver<blink::mojom::StorageArea> receiver) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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(); |
| URLLoaderNetworkContext original_context = 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 (service_worker_client->is_initiated_by_prefetch()) { |
| // Do not process auth requests associated with prefetching clients |
| // because there are no corresponding Window yet. |
| // `is_initiated_by_prefetch()` can be true here (and similarly at |
| // `OnCertificateRequested()`) e.g. when a speculation rules prefetch |
| // request is intercepted by a ServiceWorker fetch handler and then |
| // is fetched as a ServiceWorker subresource via |
| // `event.respondWith(fetch(event.request))`. |
| static_cast<mojo::Remote<network::mojom::AuthChallengeResponder>>( |
| std::move(auth_challenge_responder)) |
| ->OnAuthCredentials(std::nullopt); |
| return; |
| } 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 the request was initiated by a service worker, we should not treat it as |
| // a navigation request for the purpose of authentication. This is because |
| // even if the request is associated with an ongoing navigation for UI |
| // purposes, the network request itself is issued by the service worker. |
| // `WebRequestAPI::MaybeProxyAuthRequest` overrides the process ID to -1 |
| // for navigation requests, but the `WebRequestProxyingURLLoaderFactory` |
| // for service worker subresources is registered with the service worker's |
| // actual process ID. Treating this as a non-navigation ensures the |
| // `GlobalRequestID` matches and the proxy can be found. |
| if (original_context.type() == ContextType::kSharedOrServiceWorkerContext) { |
| is_navigation_request = false; |
| } |
| |
| 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(); |
| } |
| network::OriginatingProcessId process_id = |
| network::OriginatingProcessId::browser(); |
| if (const auto* worker_context = |
| std::get_if<URLLoaderNetworkContext::SharedOrServiceWorkerContext>( |
| &original_context.context())) { |
| // If the request was initiated by a service worker, use the service |
| // worker's process ID. This ensures the `GlobalRequestID` used to look up |
| // the proxy (e.g. in `WebRequestAPI`) matches the one used when the factory |
| // was created, which for service worker subresources is the worker's |
| // process ID. |
| process_id = worker_context->process_id; |
| } else 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::OriginatingProcessId(); |
| |
| // `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 = |
| ToOriginatingProcessId(render_frame_host->GetGlobalId().child_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.GetScheme(), |
| last_committed_url.GetHost(), |
| 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::OnLocalNetworkAccessPermissionRequired( |
| network::mojom::TransportType transport_type, |
| network::mojom::IPAddressSpace ip_address_space, |
| 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(network::mojom::LocalNetworkAccessResult::kGranted); |
| return; |
| } |
| |
| if (url_loader_network_observers_.empty()) { |
| std::move(callback).Run(network::mojom::LocalNetworkAccessResult::kDenied); |
| return; |
| } |
| const URLLoaderNetworkContext& context = |
| url_loader_network_observers_.current_context(); |
| |
| // Compute the permission that we will check, if we end up checking for one. |
| blink::PermissionType permission_type; |
| switch (ip_address_space) { |
| case network::mojom::IPAddressSpace::kLocal: |
| permission_type = blink::PermissionType::LOCAL_NETWORK; |
| break; |
| case network::mojom::IPAddressSpace::kLoopback: |
| permission_type = blink::PermissionType::LOOPBACK_NETWORK; |
| break; |
| case network::mojom::IPAddressSpace::kPublic: |
| case network::mojom::IPAddressSpace::kUnknown: |
| NOTREACHED(); |
| } |
| |
| LocalNetworkAccessResolution resolution = ResolveLocalNetworkAccess(context); |
| switch (resolution.decision) { |
| case LocalNetworkAccessDecision::kBlock: |
| std::move(callback).Run( |
| network::mojom::LocalNetworkAccessResult::kDenied); |
| return; |
| case LocalNetworkAccessDecision::kAllow: |
| std::move(callback).Run( |
| network::mojom::LocalNetworkAccessResult::kGranted); |
| return; |
| case LocalNetworkAccessDecision::kProceed: |
| break; |
| } |
| |
| if (context.navigation_or_document()) { |
| RenderFrameHost* rfh = resolution.rfh; |
| if (!rfh) { |
| std::move(callback).Run( |
| network::mojom::LocalNetworkAccessResult::kDenied); |
| return; |
| } |
| |
| PermissionController& permission_controller = |
| CHECK_DEREF(browser_context_->GetPermissionController()); |
| |
| auto status = permission_controller.GetPermissionStatusForCurrentDocument( |
| content::PermissionDescriptorUtil:: |
| CreatePermissionDescriptorForPermissionType(permission_type), |
| rfh); |
| |
| // If the request was loaded from cache, prefer retrying over the network |
| // over prompting the user or blocking. |
| // TODO(crbug.com/457969523): Consider only triggering a retry over the |
| // network once per (Document, Subresource) combination, as network changes |
| // during the lifetime of a Document may be sufficiently uncommon and we |
| // could rely on the cached subresource for any further requests during the |
| // document lifetime. |
| if ((transport_type == network::mojom::TransportType::kCached || |
| transport_type == network::mojom::TransportType::kCachedFromProxy) && |
| (status == blink::mojom::PermissionStatus::ASK || |
| status == blink::mojom::PermissionStatus::DENIED)) { |
| std::move(callback).Run( |
| network::mojom::LocalNetworkAccessResult::kRetryDueToCache); |
| return; |
| } |
| |
| if (status == blink::mojom::PermissionStatus::GRANTED) { |
| std::move(callback).Run( |
| network::mojom::LocalNetworkAccessResult::kGranted); |
| return; |
| } else if (status == blink::mojom::PermissionStatus::DENIED) { |
| std::move(callback).Run( |
| network::mojom::LocalNetworkAccessResult::kDenied); |
| return; |
| } else { |
| // PermissionStatus is ASK, so request the permission. Converts the result |
| // to pass back to `callback`, capturing whether the permission is granted |
| // or not. |
| permission_controller.RequestPermissionFromCurrentDocument( |
| rfh, |
| PermissionRequestDescription( |
| content::PermissionDescriptorUtil:: |
| CreatePermissionDescriptorForPermissionType(permission_type)), |
| base::BindOnce( |
| [](OnLocalNetworkAccessPermissionRequiredCallback cb, |
| PermissionResult permission_result) { |
| std::move(cb).Run( |
| permission_result.status == |
| blink::mojom::PermissionStatus::GRANTED |
| ? network::mojom::LocalNetworkAccessResult::kGranted |
| : network::mojom::LocalNetworkAccessResult::kDenied); |
| }, |
| std::move(callback))); |
| return; |
| } |
| } else if (const auto* worker_context = std::get_if< |
| URLLoaderNetworkContext::SharedOrServiceWorkerContext>( |
| &context.context())) { |
| // TODO(crbug.com/404887282): Plumb the `frame_window_id` of the worker, |
| // if it exists, to allow this to identify the hosting frame of the worker, |
| // when available. This would allow us to additionally _request_ the |
| // permission when a fetch that happens to go through a service worker would |
| // trigger this check. This is beneficial for developers as they can use |
| // caching service workers "as expected" without needing to re-engineer |
| // their code to do a special "non-cached request" just to trigger the |
| // permission prompt. The `frame_window_id` approach is used by other |
| // methods in this file, such as OnCertificateRequested(), which require UI |
| // dialogs to handle the event. This may also be useful for handling |
| // other types of Workers that always have an associated frame (such as |
| // Dedicated Worker). |
| |
| // If there is no associated worker origin, there is nothing that can be |
| // granted a permission, so just deny the permission request. This can |
| // potentially happen for things like workers loaded from data: URLs, which |
| // are opaque (but workers loaded from blob: URLs should have the origin |
| // which created the blob: URL). |
| // TODO(crbug.com/404887282): Revisit if opaque origins support is needed. |
| CHECK(worker_context->worker_origin); |
| if (worker_context->worker_origin->opaque()) { |
| std::move(callback).Run( |
| network::mojom::LocalNetworkAccessResult::kDenied); |
| return; |
| } |
| |
| PermissionController& permission_controller = |
| CHECK_DEREF(browser_context_->GetPermissionController()); |
| CHECK(!worker_context->process_id.is_browser()); |
| auto status = permission_controller.GetPermissionStatusForWorker( |
| content::PermissionDescriptorUtil:: |
| CreatePermissionDescriptorForPermissionType(permission_type), |
| content::RenderProcessHost::FromID( |
| ToChildProcessId(worker_context->process_id.renderer_process_id())), |
| worker_context->worker_origin.value()); |
| |
| // If the request was loaded from cache, prefer retrying over the network |
| // over prompting the user or blocking. |
| if ((transport_type == network::mojom::TransportType::kCached || |
| transport_type == network::mojom::TransportType::kCachedFromProxy) && |
| (status == blink::mojom::PermissionStatus::ASK || |
| status == blink::mojom::PermissionStatus::DENIED)) { |
| std::move(callback).Run( |
| network::mojom::LocalNetworkAccessResult::kRetryDueToCache); |
| return; |
| } |
| |
| std::move(callback).Run( |
| status == blink::mojom::PermissionStatus::GRANTED |
| ? network::mojom::LocalNetworkAccessResult::kGranted |
| : network::mojom::LocalNetworkAccessResult::kDenied); |
| return; |
| } |
| |
| // Otherwise default to denying local network access. |
| std::move(callback).Run(network::mojom::LocalNetworkAccessResult::kDenied); |
| return; |
| } |
| |
| // Called when an OS-level local network permission is required (e.g. Android |
| // ACCESS_LOCAL_NETWORK permission). |
| // |
| // Relationship and differences with OnLocalNetworkAccessPermissionRequired(): |
| // 1. Web LNA (OnLocalNetworkAccessPermissionRequired): |
| // Implements W3C LNA spec checks for document requests. |
| // |
| // 2. Platform LNA (OnPlatformLocalNetworkPermissionRequired): |
| // Handles OS-level runtime permission checks when socket connection fails |
| // with ERR_LOCAL_NETWORK_PERMISSION_MISSING. |
| // |
| // Why parameters received by OnLocalNetworkAccessPermissionRequired() |
| // (ip_address_space, transport_type) do not need to be plumbed to |
| // OnPlatformLocalNetworkPermissionRequired(): |
| // - `ip_address_space`: The target IP address space check has already been |
| // performed at the OS-level net stack when the socket connection was |
| // attempted. This method is triggered only after the OS net stack checked |
| // and rejected the connection due to missing OS permission, so re-checking |
| // `ip_address_space` here is unnecessary. |
| // - `transport_type`: This method is triggered only when an actual OS socket |
| // connection is rejected. Requests served from the HTTP cache do not |
| // attempt OS socket connections, so checking HTTP cache status is |
| // unnecessary. |
| void StoragePartitionImpl::OnPlatformLocalNetworkPermissionRequired( |
| OnPlatformLocalNetworkPermissionRequiredCallback callback) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI); |
| URLLoaderNetworkContext& context = |
| url_loader_network_observers_.current_context(); |
| |
| LocalNetworkAccessResolution resolution = ResolveLocalNetworkAccess(context); |
| if (resolution.decision == LocalNetworkAccessDecision::kBlock) { |
| std::move(callback).Run(/*granted=*/false); |
| return; |
| } |
| |
| WebContents* web_contents = context.GetWebContents(); |
| // `web_contents` may be null in the following cases: |
| // 1. Requests originating from background worker contexts (ServiceWorkers / |
| // SharedWorkers) which do not have an associated tab/frame UI. |
| // |
| // TODO(crbug.com/404887282): Consider plumbing `frame_window_id` if |
| // available to associate background worker requests with their hosting |
| // frame's WebContents for permission reprompts. |
| // 2. The WebContents was destroyed asynchronously while the network request |
| // was pending. Returning false is expected here as no UI remains to prompt |
| // the user. |
| if (!web_contents) { |
| std::move(callback).Run(/*granted=*/false); |
| return; |
| } |
| |
| GetContentClient()->browser()->RequestPlatformLocalNetworkPermission( |
| *web_contents, std::move(callback)); |
| } |
| |
| 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 (service_worker_client->is_initiated_by_prefetch()) { |
| // Do not process certification requests associated with prefetching |
| // clients because there are no corresponding Window yet. See also the |
| // comment at `OnAuthRequired()` for when `is_initiated_by_prefetch()` |
| // can be true here. |
| CallCancelRequest(std::move(cert_responder)); |
| return; |
| } 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::kSharedOrServiceWorkerContext || |
| context.type() == ContextType::kDeviceBoundSessionContext) { |
| if (const auto* worker_context = |
| std::get_if<URLLoaderNetworkContext::SharedOrServiceWorkerContext>( |
| &context.context())) { |
| // TODO(crbug.com/379869738) Remove GetUnsafeValue. |
| // TODO(crbug.com/479742988) This can be the browser process and |
| // shouldn't. |
| process_id = worker_context->process_id.GetUnsafeValue(); |
| } |
| } 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(), static_cast<net::Error>(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, |
| base::ByteSize recv_bytes, |
| base::ByteSize 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::Clone( |
| mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver> |
| observer) { |
| url_loader_network_observers_.Add( |
| this, std::move(observer), |
| url_loader_network_observers_.current_context()); |
| } |
| |
| void StoragePartitionImpl::OnWebSocketConnectedToLocalNetwork( |
| const GURL& request_url, |
| 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); |
| |
| // Log a UseCounter for potential LNA breakage, where we cannot auto-detect |
| // a mixed content bypass situation. This is similar to the check below in |
| // StoragePartitionImpl::OnUrlLoaderConnectedToLocalNetwork. |
| if (!network::IsUrlPotentiallyTrustworthy(request_url) && |
| !request_url.HostIsIPAddress() && !request_url.DomainIs("local")) { |
| GetContentClient()->browser()->LogWebFeatureForCurrentPage( |
| render_frame_host_impl, |
| blink::mojom::WebFeature:: |
| kLocalNetworkAccessWebSocketResourceNotKnownPrivate); |
| } |
| } |
| } |
| |
| void StoragePartitionImpl::OnUrlLoaderConnectedToLocalNetwork( |
| 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( |
| const content::GlobalRenderFrameHostId& frame_id) { |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote; |
| url_loader_network_observers_.Add( |
| this, remote.InitWithNewPipeAndPassReceiver(), |
| URLLoaderNetworkContext::CreateForRenderFrameHost(frame_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::CreateURLLoaderNetworkObserverForServiceOrSharedWorker( |
| const network::OriginatingProcessId& process_id, |
| const url::Origin& worker_origin, |
| const std::optional<blink::StorageKey>& storage_key) { |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote; |
| url_loader_network_observers_.Add( |
| this, remote.InitWithNewPipeAndPassReceiver(), |
| URLLoaderNetworkContext::CreateForServiceOrSharedWorker( |
| process_id, worker_origin, storage_key)); |
| return remote; |
| } |
| |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> |
| StoragePartitionImpl::CreateURLLoaderNetworkObserverForDeviceBoundSessions() { |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> remote; |
| url_loader_network_observers_.Add( |
| this, remote.InitWithNewPipeAndPassReceiver(), |
| URLLoaderNetworkContext::CreateForDeviceBoundSessions()); |
| return remote; |
| } |
| |
| void StoragePartitionImpl::OnFileUploadRequested( |
| const network::OriginatingProcessId& 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| PermissionController& permission_controller = |
| CHECK_DEREF(browser_context_->GetPermissionController()); |
| |
| 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| PermissionController& permission_controller = |
| CHECK_DEREF(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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| |
| 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(); |
| GetContentClient()->browser()->LogWebFeatureForCurrentPage( |
| web_contents->GetPrimaryMainFrame(), |
| blink::mojom::WebFeature::kClearSiteData); |
| } |
| |
| 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, |
| 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) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| bool storage_key_origin_empty = storage_key.origin().opaque(); |
| CHECK(storage_key_origin_empty || !filter_builder, base::NotFatalUntil::M159); |
| CHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null(), |
| base::NotFatalUntil::M159); |
| |
| 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, |
| base::BindOnce(&StoragePartitionImpl::DeletionHelperDone, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| // `helper` deletes itself when done in |
| // DataDeletionHelper::DecrementTaskCount(). |
| deletion_helpers_running_++; |
| helper->ClearData( |
| 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(), |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| cdm_storage_manager_.get(), |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| GetDeviceBoundSessionManager(), GetKeepAliveURLLoaderService(), |
| declarative_performance_observer_store_.get(), 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(); |
| } |
| } |
| |
| // A destructor guard that ensures Disarm() is called. Bind into closures that |
| // must be called rather than silently dropped. |
| class TaskCompletionGuard { |
| public: |
| TaskCompletionGuard() = default; |
| ~TaskCompletionGuard() { |
| // TODO(https://crbug.com/487338371): ChromeBrowsingDataRemoverDelegate |
| // fires fire-and-forget RemoveWithFilter calls for IWA storage partitions |
| // that can be abandoned during browser shutdown. Fix that and upgrade to a |
| // CHECK. |
| LOG_IF(WARNING, !disarmed_) |
| << "Task completion closure was destroyed without being called"; |
| } |
| |
| TaskCompletionGuard(const TaskCompletionGuard&) = delete; |
| TaskCompletionGuard& operator=(const TaskCompletionGuard&) = delete; |
| |
| void Disarm() { disarmed_ = true; } |
| |
| private: |
| bool disarmed_ = false; |
| }; |
| |
| base::OnceClosure |
| StoragePartitionImpl::DataDeletionHelper::CreateTaskCompletionClosure( |
| TracingDataType data_type) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| 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_BEGIN("browsing_data", "StoragePartitionImpl", |
| perfetto::NamedTrack("StoragePartitionImpl", ++tracing_id), |
| "data_type", static_cast<int>(data_type)); |
| return base::BindOnce(&DataDeletionHelper::OnTaskComplete, |
| base::Unretained(this), data_type, tracing_id) |
| .Then(base::BindOnce(&TaskCompletionGuard::Disarm, |
| std::make_unique<TaskCompletionGuard>())); |
| } |
| |
| 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); |
| // StoragePartitionImpl |
| TRACE_EVENT_END("browsing_data", |
| perfetto::NamedTrack("StoragePartitionImpl", tracing_id)); |
| |
| if (pending_tasks_.empty()) { |
| std::move(callback_).Run(); |
| delete this; |
| } |
| } |
| |
| void StoragePartitionImpl::DataDeletionHelper::RecordUnfinishedSubTasks() { |
| CHECK(!pending_tasks_.empty(), base::NotFatalUntil::M159); |
| for (TracingDataType task : pending_tasks_) { |
| base::UmaHistogramEnumeration( |
| "History.ClearBrowsingData.Duration.SlowTasks180sStoragePartition", |
| task); |
| } |
| } |
| |
| void StoragePartitionImpl::DataDeletionHelper::ClearData( |
| 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, |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| CdmStorageManager* cdm_storage_manager, |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| network::mojom::DeviceBoundSessionManager* device_bound_session_manager, |
| KeepAliveURLLoaderService* keep_alive_url_loader_service, |
| DeclarativePerformanceObserverStore* declarative_performance_observer_store, |
| bool perform_storage_cleanup, |
| const base::Time begin, |
| const base::Time end) { |
| CHECK_NE(remove_mask_, 0u, base::NotFatalUntil::M159); |
| CHECK(callback_, base::NotFatalUntil::M159); |
| |
| // 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(); |
| CHECK(storage_key_origin_empty || !filter_builder, base::NotFatalUntil::M159); |
| CHECK(storage_key_origin_empty || storage_key_policy_matcher.is_null(), |
| base::NotFatalUntil::M159); |
| |
| 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_DECLARATIVE_PERFORMANCE_OBSERVER) { |
| if (declarative_performance_observer_store) { |
| auto completion_callback = CreateTaskCompletionClosure( |
| TracingDataType::kDeclarativePerformanceObserver); |
| if (!storage_key_origin_empty) { |
| declarative_performance_observer_store->ClearDataForOrigin( |
| storage_key.origin(), std::move(completion_callback)); |
| } else if (generic_filter.is_null()) { |
| declarative_performance_observer_store->ClearAllData( |
| std::move(completion_callback)); |
| } else { |
| auto origin_matcher = base::BindRepeating( |
| [](const StorageKeyMatcherFunction& filter, |
| const url::Origin& origin) { |
| return filter.Run(blink::StorageKey::CreateFirstParty(origin)); |
| }, |
| generic_filter); |
| declarative_performance_observer_store->ClearDataWithFilter( |
| std::move(origin_matcher), std::move(completion_callback)); |
| } |
| } |
| } |
| |
| 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. |
| CHECK(!cookie_deletion_filter->created_after_time.has_value(), |
| base::NotFatalUntil::M159); |
| CHECK(!cookie_deletion_filter->created_before_time.has_value(), |
| base::NotFatalUntil::M159); |
| |
| 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. |
| CHECK(!(remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS_INTERNAL) || |
| remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS, |
| base::NotFatalUntil::M159); |
| CHECK(!(remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS_USER_CLEAR) || |
| remove_mask_ & REMOVE_DATA_MASK_INTEREST_GROUPS, |
| base::NotFatalUntil::M159); |
| |
| #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_FILE_SYSTEMS || |
| remove_mask_ & REMOVE_DATA_MASK_SERVICE_WORKERS || |
| remove_mask_ & REMOVE_DATA_MASK_CACHE_STORAGE) { |
| ClearQuotaManagedData( |
| quota_manager, begin, end, |
| StoragePartitionImpl::GenerateQuotaClientTypes(remove_mask_), |
| storage_key, storage_policy_ref, combined_storage_key_matcher, |
| perform_storage_cleanup, |
| CreateTaskCompletionClosure(TracingDataType::kQuota)); |
| } |
| |
| if (remove_mask_ & REMOVE_DATA_MASK_LOCAL_STORAGE) { |
| ClearLocalStorage( |
| base::WrapRefCounted(dom_storage_context), storage_policy_ref, |
| combined_storage_key_matcher, storage_key, perform_storage_cleanup, |
| begin, end, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kLocalStorage))); |
| |
| // TODO(crbug.com/41457196): Sometimes SessionStorage fails to call its |
| // callback. Figure out why. |
| ClearSessionStorage( |
| base::WrapRefCounted(dom_storage_context), generic_filter, |
| perform_storage_cleanup, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| CreateTaskCompletionClosure(TracingDataType::kSessionStorage))); |
| } |
| |
| if ((remove_mask_ & REMOVE_DATA_MASK_SHADER_CACHE) && |
| // Old behavior: Always execute the code block below. |
| // New behavior: If kDisablePartialStorageCleanupForGPUDiskCache == true |
| // then consider the behavior of perform_storage_cleanup |
| // in executing the code. Note that the feature flag is only relevant |
| // when perform_storage_cleanup is false. |
| (!base::FeatureList::IsEnabled( |
| features::kDisablePartialStorageCleanupForGPUDiskCache) || |
| perform_storage_cleanup)) { |
| 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)); |
| } |
| } |
| } |
| |
| if (remove_mask_ & REMOVE_DATA_MASK_DEVICE_BOUND_SESSIONS && |
| device_bound_session_manager) { |
| device_bound_session_manager->DeleteAllSessions( |
| net::device_bound_sessions::DeletionReason::kStoragePartitionCleared, |
| begin, end, |
| filter_builder ? filter_builder->BuildNetworkServiceFilter() : nullptr, |
| mojo::WrapCallbackWithDefaultInvokeIfNotRun(CreateTaskCompletionClosure( |
| TracingDataType::kDeviceBoundSessions))); |
| } |
| |
| if (remove_mask_ & REMOVE_KEEPALIVE_LOADS_ATTEMPTING_RETRY && |
| keep_alive_url_loader_service) { |
| keep_alive_url_loader_service->ClearKeepAliveURLLoadersAttemptingRetry(); |
| } |
| } |
| |
| void StoragePartitionImpl::ClearDataForOrigin(uint32_t remove_mask, |
| const GURL& storage_origin, |
| base::OnceClosure callback) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| CookieDeletionFilterPtr deletion_filter = CookieDeletionFilter::New(); |
| if (!storage_origin.GetHost().empty()) { |
| deletion_filter->host_name = storage_origin.GetHost(); |
| } |
| // 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, 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| |
| 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, |
| const blink::StorageKey& storage_key, |
| const base::Time begin, |
| const base::Time end, |
| base::OnceClosure callback) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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, 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, |
| 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| ClearDataImpl(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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| // StoragePartitionCodeCacheDataRemover deletes itself when it is done. |
| StoragePartitionCodeCacheDataRemover::Create(this, url_matcher, begin, end) |
| ->Remove(std::move(callback)); |
| } |
| |
| void StoragePartitionImpl::Flush() { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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::ClearBluetoothAllowedDevicesMap() { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| CHECK(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_); |
| CHECK(network_context_owner_->network_context, base::NotFatalUntil::M159); |
| 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() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| CHECK(cert_verifier_service_updater_, base::NotFatalUntil::M159); |
| cert_verifier_service_updater_.FlushForTesting(); // IN-TEST |
| } |
| |
| void StoragePartitionImpl::WaitForDeletionTasksForTesting() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| if (deletion_helpers_running_) { |
| base::RunLoop loop; |
| on_deletion_helpers_done_callback_ = loop.QuitClosure(); |
| loop.Run(); |
| } |
| } |
| |
| 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::OverrideDeleteStaleSessionCleanupDelayForTesting( |
| const base::TimeDelta& delay) { |
| stale_session_cleanup_delay_ = delay; |
| } |
| |
| void StoragePartitionImpl:: |
| SetClearNetworkRestrictionsParamsForTesting( // IN-TEST |
| const base::TimeDelta& delay, |
| base::RepeatingClosure callback) { |
| clear_network_restrictions_delay_ = delay; |
| clear_network_restrictions_callback_for_testing_ = callback; |
| } |
| |
| base::WeakPtr<StoragePartitionImpl> StoragePartitionImpl::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| BrowserContext* StoragePartitionImpl::browser_context() const { |
| return browser_context_; |
| } |
| |
| std::optional<base::FilePath> StoragePartitionImpl::GetStoragePartitionPath() |
| const { |
| if (is_in_memory()) { |
| return std::nullopt; |
| } |
| return browser_context_->GetPath().Append(relative_partition_path_); |
| } |
| |
| // static |
| mojo::Remote<storage::mojom::StorageService>& |
| StoragePartitionImpl::GetStorageService() { |
| return GetStorageServiceRemote(); |
| } |
| |
| void StoragePartitionImpl::BindIndexedDB( |
| const storage::BucketLocator& bucket_locator, |
| const storage::BucketClientInfo& client_info, |
| mojo::PendingReceiver<blink::mojom::IDBFactory> receiver) { |
| indexed_db_control_wrapper_->BindIndexedDB(bucket_locator, client_info, |
| std::move(receiver)); |
| } |
| |
| void StoragePartitionImpl::BindLockManager( |
| const blink::StorageKey& storage_key, |
| const base::UnguessableToken& token, |
| mojo::PendingReceiver<blink::mojom::LockManager> receiver) { |
| CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M159); |
| |
| GetQuotaManagerProxy()->UpdateOrCreateBucket( |
| storage::BucketInitParams::ForDefaultBucket(storage_key), |
| GetUIThreadTaskRunner({}), |
| base::BindOnce(&StoragePartitionImpl::CreateLockManagerWithBucketInfo, |
| weak_factory_.GetWeakPtr(), std::move(receiver), token)); |
| } |
| |
| void StoragePartitionImpl::CreateLockManagerWithBucketInfo( |
| mojo::PendingReceiver<blink::mojom::LockManager> receiver, |
| const base::UnguessableToken& token, |
| storage::QuotaErrorOr<storage::BucketInfo> bucket) { |
| GetLockManager()->BindReceiver( |
| bucket.has_value() ? bucket->id : storage::BucketId(), token, |
| std::move(receiver)); |
| } |
| |
| mojo::ReceiverId StoragePartitionImpl::BindDomStorage( |
| int process_id, |
| mojo::PendingReceiver<blink::mojom::DomStorage> receiver, |
| mojo::PendingRemote<blink::mojom::DomStorageClient> client) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| dom_storage_receivers_.Remove(receiver_id); |
| dom_storage_clients_.erase(receiver_id); |
| } |
| |
| void StoragePartitionImpl::OverrideQuotaManagerForTesting( |
| storage::QuotaManager* quota_manager) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| quota_manager_ = quota_manager; |
| } |
| |
| void StoragePartitionImpl::OverrideSpecialStoragePolicyForTesting( |
| storage::SpecialStoragePolicy* special_storage_policy) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| special_storage_policy_ = special_storage_policy; |
| } |
| |
| void StoragePartitionImpl::ShutdownBackgroundSyncContextForTesting() { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| if (GetBackgroundSyncContext()) { |
| GetBackgroundSyncContext()->Shutdown(); |
| } |
| } |
| |
| void StoragePartitionImpl::OverrideBackgroundSyncContextForTesting( |
| BackgroundSyncContextImpl* background_sync_context) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| CHECK(!GetBackgroundSyncContext() || |
| !GetBackgroundSyncContext()->background_sync_manager(), |
| base::NotFatalUntil::M159); |
| background_sync_context_ = background_sync_context; |
| } |
| |
| void StoragePartitionImpl::OverrideSharedWorkerServiceForTesting( |
| std::unique_ptr<SharedWorkerServiceImpl> shared_worker_service) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| shared_worker_service_ = std::move(shared_worker_service); |
| } |
| |
| void StoragePartitionImpl::OverrideDeviceBoundSessionManagerForTesting( |
| std::unique_ptr<network::mojom::DeviceBoundSessionManager> |
| device_bound_session_manager) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| device_bound_session_manager_.reset(); |
| |
| if (device_bound_session_manager) { |
| 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( |
| GetCorsExemptRequestedWithHeaderName()); |
| context_params->cors_exempt_header_list.push_back("Last-Event-ID"); |
| variations::UpdateCorsExemptHeaderForVariations(context_params.get()); |
| cors_exempt_header_list_ = context_params->cors_exempt_header_list; |
| |
| #if BUILDFLAG(ENABLE_DISK_CACHE_SQL_BACKEND) |
| supports_renderer_accessible_http_cache_ = |
| !context_params->enable_encrypted_http_cache && |
| context_params->file_paths && |
| context_params->file_paths->http_cache_directory && |
| disk_cache::InSqlBackendExperimentGroup(); |
| #endif // ENABLE_DISK_CACHE_SQL_BACKEND |
| |
| if (base::FeatureList::IsEnabled( |
| network::features::kCompressionDictionaryTransport) && |
| 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.has_value()) { |
| CalculateAndSetSharedDictionaryCacheMaxSize( |
| GetWeakPtr(), is_in_memory() ? base::FilePath() : partition_path_); |
| } |
| } |
| |
| #if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS) && !BUILDFLAG(IS_ANDROID) |
| // TODO(https://crbug.com/353770817): Android does not support non-interactive |
| // certificate selection. Do not enable this for Android. |
| if (base::FeatureList::IsEnabled( |
| net::features::kDeviceBoundSessionsClientCertSelection)) { |
| context_params->device_bound_sessions_network_observer = |
| CreateURLLoaderNetworkObserverForDeviceBoundSessions(); |
| } |
| #endif |
| |
| network_context_owner_->network_context.reset(); |
| CreateNetworkContextInNetworkService( |
| network_context_owner_->network_context.BindNewPipeAndPassReceiver(), |
| std::move(context_params)); |
| CHECK(network_context_owner_->network_context, base::NotFatalUntil::M159); |
| |
| // Restore the saved network revocation nonces. This allows network access |
| // states to be persisted in case of a `NetworkService` crash. |
| std::vector<network::mojom::IdAndAllowlistedPatternsPtr> dest_vector; |
| for (const auto& [id, allowlists] : network_restrictions_ids_) { |
| dest_vector.push_back( |
| network::mojom::IdAndAllowlistedPatterns::New(id, allowlists)); |
| } |
| |
| network_context_owner_->network_context->RestrictNetworkForIds( |
| std::move(dest_vector), 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::OriginatingProcessId::browser(); |
| params->automatically_assign_isolation_info = true; |
| params->is_orb_enabled = false; |
| params->is_trusted = true; |
| // For browser-process initiated requests there is no corresponding service |
| // worker origin, so just pass an opaque origin. |
| params->url_loader_network_observer = |
| CreateURLLoaderNetworkObserverForServiceOrSharedWorker(params->process_id, |
| url::Origin()); |
| params->disable_web_security = |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableWebSecurity); |
| params->network_restrictions_id = network::GetNoOpNetworkRestrictionsId(); |
| 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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) { |
| CHECK(initialized_, base::NotFatalUntil::M159); |
| 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; |
| } |
| |
| const URLLoaderNetworkContext& context = |
| url_loader_network_observers_.current_context(); |
| if (const auto* worker_context = |
| std::get_if<URLLoaderNetworkContext::SharedOrServiceWorkerContext>( |
| &context.context())) { |
| if (worker_context->storage_key.has_value()) { |
| if (nonce) { |
| return blink::StorageKey::CreateWithNonce(origin, *nonce); |
| } |
| return worker_context->storage_key->WithOrigin(origin); |
| } |
| return std::nullopt; |
| } |
| |
| NavigationOrDocumentHandle* handle = 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 `kSharedOrServiceWorkerContext`. |
| 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(); |
| } |
| |
| void StoragePartitionImpl::IncrementActiveDocumentCount( |
| const net::NetworkIsolationKey& nik) { |
| if (auto it = active_document_per_nik_count_.find(nik); |
| it != active_document_per_nik_count_.end()) { |
| it->second++; |
| CHECK_GT(it->second, 1); |
| } else { |
| active_document_per_nik_count_.emplace(nik, 1); |
| if (keep_alive_url_loader_service_) { |
| keep_alive_url_loader_service_->DidObserveNewlyActiveDocumentWithNIK(nik); |
| } |
| } |
| } |
| |
| void StoragePartitionImpl::DecrementActiveDocumentCount( |
| const net::NetworkIsolationKey& nik) { |
| auto it = active_document_per_nik_count_.find(nik); |
| CHECK(it != active_document_per_nik_count_.end()); |
| it->second--; |
| if (it->second == 0) { |
| active_document_per_nik_count_.erase(it); |
| } |
| } |
| |
| int StoragePartitionImpl::GetActiveDocumentCount( |
| const net::NetworkIsolationKey& nik) { |
| auto it = active_document_per_nik_count_.find(nik); |
| if (it == active_document_per_nik_count_.end()) { |
| return 0; |
| } |
| return it->second; |
| } |
| |
| base::UnguessableToken StoragePartitionImpl::GetPartitionUUIDPerStorageKey( |
| const blink::StorageKey& storage_key) { |
| auto uuid = partition_uuid_per_storage_key_.find(storage_key); |
| return uuid == partition_uuid_per_storage_key_.end() |
| ? partition_uuid_per_storage_key_[storage_key] = |
| base::UnguessableToken::Create() |
| : uuid->second; |
| } |
| |
| void StoragePartitionImpl::OnScenarioMatchChanged( |
| performance_scenarios::ScenarioScope scope, |
| bool matches_pattern) { |
| // If the scenario matches `performance_scenarios::kDefaultIdleScenarios` and |
| // the network context is available, call `NotifyBrowserIdle()`. It's |
| // possible to miss an idle notification if the browser becomes idle before |
| // the network context is initialized. This is not a problem because the idle |
| // state is also checked when data is committed to the cache, so a checkpoint |
| // will eventually be triggered. |
| if (matches_pattern && network_context_owner_->network_context.get()) { |
| network_context_owner_->network_context->NotifyBrowserIdle(); |
| } |
| } |
| |
| bool StoragePartitionImpl::SupportsRendererAccessibleHttpCache() { |
| #if BUILDFLAG(ENABLE_DISK_CACHE_SQL_BACKEND) |
| if (!base::FeatureList::IsEnabled( |
| net::features::kRendererAccessibleHttpCache)) { |
| return false; |
| } |
| // Ensure NetworkContext (and thus `supports_renderer_accessible_http_cache_`) |
| // is initialized. |
| if (!supports_renderer_accessible_http_cache_) { |
| GetNetworkContext(); |
| } |
| CHECK(supports_renderer_accessible_http_cache_.has_value()); |
| return *supports_renderer_accessible_http_cache_; |
| #else |
| return false; |
| #endif // BUILDFLAG(ENABLE_DISK_CACHE_SQL_BACKEND) |
| } |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext( |
| GlobalRenderFrameHostId render_frame_host_id) |
| : context_(RenderFrameHostContext{}) { |
| if (auto* rfh = RenderFrameHostImpl::FromID(render_frame_host_id)) { |
| std::get<RenderFrameHostContext>(context_).navigation_or_document = |
| rfh->GetNavigationOrDocumentHandle(); |
| } |
| } |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext( |
| NavigationRequest& navigation_request) |
| : context_(NavigationRequestContext{ |
| navigation_request.navigation_or_document_handle()}) {} |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext( |
| const network::OriginatingProcessId& process_id, |
| const url::Origin& worker_origin, |
| const std::optional<blink::StorageKey>& storage_key) |
| : context_(SharedOrServiceWorkerContext{process_id, worker_origin, |
| storage_key}) {} |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext( |
| const URLLoaderNetworkContext& other) = default; |
| |
| StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext() |
| : context_(DeviceBoundSessionContext{}) {} |
| |
| 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); |
| } |
| |
| StoragePartitionImpl::URLLoaderNetworkContext |
| StoragePartitionImpl::URLLoaderNetworkContext::CreateForServiceOrSharedWorker( |
| const network::OriginatingProcessId& process_id, |
| const url::Origin& worker_origin, |
| const std::optional<blink::StorageKey>& storage_key) { |
| return StoragePartitionImpl::URLLoaderNetworkContext( |
| process_id, worker_origin, storage_key); |
| } |
| |
| StoragePartitionImpl::URLLoaderNetworkContext |
| StoragePartitionImpl::URLLoaderNetworkContext::CreateForDeviceBoundSessions() { |
| return StoragePartitionImpl::URLLoaderNetworkContext(); |
| } |
| |
| StoragePartitionImpl::ContextType |
| StoragePartitionImpl::URLLoaderNetworkContext::type() const { |
| if (std::holds_alternative<RenderFrameHostContext>(context_)) { |
| return ContextType::kRenderFrameHostContext; |
| } |
| if (std::holds_alternative<NavigationRequestContext>(context_)) { |
| return ContextType::kNavigationRequestContext; |
| } |
| if (std::holds_alternative<SharedOrServiceWorkerContext>(context_)) { |
| return ContextType::kSharedOrServiceWorkerContext; |
| } |
| CHECK(std::holds_alternative<DeviceBoundSessionContext>(context_)); |
| return ContextType::kDeviceBoundSessionContext; |
| } |
| |
| bool StoragePartitionImpl::URLLoaderNetworkContext::IsNavigationRequestContext() |
| const { |
| return std::holds_alternative<NavigationRequestContext>(context_); |
| } |
| |
| NavigationOrDocumentHandle* |
| StoragePartitionImpl::URLLoaderNetworkContext::navigation_or_document() const { |
| if (const auto* rfh_context = |
| std::get_if<RenderFrameHostContext>(&context_)) { |
| return rfh_context->navigation_or_document.get(); |
| } |
| if (const auto* nav_context = |
| std::get_if<NavigationRequestContext>(&context_)) { |
| return nav_context->navigation_or_document.get(); |
| } |
| return nullptr; |
| } |
| |
| // 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 |