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

Merged
WizardCM merged 3 commits from chrome-runtime-changes into master 2026-09-10 13:11:48 -05:00
WizardCM commented 2026-05-23 22:43:37 -05:00 (Migrated from github.com)

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 7871/150 (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.

Originally, I tracked down a number of crashes when loading various chrome:// urls in the new runtime. This also exposed the fact that users would be able to access pages we didn't want them to see, such as chrome://settings. My original implementation therefore added a filter to block such pages from loading. Since then, CEF added its own whitelist to protect users from crashes, so my PR in its current form no longer has such protection. If it comes up, I can re-implement such protections in the future.

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.
### 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 7871/150 (yes, current stable) are now fully supported and work out of the box. > [!TIP] > * To use this with Chrome 145+ https://github.com/obsproject/obs-browser/pull/517 is required. 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`](https://github.com/chromium/chromium/blob/main/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. * https://github.com/chromiumembedded/cef/issues/3852 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. * https://github.com/chromiumembedded/cef/issues/3721 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. * https://github.com/chromiumembedded/cef/issues/3930 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. Originally, I tracked down a number of crashes when loading various `chrome://` urls in the new runtime. This also exposed the fact that users would be able to access pages we didn't want them to see, such as `chrome://settings`. My original implementation therefore added a filter to block such pages from loading. Since then, CEF added its own whitelist to protect users from crashes, so my PR in its current form no longer has such protection. If it comes up, I can re-implement such protections in the future. * The original issue https://github.com/chromiumembedded/cef/issues/3763 * The list of [allowed pages](https://github.com/chromiumembedded/cef/blob/ffa86e361d9c210136b539953663526add56191e/libcef/browser/alloy/alloy_browser_host_impl.cc#L66) * The [check itself](https://github.com/chromiumembedded/cef/blob/ffa86e361d9c210136b539953663526add56191e/libcef/browser/alloy/alloy_browser_host_impl.cc#L564-L570) ### 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: - [x] I have read the [**contributing** document](https://github.com/obsproject/obs-studio/blob/master/CONTRIBUTING.md). - [x] My code has been run through [clang-format](https://github.com/obsproject/obs-studio/blob/master/.clang-format). - [x] My code follows the project's [**style guidelines**](https://github.com/obsproject/obs-studio/blob/master/CODESTYLE.md) - [x] My code is not on the master branch. - [x] My code has been tested. - [x] All commit messages are properly formatted and commits squashed where appropriate. - [x] I have included updates to all appropriate documentation.
Warchamp7 (Migrated from github.com) reviewed 2026-05-23 22:43:37 -05:00
hoshinolina commented 2026-05-23 23:56:15 -05:00 (Migrated from github.com)

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)
  • #493 & #491 (merged)
  • #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) ^^

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) * #493 & #491 (merged) * #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](https://src.fedoraproject.org/rpms/cef/blob/rawhide/f/FindCEF.cmake) [cef.spec](https://src.fedoraproject.org/rpms/cef/blob/rawhide/f/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) \^\^
PatTheMav (Migrated from github.com) reviewed 2026-05-27 10:30:26 -05:00
@ -41,2 +45,4 @@
#endif
CefRefPtr<CefRenderProcessHandler> BrowserApp::GetRenderProcessHandler()
{
PatTheMav (Migrated from github.com) commented 2026-05-27 10:30:12 -05:00

Use constexpr for the constants:

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

constexpr std::array<std::string_view, 2> browserFeaturesToEnable {
[...]
};
	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).

Use `constexpr` for the constants: ```C++ constexpr std::array<std::string_view, 20> browserFeaturesToDisable { [...] }; constexpr std::array<std::string_view, 2> browserFeaturesToEnable { [...] }; ``` ```suggestion 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).
@ -56,0 +75,4 @@
CefRefPtr<CefValue> optionValue = CefValue::Create();
constexpr std::array<std::string_view, 20> kBrowserFeaturesToDisable{
"autofill.credit_card_enabled",
PatTheMav (Migrated from github.com) commented 2026-05-27 10:20:03 -05:00

What's the purpose of the "dummy" client? Some comment explaining the technical decision making process behind it would be helpful.

What's the purpose of the "dummy" client? Some comment explaining the technical decision making process behind it would be helpful.
@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
}
PatTheMav (Migrated from github.com) commented 2026-05-27 10:19:16 -05:00

Some high-level explanation of the issue in addition to the issue link would be helpful. Also a FIXME or TODO lead-in would be great as most IDEs and syntax-highlighters detect those.

Some high-level explanation of the issue in addition to the issue link would be helpful. Also a `FIXME` or `TODO` lead-in would be great as most IDEs and syntax-highlighters detect those.
@ -293,6 +293,9 @@ static void BrowserInit(void)
CefString(&settings.log_file) = log_path_abs;
PatTheMav (Migrated from github.com) commented 2026-05-27 09:57:05 -05:00

What's the reason to guard this against versions 7103 and up?

What's the reason to guard this against versions 7103 and up?
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
PatTheMav (Migrated from github.com) commented 2026-05-27 10:01:23 -05:00

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.

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.
PatTheMav (Migrated from github.com) commented 2026-05-27 10:16:57 -05:00

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:

	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.

Use `constexpr` for the constants: ```C++ constexpr std::array<std::string_view, 3> migrationDirectories { "Local Storage", "Session Storage", "Network", }; constexpr std::string_view defaultDirectoryPrefix {"Default/"}; ``` Which enables: ```suggestion 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.
PatTheMav (Migrated from github.com) commented 2026-05-27 10:17:58 -05:00

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.

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.
PatTheMav (Migrated from github.com) commented 2026-05-27 09:54:50 -05:00

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

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).
Phr3d13 commented 2026-05-27 11:39:42 -05:00 (Migrated from github.com)

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

