Skip to content

Enable support for CEF 6613+ (Chrome 128 to 147) and Chrome Runtime#523

Open
WizardCM wants to merge 1 commit into
obsproject:masterfrom
WizardCM:chrome-runtime-changes
Open

Enable support for CEF 6613+ (Chrome 128 to 147) and Chrome Runtime#523
WizardCM wants to merge 1 commit into
obsproject:masterfrom
WizardCM:chrome-runtime-changes

Conversation

@WizardCM
Copy link
Copy Markdown
Member

@WizardCM WizardCM commented May 24, 2026

Description

We made it! After a lot of work, burnout, and testing, support for CEF 6613 (Chrome 128) and the Chrome Runtime is ready to be reviewed!

This (finally) gives us the ability to safely migrate to the Chrome Runtime, the replacement to the Alloy Runtime that CEF has been using forever. CEF/Chrome versions from 6613/128 to 7727/147 (yes, current stable) are now fully supported and work out of the box.

Tip

Test builds of CEF can be downloaded from https://cef-builds.spotifycdn.com/index.html (use Minimal, Current Stable)


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. https://magpcss.org/ceforum/viewtopic.php?f=6&t=19898

OnBeforePopup, OnOpenURLFromTab in the Dummy Client 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. However, I've kept them just in case.

Disable Various Chrome Settings in custom docks & browser sources via SetPreferences. For more, check chrome/common/pref_names.h. We should actively keep an eye on this in future when updating CEF.

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 Chrome's Cast.. functionality
  • CalculateNativeWinOcclusion, which lowers FPS for hidden pages
  • LiveCaption, which provides automatic captions
  • DocumentPictureInPictureAPI, which provides PiP widgets (YouTube)

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. This is necessary for user's data to be maintained, as the Alloy runtime used a bit of a custom structure which has been discarded in favour of Chrome Runtime's. CEF does not provide a migration path.

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/<cookie_id>) 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.

To work around this, I've included some temporary code to translate the path received from the OBS frontend. When this PR is to be merged, I can submit a PR to obs-studio to match the intended behaviour.

Motivation and Context

Keeping CEF up to date is important - for security, reliability, support, and access to modern features.

How Has This Been Tested?

Tested on Windows 10 on CEF 147 with a plugin_config/obs-browser config directory from OBS 32.1 to ensure data is migrated correctly. Launched again after migration to ensure data was still accessible. cef_binary_147.0.14+g76d2442+chromium-147.0.7727.138_windows64_minimal

Tested again with our current CEF 127 build to ensure we can use 6533 unchanged if CEF tests themselves backfire.

Additionally tested over the past few months with the following versions as they were released. No regressions were found. Mostly my fault for taking so long.

cef_binary_128.0.0+g446b7d6+chromium-128.0.6613.0_windows64_minimal
cef_binary_132.3.2+g4997b2f+chromium-132.0.6834.161_windows64_minimal
cef_binary_136.1.5+g723b52b+chromium-136.0.7103.93_windows64_minimal
cef_binary_143.0.13+g30cb3bd+chromium-143.0.7499.170_windows64_minimal
cef_binary_145.0.28+g51162e8+chromium-145.0.7632.160_windows64_minimal

Additionally, @hoshinolina has done tremendous work testing this on Fedora, with users actively using the code in this PR since November 2025. As far as I know, users have had very few complaints.

Types of changes

  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation (a change to documentation pages)

Checklist:

  • I have read the contributing document.
  • My code has been run through clang-format.
  • My code follows the project's style guidelines
  • My code is not on the master branch.
  • My code has been tested.
  • All commit messages are properly formatted and commits squashed where appropriate.
  • I have included updates to all appropriate documentation.

@WizardCM WizardCM force-pushed the chrome-runtime-changes branch from f4f7da9 to 6087ad4 Compare May 24, 2026 03:48
@WizardCM WizardCM added kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. Seeking Testers labels May 24, 2026
@hoshinolina
Copy link
Copy Markdown
Contributor

hoshinolina commented May 24, 2026

