CefGlue Documentation

repository·main·Indexed 19 days ago

https://github.com/outsystems/cefglue

A .NET binding for the Chromium Embedded Framework (CEF) that allows developers to embed Chromium-based web browsers into .NET applications using Avalonia or WPF. It provides a .NET wrapper compatible with C# and other CLR languages, supporting Windows, macOS, and Linux on x64 and ARM64 architectures. The library includes a MessageRouter for asynchronous communication between JavaScript in the renderer process and C++ in the browser process.

Tokens
2.5K
Snippets
6
Records
10
Agent score
66%

What's inside CefGlue

  1. Overview of CefGlue

    main
    CefGlue is a .NET binding for The Chromium Embedded Framework (CEF), allowing you to embed Chromium in .NET applications. It acts as a .NET wrapper around CEF and is compatible with C# or any other CLR language. It provides web browser control implementations for both Avalonia and WPF frameworks.
  2. How the MessageRouter works

    main

    The MessageRouter enables asynchronous message routing between JavaScript in the renderer process and C++ in the browser process.

    Core Workflow

    1. JavaScript Side: The renderer-side router exposes window.cefQuery and window.cefQueryCancel to the JavaScript environment.
    2. Browser Side: The browser-side router receives these requests and passes them to one or more registered C++ Handler instances via the Handler::OnQuery callback.
    3. Handling: A Handler can choose to handle a query (returning true in OnQuery) or ignore it (returning false).
      • If handled, the handler calls Callback::Success or Callback::Failure to trigger the corresponding JavaScript callback.
      • If unhandled by all C++ handlers, the query is automatically canceled, and the JavaScript onFailure callback is executed with an error code of -1.

    Query Persistence

    • Non-persistent queries: The registration is removed automatically after the JavaScript callback is executed once.
    • Persistent queries: The registration remains active until:
      • The query is canceled in JavaScript via window.cefQueryCancel.
      • The query is canceled in C++ via Callback::Failure.
      • The context is released (browser destruction, navigation, or renderer process termination).

    Common Patterns

    • One-time Request: Use a non-persistent query for a single request/response cycle.
    • Broadcast: Use a persistent query to register a receiver. The C++ handler manages multiple registered Callback objects and executes them sequentially to deliver messages.
    • Subscription: Use a persistent query to register a subscriber. The handler manages the feed and cancels it when no more JavaScript receivers are registered.
    // JavaScript usage example
    var request_id = window.cefQuery({
        request: 'my_request',
        persistent: false,
        onSuccess: function(response) { /* handle success */ },
        onFailure: function(error_code, error_message) { /* handle error */ }
    });
    
    // Optionally cancel
    window.cefQueryCancel(request_id);
  3. Check platform and framework compatibility

    main

    Before integrating CefGlue, verify that your target operating system, architecture, and UI framework are supported.

    OSx64ARM64WPFAvalonia
    Windows✔️✔️✔️✔️
    macOS✔️✔️✔️
    Linux✔️🔘✔️

    *Note: ARM64 on Linux is marked as 'Works with issues'. See LINUX.md for details on tested distributions and known issues. Only x64 and ARM64 architectures are supported.

  4. Install CefGlue via NuGet

    main

    Stable binaries containing all necessary components to embed Chromium are available on NuGet. Choose the package that matches your target framework and architecture:

    Avalonia Support:

    • CefGlue.Avalonia (Standard)
    • CefGlue.Avalonia.ARM64 (ARM64 architecture)

    WPF Support:

    • CefGlue.WPF (Standard)
    • CefGlue.WPF.ARM64 (ARM64 architecture)

    Core/Common Components:

    • CefGlue.Common (Standard)
    • CefGlue.Common.ARM64 (ARM64 architecture)
  5. Setup the MessageRouter in C++

    main

    Integrating the MessageRouter requires configuration in both the browser and renderer processes. The configuration must be identical in both processes.

    1. Define Configuration

    Use CefMessageRouterConfig to specify the JavaScript function names. If you use multiple routers, ensure the function names are unique.

    CefMessageRouterConfig config;
    config.js_query_function = "cefQuery";
    config.js_cancel_function = "cefQueryCancel";

    2. Browser Process Setup

    • Create the router instance using CefMessageRouterBrowserSide::Create(config).
    • Register handlers using AddHandler(handler).
    • Call required lifecycle methods (e.g., OnBeforeClose) from your CefClient implementation.

    3. Renderer Process Setup

    • Create the router instance using CefMessageRouterRendererSide::Create(config).
    • Call required lifecycle methods (e.g., OnContextCreated) from your CefRenderProcessHandler implementation.

    Note: Handlers must outlive the router or be removed before the router is destroyed.

    // 1. Define config
    CefMessageRouterConfig config;
    config.js_query_function = "cefQuery";
    config.js_cancel_function = "cefQueryCancel";
    
    // 2. Browser side
    browser_side_router_ = CefMessageRouterBrowserSide::Create(config);
    browser_side_router_->AddHandler(my_handler);
    
    // 5. Renderer side
    renderer_side_router_ = CefMessageRouterRendererSide::Create(config);
  6. View CefGlue usage examples

    main

    To understand how to implement a web browser using CefGlue, refer to the following sample projects in the repository:

    • Avalonia: CefGlue.Demo.Avalonia
    • WPF: CefGlue.Demo.WPF

    These samples demonstrate the available features and how to integrate the controls into your application.

  7. Resolve ARM64 dynamic loading issues with LD_PRELOAD

    main

    On ARM64 platforms, dynamic loading of CEF may fail with the error cannot allocate memory in static TLS block. This is suspected to be caused by the CLR using excessive Thread Local Storage (TLS).

    You can resolve this by using the LD_PRELOAD environment variable to ensure libHarfBuzzSharp.so is loaded before libcef.so.

    LD_PRELOAD=/path/to/libHarfBuzzSharp.so:/path/to/libcef.so
  8. Fix ARM64 CEF loading by modifying ELF files with patchelf

    main

    If you cannot use LD_PRELOAD, you can modify the ELF files of your application to ensure the required libraries are loaded correctly on ARM64.

    For the Avalonia-based application, you must add both libHarfBuzzSharp.so and libcef.so. For the standalone Browser process, you only need to add libcef.so as it does not use Avalonia.

    # For the Avalonia application
    patchelf --add-needed libHarfBuzzSharp.so --add-needed libcef.so path/to/Xilium.CefGlue.Demo.Avalonia
    
    # For the Browser process
    patchelf --add-needed libcef.so path/to/Xilium.CefGlue.BrowserProcess
  9. Implement a C++ MessageRouter Handler

    main

    To process messages in the browser process, you must implement the Handler::OnQuery method. This method is called by the CefMessageRouterBrowserSide for every query received from JavaScript.

    Method Signature:

    void OnQuery(int64 query_id,
                 CefRefPtr<CefBrowser> browser,
                 CefRefPtr<CefFrame> frame,
                 const CefString& request,
                 bool persistent,
                 CefRefPtr<Callback> callback);

    Implementation Rules:

    • Return true if your handler successfully processed the request.
    • Return false if your handler did not process the request (allowing other handlers a chance).
    • Use callback->Success(response) or callback->Failure(error_code, error_message) to communicate back to JavaScript.
    • If a query is canceled for reasons other than Callback::Failure, the handler's OnQueryCanceled method will be called.
    void MyHandler::OnQuery(int64 query_id,
                             CefRefPtr<CefBrowser> browser,
                             CefRefPtr<CefFrame> frame,
                             const CefString& request,
                             bool persistent,
                             CefRefPtr<Callback> callback) {
      if (request == "my_request") {
        callback->Success("my_response");
        return true;
      }
      return false;  // Not handled.
    }
  10. JavaScript API for MessageRouter

    main

    The renderer-side router exposes two primary functions on the JavaScript window object:

    window.cefQuery(options)

    Creates and sends a new asynchronous query to the C++ browser process.

    Options Object:

    • request (string): The payload/command to send.
    • persistent (boolean): If true, the callbacks remain registered until explicitly canceled or the context is destroyed.
    • onSuccess (function): Callback executed when the C++ handler calls Callback::Success.
    • onFailure (function): Callback executed when the C++ handler calls Callback::Failure or if the query is unhandled (error code -1).

    Returns: request_id (integer) used for cancellation.

    window.cefQueryCancel(request_id)

    Cancels a pending persistent query using its ID.

    // Create and send a new query.
    var request_id = window.cefQuery({
        request: 'my_request',
        persistent: false,
        onSuccess: function(response) {},
        onFailure: function(error_code, error_message) {}
    });
    
    // Optionally cancel the query.
    window.cefQueryCancel(request_id);