how would I test this on arch? (sorry, really new to arch at the moment)
PatTheMav (Migrated from github.com) reviewed 2026-05-27 18:25:17 -05:00
@ -41,2 +45,4 @@
#endif
CefRefPtr<CefRenderProcessHandler> BrowserApp::GetRenderProcessHandler()
{
PatTheMav (Migrated from github.com) commented 2026-05-27 18:25:17 -05:00

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

Forgot to mention - as per our code style guidelines those `constexpr` would also have to use the `k` prefix (`kBrowserFeaturesToDisable`).
tippfehlr commented 2026-05-27 23:53:22 -05:00 (Migrated from github.com)

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.

It’s very nice to see this :tada: 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](https://wiki.archlinux.org/title/Official_repositories#Testing_repositories). Though be warned, that will install all testing packages, not just obs.
WizardCM (Migrated from github.com) reviewed 2026-05-28 04:55:12 -05:00
@ -41,2 +45,4 @@
#endif
CefRefPtr<CefRenderProcessHandler> BrowserApp::GetRenderProcessHandler()
{
WizardCM (Migrated from github.com) commented 2026-05-28 04:43:08 -05:00

Easy enough. Can do.

Easy enough. Can do.
@ -56,0 +75,4 @@
CefRefPtr<CefValue> optionValue = CefValue::Create();
constexpr std::array<std::string_view, 20> kBrowserFeaturesToDisable{
"autofill.credit_card_enabled",
WizardCM (Migrated from github.com) commented 2026-05-28 04:42:59 -05:00

I documented it in the commit description and the PR description, but I'll add the note in code as well.

I documented it in the commit description and the PR description, but I'll add the note in code as well.
@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
}
WizardCM (Migrated from github.com) commented 2026-05-28 04:43:18 -05:00

Can do.

Can do.
@ -293,6 +293,9 @@ static void BrowserInit(void)
CefString(&settings.log_file) = log_path_abs;
WizardCM (Migrated from github.com) commented 2026-05-28 04:45:23 -05:00

During the transition period, CEF shipped with a toggle. After CEF 128 (6613), it was no longer needed because the Alloy Runtime was entirely removed - therefore the toggle would error. This ensures compatibility with intermediary versions, though if we wanted to we could lock it purely to 6613. I left it in because it was, on occasion, useful in 6533 (127, what we currently ship) to test with.

During the transition period, CEF shipped with a toggle. After CEF 128 (6613), it was no longer needed because the Alloy Runtime was entirely removed - therefore the toggle would error. This ensures compatibility with intermediary versions, though if we wanted to we could lock it purely to 6613. I left it in because it was, on occasion, useful in 6533 (127, what we currently ship) to test with.
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-28 04:51:39 -05:00

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?

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?
WizardCM (Migrated from github.com) commented 2026-05-28 04:54:49 -05:00

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.

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 `nullptr`s. I'll revisit.
WizardCM (Migrated from github.com) commented 2026-05-28 04:42:37 -05:00

Can do.

Can do.
PatTheMav (Migrated from github.com) reviewed 2026-05-28 09:51:48 -05:00
@ -293,6 +293,9 @@ static void BrowserInit(void)
CefString(&settings.log_file) = log_path_abs;
PatTheMav (Migrated from github.com) commented 2026-05-28 09:29:13 -05:00

If it leads to an error starting with 6613, shouldn't it then be limited to versions before 6613 and not potentially lead to errors for all versions up to 7103?

If it leads to an error starting with 6613, shouldn't it then be limited to versions before 6613 and not potentially lead to errors for all versions up to 7103?
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
PatTheMav (Migrated from github.com) commented 2026-05-28 09:48:34 -05:00

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.

Without consulting a compiler, I'd probably do: ```C++ 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.
PatTheMav (Migrated from github.com) commented 2026-05-28 09:51:02 -05:00

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.

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.
WizardCM (Migrated from github.com) reviewed 2026-05-29 02:56:47 -05:00
@ -293,6 +293,9 @@ static void BrowserInit(void)
CefString(&settings.log_file) = log_path_abs;
WizardCM (Migrated from github.com) commented 2026-05-29 02:56:47 -05:00

Yeah, I re-checked the CEF docs and it's gone as early as 6613, so I've updated the check.

Yeah, I re-checked the CEF docs and it's gone as early as 6613, so I've updated the check.
WizardCM (Migrated from github.com) reviewed 2026-05-29 02:57:35 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 02:57:35 -05:00

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

Done. Let me know if there are additional concerns outside of the lack of u8path handling due to C++20 deprecation warnings.
WizardCM (Migrated from github.com) reviewed 2026-05-29 02:58:31 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 02:58:31 -05:00

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.

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.
WizardCM (Migrated from github.com) reviewed 2026-05-29 02:58:39 -05:00
@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
}
WizardCM (Migrated from github.com) commented 2026-05-29 02:58:38 -05:00

Done.

Done.
WizardCM (Migrated from github.com) reviewed 2026-05-29 02:58:49 -05:00
@ -56,0 +75,4 @@
CefRefPtr<CefValue> optionValue = CefValue::Create();
constexpr std::array<std::string_view, 20> kBrowserFeaturesToDisable{
"autofill.credit_card_enabled",
WizardCM (Migrated from github.com) commented 2026-05-29 02:58:48 -05:00

Note added.

Note added.
WizardCM (Migrated from github.com) reviewed 2026-05-29 02:59:59 -05:00
@ -41,2 +45,4 @@
#endif
CefRefPtr<CefRenderProcessHandler> BrowserApp::GetRenderProcessHandler()
{
WizardCM (Migrated from github.com) commented 2026-05-29 02:59:59 -05:00

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.

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.
tytan652 (Migrated from github.com) requested changes 2026-05-29 03:56:45 -05:00
tytan652 (Migrated from github.com) left a comment

C strings leaks…

C strings leaks…
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
tytan652 (Migrated from github.com) commented 2026-05-29 03:53:32 -05:00

Leaks the C string

Leaks the C string
tytan652 (Migrated from github.com) commented 2026-05-29 03:53:58 -05:00

Leaks the C string

Leaks the C string
tytan652 (Migrated from github.com) commented 2026-05-29 03:54:04 -05:00

Leaks the C string

Leaks the C string
tytan652 (Migrated from github.com) commented 2026-05-29 03:54:12 -05:00

Leaks the C string

Leaks the C string
tytan652 (Migrated from github.com) commented 2026-05-29 03:54:20 -05:00

Leaks the C string

Leaks the C string
tytan652 (Migrated from github.com) requested changes 2026-05-29 04:01:49 -05:00
tytan652 (Migrated from github.com) left a comment

Issue met while building on Arch and Flatpak.

Issue met while building on Arch and Flatpak.
@ -106,5 +109,9 @@ public:
QTimer frameTimer;
tytan652 (Migrated from github.com) commented 2026-05-29 04:01:21 -05:00
error: type qualifiers ignored on function return type [-Werror=ignored-qualifiers]
  114 |         BrowserDummyClient *const GetDummy() { return dummy.get(); };
      |         ^~~~~~~~~~~~~~~~~~
``` error: type qualifiers ignored on function return type [-Werror=ignored-qualifiers] 114 | BrowserDummyClient *const GetDummy() { return dummy.get(); }; | ^~~~~~~~~~~~~~~~~~ ```
@ -68,10 +75,18 @@ CefRefPtr<CefJSDialogHandler> QCefBrowserClient::GetJSDialogHandler()
return this;
tytan652 (Migrated from github.com) commented 2026-05-29 03:58:48 -05:00

Unused params

bool QCefBrowserClient::OnChromeCommand(CefRefPtr<CefBrowser> , int, cef_window_open_disposition_t)
Unused params ```suggestion bool QCefBrowserClient::OnChromeCommand(CefRefPtr<CefBrowser> , int, cef_window_open_disposition_t) ```
WizardCM (Migrated from github.com) reviewed 2026-05-29 05:10:36 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 05:10:36 -05:00

Fixed using my original solution. If Pat knows of a cleaner way, I'll make the additional adjustment.

Fixed using my original solution. If Pat knows of a cleaner way, I'll make the additional adjustment.
WizardCM (Migrated from github.com) reviewed 2026-05-29 05:10:42 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 05:10:42 -05:00

Fixed.

Fixed.
WizardCM (Migrated from github.com) reviewed 2026-05-29 05:10:49 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 05:10:49 -05:00

Fixed.

Fixed.
WizardCM (Migrated from github.com) reviewed 2026-05-29 05:11:02 -05:00
@ -68,10 +75,18 @@ CefRefPtr<CefJSDialogHandler> QCefBrowserClient::GetJSDialogHandler()
return this;
WizardCM (Migrated from github.com) commented 2026-05-29 05:11:02 -05:00

Oops, not sure how I missed that one. Fixed.

Oops, not sure how I missed that one. Fixed.
WizardCM (Migrated from github.com) reviewed 2026-05-29 05:11:22 -05:00
@ -106,5 +109,9 @@ public:
QTimer frameTimer;
WizardCM (Migrated from github.com) commented 2026-05-29 05:11:22 -05:00

I don't fully understand the error message itself, but I think I've fixed it. Please confirm.

I don't fully understand the error message itself, but I think I've fixed it. Please confirm.
tytan652 (Migrated from github.com) reviewed 2026-05-29 05:37:27 -05:00
@ -106,5 +109,9 @@ public:
QTimer frameTimer;
tytan652 (Migrated from github.com) commented 2026-05-29 05:37:27 -05:00

It works

It works
tytan652 (Migrated from github.com) reviewed 2026-05-29 05:45:52 -05:00
@ -106,5 +109,9 @@ public:
QTimer frameTimer;
tytan652 (Migrated from github.com) commented 2026-05-29 05:45:52 -05:00

I don't fully understand the error message itself

The type was BrowserDummyClient *const but the function return BrowserDummyClient * so mismatch, the const qualifier was assigned to the type not to the function.

> I don't fully understand the error message itself The type was `BrowserDummyClient *const` but the function return `BrowserDummyClient *` so mismatch, the `const` qualifier was assigned to the type not to the function.
PatTheMav (Migrated from github.com) reviewed 2026-05-29 16:56:51 -05:00
@ -56,0 +75,4 @@
CefRefPtr<CefValue> optionValue = CefValue::Create();
constexpr std::array<std::string_view, 20> kBrowserFeaturesToDisable{
"autofill.credit_card_enabled",
PatTheMav (Migrated from github.com) commented 2026-05-29 16:08:58 -05:00

Requires use of C++-style comments and not C-style comments, per code style guidelines.

Requires use of C++-style comments and not C-style comments, per code style guidelines.
@ -56,0 +103,4 @@
for (std::string_view feature : kBrowserFeaturesToDisable) {
requestContext->SetPreference(feature.data(), optionValue.get(), errorMessage);
}
PatTheMav (Migrated from github.com) commented 2026-05-29 16:10:31 -05:00

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

I guess this is `clang-format`'s doing? 🙈
@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
}
PatTheMav (Migrated from github.com) commented 2026-05-29 16:11:19 -05:00
	// FIXME: https://github.com/chromiumembedded/cef/issues/3852
```suggestion // FIXME: https://github.com/chromiumembedded/cef/issues/3852 ```
PatTheMav (Migrated from github.com) commented 2026-05-29 16:16:42 -05:00

Wouldn't be sufficient to decorate the frame argument with [[maybe_unused]]?

Wouldn't be sufficient to decorate the `frame` argument with `[[maybe_unused]]`?
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
PatTheMav (Migrated from github.com) commented 2026-05-29 16:22:43 -05:00
	fs::path rootPath{configPath.Get()};

Prefer direct-list-initialization to catch possible narrowing conversions.

```suggestion fs::path rootPath{configPath.Get()}; ``` Prefer direct-list-initialization to catch possible narrowing conversions.
PatTheMav (Migrated from github.com) commented 2026-05-29 16:23:13 -05:00
	fs::path defaultPath{defaultConfigPath.Get()};

See above.

```suggestion fs::path defaultPath{defaultConfigPath.Get()}; ``` See above.
PatTheMav (Migrated from github.com) commented 2026-05-29 16:23:35 -05:00
	fs::path localPrefs{localPrefsConfigPath.Get()};
```suggestion fs::path localPrefs{localPrefsConfigPath.Get()}; ```
PatTheMav (Migrated from github.com) commented 2026-05-29 16:24:43 -05:00
	fs::path newLocalPrefs{newLocalPrefsConfigPath.Get()};
```suggestion fs::path newLocalPrefs{newLocalPrefsConfigPath.Get()}; ```
PatTheMav (Migrated from github.com) commented 2026-05-29 16:25:35 -05:00

Replace with constexpr std::string_view constant.

Replace with `constexpr std::string_view` constant.
PatTheMav (Migrated from github.com) commented 2026-05-29 16:25:47 -05:00
	fs::path serviceProfiles{serviceProfilesPath.Get()};
```suggestion fs::path serviceProfiles{serviceProfilesPath.Get()}; ```
PatTheMav (Migrated from github.com) commented 2026-05-29 16:27:23 -05:00
			std::string name{dir_entry.path().filename().u8string()};
```suggestion std::string name{dir_entry.path().filename().u8string()}; ```
PatTheMav (Migrated from github.com) commented 2026-05-29 16:27:59 -05:00

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

What does this comment relate to? The call to `MigrateProfileConfigDir`?
PatTheMav (Migrated from github.com) commented 2026-05-29 16:52:31 -05:00
			std::string oldDir{prefix};
			oldDir.append("/");
			oldDir.append(name);

			std::string newDir{prefix};
			newDir.append("_");
			newDir.append(name);

Makes it easier to debug the code and keeps the amount of temporaries low.

The existing implementation is equivalent to:

std::string temp1 = prefix;
std::string temp2 = temp1.append("/");
std::string oldDir = temp2.append(name);

Which potentially involves a whole lot of copies after reallocations, and at least according to compiler explorer the resulting code uses about twice as many instructions even with -O3 optimisation.

```suggestion std::string oldDir{prefix}; oldDir.append("/"); oldDir.append(name); std::string newDir{prefix}; newDir.append("_"); newDir.append(name); ``` Makes it easier to debug the code and keeps the amount of temporaries low. The existing implementation is equivalent to: ```C++ std::string temp1 = prefix; std::string temp2 = temp1.append("/"); std::string oldDir = temp2.append(name); ``` Which potentially involves a whole lot of copies after reallocations, and at least according to compiler explorer the resulting code uses about twice as many instructions even with `-O3` optimisation.
@ -717,2 +832,4 @@
#endif
bool obs_module_load(void)
{
PatTheMav (Migrated from github.com) commented 2026-05-29 16:30:10 -05:00

Wouldn't it make more sense to move this check to before extracting the string (which will create temporary path objects on the way there) and bail out early?

Wouldn't it make more sense to move this check to before extracting the string (which will create temporary path objects on the way there) and bail out early?
PatTheMav (Migrated from github.com) commented 2026-05-29 16:14:36 -05:00
	// TODO: Update obs-studio to use the new path structure due to subdirectories not being supported by CEF

Is also missing a link to the task issue on the main repo.

Applies below as well.

```suggestion // TODO: Update obs-studio to use the new path structure due to subdirectories not being supported by CEF ``` Is also missing a link to the task issue on the main repo. Applies below as well.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:34:17 -05:00
@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
}
WizardCM (Migrated from github.com) commented 2026-05-29 19:34:17 -05:00

It would be the first instance in the entire OBS codebase, but I'm not against trying something new!

It would be the first instance in the entire OBS codebase, but I'm not against trying something new!
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:34:37 -05:00
@ -56,0 +103,4 @@
for (std::string_view feature : kBrowserFeaturesToDisable) {
requestContext->SetPreference(feature.data(), optionValue.get(), errorMessage);
}
WizardCM (Migrated from github.com) commented 2026-05-29 19:34:36 -05:00

It is, yes. :)

It is, yes. :)
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:37:37 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:37:36 -05:00

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.

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.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:39:42 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:39:42 -05:00

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>>)

