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.