Chrome extensions in WebView2, CefSharp, and DotNetBrowser

lubenskyi

Vladyslav Lubenskyi

Posted on April 18, 2023

Chrome extensions in WebView2, CefSharp, and DotNetBrowser

It's 2023, and everything is in the browser. Web technologies are remarkably versatile, and we—desktop developers—envy that versatility.

That's where web view controls come to help. And there are plenty of them in the .NET world: open-source and proprietary, free and commercial.

With embeddable Gecko and Trident retired, all modern resorted to Chromium. It complies with the latest web standards, works fast, and has a million features. But there is one feature all web views struggle with. It's the Chrome extensions.

In this article, I will examine how well web views support Chrome extensions and review their API.

TL;DR

  1. CefSharp's implementation is minimal, and most extensions don't work. It will change when they switch from the old Alloy to the new Chrome runtime.

  2. DotNetBrowser's implementation is functional, but not yet stable.

  3. WebView2 doesn't support the extensions thus far.

Today's contestants

As you already noticed, I'm going to look at three libraries:

  • CefSharp
  • WebView2
  • DotNetBrowser

CefSharp uses Chromium Embedded Framework. This framework, usually abbreviated as CEF, is a fundamental piece of the ecosystem as every open-source web view is essentially a wrapper over it. The capabilities of CEF define what CefSharp and other similar libraries can do.

WebView2 and DotNetBrowser are created and maintained by commercial companies and represent the proprietary end of the spectrum.

WebView2

WebView2 doesn't support Chrome extensions. But since it uses Microsoft Edge—whose extensions are fully compatible with Chrome's—it may start supporting them in the future.

Follow these GitHub issues to get updates on WebView2 progress:

DotNetBrowser

DotNetBrowser released a public preview of the extension support. We have yet to finalize the exact list of available browser APIs, but ultimately, we expect most of the extensions to work.

Here are the extensions that work in DotNetBrowser: uBlock and AdBlock, Windows Accounts, Web Vitals, and React Developer Tools. We tested these, but you can expect similar extensions to work too.

If you want to know if a specific extension works, mention it in a GitHub discussion.

API review

Installing and uninstalling extensions

In DotNetBrowser, you install extensions permanently. An installed extension is persisted in the profile and remains available until you explicitly uninstall it.

Before installation, grant the permissions first:

var extensions = engine.Profiles.Default.Extensions;
extensions.ValidatePermissionsHandler =
    new Handler<ValidatePermissionsParameters, ValidatePermissionsResponse>(
        p => ValidatePermissionsResponse.Grant()
    );
Enter fullscreen mode Exit fullscreen mode

Then, install the extension with a Chrome Web Store link or a CRX file:

var extension = extensions.Install("https://chrome.google.com/webstore/detail/…").Result;
// or
var extension = extensions.Install("C:\\extension.crx").Result;
Enter fullscreen mode Exit fullscreen mode

To uninstall the extension, execute the following code:

extension.Uninstall();
Enter fullscreen mode Exit fullscreen mode

Executing the extension action

Every extension has an icon in the Chrome toolbar. When it's clicked, the extension may execute the action. For example, it can show a pop-up, change the page, or do anything else.

Here's how you trigger this action from the code:

// The action will interact with this `browser`.
extension.GetAction(browser).Click();
Enter fullscreen mode Exit fullscreen mode

If the action opens a pop-up, the library will show it in a new window. Alternatively, you can leave the pop-up off the screen and manipulate it from the code:

browser.OpenExtensionActionPopupHandler =
    new Handler<OpenExtensionActionPopupParameters, OpenExtensionActionPopupResponse>(p =>
    {
        p.PopupBrowser.Navigation.FrameLoadFinished += (s, e) =>
        {
            e.Frame.GetElementById("switch").Click();
        };
        return OpenExtensionActionPopupResponse.Open();
    });
Enter fullscreen mode Exit fullscreen mode

Handling extension events

Show or suppress pop-up windows:

extension.OpenExtensionPopupHandler = 
    new Handler<OpenExtensionPopupParameters, OpenExtensionPopupResponse>
        (p => OpenExtensionPopupResponse.Suppress());
Enter fullscreen mode Exit fullscreen mode

Handle downloads started by an extension:

extension.StartDownloadHandler = 
    new Handler<StartDownloadParameters, StartDownloadResponse>
        (p => StartDownloadResponse.DownloadTo("C:\\Downloads"));
Enter fullscreen mode Exit fullscreen mode

Monitor changes in the extension action badge, tooltip, and icon:

var action = extension.GetAction(browser);
action.Updated += (s, p) =>
{
    var badge = p.ExtensionAction.Badge;
    var icon = p.ExtensionAction.Icon;
    var tooltip = p.ExtensionAction.Tooltip;
};
Enter fullscreen mode Exit fullscreen mode

CefSharp

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.

At this moment, further work is discontinued. After introducing the new integration layer (called Chrome runtime), they decided not to proceed with extensions in the old integration layer (named Alloy). We expect most extensions to work in the new integration layer as the library starts to rely on Chromium's code much more.

So, do extensions work or not? Not yet. The Chrome runtime is incompatible with CefSharp and CefGlue kind of libraries—it's a work in progress. Track these issues to check on the progress:

API review

Loading and unloading extensions

Unlike Chromium, CEF doesn't persist extensions: you load them each time the application starts. To load the extension, have it unzipped in the directory and execute the following code:

var context = Browser.GetBrowserHost().RequestContext;
context.LoadExtension("C:\\dir", null, new ExtensionHandler());
Enter fullscreen mode Exit fullscreen mode

To unload the extension, find it in RequestContext and unload:

context.GetExtension("gibkoahgjfhphbmeiphbcnhehbfdlcgo").Unload();
Enter fullscreen mode Exit fullscreen mode

Handling extension events

Extensions emit events: when loaded or unloaded, wants to create a new browser or access the active browser. These are all handled in IExtensionHandler, which you associate with the extension on loading.

Though, until CefSharp adopts the Chrome runtime, most event handlers may be never called.

Here's a short example to get the gist of the API:

public class ExtensionHandler : IExtensionHandler
{
    ...
    // Don't allow extensions to create background browsers.
    bool IExtensionHandler.OnBeforeBackgroundBrowser(IExtension extension, string url, IBrowserSettings settings)
    {  
        return true;
    }

    // Let the extension know which browser is active now.
    IBrowser IExtensionHandler.GetActiveBrowser(IExtension extension, IBrowser browser, bool includeIncognito)
    { 
        return mainBrowser;
    }

    // Don't allow extensions to access browsers that have a private page
    // loaded.
    bool IExtensionHandler.CanAccessBrowser(IExtension extension, IBrowser browser, bool includeIncognito, IBrowser targetBrowser)
    {
        var url = targetBrowser.MainFrame.Url;
        if (url.Contains("my-private-page"))
        {
            return false;
        }
        return true;
    }
}
Enter fullscreen mode Exit fullscreen mode

Useful reading

💖 💪 🙅 🚩
lubenskyi
Vladyslav Lubenskyi

Posted on April 18, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related