Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RCefWebDisplayHandle.cxx
Go to the documentation of this file.
1// Author: Sergey Linev <S.Linev@gsi.de>
2// Date: 2020-08-21
3// Warning: This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome!
4
5/*************************************************************************
6 * Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13
14#if !defined(_MSC_VER)
15#pragma GCC diagnostic ignored "-Wunused-parameter"
16#pragma GCC diagnostic ignored "-Wshadow"
17#endif
18
19
21
22#include "TTimer.h"
23#include "TROOT.h"
24#include "TSystem.h"
25#include "TEnv.h"
26#include "TApplication.h"
27
28#include "include/base/cef_build.h"
29
30#include <ROOT/RLogger.hxx>
31
32#include <memory>
33
34
35class TCefTimer : public TTimer {
36public:
38 void Timeout() override
39 {
40 // just let run loop
42 }
43};
44
45
47
49
50public:
51
53
54 ~FrameSourceVisitor() override = default;
55
56 void Visit(const CefString &str) override
57 {
58 if (fHandle && fHandle->IsValid())
59 fHandle->SetContent(str.ToString());
60 }
61
62private:
63 // Include the default reference counting implementation.
66};
67
68
70 bool *fFlag{nullptr};
71public:
73 ~HeadlessPrintCallback() override = default;
74
75 void OnPdfPrintFinished(const CefString&, bool ok) override
76 {
77 if (fFlag) *fFlag = true;
78 }
79private:
80 // Include the default reference counting implementation.
83};
84
85std::unique_ptr<ROOT::RWebDisplayHandle> RCefWebDisplayHandle::CefCreator::Display(const ROOT::RWebDisplayArgs &args)
86{
87
88 auto handle = std::make_unique<RCefWebDisplayHandle>(args.GetFullUrl());
89 if (!args.IsStandalone())
90 handle->fCloseBrowser = false;
91
92 Int_t wait_tmout = args.IsHeadless() ? gEnv->GetValue("WebGui.CefHeadlessTimeout", 30) : -1;
93
94 if (fCefApp) {
95 fCefApp->SetNextHandle(handle.get(), args.IsHeadless());
96
97 CefRect rect((args.GetX() > 0) ? args.GetX() : 0, (args.GetY() > 0) ? args.GetY() : 0,
98 (args.GetWidth() > 0) ? args.GetWidth() : 800, (args.GetHeight() > 0) ? args.GetHeight() : 600);
99
100 fCefApp->StartWindow(args.GetHttpServer(), args.GetFullUrl(), args.GetPageContent(), rect);
101
102 if (args.IsHeadless())
103 handle->WaitForContent(wait_tmout, args.GetExtraArgs());
104
105 return handle;
106 }
107
109 bool use_views = true;
110
111 TString env_use_views = gEnv->GetValue("WebGui.CefUseViews", "");
112 if ((env_use_views == "yes") || (env_use_views == "1"))
113 use_views = true;
114 else if ((env_use_views == "no") || (env_use_views == "0"))
115 use_views = false;
116
117 // Specify CEF global settings here.
119
120 TString ceflog = gEnv->GetValue("WebGui.CefLogSeveriry", "fatal");
121 if (ceflog == "fatal")
122 settings.log_severity = LOGSEVERITY_FATAL;
123 else if (ceflog == "verbose")
124 settings.log_severity = LOGSEVERITY_VERBOSE;
125 else if (ceflog == "info")
126 settings.log_severity = LOGSEVERITY_INFO;
127 else if (ceflog == "warning")
128 settings.log_severity = LOGSEVERITY_WARNING;
129 else if (ceflog == "error")
130 settings.log_severity = LOGSEVERITY_ERROR;
131 else if (ceflog == "disable")
132 settings.log_severity = LOGSEVERITY_DISABLE;
133 else
134 settings.log_severity = LOGSEVERITY_FATAL;
135
136 bool supress_log = (settings.log_severity == LOGSEVERITY_DISABLE) ||
137 (settings.log_severity == LOGSEVERITY_FATAL);
138
139 TApplication *root_app = gROOT->GetApplication();
140
141 std::vector<const char *> cef_argv = { root_app->Argv(0) };
142
143#ifdef OS_WIN
144
146
147#else
148
149 if (args.IsHeadless()) {
150 cef_argv.emplace_back("--user-data-dir=.");
151 cef_argv.emplace_back("--allow-file-access-from-files");
152 cef_argv.emplace_back("--disable-web-security");
153#ifdef OS_LINUX
154 cef_argv.emplace_back("--disable-gpu");
155 cef_argv.emplace_back("--ignore-gpu-blocklist");
156 cef_argv.emplace_back("--use-gl=swiftshader");
157 cef_argv.emplace_back("--enable-unsafe-swiftshader");
158#endif
159#ifdef OS_MACOSX
160 cef_argv.emplace_back("--use-angle=metal");
161 cef_argv.emplace_back("--ignore-gpu-blocklist");
162 cef_argv.emplace_back("--enable-webgl");
163 cef_argv.emplace_back("--enable-gpu");
164 cef_argv.emplace_back("--enable-gpu-rasterization");
165#endif
166 cef_argv.emplace_back("--off-screen-rendering-enabled");
167 if (use_views)
168 cef_argv.emplace_back("--ozone-platform=headless");
169 } else {
170#ifdef OS_MACOSX
171 cef_argv.emplace_back("--use-angle=metal");
172 cef_argv.emplace_back("--ignore-gpu-blocklist");
173 cef_argv.emplace_back("--enable-webgl");
174#endif
175 }
176
177 if (supress_log) {
178 cef_argv.emplace_back("--disable-logging");
179 cef_argv.emplace_back("--enable-logging=none");
180 cef_argv.emplace_back("--v=-1");
181 }
182
183 cef_argv.emplace_back(nullptr);
184
185 CefMainArgs main_args(cef_argv.size() - 1, (char **) cef_argv.data());
186
187#endif
188
189 // CEF applications have multiple sub-processes (render, plugin, GPU, etc)
190 // that share the same executable. This function checks the command-line and,
191 // if this is a sub-process, executes the appropriate logic.
192
193 /* int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
194 if (exit_code >= 0) {
195 // The sub-process has completed so return here.
196 return exit_code;
197 }
198 */
199
200 // Install xlib error handlers so that the application won't be terminated
201 // on non-fatal errors.
202 // XSetErrorHandler(XErrorHandlerImpl);
203 // XSetIOErrorHandler(XIOErrorHandlerImpl);
204
205
206 TString cef_main = TROOT::GetBinDir() + "/cef_main";
207 cef_string_ascii_to_utf16(cef_main.Data(), cef_main.Length(), &settings.browser_subprocess_path);
208
209#ifdef OS_LINUX
210 // on linux resource directory copied to lib/
211 TString path2 = TROOT::GetLibDir() + "/locales";
212 cef_string_ascii_to_utf16(path2.Data(), path2.Length(), &settings.locales_dir_path);
214 cef_string_ascii_to_utf16(path3.Data(), path3.Length(), &settings.resources_dir_path);
215#endif
216
217#ifdef OS_WIN
218 // on windows resource directory copied to bin/
219 TString path2 = TROOT::GetBinDir() + "/locales";
220 cef_string_ascii_to_utf16(path2.Data(), path2.Length(), &settings.locales_dir_path);
222 cef_string_ascii_to_utf16(path3.Data(), path3.Length(), &settings.resources_dir_path);
223#endif
224
225#ifdef OS_MACOSX
226 // on mac there is framework directory, where resources and libs are combined together
227 TString path = TROOT::GetDataDir() + "/Frameworks/Chromium Embedded Framework.framework";
228 cef_string_ascii_to_utf16(path.Data(), path.Length(), &settings.framework_dir_path);
229
230
231 TString dypath = gSystem->Getenv("DYLD_LIBRARY_PATH");
232 if (dypath.Length() > 0)
233 dypath.Append(":");
234 dypath.Append(path + "/Libraries/");
235 gSystem->Setenv("DYLD_LIBRARY_PATH", dypath);
236#endif
237
238 settings.no_sandbox = true;
239 // if (gROOT->IsWebDisplayBatch()) settings.single_process = true;
240
241 if (args.IsHeadless())
242 settings.windowless_rendering_enabled = true;
243
244 // settings.external_message_pump = true;
245 // settings.multi_threaded_message_loop = false;
246
247 std::string plog = "cef.log";
248 cef_string_ascii_to_utf16(plog.c_str(), plog.length(), &settings.log_file);
249
250 // settings.uncaught_exception_stack_size = 100;
251 // settings.ignore_certificate_errors = true;
252
253 // settings.remote_debugging_port = 7890;
254
255 // SimpleApp implements application-level callbacks for the browser process.
256 // It will create the first browser instance in OnContextInitialized() after
257 // CEF has initialized.
259 args.GetHttpServer(), args.GetFullUrl(), args.GetPageContent(),
260 args.GetWidth() > 0 ? args.GetWidth() : 800,
261 args.GetHeight() > 0 ? args.GetHeight() : 600,
262 args.IsHeadless());
263
264 fCefApp->SetNextHandle(handle.get(), args.IsHeadless());
265
266 // Initialize CEF for the browser process.
267 CefInitialize(main_args, settings, fCefApp.get(), nullptr);
268
269 if (args.IsHeadless()) {
270 handle->WaitForContent(wait_tmout, args.GetExtraArgs());
271 } else {
272 // Create timer to let run CEF message loop together with ROOT event loop
273 Int_t interval = gEnv->GetValue("WebGui.CefTimer", 10);
274 TCefTimer *timer = new TCefTimer((interval > 0) ? interval : 10, kTRUE);
275 timer->TurnOn();
276 }
277
278 // window not yet exists here
279 return handle;
280}
281
282///////////////////////////////////////////////////////////////////////////////////////////////////
283/// Destructor
284/// Closes browser window if any
285
292
293///////////////////////////////////////////////////////////////////////////////////////////////////
294/// Closes associated browser window
295
297{
298 if (fBrowser && fCloseBrowser) {
299 auto host = fBrowser->GetHost();
300 if (host) host->CloseBrowser(true);
301 fBrowser = nullptr;
302 }
303}
304
305///////////////////////////////////////////////////////////////////////////////////////////////////
306/// Process system events until browser content is available
307/// Used in headless mode for batch production like chrome --dump-dom is doing
308
310{
311 int expired = tmout_sec * 100;
312 bool did_try = false, print_finished = false;
313 std::string pdffile;
314 if (!extra_args.empty() && (extra_args.find("--print-to-pdf=")==0))
315 pdffile = extra_args.substr(15);
316
317 while ((--expired > 0) && GetContent().empty() && !print_finished) {
318
319 if (gSystem->ProcessEvents()) break; // interrupted, has to return
320
322
323 if (fBrowser && !did_try && fBrowser->HasDocument() && !fBrowser->IsLoading() && fBrowser->GetMainFrame()) {
324 did_try = true;
325 if (pdffile.empty()) {
326 fBrowser->GetMainFrame()->GetSource(new FrameSourceVisitor(this));
327 } else {
329 fBrowser->GetHost()->PrintToPDF(pdffile, settings, new HeadlessPrintCallback(&print_finished));
330 }
331 }
332
333 gSystem->Sleep(10); // only 10 ms sleep
334 }
335
336 CloseBrowser();
337
338 // call it once here to complete browser window closing, timer is not installed in batch mode
340
341 return !GetContent().empty();
342}
343
344///////////////////////////////////////////////////////////////////////////////////////////////////
345/// Resize browser window
346
348{
349 if (!fBrowser)
350 return false;
352}
353
354///////////////////////////////////////////////////////////////////////////////////////////////////
355/// Add CEF creator
356
358{
359 auto &entry = FindCreator("cef");
360 if (!entry)
361 GetMap().emplace("cef", std::make_unique<CefCreator>());
362}
363
364///////////////////////////////////////////////////////////////////////////////////////////////////
365/// Helper struct to add creator
366
370
struct RCefCreatorReg newRCefCreatorReg
long Long_t
Signed long integer 4 bytes (long). Size depends on architecture.
Definition RtypesCore.h:68
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
R__EXTERN TEnv * gEnv
Definition TEnv.h:126
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t rect
Option_t Option_t TPoint TPoint const char mode
Option_t Option_t width
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t height
#define gROOT
Definition TROOT.h:417
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
DISALLOW_COPY_AND_ASSIGN(FrameSourceVisitor)
void Visit(const CefString &str) override
FrameSourceVisitor(RCefWebDisplayHandle *handle)
IMPLEMENT_REFCOUNTING(FrameSourceVisitor)
RCefWebDisplayHandle * fHandle
~FrameSourceVisitor() override=default
static bool PlatformResize(CefRefPtr< CefBrowser > browser, int width, int height)
static void PlatformInit()
void OnPdfPrintFinished(const CefString &, bool ok) override
~HeadlessPrintCallback() override=default
IMPLEMENT_REFCOUNTING(HeadlessPrintCallback)
DISALLOW_COPY_AND_ASSIGN(HeadlessPrintCallback)
std::unique_ptr< ROOT::RWebDisplayHandle > Display(const ROOT::RWebDisplayArgs &args) override
CefRefPtr< CefBrowser > fBrowser
associated browser
void CloseBrowser()
Closes associated browser window.
static void AddCreator()
Add CEF creator.
bool WaitForContent(int tmout_sec, const std::string &extra_args)
Process system events until browser content is available Used in headless mode for batch production l...
unsigned fValid
used to verify if instance valid or not
~RCefWebDisplayHandle() override
Destructor Closes browser window if any.
bool Resize(int, int) override
Resize browser window.
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
THttpServer * GetHttpServer() const
returns http server instance, used for window display
int GetWidth() const
returns preferable web window width
int GetY() const
set preferable web window y position
std::string GetFullUrl() const
returns window url with append options
bool IsStandalone() const
Return true if browser should runs in standalone mode.
int GetHeight() const
returns preferable web window height
bool IsHeadless() const
returns headless mode
const std::string & GetExtraArgs() const
get extra command line arguments for starting web browser command
int GetX() const
set preferable web window x position
const std::string & GetPageContent() const
returns window url
const std::string & GetContent() const
get content
static std::map< std::string, std::unique_ptr< Creator > > & GetMap()
Static holder of registered creators of web displays.
void SetContent(const std::string &cont)
set content
static std::unique_ptr< Creator > & FindCreator(const std::string &name, const std::string &libname="")
Search for specific browser creator If not found, try to add one.
This class creates the ROOT Application Environment that interfaces to the windowing system eventloop...
void Timeout() override
TCefTimer(Long_t milliSec, Bool_t mode)
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:511
static const TString & GetBinDir()
Get the binary directory in the installation. Static utility function.
Definition TROOT.cxx:3150
static const TString & GetLibDir()
Get the library directory in the installation.
Definition TROOT.cxx:3178
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition TROOT.cxx:3391
Basic string class.
Definition TString.h:138
Ssiz_t Length() const
Definition TString.h:425
const char * Data() const
Definition TString.h:384
virtual const char * Getenv(const char *env)
Get environment variable.
Definition TSystem.cxx:1680
virtual void Sleep(UInt_t milliSec)
Sleep milliSec milli seconds.
Definition TSystem.cxx:439
virtual void Setenv(const char *name, const char *value)
Set environment variable.
Definition TSystem.cxx:1664
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:418
Handles synchronous and a-synchronous timer events.
Definition TTimer.h:51
Helper struct to add creator.