Search code examples
c#.net.net-corecefsharpchromium-embedded

How to enable dark mode for CefSharp web browser in .NET?


SCENARIO

I'm very new to CefSharp, in fact, its the first time that I'm using it. And I'm using it under a net core 6.0 winforms application, but this same question and scenario applies under .NET Framework.

I'm running Windows 10 with OS dark mode enabled, in case of that matters.

This is the method that I'm calling to initialize CefSharp environment:

public bool InitializeCefSharp()
{
    if (Cef.IsInitialized)
    {
        throw new InvalidOperationException("CefSharp is already initialized.");
    }

    string cachePath = $"{System.Windows.Forms.Application.StartupPath}\\cache\\CefSharp";

    var settings = new CefSettings
    {
        CachePath = cachePath,
        RootCachePath = cachePath,
        CommandLineArgsDisabled = false,
        IgnoreCertificateErrors = true,
        Locale = "en-US",
        PersistSessionCookies = true,
        PersistUserPreferences = false,
        WindowlessRenderingEnabled = false
    };

    bool success = Cef.Initialize(settings, performDependencyCheck: true);
    return success;
}

Note: Although that's C# code, I'm actually using VB.NET and the Startup application event to initialize CefSharp.


QUESTION

How to enable dark mode for the initialized CefSharp browser?.

My intention is that when loading a web page in the CEF-based browser, it will automatically be colored with a dark theme.

Or at least, and as a last resort, only a solution for websites that checks whether the web browser is using dark or light mode and then the website automatically adapts its colors.


RESEARCH

Please take into account that, as I said, I'm very new to Cefsharp and CEF APIs in general.

About SetAutoDarkModeOverrideAsync method

I've found this interesting method named SetAutoDarkModeOverrideAsync whose description says:

Automatically render all web contents using a dark theme.

This may be a solution, but I was unable to find an example about how to use it.

Using Command-Line Args

This answer shows different command-line arguments to enable dark mode for CEF-based? browser. Let's take for example these two:

  • --enable-features=WebContentsForceDark

  • --enable-features=WebContentsForceDark:inversion_method/hsl_based/image_behavior/none/text_lightness_threshold/256/background_lightness_threshold/0

Well, I've tried to use those command-line arguments in this way (in my custom InitializeCefSharp method):

settings.CefCommandLineArgs.Add("--enable-features=WebContentsForceDark");

and:

settings.CefCommandLineArgs.Add("--enable-features=WebContentsForceDark:inversion_method/hsl_based/image_behavior/none/text_lightness_threshold/256/background_lightness_threshold/0");

But it takes no effect.

Using web browser Extensions (LoadExtensionFromDirectory method)

After I have given up trying command-line arguments, I thought of a different methodology: use a third party extension like Darkreader

So I've downloaded the zip file, extracted it to a folder, and I used the LoadExtensionFromDirectory method to load the extension on the browser:

string extensionDirectory = @"C:\darkreader-chrome";
var extensionHandler = new ExtensionHandler();
ChromiumWebBrowser1.GetBrowserHost().RequestContext.LoadExtensionFromDirectory(extensionDirectory, extensionHandler);

But it literally breaks the web browser instance; it does not load/navigate to any webpage, it does not responds.

It just does not work, or at least not in the way I've tried it.

Using web browser Extensions (LoadExtension method)

If I use the LoadExtension method in this way:

string extensionDirectory = @"C:\darkreader-chrome";
var extensionHandler = new ExtensionHandler();
ChromiumWebBrowser1.GetBrowserHost().RequestContext.LoadExtension(extensionDirectory, null, extensionHandler);

I get the same results: the web browser brokes.

Digging into web browser extensions usage

I've also tried using the set of command-line args shown in this question to follow his same steps and maximize extension loading compatibility, without success.

And in the comments section of that question there is a useful hyperlink that points to this article which mentions important things like these:

CefSharp's implementation is minimal, and most extensions don't work.

CefSharp supports the extensions, but most of them will not work. This is because extensions need special browser APIs, of which there are over 70. And CEF has only 4—a small set required for a built-in PDF viewer.

So it seems to me that using extensions like DarkReader to enable dark mode is not a viable solution.

However, I've also found this answer confirming that the mentioned command-line argument and the DarkReader extension both works for a chrome-based browser. But I was unable to make work any of these workarounds in CefSharp.

About prefers-color-scheme

I've found this answer about CefSharp that mentions prefers-color-scheme with a documentation article from Mozilla.

Honestly, I know absolutely nothing about how to use or modify that CSS and where to use it, so I asked ChatGPT for help about prefers-color-scheme using CefSharp, and the answer I got, which is useless to me, was to use a code like this to set a custom attribute in the loaded web page document:

string script = "document.documentElement.setAttribute('data-theme', 'dark');";
browser.ExecuteScriptAsync(script);

...Yes, ChatGPT has fooled me once again, answering something that seems to have no relation to what I just asked him.

And I have not enough experience in CSS to go deep into that possible solution using prefers-color-scheme.

Changing the background color

The BrowserSettings.BackgroundColor / CefSettings.BackgroundColor property describes how to change the background color of the CefSharp web browser control, but as expected it only takes effect when there is no page loaded in the web browser.

And about using a custom dark color scheme

Anyway, my intention is not to create a custom dark color scheme or arbitrarily set the background of all loaded pages to a dark color since that would be too conflicting depending on which pages and their text colors and their colors for other html elements. So I think it would be too complicated to make a custom dark color scheme that is perfectly compatible with all kind of web pages.

I am convinced that there must be a more optimal and "safe" or compatible solution (than creating a custom dark scheme color), such as that command line argument based on the HSL color inversion method that I mentioned at the beginning of this Research section.


Final words

I have taken a lot of time and effort to do my best trying to find a solution to this problem being my first time using CefSharp, with very little knowledge of CEF and Chromium (and Html / CSS) in general from my side, but to carry out something that It should be as simple and easy to do as setting dark or light mode in the damn CefSharp browser. It seemed very complicated to me, and it shouldn't be.


Solution

  • As stated in the issue you linked, CEF is able to detect the OS setting itself. In my case, I had to uncomment the compatibility entry for Windows 10 in my app.manifest file:

    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
      <application>
        <!-- ... -->
    
        <!-- Windows 10 -->
        <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    
      </application>
    </compatibility>
    

    After setting this, a minimal Windows forms application with a single ChromiumWebBrowser control automatically detected the system's color setting.


    However, you can also force dark mode (i.e., prefers-color-scheme: dark) in your CEF browser control via

    browser.GetDevToolsClient().Emulation.SetAutoDarkModeOverrideAsync(true);
    

    This retrieves the browser's DevToolsClient and the corresponding device emulation feature, which allows to configure numerous settings, including forcing dark mode.


    I have tested both approaches with the following minimal HTML page (stored at path/to/test.html and then loaded via file:///path/to/test.html in the browser):

    <!DOCTYPE html>
    <html>
      <head>
        <title>Test page</title>
        <style>
            body {
                background-color: white;
                color: black;
            }
    
            @media (prefers-color-scheme: dark) {
              body {
                background-color: black;
                color: white;
              }
            }
        </style>
      </head>
      <body>
        <h1>Dark mode test</h1>
      </body>
    </html>