For reference, the only issues we've run into in Fedora as far as I remember have been:

  • Unrelated bugs in OBS (that affected CEF/obs-browser for computers-are-hard-tm reasons)
  • Fix stack smash crashes on Linux #493 & Require extra info for Linux hardware accel #491 (merged)
  • Do not try to create the browser before init #519 (this has always been broken but newer CEF makes it assert way more often when it fails the ABI version check, you probably want to get that one sorted out / added as a dependency to this)
  • Unrelated bugs in CEF (since fixed)
  • Binary compatibility issues: Newer versions of CEF have a stable ABI that allows decoupling CEF updates from OBS updates. Fedora takes advantage of this to ship CEF as a generic package (possibly usable by other consumers) and keep it as up to date as possible (in fact, Fedora decouples CEF and Chromium updates too, sometimes shipping Chromium security fixes within the CEF package before upstream CEF itself). This mechanism has had some teething issues with unexpected compat breaks. As long as OBS continues to ship updates in lockstep with CEF and against the EXPERIMENTAL API version (which is the default), this should not affect it (in all cases, rebuilding OBS against the current version/API of CEF would have fixed/worked around the ABI break we saw).
  • The whole no-docks-in-Wayland mess, which has nothing to do with this

So overall, nothing really relevant to this PR. That said, I should point out that Fedora never shipped nor tested the profile migration code. While Fedora did briefly ship the old obs-cef at one point in the past, that was abandoned as unmaintainable (making it build with newer compilers/libs became a nightmare), so there was a period where OBS had no CEF support in Fedora before this was reintroduced, and therefore no good reason to worry about profile migrations (it would only benefit people who had used the old version, to revive their old dormant profiles, which we figured wasn't really worth it).

If anyone wants to build this PR in Fedora against system CEF, the only required changes should be:

  1. Install cef-devel
  2. rm cmake/finders/FindCEF.cmake (cef-devel provides this systemwide, interface-compatible with the OBS version)
  3. Either run with LD_LIBRARY_PATH=/usr/lib64/cef or change the rpath setting:
    sed -e 's,INSTALL_RPATH ".*",INSTALL_RPATH "/usr/lib64/cef/",' -i plugins/obs-browser/cmake/os-linux.cmake
  4. -DENABLE_BROWSER=ON and optionally, -DCEF_API_VERSION=xxxxx if you want to build against a specific versioned API/ABI (Fedora arbitrarily picks 13700 here, but I don't think CEF has actually changed any APIs in a source-incompatible way with OBS since 13300, the very first version, so you can pick whatever right now).

Note that unlike the standard OBS build, this setup embeds the libcef_dll_wrapper build into the OBS build, so you will see that being built alongside the rest of obs-browser (the wrapper source code is shipped in cef-devel). All that is taken care of by the system FindCEF.cmake and the way the CEF package itself is patched/configured, and in fact it turned out to be quite easy to do and it works very well. We settled on this approach after trying to pre-build the wrapper in cef-devel, because we realized that it was impractical to build a separate wrapper for every possible version and additionally, consumers may want to build the wrapper with their own choice of compile-time options making "universal" wrapper builds not really a good idea (upstream CEF confirmed this too). Relevant links: FindCEF.cmake cef.spec (search for libcef_dll for the few relevant lines). I would personally encourage OBS to consider exploring this approach instead of a separate libcef_dll_wrapper build for official binaries/CI, but that's just me (and it's not a blocker for this PR in any way) ^^

@Warchamp7 Warchamp7 added this to the OBS Studio 33.0 milestone May 26, 2026
@RytoEX RytoEX requested review from PatTheMav, RytoEX and Warchamp7 May 26, 2026 18:49
Comment thread panel/browser-panel.cpp Outdated
{
std::string path = storage_path;
#if CHROME_VERSION_BUILD > 6533
// This code may be temporary, ideally the change should be in OBS Studio itself,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a task issue and add a TODO comment with a link to that issue here (outlining the issue and what needs to be changed in that issue).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do.

Comment thread obs-browser-plugin.cpp Outdated
Comment thread obs-browser-plugin.cpp Outdated

static void MigrateToChromeRuntime()
{
BPtr<char> defaultProfile = obs_module_config_path("Default");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is entirely new C++ code, so I'd prefer if we'd use standard C++ library types and functions if available (e.g. std::string, std::string_view, std::filesystem, etc.) rather than custom OBS types and APIs and treating it as "C with some OOP".

(And even though std is included by default, this would be a violation of our C++ code style standards, so use std::string explicitly so this code does not need to be touched when using namespace std hopefully is excised at a later point).

Also be mindful of any libobs API that potentially returns nullptr instead of an actual char * and guard against that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above for how best to transition from obs_module_config_path here, as I don't think we have any existing examples in the OBS codebase.

Unfortunately, MigrateProfileConfigDir is used both for migrating the Default profile, and later to migrate individual Profile cookie directories, so it has to be reusable.

Good point on catching nullptrs. I'll revisit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda agree with @notr1ch that we don't need to use nullptr checks if the occurrence of it would be a truly exceptional situation (as in: this should never happen and if it does, some developer messed up).

This would be in opposition to an API that could very well return a nullptr and that's part of its "normal operation".

The last consideration is whether even the exceptional situation is indeed "recoverable" or doing so would allow OBS to limp on as a program in a weird, undefined, state, which could lead to data corruption or data loss as a result.

Just crashing (to avoid that) is incredibly disruptive, but preferable to e.g. potentially overwriting a scene collection with garbage data.

Comment thread obs-browser-plugin.cpp Outdated
Comment on lines +751 to +756
std::string dirs[3] = {"Local Storage", "Session Storage", "Network"};

for (std::string oldDir : dirs) {
std::string newDir("Default/" + oldDir);
MigrateProfileConfigDir(parent + oldDir, parent + newDir, makeDirs);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use constexpr for the constants:

constexpr std::array<std::string_view, 3> migrationDirectories {
    "Local Storage",
    "Session Storage",
    "Network",
};

constexpr std::string_view defaultDirectoryPrefix {"Default/"};

Which enables:

Suggested change
std::string dirs[3] = {"Local Storage", "Session Storage", "Network"};
for (std::string oldDir : dirs) {
std::string newDir("Default/" + oldDir);
MigrateProfileConfigDir(parent + oldDir, parent + newDir, makeDirs);
}
for (std::string_view directory, migrationDirectories) {
std::string oldDirectory {parent};
oldDirectory.append(directory);
std::string newDirectory {parent};
newDirectory.append(defaultDirectoryPrefix);
newDirectory.append(directory)
MigrateProfileConfigDir(oldDirectory, newDirectory, makeDirs);
}

I don't know if I like the design with this function and MigrateProfileConfigDir rather than one single function. I'm kinda fine with using string concatenation at first rather than using std::filesystem::path objects as any string token would have to be converted using u8path first due to Windows using UTF-16 for paths internally.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Let me know if there are additional concerns outside of the lack of u8path handling due to C++20 deprecation warnings.

Comment thread obs-browser-plugin.cpp Outdated

try {
const auto copyOptions = std::filesystem::copy_options::recursive;
std::filesystem::copy(old_path, std::string(new_path_abs.Get()), copyOptions);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My hunch is that this might break in unforeseen ways as you pass in a std::string with UTF-8 bytes into a function that expects UTF-16 bytes instead.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, probably. Do we have documentation on how best to transfer from obs_module_config_path to std::filesystem cleanly? I have tried and failed to find out. I'd guess the same way I did old_path a few lines earlier? Just seems a bit noisy going obs_module_config_path -> os_get_abs_path_ptr -> std::filesystem::u8path, unless the os_get_abs_path_ptr can be skipped?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without consulting a compiler, I'd probably do:

using fs = std::filesystem;
// This crashes if libobs returns a nullptr here, which could be seen as a 
// critical failure that should never happen.
fs::path rootPath = fs:u8path(os_module_config_path());
fs::path oldPath = rootPath / fs:u8path(oldDir);
fs::path newPath = rootPath / fs:u8path(newDir);

if (makeDirs && !fs::exists(newPath)) {
    fs::createDirectory(newPath);
}

[..]

On macOS std::filesystem seems to handle absolute paths with relative path tokens just fine (so something like /Path/../SomePath works), otherwise std::filesystem::canonical can be used to resolve such a path completely, which you'd probably wrap the assignment to oldPath and newPath in - but I'm not sure that's necessary.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that code worked pretty much out of the box - feel free to look over my changes. I have verified std::filesystem::canonical was not necessary (though it could be used to make error logging a bit clearer).

Changes made, outside of properly handing u8paths for now.

Comment thread browser-client.cpp Outdated
Comment thread browser-app.cpp
Comment thread browser-app.cpp Outdated
Comment on lines +65 to +69
CefRefPtr<CefRequestContext> rc = CefRequestContext::GetGlobalContext();
CefString err;
CefRefPtr<CefValue> off = CefValue::Create();
off->SetBool(false);
std::string opts[20] = {"autofill.credit_card_enabled",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use constexpr for the constants:

constexpr std::array<std::string_view, 20> browserFeaturesToDisable {
[...]
};

constexpr std::array<std::string_view, 2> browserFeaturesToEnable {
[...]
};
Suggested change
CefRefPtr<CefRequestContext> rc = CefRequestContext::GetGlobalContext();
CefString err;
CefRefPtr<CefValue> off = CefValue::Create();
off->SetBool(false);
std::string opts[20] = {"autofill.credit_card_enabled",
CefRefPtr<CefRequestContext> requestContext = CefRequestContext::GetGlobalContext();
CefString errorMessage;
CefRefPtr<CefValue> optionValue = CefValue::Create();
optionValue->SetBool(false);
for (std::string_view feature : browserFeaturesToDisable) {
requestContext->SetPreference(feature.data(), optionValue.get(), errorMessage);
}
optionValue->SetBool(true);
for (std::string_view feature: browserFeatuesToEnable) {
requestContext->SetPreference(feature.data(), optionValue.get(), errorMessage);
}

I also wonder if this should maybe be wrapped in a lambda for each value, because any error while disabling this feature should be considered a critical failure (it would leave the browser with partially enabled Chromium runtime features which in turn could lead to unexpected behaviour and related support burden).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to mention - as per our code style guidelines those constexpr would also have to use the k prefix (kBrowserFeaturesToDisable).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easy enough. Can do.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes made outside of swapping to a lambda. I'll need to think on it some more, as majority of the features being disabled are mostly to avoid quirks. At the very least we could log the ones that fail to apply so we know to fix them. Happy to revisit.

@Phr3d13
Copy link
Copy Markdown

Phr3d13 commented May 27, 2026

how would I test this on arch? (sorry, really new to arch at the moment)

@tippfehlr
Copy link
Copy Markdown

It’s very nice to see this 🎉

Also – @hoshinolina: thanks for the CEF package in Fedora, it was very helpful when creating the Arch package ^^

@Phr3d13: I pushed obs-studio 32.1.2-5 and obs-studio-plugin-browser 32.1.2-5 with these changes to the [extra-testing] repository. Look at the Arch Wiki on how to use the testing repositories. Though be warned, that will install all testing packages, not just obs.

@WizardCM WizardCM force-pushed the chrome-runtime-changes branch from 6087ad4 to 8d898b5 Compare May 29, 2026 07:55
Copy link
Copy Markdown
Contributor

@tytan652 tytan652 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C strings leaks…

Comment thread obs-browser-plugin.cpp Outdated
Comment thread obs-browser-plugin.cpp Outdated
Comment thread obs-browser-plugin.cpp Outdated
Comment thread obs-browser-plugin.cpp Outdated
Comment thread obs-browser-plugin.cpp Outdated
Copy link
Copy Markdown
Contributor

@tytan652 tytan652 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue met while building on Arch and Flatpak.

Comment thread panel/browser-panel-client.cpp Outdated
Comment thread browser-app.hpp Outdated
@WizardCM WizardCM force-pushed the chrome-runtime-changes branch from 8d898b5 to d6f85aa Compare May 29, 2026 10:08
@Warchamp7 Warchamp7 added the priority/high This is an important PR label May 29, 2026
Comment thread browser-app.cpp
Comment thread browser-app.cpp
"translate",
"url_keyed_anonymized_data_collection.enabled"};

constexpr std::array<std::string_view, 2> kBrowserFeaturesToEnable{"extensions.block_external_extensions",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is clang-format's doing? 🙈

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, yes. :)

Comment thread browser-client.cpp Outdated
Comment thread panel/browser-panel.cpp Outdated
Comment thread browser-client.cpp Outdated
Comment thread obs-browser-plugin.cpp Outdated
Comment thread obs-browser-plugin.cpp Outdated
if (fs::exists(serviceProfiles)) {
// User has service integration cookies that also must be copied
for (auto const &dir_entry : std::filesystem::directory_iterator{serviceProfiles}) {
std::string name(dir_entry.path().filename().string());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::string name(dir_entry.path().filename().string());
std::string name{dir_entry.path().filename().u8string()};

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no instance of constructor matches the argument list
argument types are: (std::basic_string<char8_t, std::char_traits<char8_t>, std::allocator<char8_t>>)

Comment thread obs-browser-plugin.cpp Outdated
std::string oldDir(prefix + "/" + name);
std::string newDir(prefix + "_" + name);
MigrateProfileConfigDir(oldDir, newDir, true);
// Subdirectories do not need to be migrated because it is not the Default profile
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this comment relate to? The call to MigrateProfileConfigDir?

Copy link
Copy Markdown
Member Author

@WizardCM WizardCM May 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a leftover comment with the intent of describing why this particular path doesn't manually copy Local Storage, Session Storage or Network, but thinking on it now it's unnecessary.

Comment thread obs-browser-plugin.cpp
Comment thread obs-browser-plugin.cpp Outdated
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)

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/<cookie_id>) 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.
@WizardCM WizardCM force-pushed the chrome-runtime-changes branch from 08a3aa7 to 23ec123 Compare May 30, 2026 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. priority/high This is an important PR Seeking Testers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants