| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/browser_context_impl.h" |
| |
| #include <utility> |
| |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/trace_event/trace_event.h" |
| #include "build/build_config.h" |
| #include "content/browser/background_sync/background_sync_scheduler.h" |
| #include "content/browser/browsing_data/browsing_data_remover_impl.h" |
| #include "content/browser/download/download_manager_impl.h" |
| #include "content/browser/permissions/permission_controller_impl.h" |
| #include "content/browser/speculation_rules/prefetch/prefetch_service.h" |
| #include "content/browser/speech/tts_controller_impl.h" |
| #include "content/browser/storage_partition_impl_map.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/shared_worker_service.h" |
| #include "media/capabilities/webrtc_video_stats_db_impl.h" |
| #include "media/learning/common/media_learning_tasks.h" |
| #include "media/learning/impl/learning_session_impl.h" |
| #include "media/mojo/services/video_decode_perf_history.h" |
| #include "media/mojo/services/webrtc_video_perf_history.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "storage/browser/file_system/external_mount_points.h" |
| #endif |
| |
| namespace content { |
| |
| namespace { |
| |
| void ShutdownServiceWorkerContext(StoragePartition* partition) { |
| ServiceWorkerContextWrapper* wrapper = |
| static_cast<ServiceWorkerContextWrapper*>( |
| partition->GetServiceWorkerContext()); |
| wrapper->process_manager()->Shutdown(); |
| } |
| |
| void ShutdownSharedWorkerContext(StoragePartition* partition) { |
| partition->GetSharedWorkerService()->Shutdown(); |
| } |
| |
| void RegisterMediaLearningTask( |
| media::learning::LearningSessionImpl* learning_session, |
| const media::learning::LearningTask& task) { |
| // The RegisterTask method cannot be directly used in base::Bind, because it |
| // provides a default argument value for the 2nd parameter |
| // (`feature_provider`). |
| learning_session->RegisterTask(task); |
| } |
| |
| } // namespace |
| |
| // static |
| BrowserContextImpl* BrowserContextImpl::From(BrowserContext* self) { |
| return self->impl(); |
| } |
| |
| BrowserContextImpl::BrowserContextImpl(BrowserContext* self) : self_(self) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| background_sync_scheduler_ = base::MakeRefCounted<BackgroundSyncScheduler>(); |
| } |
| |
| BrowserContextImpl::~BrowserContextImpl() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(!storage_partition_map_) |
| << "StoragePartitionMap is not shut down properly"; |
| |
| if (!will_be_destroyed_soon_) { |
| NOTREACHED(); |
| base::debug::DumpWithoutCrashing(); |
| } |
| |
| // Verify that there are no outstanding RenderProcessHosts that reference |
| // this context. Trigger a crash report if there are still references so |
| // we can detect/diagnose potential UAFs. |
| std::string rph_crash_key_value; |
| ChildProcessSecurityPolicyImpl* policy = |
| ChildProcessSecurityPolicyImpl::GetInstance(); |
| for (RenderProcessHost::iterator host_iterator = |
| RenderProcessHost::AllHostsIterator(); |
| !host_iterator.IsAtEnd(); host_iterator.Advance()) { |
| RenderProcessHost* host = host_iterator.GetCurrentValue(); |
| if (host->GetBrowserContext() == self_) { |
| rph_crash_key_value += |
| "{ " + host->GetInfoForBrowserContextDestructionCrashReporting() + |
| " }"; |
| } |
| } |
| if (!rph_crash_key_value.empty()) { |
| NOTREACHED() << "rph_with_bc_reference : " << rph_crash_key_value; |
| |
| SCOPED_CRASH_KEY_STRING256("BrowserContext", "dangling_rph", |
| rph_crash_key_value); |
| base::debug::DumpWithoutCrashing(); |
| } |
| |
| // Clean up any isolated origins and other security state associated with this |
| // BrowserContext. |
| policy->RemoveStateForBrowserContext(*self_); |
| |
| if (download_manager_) |
| download_manager_->Shutdown(); |
| |
| TtsControllerImpl::GetInstance()->OnBrowserContextDestroyed(self_); |
| |
| TRACE_EVENT_NESTABLE_ASYNC_END1( |
| "shutdown", "BrowserContextImpl::NotifyWillBeDestroyed() called.", this, |
| "browser_context_impl", static_cast<void*>(this)); |
| } |
| |
| bool BrowserContextImpl::ShutdownStarted() { |
| return will_be_destroyed_soon_; |
| } |
| |
| void BrowserContextImpl::NotifyWillBeDestroyed() { |
| TRACE_EVENT1("shutdown", "BrowserContextImpl::NotifyWillBeDestroyed", |
| "browser_context_impl", static_cast<void*>(this)); |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( |
| "shutdown", "BrowserContextImpl::NotifyWillBeDestroyed() called.", this, |
| "browser_context_impl", static_cast<void*>(this)); |
| // Make sure NotifyWillBeDestroyed is idempotent. This helps facilitate the |
| // pattern where NotifyWillBeDestroyed is called from *both* |
| // ShellBrowserContext and its derived classes (e.g. WebTestBrowserContext). |
| if (will_be_destroyed_soon_) |
| return; |
| will_be_destroyed_soon_ = true; |
| |
| // 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. |
| self_->ForEachStoragePartition( |
| base::BindRepeating(ShutdownServiceWorkerContext)); |
| self_->ForEachStoragePartition( |
| base::BindRepeating(ShutdownSharedWorkerContext)); |
| |
| // Also forcibly release keep alive refcounts on RenderProcessHosts, to ensure |
| // they destruct before the BrowserContext does. |
| for (RenderProcessHost::iterator host_iterator = |
| RenderProcessHost::AllHostsIterator(); |
| !host_iterator.IsAtEnd(); host_iterator.Advance()) { |
| RenderProcessHost* host = host_iterator.GetCurrentValue(); |
| if (host->GetBrowserContext() == self_) { |
| // This will also clean up spare RPH references. |
| host->DisableRefCounts(); |
| } |
| } |
| } |
| |
| StoragePartitionImplMap* BrowserContextImpl::GetOrCreateStoragePartitionMap() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!storage_partition_map_) |
| storage_partition_map_ = std::make_unique<StoragePartitionImplMap>(self_); |
| |
| return storage_partition_map_.get(); |
| } |
| |
| BrowsingDataRemover* BrowserContextImpl::GetBrowsingDataRemover() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!browsing_data_remover_) { |
| browsing_data_remover_ = std::make_unique<BrowsingDataRemoverImpl>(self_); |
| browsing_data_remover_->SetEmbedderDelegate( |
| self_->GetBrowsingDataRemoverDelegate()); |
| } |
| |
| return browsing_data_remover_.get(); |
| } |
| |
| media::learning::LearningSession* BrowserContextImpl::GetLearningSession() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!learning_session_) { |
| learning_session_ = std::make_unique<media::learning::LearningSessionImpl>( |
| base::SequencedTaskRunnerHandle::Get()); |
| |
| // Using base::Unretained is safe below, because the callback here will not |
| // be called or retained after the Register method below returns. |
| media::learning::MediaLearningTasks::Register(base::BindRepeating( |
| &RegisterMediaLearningTask, base::Unretained(learning_session_.get()))); |
| } |
| |
| return learning_session_.get(); |
| } |
| |
| media::VideoDecodePerfHistory* BrowserContextImpl::GetVideoDecodePerfHistory() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!video_decode_perf_history_) |
| video_decode_perf_history_ = self_->CreateVideoDecodePerfHistory(); |
| |
| return video_decode_perf_history_.get(); |
| } |
| |
| std::unique_ptr<media::WebrtcVideoPerfHistory> |
| BrowserContextImpl::CreateWebrtcVideoPerfHistory() { |
| // TODO(https://crbug.com/1187565): Implement in memory path in |
| // off_the_record_profile_impl.cc and web_engine_browser_context.cc |
| |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto* db_provider = |
| self_->GetDefaultStoragePartition()->GetProtoDatabaseProvider(); |
| |
| std::unique_ptr<media::WebrtcVideoStatsDB> stats_db = |
| media::WebrtcVideoStatsDBImpl::Create( |
| self_->GetPath().Append(FILE_PATH_LITERAL("WebrtcVideoStats")), |
| db_provider); |
| |
| return std::make_unique<media::WebrtcVideoPerfHistory>(std::move(stats_db)); |
| } |
| |
| media::WebrtcVideoPerfHistory* BrowserContextImpl::GetWebrtcVideoPerfHistory() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!webrtc_video_perf_history_) |
| webrtc_video_perf_history_ = CreateWebrtcVideoPerfHistory(); |
| |
| return webrtc_video_perf_history_.get(); |
| } |
| |
| void BrowserContextImpl::ShutdownStoragePartitions() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // The BackgroundSyncScheduler keeps raw pointers to partitions; clear it |
| // first. |
| DCHECK(background_sync_scheduler_->HasOneRef()); |
| background_sync_scheduler_.reset(); |
| |
| storage_partition_map_.reset(); |
| } |
| |
| DownloadManager* BrowserContextImpl::GetDownloadManager() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // Lazily populate `download_manager_`. This is important to |
| // 1) Avoid constructing DownloadManagerImpl when a test might have provided |
| // an alternative object via SetDownloadManagerForTesting. |
| // 2) Avoiding calling into DownloadManagerImpl's constructor with a partially |
| // constructed BrowserContext. |
| if (!download_manager_) { |
| download_manager_ = std::make_unique<DownloadManagerImpl>(self_); |
| |
| // Note that GetDownloadManagerDelegate might call into GetDownloadManager, |
| // leading to re-entrancy concerns. We avoid re-entrancy by making sure |
| // `download_manager_` is set earlier, above. |
| download_manager_->SetDelegate(self_->GetDownloadManagerDelegate()); |
| } |
| |
| return download_manager_.get(); |
| } |
| |
| void BrowserContextImpl::SetDownloadManagerForTesting( |
| std::unique_ptr<DownloadManager> download_manager) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (download_manager_) |
| download_manager_->Shutdown(); |
| download_manager_ = std::move(download_manager); |
| } |
| |
| PermissionController* BrowserContextImpl::GetPermissionController() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!permission_controller_) |
| permission_controller_ = std::make_unique<PermissionControllerImpl>(self_); |
| |
| return permission_controller_.get(); |
| } |
| |
| void BrowserContextImpl::SetPermissionControllerForTesting( |
| std::unique_ptr<PermissionController> permission_controller) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| permission_controller_ = std::move(permission_controller); |
| } |
| |
| storage::ExternalMountPoints* BrowserContextImpl::GetMountPoints() { |
| // Ensure that these methods are called on the UI thread, except for |
| // unittests where a UI thread might not have been created. |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| !BrowserThread::IsThreadInitialized(BrowserThread::UI)); |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| if (!external_mount_points_) |
| external_mount_points_ = storage::ExternalMountPoints::CreateRefCounted(); |
| return external_mount_points_.get(); |
| #else |
| return nullptr; |
| #endif |
| } |
| |
| PrefetchService* BrowserContextImpl::GetPrefetchService() { |
| if (!prefetch_service_) |
| prefetch_service_ = PrefetchService::CreateIfPossible(self_); |
| |
| return prefetch_service_.get(); |
| } |
| |
| void BrowserContextImpl::WriteIntoTrace( |
| perfetto::TracedProto<TraceProto> proto) const { |
| proto->set_id(UniqueId()); |
| } |
| |
| } // namespace content |