OutSystems/CefGlue

CefRuntime.ExecuteProcess & Multi-Process doesn't seem to work properly on Linux

snnyyp opened this issue · 2 comments

snnyyp commented

I've only been using libcef and CefGlue for a few days. If there's any mistake, please point out.
The problem which will be mentioned below can be solved by running in single process mode, but apparently it's not the recommended way. So, I hope this issue can be fixed.

Here's the full code.

using Xilium.CefGlue;

internal static class Program
{
    private static void Main(string[] args)
    {
        var myApp = new MyApp();
        var mainArgs = new CefMainArgs(args);
        var exitCode = CefRuntime.ExecuteProcess(mainArgs, myApp, 0);

        if (exitCode >= 0)
        {
            Environment.Exit(exitCode);
        }

        var settings = new CefSettings()
        {
            Locale = "en-US",
            WindowlessRenderingEnabled = true,
            NoSandbox = true,
            MultiThreadedMessageLoop = false,
            ExternalMessagePump = false,
            UserDataPath = $"{Path.GetFullPath(".")}{Path.DirectorySeparatorChar}userData",
            LocalesDirPath = $"{Path.GetFullPath(".")}{Path.DirectorySeparatorChar}locales",
            ResourcesDirPath = $"{Path.GetFullPath(".")}",
        };

        CefRuntime.Initialize(mainArgs, settings, myApp, 0);

        var windowInfo = CefWindowInfo.Create();
        windowInfo.SetAsWindowless(0, false);

        var client = new MyClient();
        var browserSettings = new CefBrowserSettings()
        {
            WindowlessFrameRate = 60,
        };

        CefBrowserHost.CreateBrowser(windowInfo, client, browserSettings, "https://testufo.com");
        CefRuntime.RunMessageLoop();
        CefRuntime.Shutdown();
    }
}

internal class MyApp : CefApp
{
    protected override void OnBeforeCommandLineProcessing(string processType, CefCommandLine commandLine)
    {
        Array.ForEach(commandLine.GetArgv(), Console.WriteLine);
        Console.WriteLine();
        // Append the switches to the main process only
        if (!string.IsNullOrEmpty(processType))
            return;

        commandLine.AppendSwitch("disable-gpu");
        commandLine.AppendSwitch("disable-gpu-compositing");
        commandLine.AppendSwitch("enable-begin-frame-scheduling");
        commandLine.AppendSwitch("disable-surfaces");
        commandLine.AppendSwitch("no-zygote");
        commandLine.AppendSwitch("no-sandbox");
        commandLine.AppendSwitch("disable-setuid-sandbox");
        // Solves the problem but not recommended
        // commandLine.AppendSwitch("single-process");
    }
}

internal class MyClient : CefClient
{
    private readonly CefRenderHandler myRenderHandler = new MyRenderHandler();

    protected override CefLoadHandler? GetLoadHandler()
    {
        return base.GetLoadHandler();
    }

    protected override CefRenderHandler? GetRenderHandler()
    {
        return myRenderHandler;
    }

    protected override CefAudioHandler? GetAudioHandler()
    {
        return base.GetAudioHandler();
    }
}

internal class MyRenderHandler : CefRenderHandler
{
    const int width = 1280;
    const int height = 720;

    public MyRenderHandler()
    {
        Console.WriteLine("Initialized!");
    }

    protected override CefAccessibilityHandler GetAccessibilityHandler()
    {
        throw new NotImplementedException();
    }

    protected override bool GetScreenInfo(CefBrowser browser, CefScreenInfo screenInfo)
    {
        return true;
    }

    protected override void GetViewRect(CefBrowser browser, out CefRectangle rect)
    {
        rect = new CefRectangle(0, 0, width, height);
    }

    protected override void OnAcceleratedPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects, nint sharedHandle)
    {
        throw new NotImplementedException();
    }

    protected override void OnImeCompositionRangeChanged(CefBrowser browser, CefRange selectedRange, CefRectangle[] characterBounds)
    {
        throw new NotImplementedException();
    }

    protected override void OnPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects, nint buffer, int width, int height)
    {
        Console.WriteLine("onPaint called");
    }

    protected override void OnPopupSize(CefBrowser browser, CefRectangle rect)
    {
        throw new NotImplementedException();
    }

    protected override void OnScrollOffsetChanged(CefBrowser browser, double x, double y)
    {
        throw new NotImplementedException();
    }
}

The code is a little messy, but it serves the purpose.
The demo program basically initializes libcef in OSR mode and visits https://testufo.com. Later it prints out the given command line arguments, adds some command line arguments, triggers onPaint in MyRenderHandler and spams the console with "onPaint called" message.
And it did work flawlessly, at least on Windows.
But when I ran it on Ubuntu 22.04, the program spammed the console with bunch of command line arguments instead.


--no-sandbox
--lang=en-US
--log-file=/home/google/Desktop/cef/debug.log
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache

Initialized!
--type=gpu-process
--no-sandbox
--locales-dir-path=/home/google/Desktop/cef/locales
--resources-dir-path=/home/google/Desktop/cef
--lang=en-US
--user-data-dir=/home/google/Desktop/cef/userData
--gpu-preferences=WAAAAAAAAAAgAAAIAAAAAAAAAAAAAAAAAABgAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAABAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA==
--use-gl=angle
--use-angle=swiftshader-webgl
--log-file=/home/google/Desktop/cef/debug.log
--shared-files
--field-trial-handle=0,i,14014740667592638725,13604786558041850983,131072
--disable-features=BackForwardCache
--no-sandbox
--lang=en-US
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache,BackForwardCache

--type=utility
--utility-sub-type=network.mojom.NetworkService
--lang=en-US
--service-sandbox-type=none
--no-sandbox
--locales-dir-path=/home/google/Desktop/cef/locales
--resources-dir-path=/home/google/Desktop/cef
--lang=en-US
--user-data-dir=/home/google/Desktop/cef/userData
--log-file=/home/google/Desktop/cef/debug.log
--shared-files=v8_context_snapshot_data:100
--field-trial-handle=0,i,14014740667592638725,13604786558041850983,131072
--disable-features=BackForwardCache
--no-sandbox
--lang=en-US
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache,BackForwardCache

--type=renderer
--locales-dir-path=/home/google/Desktop/cef/locales
--resources-dir-path=/home/google/Desktop/cef
--user-data-dir=/home/google/Desktop/cef/userData
--no-sandbox
--log-file=/home/google/Desktop/cef/debug.log
--no-zygote
--disable-gpu-compositing
--lang=en-US
--num-raster-threads=4
--enable-main-frame-before-activation
--renderer-client-id=4
--time-ticks-at-unix-epoch=-1691198068908968
--launch-time-ticks=18423119669
--shared-files=v8_context_snapshot_data:100
--field-trial-handle=0,i,14014740667592638725,13604786558041850983,131072
--disable-features=BackForwardCache
--no-sandbox
--lang=en-US
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache,BackForwardCache

--type=renderer
--locales-dir-path=/home/google/Desktop/cef/locales
--resources-dir-path=/home/google/Desktop/cef
--user-data-dir=/home/google/Desktop/cef/userData
--first-renderer-process
--no-sandbox
--log-file=/home/google/Desktop/cef/debug.log
--no-zygote
--disable-gpu-compositing
--lang=en-US
--num-raster-threads=4
--enable-main-frame-before-activation
--renderer-client-id=5
--time-ticks-at-unix-epoch=-1691198068908968
--launch-time-ticks=18423118340
--shared-files=v8_context_snapshot_data:100
--field-trial-handle=0,i,14014740667592638725,13604786558041850983,131072
--disable-features=BackForwardCache
--no-sandbox
--lang=en-US
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache,BackForwardCache

Initialized!
Initialized!
Initialized!
Initialized!
--type=gpu-process
--no-sandbox
--use-angle=swiftshader-webgl
--locales-dir-path=/home/google/Desktop/cef/locales
--resources-dir-path=/home/google/Desktop/cef
--lang=en-US
--user-data-dir=/home/google/Desktop/cef/userData
--gpu-preferences=WAAAAAAAAAAgAAAIAAAAAAAAAAAAAAAAAABgAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAABAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA==
--use-gl=angle
--use-angle=swiftshader-webgl
--log-file=/home/google/Desktop/cef/debug.log
--shared-files
--field-trial-handle=0,i,2207804637224223773,17526720498829213866,131072
--disable-features=BackForwardCache
--no-sandbox
--lang=en-US
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache,BackForwardCache

--type=gpu-process
--no-sandbox
--locales-dir-path=/home/google/Desktop/cef/locales
--resources-dir-path=/home/google/Desktop/cef
--lang=en-US
--user-data-dir=/home/google/Desktop/cef/userData
--gpu-preferences=WAAAAAAAAAAgAAAIAAAAAAAAAAAAAAAAAABgAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAABAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA==
--use-gl=angle
--use-angle=swiftshader-webgl
--log-file=/home/google/Desktop/cef/debug.log
--shared-files
--field-trial-handle=0,i,6075439658209197302,17739411482228066076,131072
--disable-features=BackForwardCache
--no-sandbox
--lang=en-US
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache,BackForwardCache

--type=utility
--utility-sub-type=network.mojom.NetworkService
--lang=en-US
--service-sandbox-type=none
--no-sandbox
--locales-dir-path=/home/google/Desktop/cef/locales
--resources-dir-path=/home/google/Desktop/cef
--lang=en-US
--user-data-dir=/home/google/Desktop/cef/userData
--log-file=/home/google/Desktop/cef/debug.log
--shared-files=v8_context_snapshot_data:100
--field-trial-handle=0,i,6075439658209197302,17739411482228066076,131072
--disable-features=BackForwardCache
--no-sandbox
--lang=en-US
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache,BackForwardCache

