21#include "RConfigure.h"
43using namespace std::string_literals;
50 static std::map<std::string, std::unique_ptr<ROOT::Experimental::RWebDisplayHandle::Creator>> sMap;
63 auto search =
m.find(
name);
64 if (search ==
m.end()) {
66 if (libname ==
"ChromeCreator") {
67 m.emplace(
name, std::make_unique<ChromeCreator>());
68 }
else if (libname ==
"FirefoxCreator") {
69 m.emplace(
name, std::make_unique<FirefoxCreator>());
70 }
else if (libname ==
"BrowserCreator") {
71 m.emplace(
name, std::make_unique<BrowserCreator>(
false));
72 }
else if (!libname.empty()) {
76 search =
m.find(
name);
79 if (search !=
m.end())
80 return search->second;
82 static std::unique_ptr<ROOT::Experimental::RWebDisplayHandle::Creator>
dummy;
87namespace Experimental {
96 typedef int browser_process_id;
98 typedef pid_t browser_process_id;
102 browser_process_id fPid;
105 RWebBrowserHandle(
const std::string &url,
const std::string &tmpdir) : RWebDisplayHandle(url), fTmpDir(tmpdir) {}
107 RWebBrowserHandle(
const std::string &url,
const std::string &tmpdir, browser_process_id pid)
108 : RWebDisplayHandle(url), fTmpDir(tmpdir), fHasPid(true), fPid(pid)
112 virtual ~RWebBrowserHandle()
116 gSystem->
Exec((
"taskkill /F /PID "s + std::to_string(fPid) +
" >NUL 2>NUL").c_str());
117 std::string rmdir =
"rmdir /S /Q ";
121 std::string rmdir =
"rm -rf ";
123 if (!fTmpDir.empty())
139 if (exec.find(
"$url") == std::string::npos) {
142 fExec = exec +
" $url";
144 fExec = exec +
" $url &";
148 auto pos = exec.find(
" ");
149 if (pos != std::string::npos)
150 fProg = exec.substr(0, pos);
153 fExec =
"open \'$url\'";
155 fExec =
"start $url";
157 fExec =
"xdg-open \'$url\' &";
166 if (nexttry.empty() || !fProg.empty())
171 fProg = std::regex_replace(nexttry, std::regex(
"%20"),
" ");
178 if (!check_std_paths)
183 auto pos = ProgramFiles.find(
" (x86)");
184 if (pos != std::string::npos)
185 ProgramFiles.erase(pos, 6);
186 std::string ProgramFilesx86 =
gSystem->
Getenv(
"ProgramFiles(x86)");
188 if (!ProgramFiles.empty())
189 TestProg(ProgramFiles + nexttry,
false);
190 if (!ProgramFilesx86.empty())
191 TestProg(ProgramFilesx86 + nexttry,
false);
198std::unique_ptr<ROOT::Experimental::RWebDisplayHandle>
211 exec =
"$prog $url &";
216 std::string swidth = std::to_string(args.
GetWidth() > 0 ? args.
GetWidth() : 800),
218 sposx = std::to_string(args.
GetX() >= 0 ? args.
GetX() : 0),
219 sposy = std::to_string(args.
GetY() >= 0 ? args.
GetY() : 0);
221 ProcessGeometry(exec, args);
223 std::string rmdir = MakeProfile(exec, args.
IsHeadless());
225 exec = std::regex_replace(exec, std::regex(
"\\$url"), url);
226 exec = std::regex_replace(exec, std::regex(
"\\$width"), swidth);
227 exec = std::regex_replace(exec, std::regex(
"\\$height"), sheight);
228 exec = std::regex_replace(exec, std::regex(
"\\$posx"), sposx);
229 exec = std::regex_replace(exec, std::regex(
"\\$posy"), sposy);
231 if (exec.compare(0,5,
"fork:") == 0) {
233 R__ERROR_HERE(
"WebDisplay") <<
"Fork instruction without executable";
242 if (!fargs || (fargs->GetLast()<=0)) {
247 std::vector<char *> argv;
248 argv.push_back((
char *) fProg.c_str());
249 for (
Int_t n = 0;
n <= fargs->GetLast(); ++
n)
250 argv.push_back((
char *)fargs->At(
n)->GetName());
251 argv.push_back(
nullptr);
253 R__DEBUG_HERE(
"WebDisplay") <<
"Show web window in browser with posix_spawn:\n" << fProg <<
" " << exec;
256 int status = posix_spawn(&pid, argv[0],
nullptr,
nullptr, argv.data(),
nullptr);
264 return std::make_unique<RWebBrowserHandle>(url, rmdir, pid);
274 exec =
"wmic process call create '"s +
gSystem->
UnixPathName(fProg.c_str()) + exec +
"' | find \"ProcessId\" "s;
276 std::stringstream ss(process_id);
280 ss >> tmp >>
c >> pid;
288 return std::make_unique<RWebBrowserHandle>(url, rmdir, pid);
294 if (exec.rfind(
"&") == exec.length() - 1) {
297 exec.resize(exec.length() - 1);
299 std::vector<char *> argv;
300 std::string firstarg = fProg;
301 auto slashpos = firstarg.find_last_of(
"/\\");
302 if (slashpos != std::string::npos)
303 firstarg.erase(0, slashpos + 1);
304 argv.push_back((
char *)firstarg.c_str());
307 for (
Int_t n = 1;
n <= fargs->GetLast(); ++
n)
308 argv.push_back((
char *)fargs->At(
n)->GetName());
309 argv.push_back(
nullptr);
311 R__DEBUG_HERE(
"WebDisplay") <<
"Showing web window in " << fProg <<
" with:\n" << exec;
313 _spawnv(_P_NOWAIT, fProg.c_str(), argv.data());
315 return std::make_unique<RWebBrowserHandle>(url, rmdir);
323 std::string prog = std::regex_replace(fProg, std::regex(
" "),
"\\ ");
325 std::string prog = fProg;
330 exec = std::regex_replace(exec, std::regex(
"\\$prog"), prog);
333 auto p = exec.length();
334 if (exec.rfind(
"&") == p-1) --p;
338 R__DEBUG_HERE(
"WebDisplay") <<
"Showing web window in browser with:\n" << exec;
343 return std::make_unique<RWebBrowserHandle>(url, rmdir);
354 TestProg(
"\\Google\\Chrome\\Application\\chrome.exe",
true);
357 TestProg(
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
361 TestProg(
"/usr/bin/chromium-browser");
362 TestProg(
"/usr/bin/chrome-browser");
369 fExec =
gEnv->
GetValue(
"WebGui.ChromeInteractive",
"$prog $geometry --no-first-run --app=$url &");
372 fExec =
gEnv->
GetValue(
"WebGui.ChromeInteractive",
"$prog $geometry --no-first-run --incognito --app=\'$url\' &");
391 geometry.append(
"--window-position="s + std::to_string(args.
GetX() >= 0 ? args.
GetX() : 0) +
","s +
392 std::to_string(args.
GetY() >= 0 ? args.
GetY() : 0));
400 exec = std::regex_replace(exec, std::regex(
"\\$geometry"),
geometry);
409 std::string rmdir, profile_arg;
411 if (exec.find(
"$profile") == std::string::npos)
414 const char *chrome_profile =
gEnv->
GetValue(
"WebGui.ChromeProfile",
"");
415 if (chrome_profile && *chrome_profile) {
416 profile_arg = chrome_profile;
422 exec = std::regex_replace(exec, std::regex(
"\\$profile"), profile_arg);
436 TestProg(
"\\Mozilla Firefox\\firefox.exe",
true);
439 TestProg(
"/Applications/Firefox.app/Contents/MacOS/firefox");
449 fExec =
gEnv->
GetValue(
"WebGui.FirefoxInteractive",
"$prog -no-remote $profile $url &");
452 fExec =
gEnv->
GetValue(
"WebGui.FirefoxInteractive",
"$prog --private-window \'$url\' &");
461 std::string rmdir, profile_arg;
463 if (exec.find(
"$profile") == std::string::npos)
466 const char *ff_profile =
gEnv->
GetValue(
"WebGui.FirefoxProfile",
"");
467 const char *ff_profilepath =
gEnv->
GetValue(
"WebGui.FirefoxProfilePath",
"");
469 if (ff_profile && *ff_profile) {
470 profile_arg =
"-P "s + ff_profile;
471 }
else if (ff_profilepath && *ff_profilepath) {
472 profile_arg =
"-profile "s + ff_profilepath;
473 }
else if ((ff_randomprofile > 0) || (batch_mode && (ff_randomprofile >= 0))) {
476 std::string rnd_profile =
"root_ff_profile_"s + std::to_string(
gRandom->
Integer(0x100000));
479 profile_arg =
"-profile "s + profile_dir;
484 R__ERROR_HERE(
"WebDisplay") <<
"Cannot create Firefox profile directory " << profile_dir;
488 exec = std::regex_replace(exec, std::regex(
"\\$profile"), profile_arg);
502 std::unique_ptr<RWebDisplayHandle> handle;
504 auto try_creator = [&](std::unique_ptr<Creator> &creator) {
505 if (!creator || !creator->IsActive())
507 handle = creator->Display(args);
508 return handle ? true :
false;
512 if (try_creator(
FindCreator(
"cef",
"libROOTCefDisplay")))
517 if (try_creator(
FindCreator(
"qt5",
"libROOTQt5WebDisplay")))
522 R__ERROR_HERE(
"WebDisplay") <<
"Neither Qt5 nor CEF libraries were found to provide local display";
527 if (try_creator(
FindCreator(
"chrome",
"ChromeCreator")))
532 if (try_creator(
FindCreator(
"firefox",
"FirefoxCreator")))
542 std::unique_ptr<Creator> creator = std::make_unique<BrowserCreator>(
false, args.
GetCustomExec());
543 try_creator(creator);
545 try_creator(
FindCreator(
"browser",
"BrowserCreator"));
585 std::string _fname = fname;
586 std::transform(_fname.begin(), _fname.end(), _fname.begin(), ::tolower);
588 auto EndsWith = [_fname](
const std::string &suffix) {
589 return (_fname.length() > suffix.length()) ? (0 == _fname.compare (_fname.length() - suffix.length(), suffix.length(), suffix)) :
false;
593 std::ofstream ofs(fname);
603 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to locate JSROOT " << jsrootsysdflt;
606 jsrootsys = jsrootsysdflt.
Data();
609 std::string draw_kind;
631 if (filecont.empty()) {
632 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to read content of " << origin;
636 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_width"), std::to_string(
width));
637 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_height"), std::to_string(height));
639 if (strstr(jsrootsys,
"http://") || strstr(jsrootsys,
"https://") || strstr(jsrootsys,
"file://"))
640 filecont = std::regex_replace(filecont, std::regex(
"\\$jsrootsys"), jsrootsys);
642 filecont = std::regex_replace(filecont, std::regex(
"\\$jsrootsys"),
"file://"s + jsrootsys);
644 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_kind"), draw_kind);
646 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_object"),
json);
650 if (draw_kind !=
"draw") {
651 dump_name =
"canvasdump";
654 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to create temporary file for dump-dom";
657 fputs(
"placeholder", df);
663 static bool chrome_tmp_workaround =
false;
667 TString tmp_name(
"canvasbody");
670 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to create temporary file for batch job";
673 fputs(filecont.c_str(), hf);
676 TString html_name = tmp_name +
".html";
678 if (chrome_tmp_workaround) {
680 auto pos = html_name.
Last(
'/');
685 html_name = homedir + html_name.
Data();
689 std::ofstream ofs (html_name.
Data(), std::ofstream::out);
693 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to rename temp file " << tmp_name <<
" into " << html_name;
699 R__DEBUG_HERE(
"CanvasPainter") <<
"Using " << html_name <<
" content_len " << filecont.length() <<
" to produce batch image " << fname;
701 TString tgtfilename = fname.c_str();
713 if (draw_kind ==
"draw") {
715 wait_file_name = tgtfilename;
727 wait_file_name = dump_name;
739 R__DEBUG_HERE(
"CanvasPainter") <<
"Cannot start Chrome to produce image " << fname;
747 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to produce image " << fname;
751 if (draw_kind !=
"draw") {
757 if ((dumpcont.length() > 20) && (dumpcont.length() < 60) && !chrome_tmp_workaround) {
760 chrome_tmp_workaround =
true;
764 if (dumpcont.length() < 100) {
765 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to dump HTML code into " << dump_name;
769 if (draw_kind ==
"svg") {
770 auto p1 = dumpcont.find(
"<svg");
771 auto p2 = dumpcont.rfind(
"</svg>");
773 std::ofstream ofs(tgtfilename);
774 if ((p1 != std::string::npos) && (p2 != std::string::npos) && (p1 < p2)) {
775 ofs << dumpcont.substr(p1,p2-p1+6);
777 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to extract SVG from HTML dump " << dump_name;
778 ofs <<
"Failure!!!\n" << dumpcont;
783 auto p1 = dumpcont.find(
";base64,");
784 auto p2 = dumpcont.rfind(
"></div>");
786 if ((p1 != std::string::npos) && (p2 != std::string::npos) && (p1 < p2)) {
788 auto base64 = dumpcont.substr(p1+8, p2-p1-9);
791 std::ofstream ofs(tgtfilename, std::ios::binary);
792 ofs.write(binary.Data(), binary.Length());
794 R__ERROR_HERE(
"CanvasPainter") <<
"Fail to extract image from dump HTML code " << dump_name;
#define R__ERROR_HERE(GROUP)
#define R__DEBUG_HERE(GROUP)
static RooMathCoreReg dummy
include TDocParser_001 C image html pict1_TDocParser_001 png width
R__EXTERN TRandom * gRandom
R__EXTERN TSystem * gSystem
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
std::string GetCustomExec() const
returns custom executable to start web browser
RWebDisplayArgs & SetBrowserKind(const std::string &kind)
Set browser kind as string argument Recognized values: chrome - use Google Chrome web browser,...
bool IsHeadless() const
returns headless mode
void SetExtraArgs(const std::string &args)
RWebDisplayArgs & SetUrl(const std::string &url)
set window url
int GetHeight() const
returns preferable web window height
void SetHeadless(bool on=true)
set headless mode
std::string GetFullUrl() const
returns window url with append options
EBrowserKind GetBrowserKind() const
returns configured browser kind, see EBrowserKind for supported values
const std::string & GetExtraArgs() const
void SetStandalone(bool on=true)
Set standalone mode for running browser, default on When disabled, normal browser window (or just tab...
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="")
@ kFirefox
Mozilla Firefox browser.
@ kCEF
Chromium Embedded Framework - local display with CEF libs.
@ kChrome
Google Chrome browser.
@ 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
@ kQt5
QWebEngine libraries - Chrome code packed in qt5.
const std::string & GetRedirectOutput() const
RWebDisplayArgs & SetSize(int w, int h)
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
void TestProg(const std::string &nexttry, bool check_std_paths=false)
Check if browser executable exists and can be used.
std::string fBatchExec
batch execute line
BrowserCreator(bool custom=true, const std::string &exec="")
Class to handle starting of web-browsers like Chrome or Firefox.
std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args) override
Display given URL in web browser.
ChromeCreator()
Constructor.
void ProcessGeometry(std::string &, const RWebDisplayArgs &args) override
Replace $geometry placeholder with geometry settings Also RWebDisplayArgs::GetExtraArgs() are appende...
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.
static bool DisplayUrl(const std::string &url)
Display provided url in configured web browser.
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 std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args)
Create web display.
static std::map< std::string, std::unique_ptr< Creator > > & GetMap()
!< URL used to launch display
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 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 ].
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.
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
static constexpr double s
basic_json<> json
default JSON class