From 2bed7da1c339822787990171b44f2f6011fb99d3 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 15 Jun 2026 08:37:39 +0200 Subject: [PATCH 1/5] Add stressgraphics-cef test Enabled only when CEF build --- test/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 48eb5d873b1f9..10d085df4d12b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -184,6 +184,13 @@ if(opengl AND TARGET TreeViewer) FAILREGEX "FAILED|Error in" LABELS longtest) endif() + if(webgui AND TARGET ROOTCefDisplay) + ROOT_ADD_TEST(test-stressgraphics-cef + RUN_SERIAL + COMMAND stressGraphics -b -k -p=sgk --web=cef + FAILREGEX "FAILED|Error in" + LABELS longtest) + endif() endif() #--stressHistogram------------------------------------------------------------------------------------ From dfaa6c74d81ed96098c68db1d0e56a0844601963 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 17 Jun 2026 07:48:30 +0200 Subject: [PATCH 2/5] [root-ci] add cefweb on fedora44 and opensus16 These two docker images now includes CEF builds so one can enable building of CEF plugin Plus `stressGraphics --web=cef` will be enabled as well --- .github/workflows/root-ci-config/buildconfig/fedora44.txt | 1 + .../root-ci-config/buildconfig/opensuse16-march_native.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/root-ci-config/buildconfig/fedora44.txt b/.github/workflows/root-ci-config/buildconfig/fedora44.txt index b4c72b29cadc9..fba17f621c4bc 100644 --- a/.github/workflows/root-ci-config/buildconfig/fedora44.txt +++ b/.github/workflows/root-ci-config/buildconfig/fedora44.txt @@ -4,3 +4,4 @@ pythia8=ON roofit_multiprocess=ON test_distrdf_pyspark=OFF vdt=OFF +cefweb=ON diff --git a/.github/workflows/root-ci-config/buildconfig/opensuse16-march_native.txt b/.github/workflows/root-ci-config/buildconfig/opensuse16-march_native.txt index 2a3ba74ab02bf..bd84268f2d64b 100644 --- a/.github/workflows/root-ci-config/buildconfig/opensuse16-march_native.txt +++ b/.github/workflows/root-ci-config/buildconfig/opensuse16-march_native.txt @@ -10,3 +10,4 @@ test_distrdf_pyspark=OFF tmva-pymva=ON tmva-sofie=ON pythia8=OFF +cefweb=ON From 270c884a028aca207c4eb463429f76af0bd8da5a Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 17 Jun 2026 13:37:00 +0200 Subject: [PATCH 3/5] [cef] copy CEF locales to 'Frameworks/cef/locales' subdir Frameworks directory already created and installed on the Mac Reuse same name to put extra files from CEF and do not pollute "bin/" or "lib/" directory --- CMakeLists.txt | 2 +- gui/cefdisplay/CMakeLists.txt | 10 +++++-- gui/cefdisplay/src/RCefWebDisplayHandle.cxx | 32 ++++++++++----------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 45219da785fdd..6d2314110fd2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -646,7 +646,7 @@ if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_INSTALL_PREFIX) if(http) install(DIRECTORY js/ DESTINATION ${CMAKE_INSTALL_JSROOTDIR} ${DIR_PERMISSIONS}) endif() - if(cefweb AND APPLE AND TARGET ROOTCefDisplay) + if(cefweb AND TARGET ROOTCefDisplay) install(DIRECTORY ${CMAKE_BINARY_DIR}/Frameworks/ DESTINATION ${CMAKE_INSTALL_FRAMEWORKSDIR} ${DIR_PERMISSIONS}) endif() if(webgui) diff --git a/gui/cefdisplay/CMakeLists.txt b/gui/cefdisplay/CMakeLists.txt index 5f66541f5ff0c..6f57038db66f4 100644 --- a/gui/cefdisplay/CMakeLists.txt +++ b/gui/cefdisplay/CMakeLists.txt @@ -35,7 +35,10 @@ elseif(MSVC) set(CEF_LIBRARY ${CEF_RELEASE_DIR}/libcef.lib) set(CEF_DLL_WRAPPER ${CEF_root}/build/libcef_dll_wrapper/Release/libcef_dll_wrapper.lib) file(COPY ${CEF_RELEASE_DIR}/ DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - file(COPY ${CEF_RESOURCES}/ DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + # copy dlls plus some extras to binary directory to let CEF find them without extra configuration + file(COPY ${CEF_RESOURCES}/ DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} PATTERN "locales" EXCLUDE) + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/Frameworks/cef/locales) + file(COPY ${CEF_RESOURCES}/locales/ DESTINATION ${CMAKE_BINARY_DIR}/Frameworks/cef/locales) else() set(CEF_platform src/gui_handler_linux.cxx) set(CEF_RESOURCES ${CEF_root}/Resources) @@ -44,7 +47,10 @@ else() set(CEF_DLL_WRAPPER ${CEF_root}/build/libcef_dll_wrapper/libcef_dll_wrapper.a) set(CEF_LIB_DEPENDENCY ${X11_LIBRARIES}) file(COPY ${CEF_RELEASE_DIR}/ DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) - file(COPY ${CEF_RESOURCES}/ DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) + # copy shared libraries plus some extras to library directory to let CEF find them without extra configuration + file(COPY ${CEF_RESOURCES}/ DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} PATTERN "locales" EXCLUDE) + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/Frameworks/cef/locales) + file(COPY ${CEF_RESOURCES}/locales/ DESTINATION ${CMAKE_BINARY_DIR}/Frameworks/cef/locales) if(EXISTS ${CEF_RELEASE_DIR}/swiftshader) file(COPY ${CEF_RELEASE_DIR}/swiftshader DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endif() diff --git a/gui/cefdisplay/src/RCefWebDisplayHandle.cxx b/gui/cefdisplay/src/RCefWebDisplayHandle.cxx index bf398fe2172ef..904d3c81cf9fb 100644 --- a/gui/cefdisplay/src/RCefWebDisplayHandle.cxx +++ b/gui/cefdisplay/src/RCefWebDisplayHandle.cxx @@ -206,28 +206,12 @@ std::unique_ptr RCefWebDisplayHandle::CefCreator::Displ TString cef_main = TROOT::GetBinDir() + "/cef_main"; cef_string_ascii_to_utf16(cef_main.Data(), cef_main.Length(), &settings.browser_subprocess_path); -#ifdef OS_LINUX - // on linux resource directory copied to lib/ - TString path2 = TROOT::GetLibDir() + "/locales"; - cef_string_ascii_to_utf16(path2.Data(), path2.Length(), &settings.locales_dir_path); - TString path3 = TROOT::GetLibDir(); - cef_string_ascii_to_utf16(path3.Data(), path3.Length(), &settings.resources_dir_path); -#endif - -#ifdef OS_WIN - // on windows resource directory copied to bin/ - TString path2 = TROOT::GetBinDir() + "/locales"; - cef_string_ascii_to_utf16(path2.Data(), path2.Length(), &settings.locales_dir_path); - TString path3 = TROOT::GetBinDir(); - cef_string_ascii_to_utf16(path3.Data(), path3.Length(), &settings.resources_dir_path); -#endif - #ifdef OS_MACOSX // on mac there is framework directory, where resources and libs are combined together TString path = TROOT::GetDataDir() + "/Frameworks/Chromium Embedded Framework.framework"; cef_string_ascii_to_utf16(path.Data(), path.Length(), &settings.framework_dir_path); - + // add CEF libraries to DYLD library path TString dypath = gSystem->Getenv("DYLD_LIBRARY_PATH"); if (dypath.Length() > 0) dypath.Append(":"); @@ -235,6 +219,20 @@ std::unique_ptr RCefWebDisplayHandle::CefCreator::Displ gSystem->Setenv("DYLD_LIBRARY_PATH", dypath); #endif +#ifdef OS_WIN + TString resource_dir = TROOT::GetBinDir(); + cef_string_ascii_to_utf16(resource_dir.Data(), resource_dir.Length(), &settings.resources_dir_path); + TString locales_dir = TROOT::GetDataDir() + "/Frameworks/cef/locales"; + cef_string_ascii_to_utf16(locales_dir.Data(), locales_dir.Length(), &settings.locales_dir_path); +#endif + +#ifdef OS_LINUX + TString resource_dir = TROOT::GetLibDir(); + cef_string_ascii_to_utf16(resource_dir.Data(), resource_dir.Length(), &settings.resources_dir_path); + TString locales_dir = TROOT::GetDataDir() + "/Frameworks/cef/locales"; + cef_string_ascii_to_utf16(locales_dir.Data(), locales_dir.Length(), &settings.locales_dir_path); +#endif + settings.no_sandbox = true; // if (gROOT->IsWebDisplayBatch()) settings.single_process = true; From ec9d8ad37ca1d4674a2d7c0228f3d2ec6a569a42 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 17 Jun 2026 14:23:46 +0200 Subject: [PATCH 4/5] [cef] handle all command line args in app One can provide many arguments already when starting cef_main. But in principle there is special method in SimpleApp to append command line arguments when required. Allows to properly use CEF for both on-screen and off-screen outputs simulataneousely --- gui/cefdisplay/inc/simple_app.h | 2 +- gui/cefdisplay/src/RCefWebDisplayHandle.cxx | 39 ++++----------------- gui/cefdisplay/src/simple_app.cxx | 34 +++++++++++++++--- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/gui/cefdisplay/inc/simple_app.h b/gui/cefdisplay/inc/simple_app.h index f9540bed25d3a..f78264f14b0ec 100644 --- a/gui/cefdisplay/inc/simple_app.h +++ b/gui/cefdisplay/inc/simple_app.h @@ -81,7 +81,7 @@ class SimpleApp : public CefApp, #endif - void StartWindow(THttpServer *serv, const std::string &url, const std::string &cont, CefRect &rect); + void StartWindow(THttpServer *serv, const std::string &url, const std::string &cont, CefRect &rect, bool is_headless = false); // CefRenderProcessHandler methods // void OnContextCreated(CefRefPtr browser, CefRefPtr frame, diff --git a/gui/cefdisplay/src/RCefWebDisplayHandle.cxx b/gui/cefdisplay/src/RCefWebDisplayHandle.cxx index 904d3c81cf9fb..2773ba2d85f62 100644 --- a/gui/cefdisplay/src/RCefWebDisplayHandle.cxx +++ b/gui/cefdisplay/src/RCefWebDisplayHandle.cxx @@ -97,7 +97,7 @@ std::unique_ptr RCefWebDisplayHandle::CefCreator::Displ CefRect rect((args.GetX() > 0) ? args.GetX() : 0, (args.GetY() > 0) ? args.GetY() : 0, (args.GetWidth() > 0) ? args.GetWidth() : 800, (args.GetHeight() > 0) ? args.GetHeight() : 600); - fCefApp->StartWindow(args.GetHttpServer(), args.GetFullUrl(), args.GetPageContent(), rect); + fCefApp->StartWindow(args.GetHttpServer(), args.GetFullUrl(), args.GetPageContent(), rect, args.IsHeadless()); if (args.IsHeadless()) handle->WaitForContent(wait_tmout, args.GetExtraArgs()); @@ -136,43 +136,18 @@ std::unique_ptr RCefWebDisplayHandle::CefCreator::Displ bool supress_log = (settings.log_severity == LOGSEVERITY_DISABLE) || (settings.log_severity == LOGSEVERITY_FATAL); - TApplication *root_app = gROOT->GetApplication(); - - std::vector cef_argv = { root_app->Argv(0) }; #ifdef OS_WIN - CefMainArgs main_args(args.IsHeadless() ? (HINSTANCE) 0 : GetModuleHandle(nullptr)); + // CefMainArgs main_args(args.IsHeadless() ? (HINSTANCE) 0 : GetModuleHandle(nullptr)); + + CefMainArgs main_args(GetModuleHandle(nullptr)); #else - if (args.IsHeadless()) { - cef_argv.emplace_back("--user-data-dir=."); - cef_argv.emplace_back("--allow-file-access-from-files"); - cef_argv.emplace_back("--disable-web-security"); -#ifdef OS_LINUX - cef_argv.emplace_back("--disable-gpu"); - cef_argv.emplace_back("--ignore-gpu-blocklist"); - cef_argv.emplace_back("--use-gl=swiftshader"); - cef_argv.emplace_back("--enable-unsafe-swiftshader"); -#endif -#ifdef OS_MACOSX - cef_argv.emplace_back("--use-angle=metal"); - cef_argv.emplace_back("--ignore-gpu-blocklist"); - cef_argv.emplace_back("--enable-webgl"); - cef_argv.emplace_back("--enable-gpu"); - cef_argv.emplace_back("--enable-gpu-rasterization"); -#endif - cef_argv.emplace_back("--off-screen-rendering-enabled"); - if (use_views) - cef_argv.emplace_back("--ozone-platform=headless"); - } else { -#ifdef OS_MACOSX - cef_argv.emplace_back("--use-angle=metal"); - cef_argv.emplace_back("--ignore-gpu-blocklist"); - cef_argv.emplace_back("--enable-webgl"); -#endif - } + TApplication *root_app = gROOT->GetApplication(); + + std::vector cef_argv = { root_app->Argv(0) }; if (supress_log) { cef_argv.emplace_back("--disable-logging"); diff --git a/gui/cefdisplay/src/simple_app.cxx b/gui/cefdisplay/src/simple_app.cxx index 4b41daab82190..213f11d24f61d 100644 --- a/gui/cefdisplay/src/simple_app.cxx +++ b/gui/cefdisplay/src/simple_app.cxx @@ -117,7 +117,8 @@ class SimpleBrowserViewDelegate : public CefBrowserViewDelegate { SimpleApp::SimpleApp(bool use_viewes, bool supress_log, THttpServer *serv, const std::string &url, const std::string &cont, int width, int height, bool headless) - : CefApp(), CefBrowserProcessHandler(), fUseViewes(use_viewes), fSupressLog(supress_log), fFirstServer(serv), fFirstUrl(url), fFirstContent(cont), fFirstHeadless(headless) + : CefApp(), CefBrowserProcessHandler(), fUseViewes(use_viewes), fSupressLog(supress_log), + fFirstServer(serv), fFirstUrl(url), fFirstContent(cont), fFirstHeadless(headless) { fFirstRect.Set(0, 0, width, height); @@ -157,18 +158,41 @@ void SimpleApp::OnBeforeCommandLineProcessing(const CefString &process_type, Cef command_line->AppendSwitchWithValue("enable-logging", "none"); } -#ifdef OS_MACOSX + if (fNextHeadless) { + command_line->AppendSwitchWithValue("user-data-dir", "."); + command_line->AppendSwitch("allow-file-access-from-files"); + command_line->AppendSwitch("disable-web-security"); + command_line->AppendSwitch("off-screen-rendering-enabled"); + } +#ifdef OS_MACOSX if (fNextHeadless) { + command_line->AppendSwitch("headless"); command_line->AppendSwitchWithValue("use-angle", "swiftshader"); command_line->AppendSwitch("enable-unsafe-swiftshader"); command_line->AppendSwitch("disable-gpu"); + // command_line->AppendSwitch("enable-gpu-rasterization"); + // if (fUseViewes) + // command_line->AppendSwitchWithValue("ozone-platform", "headless"); } else { command_line->AppendSwitchWithValue("use-angle", "metal"); command_line->AppendSwitch("ignore-gpu-blocklist"); command_line->AppendSwitch("enable-webgl"); } #endif + +#ifdef OS_LINUX + if (fNextHeadless) { + command_line->AppendSwitch("headless"); + command_line->AppendSwitchWithValue("use-gl", "swiftshader"); + command_line->AppendSwitch("disable-gpu"); + command_line->AppendSwitch("ignore-gpu-blocklist"); + command_line->AppendSwitch("enable-unsafe-swiftshader"); + + if (fUseViewes) + command_line->AppendSwitchWithValue("ozone-platform", "headless"); + } +#endif } void SimpleApp::OnBeforeChildProcessLaunch(CefRefPtr command_line) @@ -180,18 +204,18 @@ void SimpleApp::OnContextInitialized() CEF_REQUIRE_UI_THREAD(); if (!fFirstUrl.empty() || !fFirstContent.empty()) { - StartWindow(fFirstServer, fFirstUrl, fFirstContent, fFirstRect); + StartWindow(fFirstServer, fFirstUrl, fFirstContent, fFirstRect, fFirstHeadless); fFirstUrl.clear(); fFirstContent.clear(); } } -void SimpleApp::StartWindow(THttpServer *serv, const std::string &addr, const std::string &cont, CefRect &rect) +void SimpleApp::StartWindow(THttpServer *serv, const std::string &addr, const std::string &cont, CefRect &rect, bool is_headless) { CEF_REQUIRE_UI_THREAD(); - bool is_batch = addr.empty() && !cont.empty(); + bool is_batch = (addr.empty() && !cont.empty()) || is_headless; if (!fGuiHandler) fGuiHandler = new GuiHandler(fUseViewes); From 6d65b65c7ef885b405dfd8a0a4dfe5a2d882917d Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 19 Jun 2026 11:19:18 +0200 Subject: [PATCH 5/5] [cef] adjust stressGraphics On docker platforms CEF has limited resources, so reduce number of generated images when running with cef Also adjust some ref values while CEF on opensuse16 docker has more variations --- test/stressGraphics.cxx | 11 ++++++++--- test/stressGraphics_web.ref | 14 +++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/test/stressGraphics.cxx b/test/stressGraphics.cxx index 32d8645283b16..eac4602893595 100644 --- a/test/stressGraphics.cxx +++ b/test/stressGraphics.cxx @@ -566,7 +566,7 @@ void webcanv_batch_mode(int number) /// Starts new block of tests /// In web mode configure number of batch images -void start_block(const TString &title) +void start_block(const TString &title, bool is3d = false) { if (!gOptionR) { std::cout << "**********************************************************************\n"; @@ -574,7 +574,12 @@ void start_block(const TString &title) std::cout << "**********************************************************************\n"; } - webcanv_batch_mode(80); + int batch_size = 80; + // cef makes problem with many images in 3D mode, so reduce it + if (is3d && TString("cef") == gROOT->GetWebDisplay()) + batch_size = 10; + + webcanv_batch_mode(batch_size); } //////////////////////////////////////////////////////////////////////////////// @@ -4647,7 +4652,7 @@ void stressGraphics(Int_t verbose = 0, Bool_t generate = kFALSE, Bool_t keep_fil graphpolar (); print_reports (); - start_block("High Level 3D Primitives"); + start_block("High Level 3D Primitives", true); options2d1 (); options2d2 (); options2d3 (); diff --git a/test/stressGraphics_web.ref b/test/stressGraphics_web.ref index 8416eec1646b4..5411a55820536 100644 --- a/test/stressGraphics_web.ref +++ b/test/stressGraphics_web.ref @@ -11,20 +11,20 @@ ttext2 577 50 3642 50 16539 6033 13079 6000 577 50 tlatex0 12533 60 19680 70 170000 60000 185000 65000 12533 60 tlatex1 6585 50 10186 50 37509 9184 34969 7388 6585 50 - tlatex2 4760 50 9867 62 44760 8732 30329 7888 4760 50 + tlatex2 4760 50 9867 62 44760 8732 32000 10000 4760 50 tlatex3 9971 50 18489 50 44087 8064 32000 10000 9971 50 - tlatex4 20678 50 15186 50 72078 10677 45000 30000 20678 50 - tlatex5 31915 50 21579 50 98532 11922 102142 33853 31915 50 + tlatex4 20678 50 15186 50 72078 10677 55000 40000 20678 50 + tlatex5 31915 50 21579 50 105000 20000 102142 33853 31915 50 kerning 13535 50 12671 50 179782 31960 66694 28160 13535 50 itbf 4538 50 9061 70 45280 8144 39000 14000 4538 50 tmathtext 109566 50 387050 100 76838 7616 62298 28760 109921 50 transparency 1781 50 6264 100 41491 15000 75006 25000 1781 50 - transpad 9893 50 18592 84 63022 16300 40204 15625 9555 100 + transpad 9893 50 18650 150 63022 16300 40204 15625 9555 100 statfitparam 34595 67 47449 2367 110520 19707 58703 18882 34600 100 - tgaxis1 18061 50 35587 173 67725 8774 36566 15109 18061 50 + tgaxis1 18061 50 35587 173 67725 8774 38000 18000 18061 50 tgaxis2 13608 50 28297 184 63580 9566 44000 14000 13608 50 tgaxis3 17470 50 42002 523 115108 33410 66220 10768 17473 50 - tgaxis4 4442 50 9627 84 40009 8482 31730 8009 4445 50 + tgaxis4 4550 200 9720 200 40009 8482 31730 8009 4550 200 tgaxis5 14297 200 14920 173 190869 50682 72947 38985 14297 200 tgaxis6 5021 190 10944 173 45000 11000 37464 7000 5021 290 padticks 22245 200 37300 700 71900 30000 58600 20000 22245 200 @@ -66,7 +66,7 @@ tgraph2d2 177243 181081 589798 763841 153361 113920 176486 26369 177099 181080 tgraph2derr 113277 50000 1099040 763841 85018 40000 79189 20000 113277 50000 tgraph2dassym 106676 50000 1099035 763841 80483 40000 74371 20000 106676 50000 - tprofile3d 51076 20000 1062206 500000 73964 35000 50008 15000 51072 20000 + tprofile3d 51076 20000 1062206 500000 73964 35000 53000 18000 51072 20000 tf3 86268 20000 1099059 500000 100000 50000 68806 17000 86268 20000 basic3d 18981 10000 573339 300000 40000 20000 30328 15000 18981 10000 annotation3d 114858 40000 731079 200000 128048 50000 156824 50000 115254 40000