--type=gpu-process
--no-sandbox
--locales-dir-path=/home/google/Desktop/cef/locales
--resources-dir-path=/home/google/Desktop/cef
--lang=en-US
--user-data-dir=/home/google/Desktop/cef/userData
--gpu-preferences=WAAAAAAAAAAgAAAIAAAAAAAAAAAAAAAAAABgAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAABAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA==
--use-gl=angle
--use-angle=swiftshader-webgl
--log-file=/home/google/Desktop/cef/debug.log
--shared-files
--field-trial-handle=0,i,2605854638024410994,9227336841900173362,131072
--disable-features=BackForwardCache
--no-sandbox
--lang=en-US
--resources-dir-path=/home/google/Desktop/cef
--locales-dir-path=/home/google/Desktop/cef/locales
--disable-features=BackForwardCache,BackForwardCache

The log went on and on, and the program drained all the memory and crashed X-Server faster than I closed the terminal window.
It is obvious that the sub-processes didn't launched correctly.

To make it clearer, I'll specify the BrowserSubprocessPath explicitly.

var settings = new CefSettings()
{
    Locale = "en-US",
    WindowlessRenderingEnabled = true,
    NoSandbox = true,
    MultiThreadedMessageLoop = false,
    ExternalMessagePump = false,
    UserDataPath = $"{Path.GetFullPath(".")}{Path.DirectorySeparatorChar}userData",
    // Linux
    LocalesDirPath = $"{Path.GetFullPath(".")}{Path.DirectorySeparatorChar}locales",
    ResourcesDirPath = $"{Path.GetFullPath(".")}",
    // Specify the BrowserSubprocessPath explicitly
    BrowserSubprocessPath = $"{Path.GetFullPath(".")}{Path.DirectorySeparatorChar}CefHelper.exe",
};

CefHelper.exe is the "helper" program, and here's the code.

using Xilium.CefGlue;

internal class Program
{
    private static void Main(string[] args)
    {
        var mainArgs = new CefMainArgs(args);
        var exitCode = CefRuntime.ExecuteProcess(mainArgs, null, 0);

        if (exitCode >= 0)
            Environment.Exit(exitCode);
    }
}

Just the bare minimum code in order to launch the subprocesses required by libcef.
I compiled it and copied to the root directory.
Firstly, I ran the main program on Windows. And again, everything worked fine.
Then I changed CefHelper.exe to CefHelper, and copied both of them to the Ubuntu machine.
Surprisingly, the GUI environment didn't crash. But the main program spammed the console with another kind of error message.

[0805/144236.701339:ERROR:network_service_instance_impl.cc(499)] Network service crashed, restarting service.

Finally, I decided to dig out the official cpp demo cefsimple and made a few changes.

#include <iostream>
#include "tests/cefsimple/simple_app.h"
#include "include/cef_command_line.h"

using namespace std;

int main(int argc, char* argv[]) {
  cout << "Launches sub-processes only." << endl;

  CefMainArgs main_args(argc, argv);
  int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);

  if (exit_code >= 0) {
    return exit_code;
  }
}

Still the bare minimum cpp code in order to launch the subprocesses.
Then I compiled the cpp version of CefHelper.exe and started the main program.
Boom, the program worked like a charm, it spammed the console with "onPaint" message.


Since my program could run on Windows with/without any helper program or run on Linux with the cpp version of the helper program, I would assume that there might be some problems with CefGlue's ExecuteProcess, especially on Linux platform.

I'm expecting to get this potential bug fixed (or maybe I did something wrong) and I'm willing to collaborate.
I'm not a native English speaker, so if there's any grammar mistake or typo, please forgive me.
Thanks.

Hi,

The purpose of this CefGlue fork is to provide and out-of-the box working webview that can be used in Windows and MacOS Avalonia (GUI) apps. Thats why we provide a nuget which can be used without much effort, just a few lines of codes needed to bootstrap (you may check here.
All of the heavy lifting of instantiating CefGlue and its sub processes is already abstracted by this CefGlue fork, and we don't expect that the consumers need to make substancial changes on that. Also Linux not yet supported. There's a PR opened by the community but the last interactions there, seem that it's not working yet.

Having that said, if you need to heavily customise the way CefGlue works, probably you're looking in the wrong place, and I would recommend that you look at https://gitlab.com/xiliumhq/chromiumembedded/cefglue or https://bitbucket.org/chromiumembedded/cef/src/master/.

snnyyp commented

OK, thanks. I'll check out the original repo on GitLab.
I thought this repo was the original one because this repo was the first search result that popped up on Google and it had a more recent commit history.
Again, sorry for my carelessness and thanks for your reply.