diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 15dcd39c5..aa7125d33 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -170,23 +170,25 @@ typedef struct _cef_settings_t { // will be used. If this value is empty on macOS then a helper executable must // exist at "Contents/Frameworks/ Helper.app/Contents/MacOS/ Helper" // in the top-level app bundle. See the comments on CefExecuteProcess() for - // details. Also configurable using the "browser-subprocess-path" command-line - // switch. + // details. If this value is non-empty then it must be an absolute path. Also + // configurable using the "browser-subprocess-path" command-line switch. /// cef_string_t browser_subprocess_path; /// // The path to the CEF framework directory on macOS. If this value is empty // then the framework must exist at "Contents/Frameworks/Chromium Embedded - // Framework.framework" in the top-level app bundle. Also configurable using - // the "framework-dir-path" command-line switch. + // Framework.framework" in the top-level app bundle. If this value is + // non-empty then it must be an absolute path. Also configurable using the + // "framework-dir-path" command-line switch. /// cef_string_t framework_dir_path; /// // The path to the main bundle on macOS. If this value is empty then it - // defaults to the top-level app bundle. Also configurable using - // the "main-bundle-path" command-line switch. + // defaults to the top-level app bundle. If this value is non-empty then it + // must be an absolute path. Also configurable using the "main-bundle-path" + // command-line switch. /// cef_string_t main_bundle_path; @@ -227,33 +229,35 @@ typedef struct _cef_settings_t { /// // The location where data for the global browser cache will be stored on - // disk. If non-empty this must be either equal to or a child directory of - // CefSettings.root_cache_path. If empty then browsers will be created in - // "incognito mode" where in-memory caches are used for storage and no data is - // persisted to disk. HTML5 databases such as localStorage will only persist - // across sessions if a cache path is specified. Can be overridden for - // individual CefRequestContext instances via the - // CefRequestContextSettings.cache_path value. + // disk. If this value is non-empty then it must be an absolute path that is + // either equal to or a child directory of CefSettings.root_cache_path. If + // this value is empty then browsers will be created in "incognito mode" where + // in-memory caches are used for storage and no data is persisted to disk. + // HTML5 databases such as localStorage will only persist across sessions if a + // cache path is specified. Can be overridden for individual CefRequestContext + // instances via the CefRequestContextSettings.cache_path value. /// cef_string_t cache_path; /// // The root directory that all CefSettings.cache_path and // CefRequestContextSettings.cache_path values must have in common. If this - // value is empty and CefSettings.cache_path is non-empty then this value will - // default to the CefSettings.cache_path value. Failure to set this value - // correctly may result in the sandbox blocking read/write access to the - // cache_path directory. + // value is empty and CefSettings.cache_path is non-empty then it will + // default to the CefSettings.cache_path value. If this value is non-empty + // then it must be an absolute path. Failure to set this value correctly may + // result in the sandbox blocking read/write access to the cache_path + // directory. /// cef_string_t root_cache_path; /// // The location where user data such as spell checking dictionary files will - // be stored on disk. If empty then the default platform-specific user data - // directory will be used ("~/.cef_user_data" directory on Linux, - // "~/Library/Application Support/CEF/User Data" directory on Mac OS X, - // "Local Settings\Application Data\CEF\User Data" directory under the user - // profile directory on Windows). + // be stored on disk. If this value is empty then the default + // platform-specific user data directory will be used ("~/.cef_user_data" + // directory on Linux, "~/Library/Application Support/CEF/User Data" directory + // on Mac OS X, "Local Settings\Application Data\CEF\User Data" directory + // under the user profile directory on Windows). If this value is non-empty + // then it must be an absolute path. /// cef_string_t user_data_path; @@ -333,16 +337,17 @@ typedef struct _cef_settings_t { // The fully qualified path for the resources directory. If this value is // empty the cef.pak and/or devtools_resources.pak files must be located in // the module directory on Windows/Linux or the app bundle Resources directory - // on Mac OS X. Also configurable using the "resources-dir-path" command-line - // switch. + // on Mac OS X. If this value is non-empty then it must be an absolute path. + // Also configurable using the "resources-dir-path" command-line switch. /// cef_string_t resources_dir_path; /// // The fully qualified path for the locales directory. If this value is empty - // the locales directory must be located in the module directory. This value - // is ignored on Mac OS X where pack files are always loaded from the app - // bundle Resources directory. Also configurable using the "locales-dir-path" + // the locales directory must be located in the module directory. If this + // value is non-empty then it must be an absolute path. This value is ignored + // on Mac OS X where pack files are always loaded from the app bundle + // Resources directory. Also configurable using the "locales-dir-path" // command-line switch. /// cef_string_t locales_dir_path; @@ -428,13 +433,13 @@ typedef struct _cef_request_context_settings_t { /// // The location where cache data for this request context will be stored on - // disk. If non-empty this must be either equal to or a child directory of - // CefSettings.root_cache_path. If empty then browsers will be created in - // "incognito mode" where in-memory caches are used for storage and no data is - // persisted to disk. HTML5 databases such as localStorage will only persist - // across sessions if a cache path is specified. To share the global browser - // cache and related configuration set this value to match the - // CefSettings.cache_path value. + // disk. If this value is non-empty then it must be an absolute path that is + // either equal to or a child directory of CefSettings.root_cache_path. If + // this value is empty then browsers will be created in "incognito mode" where + // in-memory caches are used for storage and no data is persisted to disk. + // HTML5 databases such as localStorage will only persist across sessions if a + // cache path is specified. To share the global browser cache and related + // configuration set this value to match the CefSettings.cache_path value. /// cef_string_t cache_path; diff --git a/libcef/browser/browser_context.cc b/libcef/browser/browser_context.cc index 96e611c18..2a81bca26 100644 --- a/libcef/browser/browser_context.cc +++ b/libcef/browser/browser_context.cc @@ -8,7 +8,6 @@ #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/media_router/media_router_manager.h" @@ -292,36 +291,23 @@ CefBrowserContext::~CefBrowserContext() { } 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()); + key_ = std::make_unique(cache_path_); 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()); + content::BrowserContext::Initialize(this, cache_path_); resource_context_.reset(new CefResourceContext(IsOffTheRecord())); diff --git a/libcef/browser/browser_context.h b/libcef/browser/browser_context.h index ddd8545fa..232de1149 100644 --- a/libcef/browser/browser_context.h +++ b/libcef/browser/browser_context.h @@ -296,7 +296,7 @@ class CefBrowserContext : public ChromeProfileStub, ~CefBrowserContext() override; // Members initialized during construction are safe to access from any thread. - CefRequestContextSettings settings_; + const CefRequestContextSettings settings_; base::FilePath cache_path_; // CefRequestContextImpl objects referencing this object. diff --git a/libcef/browser/browser_main.cc b/libcef/browser/browser_main.cc index 9aa105ca1..b7567c3a1 100644 --- a/libcef/browser/browser_main.cc +++ b/libcef/browser/browser_main.cc @@ -191,7 +191,7 @@ void CefBrowserMainParts::PreMainMessageLoopRun() { base::TaskShutdownBehavior::BLOCK_SHUTDOWN, base::MayBlock()}); CefRequestContextSettings settings; - CefContext::Get()->PopulateRequestContextSettings(&settings); + CefContext::Get()->PopulateGlobalRequestContextSettings(&settings); // Create the global RequestContext. global_request_context_ = diff --git a/libcef/browser/context.cc b/libcef/browser/context.cc index 4e88731b6..5fd2138b4 100644 --- a/libcef/browser/context.cc +++ b/libcef/browser/context.cc @@ -160,6 +160,85 @@ bool GetColor(const cef_color_t cef_in, bool is_windowless, SkColor* sk_out) { return true; } +// Convert |path_str| to a normalized FilePath. +base::FilePath NormalizePath(const cef_string_t& path_str, + const char* name, + bool* has_error = nullptr) { + if (has_error) + *has_error = false; + + base::FilePath path = base::FilePath(CefString(&path_str)); + if (path.EndsWithSeparator()) { + // Remove the trailing separator because it will interfere with future + // equality checks. + path = path.StripTrailingSeparators(); + } + + if (!path.empty() && !path.IsAbsolute()) { + LOG(ERROR) << "The " << name << " directory (" << path.value() + << ") is not an absolute path. Defaulting to empty."; + if (has_error) + *has_error = true; + path = base::FilePath(); + } + + return path; +} + +void SetPath(cef_string_t& path_str, const base::FilePath& path) { +#if defined(OS_WIN) + CefString(&path_str).FromWString(path.value()); +#else + CefString(&path_str).FromString(path.value()); +#endif +} + +// Convert |path_str| to a normalized FilePath and update the |path_str| value. +base::FilePath NormalizePathAndSet(cef_string_t& path_str, const char* name) { + const base::FilePath& path = NormalizePath(path_str, name); + SetPath(path_str, path); + return path; +} + +// Verify that |cache_path| is valid and create it if necessary. +bool ValidateCachePath(const base::FilePath& cache_path, + const base::FilePath& root_cache_path) { + if (cache_path.empty()) + return true; + + if (!root_cache_path.empty() && root_cache_path != cache_path && + !root_cache_path.IsParent(cache_path)) { + LOG(ERROR) << "The cache_path directory (" << cache_path.value() + << ") is not a child of the root_cache_path directory (" + << root_cache_path.value() << ")"; + return false; + } + + base::ThreadRestrictions::ScopedAllowIO allow_io; + if (!base::DirectoryExists(cache_path) && + !base::CreateDirectory(cache_path)) { + LOG(ERROR) << "The cache_path directory (" << cache_path.value() + << ") could not be created."; + return false; + } + + return true; +} + +// Like NormalizePathAndSet but with additional checks specific to the +// cache_path value. +base::FilePath NormalizeCachePathAndSet(cef_string_t& path_str, + const base::FilePath& root_cache_path) { + bool has_error = false; + base::FilePath path = NormalizePath(path_str, "cache_path", &has_error); + if (has_error || !ValidateCachePath(path, root_cache_path)) { + LOG(ERROR) << "The cache_path is invalid. Defaulting to in-memory storage."; + path = base::FilePath(); + } + SetPath(path_str, path); + return path; +} + } // namespace int CefExecuteProcess(const CefMainArgs& args, @@ -370,18 +449,23 @@ bool CefContext::Initialize(const CefMainArgs& args, SignalChromeElf(); #endif - base::FilePath cache_path = base::FilePath(CefString(&settings_.cache_path)); - if (!ValidateCachePath(cache_path)) { - // Reset to in-memory storage. - CefString(&settings_.cache_path).clear(); - cache_path = base::FilePath(); - } const base::FilePath& root_cache_path = - base::FilePath(CefString(&settings_.root_cache_path)); + NormalizePathAndSet(settings_.root_cache_path, "root_cache_path"); + const base::FilePath& cache_path = + NormalizeCachePathAndSet(settings_.cache_path, root_cache_path); if (root_cache_path.empty() && !cache_path.empty()) { - CefString(&settings_.root_cache_path) = CefString(&settings_.cache_path); + CefString(&settings_.root_cache_path) = cache_path.value(); } + // All other paths that need to be normalized. + NormalizePathAndSet(settings_.browser_subprocess_path, + "browser_subprocess_path"); + NormalizePathAndSet(settings_.framework_dir_path, "framework_dir_path"); + NormalizePathAndSet(settings_.main_bundle_path, "main_bundle_path"); + NormalizePathAndSet(settings_.user_data_path, "user_data_path"); + NormalizePathAndSet(settings_.resources_dir_path, "resources_dir_path"); + NormalizePathAndSet(settings_.locales_dir_path, "locales_dir_path"); + main_delegate_.reset(new CefMainDelegate(application)); browser_info_manager_.reset(new CefBrowserInfoManager); @@ -518,11 +602,14 @@ CefTraceSubscriber* CefContext::GetTraceSubscriber() { return trace_subscriber_.get(); } -void CefContext::PopulateRequestContextSettings( +void CefContext::PopulateGlobalRequestContextSettings( CefRequestContextSettings* settings) { CefRefPtr command_line = CefCommandLine::GetGlobalCommandLine(); + + // This value was already normalized in Initialize. CefString(&settings->cache_path) = CefString(&settings_.cache_path); + settings->persist_session_cookies = settings_.persist_session_cookies || command_line->HasSwitch(switches::kPersistSessionCookies); @@ -536,29 +623,17 @@ void CefContext::PopulateRequestContextSettings( CefString(&settings_.accept_language_list); } -bool CefContext::ValidateCachePath(const base::FilePath& cache_path) { - if (cache_path.empty()) - return true; +void CefContext::NormalizeRequestContextSettings( + CefRequestContextSettings* settings) { + // The |root_cache_path| value was already normalized in Initialize. + const base::FilePath& root_cache_path = CefString(&settings_.root_cache_path); + NormalizeCachePathAndSet(settings->cache_path, root_cache_path); - const base::FilePath& root_cache_path = - base::FilePath(CefString(&settings_.root_cache_path)); - if (!root_cache_path.empty() && root_cache_path != cache_path && - !root_cache_path.IsParent(cache_path)) { - LOG(ERROR) << "The cache_path directory (" << cache_path.value() - << ") is not a child of the root_cache_path directory (" - << root_cache_path.value() << ")"; - return false; + if (settings->accept_language_list.length == 0) { + // Use the global language list setting. + CefString(&settings->accept_language_list) = + CefString(&settings_.accept_language_list); } - - base::ThreadRestrictions::ScopedAllowIO allow_io; - if (!base::DirectoryExists(cache_path) && - !base::CreateDirectory(cache_path)) { - LOG(ERROR) << "The cache_path directory (" << cache_path.value() - << ") could not be created."; - return false; - } - - return true; } void CefContext::AddObserver(Observer* observer) { diff --git a/libcef/browser/context.h b/libcef/browser/context.h index 060e2c73b..eacc02a52 100644 --- a/libcef/browser/context.h +++ b/libcef/browser/context.h @@ -87,10 +87,11 @@ class CefContext { // Populate request context settings for the global system context based on // CefSettings and command-line flags. - void PopulateRequestContextSettings(CefRequestContextSettings* settings); + void PopulateGlobalRequestContextSettings( + CefRequestContextSettings* settings); - // Verify that |cache_path| is valid and create it if necessary. - bool ValidateCachePath(const base::FilePath& cache_path); + // Normalize and validate request context settings for user-created contexts. + void NormalizeRequestContextSettings(CefRequestContextSettings* settings); // Manage observer objects. The observer must either outlive this object or // remove itself before destruction. These methods can only be called on the diff --git a/libcef/browser/request_context_impl.cc b/libcef/browser/request_context_impl.cc index 52eb37fbf..cace18d50 100644 --- a/libcef/browser/request_context_impl.cc +++ b/libcef/browser/request_context_impl.cc @@ -645,9 +645,15 @@ void CefRequestContextImpl::Initialize() { // Share storage with |config_.other|. browser_context_ = CefBrowserContext::GetForContext(config_.other->GetBrowserContext()); + DCHECK(browser_context_); } if (!browser_context_) { + if (!config_.is_global) { + // User-specified settings need to be normalized. + CefContext::Get()->NormalizeRequestContextSettings(&config_.settings); + } + const base::FilePath& cache_path = base::FilePath(CefString(&config_.settings.cache_path)); if (!cache_path.empty()) { @@ -661,17 +667,17 @@ void CefRequestContextImpl::Initialize() { // Create a new CefBrowserContext instance. If the cache path is non- // empty then this new instance will become the globally registered // CefBrowserContext for that path. Otherwise, this new instance will - // be a completely isolated "incongento mode" context. + // be a completely isolated "incognito mode" context. browser_context_ = new CefBrowserContext(config_.settings); browser_context_->Initialize(); + } else { + // Share the same settings as the existing context. + config_.settings = browser_context_->GetSettings(); } // We'll disassociate from |browser_context_| on destruction. browser_context_->AddCefRequestContext(this); - // Force our settings to match |browser_context_|. - config_.settings = browser_context_->GetSettings(); - if (config_.other) { // Clear the reference to |config_.other| after setting // |request_context_getter_|. This is the reverse order of checks in