> 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>>)
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:40:45 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:40:45 -05:00

Done.

Done.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:40:51 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:40:51 -05:00

Done.

Done.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:40:58 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:40:57 -05:00

Done.

Done.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:41:13 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:41:13 -05:00

Done. Switched to using .data() for the libobs call.

Done. Switched to using `.data()` for the libobs call.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:41:19 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:41:19 -05:00

Done.

Done.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:41:34 -05:00
@ -717,2 +832,4 @@
#endif
bool obs_module_load(void)
{
WizardCM (Migrated from github.com) commented 2026-05-29 19:41:34 -05:00

You're right. Fixed.

You're right. Fixed.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:42:11 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:42:11 -05:00

Unfortunate, I love a one-liner. Fixed.

Unfortunate, I love a one-liner. Fixed.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:43:20 -05:00
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
WizardCM (Migrated from github.com) commented 2026-05-29 19:43:20 -05:00

Done.

Done.
WizardCM (Migrated from github.com) reviewed 2026-05-29 19:44:33 -05:00
@ -56,0 +75,4 @@
CefRefPtr<CefValue> optionValue = CefValue::Create();
constexpr std::array<std::string_view, 20> kBrowserFeaturesToDisable{
"autofill.credit_card_enabled",
WizardCM (Migrated from github.com) commented 2026-05-29 19:44:33 -05:00

Oops, my JavaScript Developer is showing. Fixed.

Oops, my JavaScript Developer is showing. Fixed.
WizardCM (Migrated from github.com) reviewed 2026-05-29 20:08:11 -05:00
@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
}
WizardCM (Migrated from github.com) commented 2026-05-29 20:08:11 -05:00

Seems to build fine on both Windows and Linux, therefore marking as resolved.

Seems to build fine on both Windows and Linux, therefore marking as resolved.
WizardCM (Migrated from github.com) reviewed 2026-05-29 20:08:31 -05:00
WizardCM (Migrated from github.com) commented 2026-05-29 20:08:31 -05:00

Took me a bit to figure out how to create a Task. Done.

Took me a bit to figure out how to create a Task. Done.
WizardCM (Migrated from github.com) reviewed 2026-05-29 20:08:42 -05:00
@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
}
WizardCM (Migrated from github.com) commented 2026-05-29 20:08:42 -05:00

Done, and created Task.

Done, and created Task.
PatTheMav (Migrated from github.com) reviewed 2026-05-30 07:53:58 -05:00
PatTheMav (Migrated from github.com) left a comment

Looks fine otherwise. Haven't given this a test on my machine yet though, will do so soon.

Looks fine otherwise. Haven't given this a test on my machine yet though, will do so soon.
@ -553,1 +569,4 @@
}
} // namespace
QCefCookieManager *QCefInternal::create_cookie_manager(const std::string &storage_path, bool persist_session_cookies)
PatTheMav (Migrated from github.com) commented 2026-05-30 07:45:32 -05:00
	// 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
```suggestion // 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 ```
WizardCM (Migrated from github.com) reviewed 2026-05-31 08:43:05 -05:00
@ -553,1 +569,4 @@
}
} // namespace
QCefCookieManager *QCefInternal::create_cookie_manager(const std::string &storage_path, bool persist_session_cookies)
WizardCM (Migrated from github.com) commented 2026-05-31 08:43:05 -05:00

Fixed

Fixed
WizardCM commented 2026-05-31 08:54:41 -05:00 (Migrated from github.com)

I've rebased and pushed the latest review fixes, with a couple minor additions

  1. An os_event_try(cef_started_event) check to QCefWidgetInternal::Resize() to fix a crash when the user has no browser sources but does have browser docks. This is initiated by the restoreDockState function before CEF has initialised. Docks do correctly resize once they've loaded.
    • I could instead put this in bool QueueCEFTask for ultimate protection. Open to feedback on this.
  2. Disabling of StorageNotificationService just in case

Additionally, after some testing from Warchamp, we have confirmed the two following regressions

  1. The remote debugging hosted webpage doesn't load. Current running theory is the shell_devtools_discovery_page.html resource is not being shipped in the .pak file by CEF, so the website returns an empty string. We are experimenting with options. Users do have the alternative of using chrome://inspect from a desktop browser instead, though it is very slow to refresh its list and currently displays duplicates.
  2. Access to media devices (eg mic) and network devices is implicitly denied by browser docks due to running on the Alloy style. Our options are to either implicitly allow, or implement a permission UX of some kind. This will likely be via a separate PR.

Additional area I'm looking into, which may be separate PR

  1. Dev Tools windows (right click Inspect from a browser dock) currently display a Chromium icon and a profile badge on Windows. To fix, this may require a change to obs.rc.in in the main repo if I don't successfully guess the icon ID number.
I've rebased and pushed the latest review fixes, with a couple minor additions 1. An `os_event_try(cef_started_event)` check to `QCefWidgetInternal::Resize()` to fix a crash when the user has no browser sources but does have browser docks. This is initiated by the `restoreDockState` function before CEF has initialised. Docks do correctly resize once they've loaded. * I could instead put this in `bool QueueCEFTask` for ultimate protection. Open to feedback on this. 3. Disabling of `StorageNotificationService` just in case * https://github.com/chromiumembedded/cef/issues/3966 Additionally, after some testing from Warchamp, we have confirmed the two following regressions 1. The remote debugging hosted webpage doesn't load. Current running theory is the `shell_devtools_discovery_page.html` resource is not being shipped in the `.pak` file by CEF, so the website returns an empty string. We are experimenting with options. Users do have the alternative of using `chrome://inspect` from a desktop browser instead, though it is very slow to refresh its list and currently displays duplicates. 2. Access to media devices (eg mic) and network devices is implicitly denied by browser docks due to running on the Alloy style. Our options are to either implicitly allow, or implement a permission UX of some kind. This will likely be via a separate PR. * https://github.com/WizardCM/obs-browser/commit/4c65eaaf4044240f16e15a0258db3023b79945d7 Additional area I'm looking into, which may be separate PR 1. Dev Tools windows (right click Inspect from a browser dock) currently display a Chromium icon and a profile badge on Windows. To fix, this may require a change to `obs.rc.in` in the main repo if I don't successfully guess the icon ID number.
tytan652 (Migrated from github.com) reviewed 2026-05-31 09:31:59 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
}
tytan652 (Migrated from github.com) commented 2026-05-31 09:31:59 -05:00

?

		disableFeatures += ",StorageNotificationService";
? ```suggestion disableFeatures += ",StorageNotificationService"; ```
tippfehlr commented 2026-05-31 09:53:34 -05:00 (Migrated from github.com)

@tytan652 I saw your 👎, is that only related to generally using obs-browser with cef 148, or is it related to applying the patch? If it’s the latter, could you elaborate?

As far as I know, obs-browser worked before this patch, and it still works with this patch.

@tytan652 I saw your :-1:, is that only related to generally using obs-browser with cef 148, or is it related to applying the patch? If it’s the latter, could you elaborate? As far as I know, obs-browser worked before this patch, and it still works with this patch.
mihawk90 commented 2026-05-31 15:35:40 -05:00 (Migrated from github.com)

2. Access to media devices (eg mic) and network devices is implicitly denied by browser docks due to running on the Alloy style. Our options are to either implicitly allow, or implement a permission UX of some kind. This will likely be via a separate PR.

Is this not what the --enable-media-stream currently does? Is that removed in newer Chromium versions?

I've been using this for a while for browser docks to have access to output device listing, but AFAIK this also allows input device listing.

> 2\. Access to media devices (eg mic) and network devices is implicitly denied by browser docks due to running on the Alloy style. Our options are to either implicitly allow, or implement a permission UX of some kind. This will likely be via a separate PR. Is this not what the `--enable-media-stream` currently does? Is that removed in newer Chromium versions? I've been using this for a while for browser docks to have access to output device listing, but AFAIK this also allows input device listing.
WizardCM (Migrated from github.com) reviewed 2026-06-01 04:14:09 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
}
WizardCM (Migrated from github.com) commented 2026-06-01 04:14:09 -05:00

Of course I did something this silly. Fixed.

Of course I did something this silly. Fixed.
WizardCM commented 2026-06-01 04:16:37 -05:00 (Migrated from github.com)
  1. Access to media devices (eg mic) and network devices is implicitly denied by browser docks due to running on the Alloy style. Our options are to either implicitly allow, or implement a permission UX of some kind. This will likely be via a separate PR.

Is this not what the --enable-media-stream currently does? Is that removed in newer Chromium versions?

I've been using this for a while for browser docks to have access to output device listing, but AFAIK this also allows input device listing.

I'm unsure if it was removed, or deprecated in favour of other things. At the very least, CEF explicitly denies the request in the Chrome Runtime's Alloy Style when it didn't in the Alloy Runtime.

We could, as a temporary solution, read the flag and manually allow those calls for now, but ultimately I think having some kind of UI for it would be better, especially as there's a proper API for it.

> > 2. Access to media devices (eg mic) and network devices is implicitly denied by browser docks due to running on the Alloy style. Our options are to either implicitly allow, or implement a permission UX of some kind. This will likely be via a separate PR. > > Is this not what the `--enable-media-stream` currently does? Is that removed in newer Chromium versions? > > I've been using this for a while for browser docks to have access to output device listing, but AFAIK this also allows input device listing. I'm unsure if it was removed, or deprecated in favour of other things. At the very least, CEF explicitly denies the request in the Chrome Runtime's Alloy Style when it didn't in the Alloy Runtime. We could, as a temporary solution, read the flag and manually allow those calls for now, but ultimately I think having some kind of UI for it would be better, especially as there's a proper API for it.
kurehajime commented 2026-06-22 03:05:23 -05:00 (Migrated from github.com)

Thanks a lot for working on this — I’m really looking forward to using a modern Chromium build in browser sources.

I have one request.
If possible, I’d like you to consider including IndexedDB in the scope of this migration.

github.com/WizardCM/obs-browser@6d1031e0e8/obs-browser-plugin.cpp (L749-L755)

Thanks again!

Thanks a lot for working on this — I’m really looking forward to using a modern Chromium build in browser sources. I have one request. If possible, I’d like you to consider including IndexedDB in the scope of this migration. https://github.com/WizardCM/obs-browser/blob/6d1031e0e88b31a19a0fbc96ed101cb1b1a596b3/obs-browser-plugin.cpp#L749-L755 Thanks again!
WizardCM commented 2026-06-22 04:14:37 -05:00 (Migrated from github.com)

I have one request. If possible, I’d like you to consider including IndexedDB in the scope of this migration.

Thanks again!

Thanks for the feedback! I see my mistake was using a barebones scene collection with basic browser sources & docks as a testing ground - there do seem to be other directories, like IndexedDB (and probably Service Worker or WebStorage) that should be migrated too. I'll have to do some additional research on this and I'll update the PR.

> I have one request. If possible, I’d like you to consider including IndexedDB in the scope of this migration. > > Thanks again! Thanks for the feedback! I see my mistake was using a barebones scene collection with basic browser sources & docks as a testing ground - there do seem to be other directories, like IndexedDB (and probably Service Worker or WebStorage) that should be migrated too. I'll have to do some additional research on this and I'll update the PR.
PatTheMav (Migrated from github.com) reviewed 2026-06-25 07:07:32 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
}
PatTheMav (Migrated from github.com) commented 2026-06-25 07:04:27 -05:00

At this point, wouldn't it be more efficient to do this:

#ifdef _WIN32
constexpr bool kIsPlatformWindows = true;
#else
constexpr bool kIsPlatformWindows = false;
#endif

[...]

void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefCommandLine> command_line)
{

[...]

// See https://github.com/chromiumembedded/cef/issues/3966 for 'StorageNotificationService' requirement
constexpr std::string_view kDefaultDisabledFeatures{
    "HardwareMediaKeyHandling,WebBluetooth,MediaRouter,"
    "CalculateNativeWinOcclusion,LiveCaption,StorageNotificationService,"
    "EnableWindowsGamingInputDataFetcher"};

std::string disableFeatures{};

if constexpr (kIsPlatformWindows) {
    disableFeatures.append(kDefaultDisabledFeatures);
} else {
    constexpr std::string_view kWindowsFeature{",EnableWindowsGamingInputDataFetcher"};
    constexpr size_t kNonWindowsLength = kDefaultDisabledFeatures.size() - kWindowsFeature.size();
    disableFeatures.append(kDefaultDisabledFeatures.substr(0, kNonWindowsLength);
}

if (command_line->HasSwitch("disable-features")) {
    disableFeatures.append(",");
    disableFeatures.append(command_line->GetSwitchValue("disable_features"));
}

command_line->AppendSwitchWithValue("disable-features", disableFeatures);

[...]

The constexpr values are all evaluated at compile time, so the resulting code will only contain a single disableFeatures.append(char const*, unsigned long) call, with the correct length value computed by the compiler.

This brings the code down to a single append operation by default, and at most 3 append operations in the case of an existing disable-features command-line argument.

Because Windows always requires this feature to be disabled, appending it at runtime would be a permanent runtime cost that this variant avoids.

(As an added bonus, converting the preprocessor statement into constexpr bool and using if constexpr makes navigating multi-platform code easier because IDEs and language servers will now "see" the entire code again.)

At this point, wouldn't it be more efficient to do this: ```C++ #ifdef _WIN32 constexpr bool kIsPlatformWindows = true; #else constexpr bool kIsPlatformWindows = false; #endif [...] void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefCommandLine> command_line) { [...] // See https://github.com/chromiumembedded/cef/issues/3966 for 'StorageNotificationService' requirement constexpr std::string_view kDefaultDisabledFeatures{ "HardwareMediaKeyHandling,WebBluetooth,MediaRouter," "CalculateNativeWinOcclusion,LiveCaption,StorageNotificationService," "EnableWindowsGamingInputDataFetcher"}; std::string disableFeatures{}; if constexpr (kIsPlatformWindows) { disableFeatures.append(kDefaultDisabledFeatures); } else { constexpr std::string_view kWindowsFeature{",EnableWindowsGamingInputDataFetcher"}; constexpr size_t kNonWindowsLength = kDefaultDisabledFeatures.size() - kWindowsFeature.size(); disableFeatures.append(kDefaultDisabledFeatures.substr(0, kNonWindowsLength); } if (command_line->HasSwitch("disable-features")) { disableFeatures.append(","); disableFeatures.append(command_line->GetSwitchValue("disable_features")); } command_line->AppendSwitchWithValue("disable-features", disableFeatures); [...] ``` The `constexpr` values are all evaluated at compile time, so the resulting code will only contain a single `disableFeatures.append(char const*, unsigned long)` call, with the correct length value computed by the compiler. This brings the code down to a single `append` operation by default, and at most 3 `append` operations in the case of an existing `disable-features` command-line argument. Because Windows always requires this feature to be disabled, appending it at runtime would be a permanent runtime cost that this variant avoids. (As an added bonus, converting the preprocessor statement into `constexpr bool` and using `if constexpr` makes navigating multi-platform code easier because IDEs and language servers will now "see" the entire code again.)
@ -554,1 +573,4 @@
{
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
PatTheMav (Migrated from github.com) commented 2026-06-25 05:59:09 -05:00

See above, this should also use the helper function.

See above, this should also use the helper function.
@ -565,1 +590,3 @@
BPtr<char> 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
PatTheMav (Migrated from github.com) commented 2026-06-25 06:04:03 -05:00
#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 path = getUpdatedCookiePath(storage_path);
#endif

As this code is effectively implemented twice, it should be factored out:

namespace {
std::string getUpdatedCookiePath(std::string storagePath)
{
	constexpr std::string_view searchToken{"obs_profile_cookies/};
	constexpr std::string_view replaceToken{"obs_profile_cookies_}"

	size_t pos = storage_path.find(searchToken);
	if (pos != std::string::npos) {
        storage_path.replace(pos, legacy.length(), replaceToken);
	}

	return storagePath;
}
}

Which enables the suggestion at the top. The helper function gets its own copy of the referenced string by default, and I would also expect this code to be optimised via RVO, so there's only a single copy involved (the string is created in the calling function).

It's a bit unfortunate that this copy is done unconditionally, even though over time there should be less and less users for which this "upgrade" (and thus copy) is necessary, but it's a drop in the bucket for all the string copies and mutations we do in the program already.

```suggestion #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 path = getUpdatedCookiePath(storage_path); #endif ``` As this code is effectively implemented twice, it should be factored out: ```C++ namespace { std::string getUpdatedCookiePath(std::string storagePath) { constexpr std::string_view searchToken{"obs_profile_cookies/}; constexpr std::string_view replaceToken{"obs_profile_cookies_}" size_t pos = storage_path.find(searchToken); if (pos != std::string::npos) { storage_path.replace(pos, legacy.length(), replaceToken); } return storagePath; } } ``` Which enables the suggestion at the top. The helper function gets its own copy of the referenced string by default, and I would also expect this code to be optimised via RVO, so there's only a single copy involved (the string is created in the calling function). It's a bit unfortunate that this copy is done unconditionally, even though over time there should be less and less users for which this "upgrade" (and thus copy) is necessary, but it's a drop in the bucket for all the string copies and mutations we do in the program already.
WizardCM (Migrated from github.com) reviewed 2026-06-26 04:29:49 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
}
WizardCM (Migrated from github.com) commented 2026-06-26 04:29:49 -05:00

Yeah when I was preparing this PR the same consideration had crossed my mind, but I felt it was out of scope and that I'd do it first thing after this PR is merged to make this one more straightforward to review. Let me know which you'd prefer - a separate PR post-merge or a separate commit within this PR and I'll get it done.

Yeah when I was preparing this PR the same consideration had crossed my mind, but I felt it was out of scope and that I'd do it first thing after this PR is merged to make this one more straightforward to review. Let me know which you'd prefer - a separate PR post-merge or a separate commit within this PR and I'll get it done.
PatTheMav (Migrated from github.com) reviewed 2026-06-26 06:04:15 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
}
PatTheMav (Migrated from github.com) commented 2026-06-26 06:04:15 -05:00

I personally prefer the "if I'm already here making changes, I might as well clean this up and make it nice, tidy, and modern" approach (because then it's done with and not added to a pile of stuff that is usually forgotten), but @RytoEX is more selective in this regard.

I defer to him on this one.

I personally prefer the "if I'm already here making changes, I might as well clean this up and make it nice, tidy, and modern" approach (because then it's done with and not added to a pile of stuff that is usually forgotten), but @RytoEX is more selective in this regard. I defer to him on this one.
RytoEX (Migrated from github.com) reviewed 2026-06-26 14:18:14 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
}
RytoEX (Migrated from github.com) commented 2026-06-26 14:18:14 -05:00

Feel free to add it as a separate commit and I'll evaluate if it should be squashed or merged as a separate PR.

Feel free to add it as a separate commit and I'll evaluate if it should be squashed or merged as a separate PR.
WizardCM (Migrated from github.com) reviewed 2026-06-27 23:01:15 -05:00
@ -565,1 +590,3 @@
BPtr<char> 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
WizardCM (Migrated from github.com) commented 2026-06-27 23:01:15 -05:00

This code path literally won't be around that long, it'll only exist for as long as it takes to merge this PR, create a PR in obs-studio, and then merge that PR - ideally never actually released. Or the check could be reversed I guess? I have made the change, let me know if it needs any additional adjustment.

This code path literally won't be around that long, it'll only exist for as long as it takes to merge this PR, create a PR in obs-studio, and then merge that PR - ideally never actually released. Or the check could be reversed I guess? I have made the change, let me know if it needs any additional adjustment.
WizardCM (Migrated from github.com) reviewed 2026-06-27 23:01:29 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
}
WizardCM (Migrated from github.com) commented 2026-06-27 23:01:29 -05:00

Added as a separate commit.

Added as a separate commit.
out commented 2026-07-19 20:51:13 -05:00 (Migrated from github.com)

Tested this on macOS arm64 (Apple silicon) against CEF 147 — browser sources render, Chrome Runtime initialises, and the Alloy→Chrome Runtime profile migration completes cleanly against a real long-lived profile. Thanks for grinding through this one.

Two macOS-specific notes:

#517 isn't quite enough on macOS. It bumps the plugin and the Linux helper, but cmake/os-macos.cmake builds four more helper executables that also compile the CEF headers, and they still fail on std::same_as / derived_from / convertible_to. One extra target_compile_features(... cxx_std_20) in the helper loop fixes it — details in https://github.com/obsproject/obs-browser/pull/517.

The migration writes to the real profile even in portable mode. obs_module_config_path() doesn't honour --portable, so the migration runs against ~/Library/Application Support/obs-studio/plugin_config/obs-browser regardless. It's copy-only so nothing is destroyed, and I confirmed a stock OBS 32.1.2 (Alloy, CEF 127.0.6533.120) still loads and renders browser sources against the migrated profile afterwards — so the two can coexist on one profile. Worth mentioning to testers so they back up first, since --portable looks like it would isolate things but doesn't.

Environment

OS macOS 26.5.2, arm64
Toolchain Xcode 26.6 (17F113), Apple clang 21.0.0, CMake 4.1.2
CEF 147.0.14+g76d2442+chromium-147.0.7727.138 (macosarm64), wrapper built with USE_SANDBOX=OFF
obs-studio 0218aa658
PR head 738d9d0
Tested this on macOS arm64 (Apple silicon) against CEF 147 — browser sources render, Chrome Runtime initialises, and the Alloy→Chrome Runtime profile migration completes cleanly against a real long-lived profile. Thanks for grinding through this one. Two macOS-specific notes: **#517 isn't quite enough on macOS.** It bumps the plugin and the Linux helper, but `cmake/os-macos.cmake` builds four more helper executables that also compile the CEF headers, and they still fail on `std::same_as` / `derived_from` / `convertible_to`. One extra `target_compile_features(... cxx_std_20)` in the helper loop fixes it — details in https://github.com/obsproject/obs-browser/pull/517. **The migration writes to the real profile even in portable mode.** `obs_module_config_path()` doesn't honour `--portable`, so the migration runs against `~/Library/Application Support/obs-studio/plugin_config/obs-browser` regardless. It's copy-only so nothing is destroyed, and I confirmed a stock OBS 32.1.2 (Alloy, CEF 127.0.6533.120) still loads and renders browser sources against the migrated profile afterwards — so the two can coexist on one profile. Worth mentioning to testers so they back up first, since `--portable` looks like it would isolate things but doesn't. ### Environment | | | |---|---| | OS | macOS 26.5.2, arm64 | | Toolchain | Xcode 26.6 (17F113), Apple clang 21.0.0, CMake 4.1.2 | | CEF | `147.0.14+g76d2442+chromium-147.0.7727.138` (macosarm64), wrapper built with `USE_SANDBOX=OFF` | | obs-studio | `0218aa658` | | PR head | `738d9d0` |
WizardCM commented 2026-08-03 05:19:11 -05:00 (Migrated from github.com)

Warchamp performed some quick testing and noticed that CEF 151 (7922) crashes when interacting with browser docks. I have verified that is the case, and that CEF 150 (7871) behaves normally. I have updated the title and description to reflect this, and will separately look into why CEF 151 is misbehaving.

Warchamp performed some quick testing and noticed that CEF 151 (7922) crashes when interacting with browser docks. I have verified that is the case, and that CEF 150 (7871) behaves normally. I have updated the title and description to reflect this, and will separately look into why CEF 151 is misbehaving.
kurehajime commented 2026-08-10 02:09:12 -05:00 (Migrated from github.com)

Thanks for the huge effort on the Chrome runtime migration!

I wanted to flag a potential compatibility issue related to the permission handling that was deferred to a follow-up PR.

Chromium 142 introduced permission-gated Local Network Access (LNA) checks for requests from public HTTPS origins to local and loopback destinations (Chrome blog). Chromium 147 extends these checks to WebSocket connections, including loopback destinations such as ws://127.0.0.1 (Intent to Ship).

The CEF range supported by this PR includes Chromium 147+. Since OBS browser sources and docks use Alloy style, where permission prompts are not shown by default, I'm concerned that remotely hosted HTTPS overlays connecting to local WebSocket servers may no longer be able to connect.

Connecting from a remotely hosted page to a WebSocket server running on the local machine is a fairly common pattern among streaming tools and overlays, so the impact could be fairly broad in the streaming ecosystem.

Would this be worth taking into account in the follow-up permission work, or tracking as a separate issue?

I mainly wanted to make sure it was on the radar before a CEF 147+ build ships.

Thanks for the huge effort on the Chrome runtime migration! I wanted to flag a potential compatibility issue related to the permission handling that was deferred to a follow-up PR. Chromium 142 introduced permission-gated Local Network Access (LNA) checks for requests from public HTTPS origins to local and loopback destinations ([Chrome blog](https://developer.chrome.com/blog/local-network-access)). Chromium 147 extends these checks to WebSocket connections, including loopback destinations such as `ws://127.0.0.1` ([Intent to Ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/O6GMKt44Ups)). The CEF range supported by this PR includes Chromium 147+. Since OBS browser sources and docks use Alloy style, where permission prompts are not shown by default, I'm concerned that remotely hosted HTTPS overlays connecting to local WebSocket servers may no longer be able to connect. Connecting from a remotely hosted page to a WebSocket server running on the local machine is a fairly common pattern among streaming tools and overlays, so the impact could be fairly broad in the streaming ecosystem. Would this be worth taking into account in the follow-up permission work, or tracking as a separate issue? I mainly wanted to make sure it was on the radar before a CEF 147+ build ships.
WizardCM commented 2026-08-10 04:05:41 -05:00 (Migrated from github.com)

Thanks for the feedback @kurehajime - it'll definitely be taken into account with an initial permission implementation alongside the release of this PR. Keep an eye out for a follow-up PR, likely once this one is merged.

Thanks for the feedback @kurehajime - it'll definitely be taken into account with an initial permission implementation alongside the release of this PR. Keep an eye out for a follow-up PR, likely once this one is merged.
RytoEX (Migrated from github.com) requested changes 2026-08-20 13:55:24 -05:00
RytoEX (Migrated from github.com) left a comment

Mostly nits.

Mostly nits.
@ -48,11 +54,62 @@ CefRefPtr<CefBrowserProcessHandler> BrowserApp::GetBrowserProcessHandler()
return this;
RytoEX (Migrated from github.com) commented 2026-08-20 13:49:00 -05:00
	// 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.
```suggestion // 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. ```
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
RytoEX (Migrated from github.com) commented 2026-08-20 13:50:10 -05:00
	// See https://github.com/chromiumembedded/cef/issues/3966 for 'StorageNotificationService' requirement.
```suggestion // See https://github.com/chromiumembedded/cef/issues/3966 for 'StorageNotificationService' requirement. ```
@ -626,6 +626,17 @@ void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
}
RytoEX (Migrated from github.com) commented 2026-08-20 13:51:19 -05:00
	// CEF doesn't currently provide a way to properly disable/override the default Chrome error page.
```suggestion // CEF doesn't currently provide a way to properly disable/override the default Chrome error page. ```
@ -0,0 +14,4 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
RytoEX (Migrated from github.com) commented 2026-08-20 13:51:50 -05:00

Add an appropriate license header comment block (GPLv2+).

Add an appropriate license header comment block (GPLv2+).
@ -0,0 +15,4 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
RytoEX (Migrated from github.com) commented 2026-08-20 13:52:06 -05:00

Add an appropriate license header comment block (GPLv2+).

Add an appropriate license header comment block (GPLv2+).
@ -14,7 +14,8 @@ add_executable(OBS::browser-helper ALIAS browser-helper)
RytoEX (Migrated from github.com) commented 2026-08-20 13:46:04 -05:00
                         browser-app.cpp browser-app.hpp browser-dummy-client.cpp browser-dummy-client.hpp
```suggestion browser-app.cpp browser-app.hpp browser-dummy-client.cpp browser-dummy-client.hpp ```
@ -29,7 +29,8 @@ foreach(helper IN LISTS helper_suffixes)
target_sources(
${target_name} PRIVATE # cmake-format: sortable
RytoEX (Migrated from github.com) commented 2026-08-20 13:46:16 -05:00
                           browser-app.cpp browser-app.hpp browser-dummy-client.cpp browser-dummy-client.hpp
```suggestion browser-app.cpp browser-app.hpp browser-dummy-client.cpp browser-dummy-client.hpp ```
@ -13,8 +13,8 @@ add_executable(OBS::browser-helper ALIAS obs-browser-helper)
target_sources(
RytoEX (Migrated from github.com) commented 2026-08-20 13:46:32 -05:00
          browser-app.cpp browser-app.hpp browser-dummy-client.cpp browser-dummy-client.hpp
```suggestion browser-app.cpp browser-app.hpp browser-dummy-client.cpp browser-dummy-client.hpp ```
@ -714,8 +720,122 @@ static void check_hwaccel_support(void)
#endif
RytoEX (Migrated from github.com) commented 2026-08-20 13:54:15 -05:00
	blog(LOG_INFO, "[obs-browser]: Migrating files to Chrome Runtime...");
```suggestion blog(LOG_INFO, "[obs-browser]: Migrating files to Chrome Runtime..."); ```
WizardCM (Migrated from github.com) reviewed 2026-08-21 07:50:56 -05:00
@ -0,0 +14,4 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
WizardCM (Migrated from github.com) commented 2026-08-21 07:50:55 -05:00

Fixed

Fixed
WizardCM (Migrated from github.com) reviewed 2026-08-21 07:51:03 -05:00
@ -0,0 +15,4 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
WizardCM (Migrated from github.com) commented 2026-08-21 07:51:02 -05:00

Fixed

Fixed
PatTheMav (Migrated from github.com) reviewed 2026-08-21 09:31:12 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
PatTheMav (Migrated from github.com) commented 2026-08-21 08:45:58 -05:00

In my testing the condition returns true, but the value is empty, so a single , is added to disableFeatures.

In my testing the condition returns `true`, but the value is empty, so a single `,` is added to `disableFeatures`.
PatTheMav (Migrated from github.com) commented 2026-08-21 08:46:57 -05:00

I don't see disableFeatures actually being used here to actually disable the features, or am I missing something?

I don't see `disableFeatures` actually being used here to _actually_ disable the features, or am I missing something?
RytoEX (Migrated from github.com) reviewed 2026-08-21 11:50:48 -05:00
RytoEX (Migrated from github.com) commented 2026-08-21 11:50:48 -05:00

It looks like this line was removed and not re-added:

command_line->AppendSwitchWithValue("disable-features", disableFeatures);
It looks like this line was removed and not re-added: ```cpp command_line->AppendSwitchWithValue("disable-features", disableFeatures); ```
WizardCM (Migrated from github.com) reviewed 2026-08-21 21:26:20 -05:00
WizardCM (Migrated from github.com) commented 2026-08-21 21:26:20 -05:00

Oops, must've disappeared in the rebase or just as part of the refactor commit. Fixed.

Oops, must've disappeared in the rebase or just as part of the refactor commit. Fixed.
WizardCM (Migrated from github.com) reviewed 2026-08-21 21:28:32 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
WizardCM (Migrated from github.com) commented 2026-08-21 21:28:32 -05:00

Hmm, interesting. I could extend the check to first check the length of GetSwitchValue? Very weird that it would do that at all, though.

Hmm, interesting. I could extend the check to first check the length of `GetSwitchValue`? Very weird that it would do that at all, though.
RytoEX (Migrated from github.com) reviewed 2026-08-24 13:27:41 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
RytoEX (Migrated from github.com) commented 2026-08-24 13:27:41 -05:00

The if statement is checking the value of:

command_line->HasSwitch("disable-features")

but the append operation in the if statement block is using:

command_line->GetSwitchValue("disable_features")

Note the swapped hyphen (-) and underscore (_). Maybe this is why the value is empty?

The if statement is checking the value of: ```cpp command_line->HasSwitch("disable-features") ``` but the append operation in the `if` statement block is using: ```cpp command_line->GetSwitchValue("disable_features") ``` Note the swapped hyphen (`-`) and underscore (`_`). Maybe this is why the value is empty?
WizardCM (Migrated from github.com) reviewed 2026-08-25 04:09:26 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
WizardCM (Migrated from github.com) commented 2026-08-25 04:09:25 -05:00

Good catch, this should be a hyphen. I've now fixed it, and Windows correctly includes the additional AutofillActorMode.

Good catch, this should be a hyphen. I've now fixed it, and Windows correctly includes the additional `AutofillActorMode`.
RytoEX (Migrated from github.com) requested changes 2026-09-02 12:20:39 -05:00
RytoEX (Migrated from github.com) left a comment

Nits.

Nits.
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
RytoEX (Migrated from github.com) commented 2026-09-02 12:18:07 -05:00

Ordering nit. The last item is left "out of order" to accommodate the substring usage below.

		"CalculateNativeWinOcclusion,HardwareMediaKeyHandling,LiveCaption,"
		"MediaRouter,StorageNotificationService,WebBluetooth"
Ordering nit. The last item is left "out of order" to accommodate the substring usage below. ```suggestion "CalculateNativeWinOcclusion,HardwareMediaKeyHandling,LiveCaption," "MediaRouter,StorageNotificationService,WebBluetooth" ```
RytoEX (Migrated from github.com) commented 2026-09-02 12:18:48 -05:00

Nit, move disable-features below disable-extensions:

	command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required");
	command_line->AppendSwitch("disable-extensions");
	command_line->AppendSwitchWithValue("disable-features", disableFeatures);
Nit, move `disable-features` below `disable-extensions`: ```suggestion command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); command_line->AppendSwitch("disable-extensions"); command_line->AppendSwitchWithValue("disable-features", disableFeatures); ```
tytan652 commented 2026-09-03 10:39:42 -05:00 (Migrated from github.com)

Adding GlobalShortcutsPortal in disabled features for the side Linux is needed (CEF should not meddle with this portal).

Adding `GlobalShortcutsPortal` in disabled features for the side Linux is needed (CEF should not meddle with this portal).
WizardCM (Migrated from github.com) reviewed 2026-09-03 17:56:00 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
WizardCM (Migrated from github.com) commented 2026-09-03 17:56:00 -05:00

Done

Done
WizardCM (Migrated from github.com) reviewed 2026-09-03 17:56:06 -05:00
@ -74,24 +131,40 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
}
WizardCM (Migrated from github.com) commented 2026-09-03 17:56:06 -05:00

Done

Done
PatTheMav (Migrated from github.com) approved these changes 2026-09-04 11:30:09 -05:00
PatTheMav (Migrated from github.com) left a comment

Checked with CEF 150 on macOS, profiles were migrated correctly as far as I could tell and new profiles (and profile data) is not visible to current master either.

Checked with CEF 150 on macOS, profiles were migrated correctly as far as I could tell and new profiles (and profile data) is not visible to current master either.
WizardCM commented 2026-09-09 06:01:20 -05:00 (Migrated from github.com)

Added a commit based on @tytan652's request. I have verified with Chrome verbose logging that CEF doesn't complain about unrecognised entries on other platforms. A future change will be to do platform-specific implementations so we're not disabling unrecognised features on all platforms and don't overcomplicate the conditional checks in this area of the code.

Added a commit based on @tytan652's request. I have verified with Chrome verbose logging that CEF doesn't complain about unrecognised entries on other platforms. A future change will be to do platform-specific implementations so we're not disabling unrecognised features on all platforms and don't overcomplicate the conditional checks in this area of the code.
RytoEX (Migrated from github.com) approved these changes 2026-09-09 17:06:00 -05:00
tippfehlr commented 2026-09-10 14:39:48 -05:00 (Migrated from github.com)

Great to see this merged, thanks to everyone involved!

Great to see this merged, thanks to everyone involved!
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-obsproject/obs-browser!523
No description provided.