From 6d1031e0e88b31a19a0fbc96ed101cb1b1a596b3 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Thu, 1 Aug 2024 23:54:18 +1000 Subject: [PATCH] Enable support for CEF 6613+ and Chrome Runtime Add a dummy Browser Client to introduce limitations into the Chrome UI windows that can be opened in uncontrolled edge cases. Chrome UI windows are unmanaged by default. By returning something other than null in GetDefaultClient() we can lock it down in various ways. OnBeforePopup, OnOpenURLFromTab can probably be removed as they don't get called. There's no direct way to stop the Chrome UI window from being opened, so instead close it immediately. Disable Various Chrome Settings in custom docks & browser sources via SetPreferences. For more, check `chrome/common/pref_names.h`. Chrome's default error display is now used by CEF, so on top of the existing override in browser docks, a similar override has been added to browser sources which just redirects to about:blank. Also blocks - Chrome Extensions, as they are largely untested and unpredictable - MediaRouter, which provides Cast.. functionality - CalculateNativeWinOcclusion, which lowers FPS for hidden pages - LiveCaption, which provides automatic captions - DocumentPictureInPictureAPI, which provides PiP widgets (YouTube) - StorageNotificationService, when approaching low cache space A data migration function is included. It moves some directories into a new 'Default' profile subdirectory, and performs the necessary renaming of certain files to ensure the new Profile loads. Additionally, the cookie directories for each service integration must be moved into the root config directory, as subdirectories in their current form (obs_profile_cookies/) are not supported. This is intentional, and according to Marshall this was never a supported setup. cache_path *must* be a direct child of the root_cache_path. Invalid cache_path_ is silently treated as Incognito Mode and cookies are not stored. --- CMakeLists.txt | 2 + browser-app.cpp | 70 +++++++++++++++++++ browser-app.hpp | 7 ++ browser-client.cpp | 11 +++ browser-client.hpp | 4 ++ browser-dummy-client.cpp | 71 +++++++++++++++++++ browser-dummy-client.hpp | 47 +++++++++++++ cmake/os-linux.cmake | 3 +- cmake/os-macos.cmake | 3 +- cmake/os-windows.cmake | 4 +- obs-browser-plugin.cpp | 120 +++++++++++++++++++++++++++++++++ panel/browser-panel-client.cpp | 17 ++++- panel/browser-panel-client.hpp | 12 ++++ panel/browser-panel.cpp | 27 +++++++- 14 files changed, 391 insertions(+), 7 deletions(-) create mode 100644 browser-dummy-client.cpp create mode 100644 browser-dummy-client.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0853ac0c2..16abf4ca3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,8 @@ target_sources( browser-app.hpp browser-client.cpp browser-client.hpp + browser-dummy-client.cpp + browser-dummy-client.hpp browser-scheme.cpp browser-scheme.hpp browser-version.h diff --git a/browser-app.cpp b/browser-app.cpp index 5d2c4dc76..9c6d34b0c 100644 --- a/browser-app.cpp +++ b/browser-app.cpp @@ -48,11 +48,62 @@ CefRefPtr BrowserApp::GetBrowserProcessHandler() return this; } +CefRefPtr BrowserApp::GetDefaultClient() +{ + return GetDummy(); +} + void BrowserApp::OnRegisterCustomSchemes(CefRawPtr registrar) { registrar->AddCustomScheme("http", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED); } +void BrowserApp::OnContextInitialized() +{ + // Without a default client, CefBrowser is unmanaged, allowing full-blown Chromium windows outside of our control + // We don't actually want those, so define a dummy client which will automatically close any such windows + dummy = new BrowserDummyClient(); + + CefRefPtr requestContext = CefRequestContext::GetGlobalContext(); + CefString errorMessage; + CefRefPtr optionValue = CefValue::Create(); + + constexpr std::array kBrowserFeaturesToDisable{ + "autofill.credit_card_enabled", + "autofill.enabled", + "autofill.iban_enabled", + "autofill.payment_card_benefits", + "autofill.payment_cvc_storage", + "autofill.profile_enabled", + "autologin.enabled", + "browser_labs_enabled", + "credentials_enable_autosignin", + "credentials_enable_service", + "payments.can_make_payment_enabled", + "printing.enabled", + "search.suggest_enabled", + "shopping_list_enabled", + "side_panel.google_search_side_panel_enabled", + "side_search.enabled", + "signin.allowed", + "signin.allowed_on_next_startup", + "translate", + "url_keyed_anonymized_data_collection.enabled"}; + + constexpr std::array kBrowserFeaturesToEnable{"extensions.block_external_extensions", + "extensions.disabled"}; + + optionValue->SetBool(false); + for (std::string_view feature : kBrowserFeaturesToDisable) { + requestContext->SetPreference(feature.data(), optionValue.get(), errorMessage); + } + + optionValue->SetBool(true); + for (std::string_view feature : kBrowserFeaturesToEnable) { + requestContext->SetPreference(feature.data(), optionValue.get(), errorMessage); + } +} + void BrowserApp::OnBeforeChildProcessLaunch(CefRefPtr command_line) { #ifdef _WIN32 @@ -82,16 +133,35 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtrAppendSwitchWithValue("disable-features", disableFeatures); } else { command_line->AppendSwitchWithValue("disable-features", "WebBluetooth," #ifdef _WIN32 "EnableWindowsGamingInputDataFetcher," #endif + "MediaRouter," + "CalculateNativeWinOcclusion," + "LiveCaption," + "StorageNotificationService," "HardwareMediaKeyHandling"); } + if (command_line->HasSwitch("disable-blink-features")) { + std::string disableBlinkFeatures = command_line->GetSwitchValue("disable-blink-features"); + disableBlinkFeatures += ",DocumentPictureInPictureAPI"; + command_line->AppendSwitchWithValue("disable-blink-features", disableBlinkFeatures); + } else { + command_line->AppendSwitchWithValue("disable-blink-features", "DocumentPictureInPictureAPI"); + } + command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); + command_line->AppendSwitch("disable-extensions"); + command_line->AppendSwitch("hide-crash-restore-bubble"); #ifdef __APPLE__ command_line->AppendSwitch("use-mock-keychain"); #elif !defined(_WIN32) diff --git a/browser-app.hpp b/browser-app.hpp index b25d9accd..0694d0c8e 100644 --- a/browser-app.hpp +++ b/browser-app.hpp @@ -22,6 +22,7 @@ #include #include #include "cef-headers.hpp" +#include "browser-dummy-client.hpp" typedef std::function)> BrowserFunc; @@ -85,6 +86,8 @@ class BrowserApp : public CefApp, public CefRenderProcessHandler, public CefBrow virtual CefRefPtr GetRenderProcessHandler() override; virtual CefRefPtr GetBrowserProcessHandler() override; + virtual CefRefPtr GetDefaultClient() override; + virtual void OnContextInitialized() override; virtual void OnBeforeChildProcessLaunch(CefRefPtr command_line) override; virtual void OnRegisterCustomSchemes(CefRawPtr registrar) override; virtual void OnBeforeCommandLineProcessing(const CefString &process_type, @@ -106,5 +109,9 @@ class BrowserApp : public CefApp, public CefRenderProcessHandler, public CefBrow QTimer frameTimer; #endif + CefRefPtr dummy = nullptr; + + BrowserDummyClient *GetDummy() const { return dummy.get(); }; + IMPLEMENT_REFCOUNTING(BrowserApp); }; diff --git a/browser-client.cpp b/browser-client.cpp index bf7baada9..bfe7135d1 100644 --- a/browser-client.cpp +++ b/browser-client.cpp @@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr, CefRefPtr frame, } } +void BrowserClient::OnLoadError(CefRefPtr, [[maybe_unused]] CefRefPtr frame, + CefLoadHandler::ErrorCode, const CefString &, const CefString &) +{ +#if CHROME_VERSION_BUILD > 6533 + // CEF doesn't currently provide a way to properly disable/override the default Chrome error page + // https://github.com/obsproject/obs-studio/issues/13499 + // FIXME: https://github.com/chromiumembedded/cef/issues/3852 + frame->LoadURL("about:blank"); +#endif +} + bool BrowserClient::OnConsoleMessage(CefRefPtr, cef_log_severity_t level, const CefString &message, const CefString &source, int line) { diff --git a/browser-client.hpp b/browser-client.hpp index cba0be2dd..069c3ff42 100644 --- a/browser-client.hpp +++ b/browser-client.hpp @@ -146,5 +146,9 @@ class BrowserClient : public CefClient, /* CefLoadHandler */ virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override; + virtual void OnLoadError(CefRefPtr browser, CefRefPtr frame, + CefLoadHandler::ErrorCode errorCode, const CefString &errorText, + const CefString &failedUrl) override; + IMPLEMENT_REFCOUNTING(BrowserClient); }; diff --git a/browser-dummy-client.cpp b/browser-dummy-client.cpp new file mode 100644 index 000000000..ee2726b7e --- /dev/null +++ b/browser-dummy-client.cpp @@ -0,0 +1,71 @@ + +#include "browser-dummy-client.hpp" + +CefRefPtr BrowserDummyClient::GetCommandHandler() +{ + return this; +} + +CefRefPtr BrowserDummyClient::GetRequestHandler() +{ + return this; +} + +CefRefPtr BrowserDummyClient::GetLifeSpanHandler() +{ + return this; +} + +bool BrowserDummyClient::OnChromeCommand(CefRefPtr, int, cef_window_open_disposition_t) +{ + return true; +} + +bool BrowserDummyClient::IsChromeAppMenuItemVisible(CefRefPtr, int) +{ + return false; +} + +bool BrowserDummyClient::IsChromeToolbarButtonVisible(cef_chrome_toolbar_button_type_t) +{ + return false; +} + +bool BrowserDummyClient::IsChromePageActionIconVisible(cef_chrome_page_action_icon_type_t) +{ + return false; +} + +bool BrowserDummyClient::IsChromeAppMenuItemEnabled(CefRefPtr, int) +{ + return false; +} + +bool BrowserDummyClient::OnBeforePopup(CefRefPtr, CefRefPtr, +#if CHROME_VERSION_BUILD >= 6834 + int, +#endif + const CefString &, const CefString &, cef_window_open_disposition_t, bool, + const CefPopupFeatures &, CefWindowInfo &, CefRefPtr &, + CefBrowserSettings &, CefRefPtr &, bool *) +{ + return true; +} + +void BrowserDummyClient::OnAfterCreated(CefRefPtr browser) +{ + if (browser && browser->GetHost()) { + browser->GetHost()->CloseBrowser(false); + } +} + +bool BrowserDummyClient::OnOpenURLFromTab(CefRefPtr, CefRefPtr, const CefString &, + CefRequestHandler::WindowOpenDisposition, bool) +{ + return true; +} + +bool BrowserDummyClient::OnBeforeBrowse(CefRefPtr, CefRefPtr, CefRefPtr, bool, bool) +{ + return true; +} diff --git a/browser-dummy-client.hpp b/browser-dummy-client.hpp new file mode 100644 index 000000000..80c9280d6 --- /dev/null +++ b/browser-dummy-client.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "cef-headers.hpp" + +class BrowserDummyClient : public CefClient, + public CefCommandHandler, + public CefRequestHandler, + public CefLifeSpanHandler { +public: + virtual CefRefPtr GetCommandHandler() override; + virtual CefRefPtr GetRequestHandler() override; + virtual CefRefPtr GetLifeSpanHandler() override; + + /* CefCommandHandler */ + virtual bool OnChromeCommand(CefRefPtr browser, int command_id, + cef_window_open_disposition_t disposition) override; + virtual bool IsChromeAppMenuItemVisible(CefRefPtr browser, int command_id) override; + virtual bool IsChromeToolbarButtonVisible(cef_chrome_toolbar_button_type_t button_type) override; + + virtual bool IsChromePageActionIconVisible(cef_chrome_page_action_icon_type_t icon_type) override; + + virtual bool IsChromeAppMenuItemEnabled(CefRefPtr browser, int command_id) override; + + /* CefLifeSpanHandler */ + virtual bool OnBeforePopup(CefRefPtr browser, CefRefPtr frame, +#if CHROME_VERSION_BUILD >= 6834 + int, +#endif + const CefString &target_url, const CefString &target_frame_name, + cef_window_open_disposition_t target_disposition, bool user_gesture, + const CefPopupFeatures &popupFeatures, CefWindowInfo &windowInfo, + CefRefPtr &client, CefBrowserSettings &settings, + CefRefPtr &extra_info, bool *no_javascript_access) override; + + virtual void OnAfterCreated(CefRefPtr browser) override; + + /* CefRequestHandler */ + virtual bool OnOpenURLFromTab(CefRefPtr browser, CefRefPtr frame, + const CefString &target_url, + CefRequestHandler::WindowOpenDisposition target_disposition, + bool user_gesture) override; + + virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, + CefRefPtr request, bool user_gesture, bool is_redirect) override; + + IMPLEMENT_REFCOUNTING(BrowserDummyClient); +}; diff --git a/cmake/os-linux.cmake b/cmake/os-linux.cmake index f74797a62..55b4eca75 100644 --- a/cmake/os-linux.cmake +++ b/cmake/os-linux.cmake @@ -14,7 +14,8 @@ add_executable(OBS::browser-helper ALIAS browser-helper) target_sources( browser-helper PRIVATE # cmake-format: sortable - browser-app.cpp browser-app.hpp cef-headers.hpp obs-browser-page/obs-browser-page-main.cpp) + browser-app.cpp browser-app.hpp browser-dummy-client.hpp browser-dummy-client.cpp + cef-headers.hpp obs-browser-page/obs-browser-page-main.cpp) target_include_directories(browser-helper PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/deps" "${CMAKE_CURRENT_SOURCE_DIR}/obs-browser-page") diff --git a/cmake/os-macos.cmake b/cmake/os-macos.cmake index 6d6dc51c8..00476ed13 100644 --- a/cmake/os-macos.cmake +++ b/cmake/os-macos.cmake @@ -31,7 +31,8 @@ foreach(helper IN LISTS helper_suffixes) target_sources( ${target_name} PRIVATE # cmake-format: sortable - browser-app.cpp browser-app.hpp cef-headers.hpp obs-browser-page/obs-browser-page-main.cpp) + browser-app.cpp browser-app.hpp browser-dummy-client.hpp browser-dummy-client.cpp + cef-headers.hpp obs-browser-page/obs-browser-page-main.cpp) target_compile_definitions(${target_name} PRIVATE ENABLE_BROWSER_SHARED_TEXTURE) diff --git a/cmake/os-windows.cmake b/cmake/os-windows.cmake index 43f9d8558..b0250738f 100644 --- a/cmake/os-windows.cmake +++ b/cmake/os-windows.cmake @@ -13,8 +13,8 @@ add_executable(OBS::browser-helper ALIAS obs-browser-helper) target_sources( obs-browser-helper PRIVATE # cmake-format: sortable - browser-app.cpp browser-app.hpp cef-headers.hpp obs-browser-page.manifest - obs-browser-page/obs-browser-page-main.cpp) + browser-app.cpp browser-app.hpp browser-dummy-client.hpp browser-dummy-client.cpp + cef-headers.hpp obs-browser-page.manifest obs-browser-page/obs-browser-page-main.cpp) configure_file(cmake/windows/obs-module-helper.rc.in obs-browser-page.rc) target_sources(obs-browser-helper PRIVATE obs-browser-page.rc) diff --git a/obs-browser-plugin.cpp b/obs-browser-plugin.cpp index 95a168c6b..65d655195 100644 --- a/obs-browser-plugin.cpp +++ b/obs-browser-plugin.cpp @@ -293,6 +293,9 @@ static void BrowserInit(void) CefString(&settings.log_file) = log_path_abs; settings.windowless_rendering_enabled = true; settings.no_sandbox = true; +#if CHROME_VERSION_BUILD > 6533 && CHROME_VERSION_BUILD <= 6613 + settings.chrome_runtime = true; +#endif uint32_t obs_ver = obs_get_version(); uint32_t obs_maj = obs_ver >> 24; @@ -341,6 +344,9 @@ static void BrowserInit(void) settings.persist_user_preferences = 1; #endif CefString(&settings.cache_path) = conf_path_abs; +#if CHROME_VERSION_BUILD > 6533 + CefString(&settings.root_cache_path) = conf_path_abs; +#endif #if !defined(__APPLE__) || defined(ENABLE_BROWSER_LEGACY) char *abs_path = os_get_abs_path_ptr(path.c_str()); CefString(&settings.browser_subprocess_path) = abs_path; @@ -714,8 +720,122 @@ static void check_hwaccel_support(void) #endif #endif +#if CHROME_VERSION_BUILD > 6533 +static void MigrateProfileConfigDir(std::string oldDir, std::string newDir, bool makeDirs) +{ + namespace fs = std::filesystem; + + BPtr configPath = obs_module_config_path("."); + fs::path rootPath{configPath.Get()}; + + fs::path oldPath = rootPath / fs::path(oldDir); + fs::path newPath = rootPath / fs::path(newDir); + + if (!fs::exists(oldPath)) { + return; + } + + try { + if (makeDirs && !fs::exists(newPath)) { + fs::create_directories(newPath); + } + const auto copyOptions = fs::copy_options::recursive; + fs::copy(oldPath, newPath, copyOptions); + } catch (const fs::filesystem_error &error) { + blog(LOG_WARNING, "[obs-browser]: Error migrating cookies for '%s': %s", oldPath.c_str(), error.what()); + } +} + +static void MigrateDefaultProfileSubdirs(std::string parent, bool makeDirs) +{ + constexpr std::array kMigrationDirectories{ + "Local Storage", + "Session Storage", + "Network", + }; + constexpr std::string_view kDefaultDirectoryPrefix{"Default/"}; + + for (std::string_view directory : kMigrationDirectories) { + std::string oldDirectory{parent}; + oldDirectory.append(directory); + std::string newDirectory{parent}; + newDirectory.append(kDefaultDirectoryPrefix); + newDirectory.append(directory); + + MigrateProfileConfigDir(oldDirectory, newDirectory, makeDirs); + } +} + +static void MigrateToChromeRuntime() +{ + namespace fs = std::filesystem; + BPtr defaultConfigPath = obs_module_config_path("Default"); + fs::path defaultPath{defaultConfigPath.Get()}; + // User has already previously launched with Chrome Runtime + if (fs::exists(defaultPath)) { + blog(LOG_INFO, "[obs-browser]: Chrome Runtime Default profile detected. No migration necessary."); + return; + } + + BPtr localPrefsConfigPath = obs_module_config_path("LocalPrefs.json"); + fs::path localPrefs{localPrefsConfigPath.Get()}; + // User has never launched old OBS Browser before + if (!fs::exists(localPrefs)) { + blog(LOG_DEBUG, "[obs-browser]: Alloy Runtime preferences not found. No migration necessary."); + return; + } + + blog(LOG_INFO, "[obs-browser]: Migrating files to Chrome Runtime.."); + + try { + fs::create_directories(defaultPath); + } catch (const fs::filesystem_error &error) { + blog(LOG_WARNING, "[obs-browser]: Error creating Default profile: %s", error.what()); + } + + MigrateDefaultProfileSubdirs("", false); + + BPtr newLocalPrefsConfigPath = obs_module_config_path("Local State"); + fs::path newLocalPrefs{newLocalPrefsConfigPath.Get()}; + try { + fs::copy(localPrefs, newLocalPrefs); + } catch (const fs::filesystem_error &error) { + blog(LOG_WARNING, "[obs-browser]: Error migrating preferences: %s", error.what()); + } + + constexpr std::string_view prefix = "obs_profile_cookies"; + + BPtr serviceProfilesPath = obs_module_config_path(prefix.data()); + fs::path serviceProfiles{serviceProfilesPath.Get()}; + if (fs::exists(serviceProfiles)) { + // User has service integration cookies that also must be copied + for (auto const &dir_entry : std::filesystem::directory_iterator{serviceProfiles}) { + if (!dir_entry.is_directory()) { + continue; + } + std::string name{dir_entry.path().filename().string()}; + std::string oldDir{prefix}; + oldDir.append("/"); + oldDir.append(name); + + std::string newDir{prefix}; + newDir.append("_"); + newDir.append(name); + MigrateProfileConfigDir(oldDir, newDir, true); + + blog(LOG_INFO, "[obs-browser]: Migrated cookies for %s.", name.c_str()); + } + } + + blog(LOG_INFO, "[obs-browser]: Migration of browser cookies and session completed."); +} +#endif + bool obs_module_load(void) { +#if CHROME_VERSION_BUILD > 6533 + MigrateToChromeRuntime(); +#endif #ifdef ENABLE_BROWSER_QT_LOOP qRegisterMetaType("MessageTask"); #endif diff --git a/panel/browser-panel-client.cpp b/panel/browser-panel-client.cpp index 868f01b62..1d61f4f96 100644 --- a/panel/browser-panel-client.cpp +++ b/panel/browser-panel-client.cpp @@ -38,6 +38,13 @@ CefRefPtr QCefBrowserClient::GetDisplayHandler() return this; } +#if CHROME_VERSION_BUILD >= 6533 +CefRefPtr QCefBrowserClient::GetCommandHandler() +{ + return this; +} +#endif + CefRefPtr QCefBrowserClient::GetRequestHandler() { return this; @@ -68,10 +75,18 @@ CefRefPtr QCefBrowserClient::GetJSDialogHandler() return this; } +/* CefCommandHandler */ +#if CHROME_VERSION_BUILD >= 6533 +bool QCefBrowserClient::OnChromeCommand(CefRefPtr, int, cef_window_open_disposition_t) +{ + return true; +} +#endif + /* CefDisplayHandler */ void QCefBrowserClient::OnTitleChange(CefRefPtr browser, const CefString &title) { - if (widget && widget->cefBrowser->IsSame(browser)) { + if (widget && widget->cefBrowser && widget->cefBrowser->IsSame(browser)) { std::string str_title = title; QString qt_title = QString::fromUtf8(str_title.c_str()); QMetaObject::invokeMethod(widget, "titleChanged", Q_ARG(QString, qt_title)); diff --git a/panel/browser-panel-client.hpp b/panel/browser-panel-client.hpp index 417021fa9..ea44000b0 100644 --- a/panel/browser-panel-client.hpp +++ b/panel/browser-panel-client.hpp @@ -7,6 +7,9 @@ class QCefBrowserClient : public CefClient, public CefDisplayHandler, +#if CHROME_VERSION_BUILD >= 6533 + public CefCommandHandler, +#endif public CefRequestHandler, public CefLifeSpanHandler, public CefContextMenuHandler, @@ -26,6 +29,9 @@ class QCefBrowserClient : public CefClient, /* CefClient */ virtual CefRefPtr GetLoadHandler() override; virtual CefRefPtr GetDisplayHandler() override; +#if CHROME_VERSION_BUILD >= 6533 + virtual CefRefPtr GetCommandHandler() override; +#endif virtual CefRefPtr GetRequestHandler() override; virtual CefRefPtr GetLifeSpanHandler() override; virtual CefRefPtr GetKeyboardHandler() override; @@ -33,6 +39,12 @@ class QCefBrowserClient : public CefClient, virtual CefRefPtr GetContextMenuHandler() override; virtual CefRefPtr GetJSDialogHandler() override; + /* CefCommandHandler */ +#if CHROME_VERSION_BUILD >= 6533 + virtual bool OnChromeCommand(CefRefPtr browser, int command_id, + cef_window_open_disposition_t disposition) override; +#endif + /* CefDisplayHandler */ virtual void OnTitleChange(CefRefPtr browser, const CefString &title) override; diff --git a/panel/browser-panel.cpp b/panel/browser-panel.cpp index b3fbe289d..099d07eb3 100644 --- a/panel/browser-panel.cpp +++ b/panel/browser-panel.cpp @@ -368,6 +368,10 @@ void QCefWidgetInternal::resizeEvent(QResizeEvent *event) void QCefWidgetInternal::Resize() { + if (os_event_try(cef_started_event) != 0) { + return; + } + QSize size = this->size() * devicePixelRatioF(); bool success = QueueCEFTask([this, size]() { @@ -552,8 +556,18 @@ QCefWidget *QCefInternal::create_widget(QWidget *parent, const std::string &url, QCefCookieManager *QCefInternal::create_cookie_manager(const std::string &storage_path, bool persist_session_cookies) { + std::string path = storage_path; +#if CHROME_VERSION_BUILD > 6533 + // TODO: Update obs-studio to use the new path structure due to subdirectories not being supported by CEF + // https://github.com/obsproject/obs-studio/issues/13498 + std::string legacy = "obs_profile_cookies/"; + size_t pos = storage_path.find(legacy); + if (pos != std::string::npos) { + path.replace(pos, legacy.length(), "obs_profile_cookies_"); + } +#endif try { - return new QCefCookieManagerInternal(storage_path, persist_session_cookies); + return new QCefCookieManagerInternal(path, persist_session_cookies); } catch (const char *error) { blog(LOG_ERROR, "Failed to create cookie manager: %s", error); return nullptr; @@ -562,7 +576,16 @@ QCefCookieManager *QCefInternal::create_cookie_manager(const std::string &storag BPtr QCefInternal::get_cookie_path(const std::string &storage_path) { - BPtr rpath = obs_module_config_path(storage_path.c_str()); + std::string path = storage_path; +#if CHROME_VERSION_BUILD > 6533 + // TODO: Update obs-studio to use the new path structure due to subdirectories not being supported by CEF + std::string legacy = "obs_profile_cookies/"; + size_t pos = storage_path.find(legacy); + if (pos != std::string::npos) { + path.replace(pos, legacy.length(), "obs_profile_cookies_"); + } +#endif + BPtr rpath = obs_module_config_path(path.c_str()); return os_get_abs_path_ptr(rpath.Get()); }