// Copyright (c) 2012 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 "libcef/browser/browser_context.h" #include #include #include "libcef/browser/content_browser_client.h" #include "libcef/browser/context.h" #include "libcef/browser/download_manager_delegate.h" #include "libcef/browser/extensions/extension_system.h" #include "libcef/browser/prefs/browser_prefs.h" #include "libcef/browser/request_context_impl.h" #include "libcef/browser/ssl_host_state_delegate.h" #include "libcef/browser/thread_util.h" #include "libcef/common/cef_switches.h" #include "libcef/common/extensions/extensions_util.h" #include "base/command_line.h" #include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "chrome/browser/font_family_cache.h" #include "chrome/browser/plugins/chrome_plugin_service_filter.h" #include "chrome/browser/profiles/profile_key.h" #include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h" #include "chrome/common/pref_names.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/guest_view/browser/guest_view_manager.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/core/simple_dependency_manager.h" #include "components/keyed_service/core/simple_key_map.h" #include "components/prefs/pref_service.h" #include "components/proxy_config/pref_proxy_config_tracker_impl.h" #include "components/user_prefs/user_prefs.h" #include "components/visitedlink/browser/visitedlink_event_listener.h" #include "components/visitedlink/browser/visitedlink_master.h" #include "components/zoom/zoom_event_manager.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/storage_partition.h" #include "extensions/browser/extension_protocols.h" #include "extensions/browser/process_manager.h" #include "extensions/common/constants.h" #include "net/proxy_resolution/proxy_config_service.h" #include "net/proxy_resolution/proxy_resolution_service.h" #include "services/network/public/mojom/cors_origin_pattern.mojom.h" using content::BrowserThread; namespace { // Manages the global list of Impl instances. class ImplManager { public: typedef std::vector Vector; ImplManager() {} ~ImplManager() { DCHECK(all_.empty()); DCHECK(map_.empty()); } void AddImpl(CefBrowserContext* impl) { CEF_REQUIRE_UIT(); DCHECK(!IsValidImpl(impl)); all_.push_back(impl); } void RemoveImpl(CefBrowserContext* impl, const base::FilePath& path) { CEF_REQUIRE_UIT(); Vector::iterator it = GetImplPos(impl); DCHECK(it != all_.end()); all_.erase(it); if (!path.empty()) { PathMap::iterator it = map_.find(path); DCHECK(it != map_.end()); if (it != map_.end()) map_.erase(it); } } bool IsValidImpl(const CefBrowserContext* impl) { CEF_REQUIRE_UIT(); return GetImplPos(impl) != all_.end(); } CefBrowserContext* GetImplForIDs(int render_process_id, int render_frame_id, int frame_tree_node_id, bool require_frame_match) { CEF_REQUIRE_UIT(); for (const auto& context : all_) { if (context->IsAssociatedContext(render_process_id, render_frame_id, frame_tree_node_id, require_frame_match)) { return context; } } return nullptr; } CefBrowserContext* GetImplForContext(const content::BrowserContext* context) { CEF_REQUIRE_UIT(); if (!context) return nullptr; Vector::iterator it = all_.begin(); for (; it != all_.end(); ++it) { if (*it == context) return *it; } return nullptr; } void SetImplPath(CefBrowserContext* impl, const base::FilePath& path) { CEF_REQUIRE_UIT(); DCHECK(!path.empty()); DCHECK(IsValidImpl(impl)); DCHECK(GetImplForPath(path) == nullptr); map_.insert(std::make_pair(path, impl)); } CefBrowserContext* GetImplForPath(const base::FilePath& path) { CEF_REQUIRE_UIT(); DCHECK(!path.empty()); PathMap::const_iterator it = map_.find(path); if (it != map_.end()) return it->second; return nullptr; } const Vector GetAllImpl() const { return all_; } private: Vector::iterator GetImplPos(const CefBrowserContext* impl) { Vector::iterator it = all_.begin(); for (; it != all_.end(); ++it) { if (*it == impl) return it; } return all_.end(); } typedef std::map PathMap; PathMap map_; Vector all_; DISALLOW_COPY_AND_ASSIGN(ImplManager); }; #if DCHECK_IS_ON() // Because of DCHECK()s in the object destructor. base::LazyInstance::DestructorAtExit g_manager = LAZY_INSTANCE_INITIALIZER; #else base::LazyInstance::Leaky g_manager = LAZY_INSTANCE_INITIALIZER; #endif } // namespace // Creates and manages VisitedLinkEventListener objects for each // CefBrowserContext sharing the same VisitedLinkMaster. class CefVisitedLinkListener : public visitedlink::VisitedLinkMaster::Listener { public: CefVisitedLinkListener() { DCHECK(listener_map_.empty()); } void CreateListenerForContext(const CefBrowserContext* context) { CEF_REQUIRE_UIT(); auto listener = std::make_unique( const_cast(context)); listener_map_.insert(std::make_pair(context, std::move(listener))); } void RemoveListenerForContext(const CefBrowserContext* context) { CEF_REQUIRE_UIT(); ListenerMap::iterator it = listener_map_.find(context); DCHECK(it != listener_map_.end()); listener_map_.erase(it); } // visitedlink::VisitedLinkMaster::Listener methods. void NewTable(base::ReadOnlySharedMemoryRegion* table_region) override { CEF_REQUIRE_UIT(); ListenerMap::iterator it = listener_map_.begin(); for (; it != listener_map_.end(); ++it) it->second->NewTable(table_region); } void Add(visitedlink::VisitedLinkCommon::Fingerprint fingerprint) override { CEF_REQUIRE_UIT(); ListenerMap::iterator it = listener_map_.begin(); for (; it != listener_map_.end(); ++it) it->second->Add(fingerprint); } void Reset(bool invalidate_hashes) override { CEF_REQUIRE_UIT(); ListenerMap::iterator it = listener_map_.begin(); for (; it != listener_map_.end(); ++it) it->second->Reset(invalidate_hashes); } private: // Map of CefBrowserContext to the associated VisitedLinkEventListener. typedef std::map> ListenerMap; ListenerMap listener_map_; DISALLOW_COPY_AND_ASSIGN(CefVisitedLinkListener); }; CefBrowserContext::CefBrowserContext(const CefRequestContextSettings& settings) : settings_(settings) { g_manager.Get().AddImpl(this); } CefBrowserContext::~CefBrowserContext() { CEF_REQUIRE_UIT(); // No CefRequestContext should be referencing this object any longer. DCHECK(request_context_set_.empty()); // Unregister the context first to avoid re-entrancy during shutdown. g_manager.Get().RemoveImpl(this, cache_path_); // Send notifications to clean up objects associated with this Profile. MaybeSendDestroyedNotification(); ChromePluginServiceFilter::GetInstance()->UnregisterProfile(this); // Remove any BrowserContextKeyedServiceFactory associations. This must be // called before the ProxyService owned by CefBrowserContext is destroyed. // The SimpleDependencyManager should always be passed after the // BrowserContextDependencyManager. This is because the KeyedService instances // in the BrowserContextDependencyManager's dependency graph can depend on the // ones in the SimpleDependencyManager's graph. DependencyManager::PerformInterlockedTwoPhaseShutdown( BrowserContextDependencyManager::GetInstance(), this, SimpleDependencyManager::GetInstance(), key_.get()); key_.reset(); SimpleKeyMap::GetInstance()->Dissociate(this); // Shuts down the storage partitions associated with this browser context. // This must be called before the browser context is actually destroyed // and before a clean-up task for its corresponding IO thread residents // (e.g. ResourceContext) is posted, so that the classes that hung on // StoragePartition can have time to do necessary cleanups on IO thread. ShutdownStoragePartitions(); if (resource_context_.get()) { // Destruction of the ResourceContext will trigger destruction of all // associated network requests. content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE, resource_context_.release()); } visitedlink_listener_->RemoveListenerForContext(this); // The FontFamilyCache references the ProxyService so delete it before the // ProxyService is deleted. SetUserData(&kFontFamilyCacheKey, nullptr); pref_proxy_config_tracker_->DetachFromPrefService(); if (host_content_settings_map_) host_content_settings_map_->ShutdownOnUIThread(); // Delete the download manager delegate here because otherwise we'll crash // when it's accessed from the content::BrowserContext destructor. if (download_manager_delegate_) download_manager_delegate_.reset(nullptr); } void CefBrowserContext::Initialize() { CefContext* context = CefContext::Get(); cache_path_ = base::FilePath(CefString(&settings_.cache_path)); if (!context->ValidateCachePath(cache_path_)) { // Reset to in-memory storage. CefString(&settings_.cache_path).clear(); cache_path_ = base::FilePath(); } if (!cache_path_.empty()) g_manager.Get().SetImplPath(this, cache_path_); if (settings_.accept_language_list.length == 0) { // Use the global language list setting. CefString(&settings_.accept_language_list) = CefString(&context->settings().accept_language_list); } if (!!settings_.persist_session_cookies) { set_should_persist_session_cookies(true); } key_ = std::make_unique(GetPath()); SimpleKeyMap::GetInstance()->Associate(this, key_.get()); // Initialize the PrefService object. pref_service_ = browser_prefs::CreatePrefService( this, cache_path_, !!settings_.persist_user_preferences); content::BrowserContext::Initialize(this, GetPath()); resource_context_.reset(new CefResourceContext(IsOffTheRecord())); // This must be called before creating any services to avoid hitting // DependencyManager::AssertContextWasntDestroyed when creating/destroying // multiple browser contexts (due to pointer address reuse). BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices( this); const bool extensions_enabled = extensions::ExtensionsEnabled(); if (extensions_enabled) { // Create the custom ExtensionSystem first because other KeyedServices // depend on it. extension_system_ = static_cast( extensions::ExtensionSystem::Get(this)); extension_system_->InitForRegularProfile(true); // Make sure the ProcessManager is created so that it receives extension // load notifications. This is necessary for the proper initialization of // background/event pages. extensions::ProcessManager::Get(this); } // Initialize visited links management. base::FilePath visited_link_path; if (!cache_path_.empty()) visited_link_path = cache_path_.Append(FILE_PATH_LITERAL("Visited Links")); visitedlink_listener_ = new CefVisitedLinkListener; visitedlink_master_.reset(new visitedlink::VisitedLinkMaster( visitedlink_listener_, this, !visited_link_path.empty(), false, visited_link_path, 0)); visitedlink_listener_->CreateListenerForContext(this); visitedlink_master_->Init(); // Initialize proxy configuration tracker. pref_proxy_config_tracker_.reset(new PrefProxyConfigTrackerImpl( GetPrefs(), base::CreateSingleThreadTaskRunner({BrowserThread::IO}))); // Spell checking support and possibly other subsystems retrieve the // PrefService associated with a BrowserContext via UserPrefs::Get(). PrefService* pref_service = GetPrefs(); DCHECK(pref_service); user_prefs::UserPrefs::Set(this, pref_service); key_->SetPrefs(pref_service); if (extensions_enabled) extension_system_->Init(); ChromePluginServiceFilter::GetInstance()->RegisterProfile(this); } void CefBrowserContext::AddCefRequestContext(CefRequestContextImpl* context) { CEF_REQUIRE_UIT(); request_context_set_.insert(context); } void CefBrowserContext::RemoveCefRequestContext( CefRequestContextImpl* context) { CEF_REQUIRE_UIT(); if (extensions::ExtensionsEnabled()) { extension_system()->OnRequestContextDeleted(context); } request_context_set_.erase(context); // Delete ourselves when the reference count reaches zero. if (request_context_set_.empty()) delete this; } // static CefBrowserContext* CefBrowserContext::GetForCachePath( const base::FilePath& cache_path) { return g_manager.Get().GetImplForPath(cache_path); } // static CefBrowserContext* CefBrowserContext::GetForIDs(int render_process_id, int render_frame_id, int frame_tree_node_id, bool require_frame_match) { return g_manager.Get().GetImplForIDs(render_process_id, render_frame_id, frame_tree_node_id, require_frame_match); } // static CefBrowserContext* CefBrowserContext::GetForContext( content::BrowserContext* context) { return g_manager.Get().GetImplForContext(context); } // static std::vector CefBrowserContext::GetAll() { return g_manager.Get().GetAllImpl(); } content::ResourceContext* CefBrowserContext::GetResourceContext() { return resource_context_.get(); } content::ClientHintsControllerDelegate* CefBrowserContext::GetClientHintsControllerDelegate() { return nullptr; } void CefBrowserContext::SetCorsOriginAccessListForOrigin( const url::Origin& source_origin, std::vector allow_patterns, std::vector block_patterns, base::OnceClosure closure) { // This method is called for Extension support. base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(closure)); } ChromeZoomLevelPrefs* CefBrowserContext::GetZoomLevelPrefs() { return static_cast( GetStoragePartition(this, nullptr)->GetZoomLevelDelegate()); } scoped_refptr CefBrowserContext::GetURLLoaderFactory() { return GetDefaultStoragePartition(this) ->GetURLLoaderFactoryForBrowserProcess(); } base::FilePath CefBrowserContext::GetPath() { return cache_path_; } base::FilePath CefBrowserContext::GetPath() const { return cache_path_; } std::unique_ptr CefBrowserContext::CreateZoomLevelDelegate( const base::FilePath& partition_path) { if (cache_path_.empty()) return std::unique_ptr(); return base::WrapUnique(new ChromeZoomLevelPrefs( GetPrefs(), cache_path_, partition_path, zoom::ZoomEventManager::GetForBrowserContext(this)->GetWeakPtr())); } bool CefBrowserContext::IsOffTheRecord() const { // CEF contexts are never flagged as off-the-record. It causes problems // for the extension system. return false; } content::DownloadManagerDelegate* CefBrowserContext::GetDownloadManagerDelegate() { if (!download_manager_delegate_) { content::DownloadManager* manager = BrowserContext::GetDownloadManager(this); download_manager_delegate_.reset(new CefDownloadManagerDelegate(manager)); } return download_manager_delegate_.get(); } content::BrowserPluginGuestManager* CefBrowserContext::GetGuestManager() { DCHECK(extensions::ExtensionsEnabled()); return guest_view::GuestViewManager::FromBrowserContext(this); } storage::SpecialStoragePolicy* CefBrowserContext::GetSpecialStoragePolicy() { return nullptr; } content::PushMessagingService* CefBrowserContext::GetPushMessagingService() { return nullptr; } content::StorageNotificationService* CefBrowserContext::GetStorageNotificationService() { return nullptr; } content::SSLHostStateDelegate* CefBrowserContext::GetSSLHostStateDelegate() { if (!ssl_host_state_delegate_.get()) ssl_host_state_delegate_.reset(new CefSSLHostStateDelegate()); return ssl_host_state_delegate_.get(); } content::PermissionControllerDelegate* CefBrowserContext::GetPermissionControllerDelegate() { return nullptr; } content::BackgroundFetchDelegate* CefBrowserContext::GetBackgroundFetchDelegate() { return nullptr; } content::BackgroundSyncController* CefBrowserContext::GetBackgroundSyncController() { return nullptr; } content::BrowsingDataRemoverDelegate* CefBrowserContext::GetBrowsingDataRemoverDelegate() { return nullptr; } PrefService* CefBrowserContext::GetPrefs() { return pref_service_.get(); } const PrefService* CefBrowserContext::GetPrefs() const { return pref_service_.get(); } ProfileKey* CefBrowserContext::GetProfileKey() const { DCHECK(key_); return key_.get(); } policy::SchemaRegistryService* CefBrowserContext::GetPolicySchemaRegistryService() { NOTREACHED(); return nullptr; } policy::UserCloudPolicyManager* CefBrowserContext::GetUserCloudPolicyManager() { NOTREACHED(); return nullptr; } policy::ProfilePolicyConnector* CefBrowserContext::GetProfilePolicyConnector() { NOTREACHED(); return nullptr; } const policy::ProfilePolicyConnector* CefBrowserContext::GetProfilePolicyConnector() const { NOTREACHED(); return nullptr; } const CefRequestContextSettings& CefBrowserContext::GetSettings() const { return settings_; } HostContentSettingsMap* CefBrowserContext::GetHostContentSettingsMap() { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!host_content_settings_map_.get()) { // The |is_incognito_profile| and |is_guest_profile| arguments are // intentionally set to false as they otherwise limit the types of values // that can be stored in the settings map (for example, default values set // via DefaultProvider::SetWebsiteSetting). host_content_settings_map_ = new HostContentSettingsMap(GetPrefs(), false, false, false); // Change the default plugin policy. const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); const std::string& plugin_policy_str = command_line->GetSwitchValueASCII(switches::kPluginPolicy); if (!plugin_policy_str.empty()) { ContentSetting plugin_policy = CONTENT_SETTING_ALLOW; if (base::LowerCaseEqualsASCII(plugin_policy_str, switches::kPluginPolicy_Detect)) { plugin_policy = CONTENT_SETTING_DETECT_IMPORTANT_CONTENT; } else if (base::LowerCaseEqualsASCII(plugin_policy_str, switches::kPluginPolicy_Block)) { plugin_policy = CONTENT_SETTING_BLOCK; } host_content_settings_map_->SetDefaultContentSetting( CONTENT_SETTINGS_TYPE_PLUGINS, plugin_policy); } } return host_content_settings_map_.get(); } void CefBrowserContext::AddVisitedURLs(const std::vector& urls) { visitedlink_master_->AddURLs(urls); } void CefBrowserContext::RebuildTable( const scoped_refptr& enumerator) { // Called when visited links will not or cannot be loaded from disk. enumerator->OnComplete(true); } void CefBrowserContext::OnRenderFrameCreated( CefRequestContextImpl* request_context, int render_process_id, int render_frame_id, int frame_tree_node_id, bool is_main_frame, bool is_guest_view) { CEF_REQUIRE_UIT(); DCHECK_GE(render_process_id, 0); DCHECK_GE(render_frame_id, 0); DCHECK_GE(frame_tree_node_id, 0); render_id_set_.insert(std::make_pair(render_process_id, render_frame_id)); node_id_set_.insert(frame_tree_node_id); CefRefPtr handler = request_context->GetHandler(); if (handler) { handler_map_.AddHandler(render_process_id, render_frame_id, frame_tree_node_id, handler); if (resource_context_) { // Using base::Unretained() is safe because both this callback and // possible deletion of |resource_context_| will execute on the IO thread, // and this callback will be executed first. CEF_POST_TASK(CEF_IOT, base::Bind(&CefResourceContext::AddHandler, base::Unretained(resource_context_.get()), render_process_id, render_frame_id, frame_tree_node_id, handler)); } } } void CefBrowserContext::OnRenderFrameDeleted( CefRequestContextImpl* request_context, int render_process_id, int render_frame_id, int frame_tree_node_id, bool is_main_frame, bool is_guest_view) { CEF_REQUIRE_UIT(); DCHECK_GE(render_process_id, 0); DCHECK_GE(render_frame_id, 0); DCHECK_GE(frame_tree_node_id, 0); auto it1 = render_id_set_.find(std::make_pair(render_process_id, render_frame_id)); if (it1 != render_id_set_.end()) render_id_set_.erase(it1); auto it2 = node_id_set_.find(frame_tree_node_id); if (it2 != node_id_set_.end()) node_id_set_.erase(it2); CefRefPtr handler = request_context->GetHandler(); if (handler) { handler_map_.RemoveHandler(render_process_id, render_frame_id, frame_tree_node_id); if (resource_context_) { // Using base::Unretained() is safe because both this callback and // possible deletion of |resource_context_| will execute on the IO thread, // and this callback will be executed first. CEF_POST_TASK( CEF_IOT, base::Bind(&CefResourceContext::RemoveHandler, base::Unretained(resource_context_.get()), render_process_id, render_frame_id, frame_tree_node_id)); } } if (is_main_frame) { ClearPluginLoadDecision(render_process_id); } } CefRefPtr CefBrowserContext::GetHandler( int render_process_id, int render_frame_id, int frame_tree_node_id, bool require_frame_match) const { CEF_REQUIRE_UIT(); return handler_map_.GetHandler(render_process_id, render_frame_id, frame_tree_node_id, require_frame_match); } bool CefBrowserContext::IsAssociatedContext(int render_process_id, int render_frame_id, int frame_tree_node_id, bool require_frame_match) const { CEF_REQUIRE_UIT(); if (render_process_id >= 0 && render_frame_id >= 0) { const auto it1 = render_id_set_.find(std::make_pair(render_process_id, render_frame_id)); if (it1 != render_id_set_.end()) return true; } if (frame_tree_node_id >= 0) { const auto it2 = node_id_set_.find(frame_tree_node_id); if (it2 != node_id_set_.end()) return true; } if (render_process_id >= 0 && !require_frame_match) { // Choose an arbitrary handler for the same process. for (const auto& render_ids : render_id_set_) { if (render_ids.first == render_process_id) return true; } } return false; } void CefBrowserContext::AddPluginLoadDecision( int render_process_id, const base::FilePath& plugin_path, bool is_main_frame, const url::Origin& main_frame_origin, chrome::mojom::PluginStatus status) { CEF_REQUIRE_UIT(); DCHECK_GE(render_process_id, 0); DCHECK(!plugin_path.empty()); plugin_load_decision_map_.insert(std::make_pair( std::make_pair(std::make_pair(render_process_id, plugin_path), std::make_pair(is_main_frame, main_frame_origin)), status)); } bool CefBrowserContext::HasPluginLoadDecision( int render_process_id, const base::FilePath& plugin_path, bool is_main_frame, const url::Origin& main_frame_origin, chrome::mojom::PluginStatus* status) const { CEF_REQUIRE_UIT(); DCHECK_GE(render_process_id, 0); DCHECK(!plugin_path.empty()); PluginLoadDecisionMap::const_iterator it = plugin_load_decision_map_.find( std::make_pair(std::make_pair(render_process_id, plugin_path), std::make_pair(is_main_frame, main_frame_origin))); if (it == plugin_load_decision_map_.end()) return false; *status = it->second; return true; } void CefBrowserContext::ClearPluginLoadDecision(int render_process_id) { CEF_REQUIRE_UIT(); if (render_process_id == -1) { plugin_load_decision_map_.clear(); } else { PluginLoadDecisionMap::iterator it = plugin_load_decision_map_.begin(); while (it != plugin_load_decision_map_.end()) { if (it->first.first.first == render_process_id) it = plugin_load_decision_map_.erase(it); else ++it; } } } void CefBrowserContext::RegisterSchemeHandlerFactory( const std::string& scheme_name, const std::string& domain_name, CefRefPtr factory) { if (resource_context_) { // Using base::Unretained() is safe because both this callback and possible // deletion of |resource_context_| will execute on the IO thread, and this // callback will be executed first. CEF_POST_TASK(CEF_IOT, base::Bind(&CefResourceContext::RegisterSchemeHandlerFactory, base::Unretained(resource_context_.get()), scheme_name, domain_name, factory)); } } void CefBrowserContext::ClearSchemeHandlerFactories() { if (resource_context_) { // Using base::Unretained() is safe because both this callback and possible // deletion of |resource_context_| will execute on the IO thread, and this // callback will be executed first. CEF_POST_TASK(CEF_IOT, base::Bind(&CefResourceContext::ClearSchemeHandlerFactories, base::Unretained(resource_context_.get()))); } } network::mojom::NetworkContext* CefBrowserContext::GetNetworkContext() { CEF_REQUIRE_UIT(); return GetDefaultStoragePartition(this)->GetNetworkContext(); } DownloadPrefs* CefBrowserContext::GetDownloadPrefs() { CEF_REQUIRE_UIT(); if (!download_prefs_) { download_prefs_.reset(new DownloadPrefs(this)); } return download_prefs_.get(); } bool CefBrowserContext::IsPrintPreviewSupported() const { CEF_REQUIRE_UIT(); if (!extensions::PrintPreviewEnabled()) return false; return !GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled); }