17#include "RConfigure.h"
41using namespace std::string_literals;
57 static std::map<std::string, std::unique_ptr<RWebDisplayHandle::Creator>> sMap;
70 auto search =
m.find(
name);
71 if (search ==
m.end()) {
73 if (libname ==
"ChromeCreator") {
74 m.emplace(
name, std::make_unique<ChromeCreator>());
75 }
else if (libname ==
"FirefoxCreator") {
76 m.emplace(
name, std::make_unique<FirefoxCreator>());
77 }
else if (libname ==
"BrowserCreator") {
78 m.emplace(
name, std::make_unique<BrowserCreator>(
false));
79 }
else if (!libname.empty()) {
83 search =
m.find(
name);
86 if (search !=
m.end())
87 return search->second;
89 static std::unique_ptr<RWebDisplayHandle::Creator> dummy;
94namespace Experimental {
126 gSystem->
Exec((
"taskkill /F /PID "s + std::to_string(fPid) +
" >NUL 2>NUL").c_str());
127 std::string rmdir =
"rmdir /S /Q ";
131 std::string rmdir =
"rm -rf ";
133 if (!fTmpDir.empty())
150 if (exec.find(
"$url") == std::string::npos) {
153 fExec = exec +
" $url";
155 fExec = exec +
" $url &";
159 auto pos = exec.find(
" ");
160 if (pos != std::string::npos)
161 fProg = exec.substr(0, pos);
164 fExec =
"open \'$url\'";
166 fExec =
"start $url";
168 fExec =
"xdg-open \'$url\' &";
177 if (nexttry.empty() || !fProg.empty())
182 fProg = std::regex_replace(nexttry, std::regex(
"%20"),
" ");
189 if (!check_std_paths)
194 auto pos = ProgramFiles.find(
" (x86)");
195 if (pos != std::string::npos)
196 ProgramFiles.erase(pos, 6);
197 std::string ProgramFilesx86 =
gSystem->
Getenv(
"ProgramFiles(x86)");
199 if (!ProgramFiles.empty())
200 TestProg(ProgramFiles + nexttry,
false);
201 if (!ProgramFilesx86.empty())
202 TestProg(ProgramFilesx86 + nexttry,
false);
209std::unique_ptr<RWebDisplayHandle>
220 exec = fHeadlessExec;
224 exec =
"$prog $url &";
229 std::string swidth = std::to_string(args.
GetWidth() > 0 ? args.
GetWidth() : 800),
231 sposx = std::to_string(args.
GetX() >= 0 ? args.
GetX() : 0),
232 sposy = std::to_string(args.
GetY() >= 0 ? args.
GetY() : 0);
234 ProcessGeometry(exec, args);
236 std::string rmdir = MakeProfile(exec, args.
IsHeadless());
238 exec = std::regex_replace(exec, std::regex(
"\\$url"), url);
239 exec = std::regex_replace(exec, std::regex(
"\\$width"), swidth);
240 exec = std::regex_replace(exec, std::regex(
"\\$height"), sheight);
241 exec = std::regex_replace(exec, std::regex(
"\\$posx"), sposx);
242 exec = std::regex_replace(exec, std::regex(
"\\$posy"), sposy);
244 if (exec.compare(0,5,
"fork:") == 0) {
255 if (!fargs || (fargs->GetLast()<=0)) {
260 std::vector<char *> argv;
261 argv.push_back((
char *) fProg.c_str());
262 for (
Int_t n = 0;
n <= fargs->GetLast(); ++
n)
263 argv.push_back((
char *)fargs->At(
n)->GetName());
264 argv.push_back(
nullptr);
266 R__LOG_DEBUG(0,
WebGUILog()) <<
"Show web window in browser with posix_spawn:\n" << fProg <<
" " << exec;
269 int status = posix_spawn(&pid, argv[0],
nullptr,
nullptr, argv.data(),
nullptr);
277 return std::make_unique<RWebBrowserHandle>(url, rmdir, pid);
287 exec =
"wmic process call create '"s +
gSystem->
UnixPathName(fProg.c_str()) + exec +
"' | find \"ProcessId\" "s;
289 std::stringstream ss(process_id);
293 ss >> tmp >>
c >> pid;
301 return std::make_unique<RWebBrowserHandle>(url, rmdir, pid);
307 if (exec.rfind(
"&") == exec.length() - 1) {
310 exec.resize(exec.length() - 1);
312 std::vector<char *> argv;
313 std::string firstarg = fProg;
314 auto slashpos = firstarg.find_last_of(
"/\\");
315 if (slashpos != std::string::npos)
316 firstarg.erase(0, slashpos + 1);
317 argv.push_back((
char *)firstarg.c_str());
320 for (
Int_t n = 1;
n <= fargs->GetLast(); ++
n)
321 argv.push_back((
char *)fargs->At(
n)->GetName());
322 argv.push_back(
nullptr);
326 _spawnv(_P_NOWAIT, fProg.c_str(), argv.data());
328 return std::make_unique<RWebBrowserHandle>(url, rmdir,
""s);
336 std::string prog = std::regex_replace(fProg, std::regex(
" "),
"\\ ");
338 std::string prog = fProg;
343 exec = std::regex_replace(exec, std::regex(
"\\$prog"), prog);
347 if (!redirect.empty()) {
348 auto p = exec.length();
349 if (exec.rfind(
"&") == p-1) --p;
350 exec.insert(p,
" >"s + redirect +
" "s);
358 if (!redirect.empty()) {
365 return std::make_unique<RWebBrowserHandle>(url, rmdir, dump_content);
376 TestProg(
"\\Google\\Chrome\\Application\\chrome.exe",
true);
379 TestProg(
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
383 TestProg(
"/usr/bin/chromium-browser");
384 TestProg(
"/usr/bin/chrome-browser");
390 fExec =
gEnv->
GetValue(
"WebGui.ChromeInteractive",
"$prog $geometry --new-window --app=$url &");
392 fBatchExec =
gEnv->
GetValue(
"WebGui.ChromeBatch",
"$prog --headless --no-sandbox --no-zygote --disable-extensions --disable-gpu --disable-audio-output $geometry $url");
393 fHeadlessExec =
gEnv->
GetValue(
"WebGui.ChromeHeadless",
"fork: --headless --no-sandbox --no-zygote --disable-extensions --disable-gpu --disable-audio-output $geometry $url");
394 fExec =
gEnv->
GetValue(
"WebGui.ChromeInteractive",
"$prog $geometry --new-window --app=\'$url\' &");
413 geometry.append(
"--window-position="s + std::to_string(args.
GetX() >= 0 ? args.
GetX() : 0) +
","s +
414 std::to_string(args.
GetY() >= 0 ? args.
GetY() : 0));
422 exec = std::regex_replace(exec, std::regex(
"\\$geometry"),
geometry);
431 std::string rmdir, profile_arg;
433 if (exec.find(
"$profile") == std::string::npos)
436 const char *chrome_profile =
gEnv->
GetValue(
"WebGui.ChromeProfile",
"");
437 if (chrome_profile && *chrome_profile) {
438 profile_arg = chrome_profile;
441 std::string rnd_profile =
"root_chrome_profile_"s + std::to_string(
gRandom->
Integer(0x100000));
445 profile_arg +=
"\\"s + rnd_profile;
447 profile_arg +=
"/"s + rnd_profile;
452 exec = std::regex_replace(exec, std::regex(
"\\$profile"), profile_arg);
466 TestProg(
"\\Mozilla Firefox\\firefox.exe",
true);
469 TestProg(
"/Applications/Firefox.app/Contents/MacOS/firefox");
480 fExec =
gEnv->
GetValue(
"WebGui.FirefoxInteractive",
"$prog -no-remote $profile $url &");
482 fBatchExec =
gEnv->
GetValue(
"WebGui.FirefoxBatch",
"$prog --headless --private-window --no-remote $profile $url");
484 fExec =
gEnv->
GetValue(
"WebGui.FirefoxInteractive",
"$prog --private-window \'$url\' &");
493 std::string rmdir, profile_arg;
495 if (exec.find(
"$profile") == std::string::npos)
498 const char *ff_profile =
gEnv->
GetValue(
"WebGui.FirefoxProfile",
"");
499 const char *ff_profilepath =
gEnv->
GetValue(
"WebGui.FirefoxProfilePath",
"");
501 if (ff_profile && *ff_profile) {
502 profile_arg =
"-P "s + ff_profile;
503 }
else if (ff_profilepath && *ff_profilepath) {
504 profile_arg =
"-profile "s + ff_profilepath;
505 }
else if ((ff_randomprofile > 0) || (batch_mode && (ff_randomprofile >= 0))) {
508 std::string rnd_profile =
"root_ff_profile_"s + std::to_string(
gRandom->
Integer(0x100000));
512 profile_dir +=
"\\"s + rnd_profile;
514 profile_dir +=
"/"s + rnd_profile;
517 profile_arg =
"-profile "s + profile_dir;
523 std::ofstream user_js(profile_dir +
"/user.js", std::ios::trunc);
524 user_js <<
"user_pref(\"browser.dom.window.dump.enabled\", true);" << std::endl;
526 user_js <<
"user_pref(\"datareporting.policy.dataSubmissionPolicyAcceptedVersion\", 2);" << std::endl;
527 user_js <<
"user_pref(\"datareporting.policy.dataSubmissionPolicyNotifiedTime\", \"1635760572813\");" << std::endl;
535 exec = std::regex_replace(exec, std::regex(
"\\$profile"), profile_arg);
548 std::unique_ptr<RWebDisplayHandle> handle;
553 auto try_creator = [&](std::unique_ptr<Creator> &creator) {
554 if (!creator || !creator->IsActive())
556 handle = creator->Display(args);
557 return handle ? true :
false;
561 if (try_creator(
FindCreator(
"cef",
"libROOTCefDisplay")))
566 if (try_creator(
FindCreator(
"qt5",
"libROOTQt5WebDisplay")))
571 if (try_creator(
FindCreator(
"qt6",
"libROOTQt6WebDisplay")))
581 if (try_creator(
FindCreator(
"chrome",
"ChromeCreator")))
586 if (try_creator(
FindCreator(
"firefox",
"FirefoxCreator")))
596 std::unique_ptr<Creator> creator = std::make_unique<BrowserCreator>(
false, args.
GetCustomExec());
597 try_creator(creator);
599 try_creator(
FindCreator(
"browser",
"BrowserCreator"));
639 std::string _fname = fname;
640 std::transform(_fname.begin(), _fname.end(), _fname.begin(), ::tolower);
642 auto EndsWith = [_fname](
const std::string &suffix) {
643 return (_fname.length() > suffix.length()) ? (0 == _fname.compare (_fname.length() - suffix.length(), suffix.length(), suffix)) :
false;
646 if (EndsWith(
".json")) {
647 std::ofstream ofs(fname);
660 jsrootsys = jsrootsysdflt.
Data();
668 std::string draw_kind;
670 if (EndsWith(
".pdf"))
672 else if (EndsWith(
"shot.png"))
674 else if (EndsWith(
".svg"))
676 else if (EndsWith(
".png"))
678 else if (EndsWith(
".jpg") || EndsWith(
".jpeg"))
680 else if (EndsWith(
".webp"))
692 if (filecont.empty()) {
697 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_width"), std::to_string(
width));
698 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_height"), std::to_string(height));
700 if (strstr(jsrootsys,
"http://") || strstr(jsrootsys,
"https://") || strstr(jsrootsys,
"file://"))
701 filecont = std::regex_replace(filecont, std::regex(
"\\$jsrootsys"), jsrootsys);
703 filecont = std::regex_replace(filecont, std::regex(
"\\$jsrootsys"),
"file://"s + jsrootsys);
705 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_kind"), draw_kind);
707 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_object"), json);
710 if (draw_kind ==
"draw") {
716 dump_name =
"canvasdump";
722 fputs(
"placeholder", df);
728 static bool chrome_tmp_workaround =
false;
741 R__LOG_DEBUG(0,
WebGUILog()) <<
"Using file content_len " << filecont.length() <<
" to produce batch image " << fname;
744 tmp_name =
"canvasbody";
750 fputs(filecont.c_str(), hf);
753 html_name = tmp_name +
".html";
755 if (chrome_tmp_workaround) {
757 auto pos = html_name.
Last(
'/');
762 html_name = homedir + html_name.
Data();
766 std::ofstream ofs(html_name.
Data(), std::ofstream::out);
779 R__LOG_DEBUG(0,
WebGUILog()) <<
"Using " << html_name <<
" content_len " << filecont.length() <<
" to produce batch image " << fname;
782 TString tgtfilename = fname.c_str();
793 if (draw_kind ==
"draw") {
795 wait_file_name = tgtfilename;
797 if (EndsWith(
".pdf"))
827 if (html_name.
Length() > 0)
835 if (draw_kind !=
"draw") {
837 auto dumpcont = handle->GetContent();
842 chrome_tmp_workaround =
true;
846 if (dumpcont.length() < 100) {
851 if (draw_kind ==
"svg") {
852 auto p1 = dumpcont.find(
"<svg");
853 auto p2 = dumpcont.rfind(
"</svg>");
855 std::ofstream ofs(tgtfilename);
856 if ((p1 != std::string::npos) && (p2 != std::string::npos) && (p1 < p2)) {
857 ofs << dumpcont.substr(p1,p2-p1+6);
860 ofs <<
"Failure!!!\n" << dumpcont;
865 auto p1 = dumpcont.rfind(
";base64,");
866 auto p2 = dumpcont.rfind(
"></div>");
868 if ((p1 != std::string::npos) && (p2 != std::string::npos) && (p1 < p2)) {
870 auto base64 = dumpcont.substr(p1+8, p2-p1-9);
873 std::ofstream ofs(tgtfilename, std::ios::binary);
874 ofs.write(binary.Data(), binary.Length());
#define R__LOG_ERROR(...)
#define R__LOG_DEBUG(DEBUGLEVEL,...)
include TDocParser_001 C image html pict1_TDocParser_001 png width
R__EXTERN TRandom * gRandom
R__EXTERN TSystem * gSystem
Specialized handle to hold information about running browser process Used to correctly cleanup all pr...
std::string fTmpDir
temporary directory to delete at the end
RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &dump)
virtual ~RWebBrowserHandle()
RWebBrowserHandle(const std::string &url, const std::string &tmpdir, browser_process_id pid)
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
std::string GetBrowserName() const
Returns configured browser name.
bool IsHeadless() const
returns headless mode
RWebDisplayArgs & SetPageContent(const std::string &cont)
set window url
void SetExtraArgs(const std::string &args)
set extra command line arguments for starting web browser command
RWebDisplayArgs & SetUrl(const std::string &url)
set window url
bool IsBatchMode() const
returns batch mode
void SetBatchMode(bool on=true)
set batch mode
int GetHeight() const
returns preferable web window height
void SetHeadless(bool on=true)
set headless mode
EBrowserKind GetBrowserKind() const
returns configured browser kind, see EBrowserKind for supported values
const std::string & GetExtraArgs() const
get extra command line arguments for starting web browser command
void SetStandalone(bool on=true)
Set standalone mode for running browser, default on When disabled, normal browser window (or just tab...
std::string GetFullUrl() const
returns window url with append options
RWebDisplayArgs & SetBrowserKind(const std::string &kind)
Set browser kind as string argument Recognized values: chrome - use Google Chrome web browser,...
std::string GetCustomExec() const
returns custom executable to start web browser
bool IsStandalone() const
Return true if browser should runs in standalone mode.
bool IsLocalDisplay() const
returns true if local display like CEF or Qt5 QWebEngine should be used
void SetRedirectOutput(const std::string &fname="")
specify file name to which web browser output should be redirected
@ kFirefox
Mozilla Firefox browser.
@ kCEF
Chromium Embedded Framework - local display with CEF libs.
@ kChrome
Google Chrome browser.
@ kQt6
Qt6 QWebEngine libraries - Chromium code packed in qt6.
@ kCustom
custom web browser, execution string should be provided
@ kNative
either Chrome or Firefox - both support major functionality
@ kLocal
either CEF or Qt5 - both runs on local display without real http server
@ kOff
disable web display, do not start any browser
@ kQt5
Qt5 QWebEngine libraries - Chromium code packed in qt5.
const std::string & GetRedirectOutput() const
get file name to which web browser output should be redirected
RWebDisplayArgs & SetSize(int w, int h)
set preferable web window width and height
int GetY() const
set preferable web window y position
int GetX() const
set preferable web window x position
int GetWidth() const
returns preferable web window width
std::string fExec
standard execute line
std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args) override
Display given URL in web browser.
std::string fBatchExec
batch execute line
std::string fProg
browser executable
void TestProg(const std::string &nexttry, bool check_std_paths=false)
Check if browser executable exists and can be used.
std::string fHeadlessExec
headless execute line
BrowserCreator(bool custom=true, const std::string &exec="")
Class to handle starting of web-browsers like Chrome or Firefox.
void ProcessGeometry(std::string &, const RWebDisplayArgs &args) override
Replace $geometry placeholder with geometry settings Also RWebDisplayArgs::GetExtraArgs() are appende...
ChromeCreator()
Constructor.
std::string MakeProfile(std::string &exec, bool) override
Handle profile argument.
FirefoxCreator()
Constructor.
std::string MakeProfile(std::string &exec, bool batch) override
Create Firefox profile to run independent browser window.
Handle of created web-based display Depending from type of web display, holds handle of started brows...
static std::map< std::string, std::unique_ptr< Creator > > & GetMap()
Static holder of registered creators of web displays.
static bool ProduceImage(const std::string &fname, const std::string &json, int width=800, int height=600)
Produce image file using JSON data as source Invokes JSROOT drawing functionality in headless browser...
static bool DisplayUrl(const std::string &url)
Display provided url in configured web browser.
static std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args)
Create web display.
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.
static TString Decode(const char *data)
Decode a base64 string date into a generic TString.
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
static char * ReadFileContent(const char *filename, Int_t &len)
Reads content of file from the disk.
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
virtual void SetSeed(ULong_t seed=0)
Set the random generator seed.
virtual UInt_t Integer(UInt_t imax)
Returns a random integer uniformly distributed on the interval [ 0, imax-1 ].
void Clear()
Clear string without changing its capacity.
const char * Data() const
Ssiz_t Last(char c) const
Find last occurrence of a character c.
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
TString & Remove(Ssiz_t pos)
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
virtual const char * Getenv(const char *env)
Get environment variable.
virtual int mkdir(const char *name, Bool_t recursive=kFALSE)
Make a file system directory.
virtual Int_t Exec(const char *shellcmd)
Execute a command.
virtual int Load(const char *module, const char *entry="", Bool_t system=kFALSE)
Load a shared library.
virtual const char * PrependPathName(const char *dir, TString &name)
Concatenate a directory and a file name.
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
virtual FILE * TempFileName(TString &base, const char *dir=nullptr)
Create a secure temporary file by appending a unique 6 letter string to base.
virtual std::string GetHomeDirectory(const char *userName=nullptr) const
Return the user's home directory.
virtual const char * UnixPathName(const char *unixpathname)
Convert from a local pathname to a Unix pathname.
virtual int Rename(const char *from, const char *to)
Rename a file.
virtual TString GetFromPipe(const char *command)
Execute command and return output in TString.
virtual Bool_t IsAbsoluteFileName(const char *dir)
Return true if dir is an absolute pathname.
virtual const char * WorkingDirectory()
Return working directory.
virtual int Unlink(const char *name)
Unlink, i.e.
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
RLogChannel & WebGUILog()
Log channel for WebGUI diagnostics.
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...