17#include "RConfigure.h"
42using namespace std::string_literals;
58 static std::map<std::string, std::unique_ptr<RWebDisplayHandle::Creator>> sMap;
71 auto search =
m.find(
name);
72 if (search ==
m.end()) {
74 if (libname ==
"ChromeCreator") {
75 m.emplace(
name, std::make_unique<ChromeCreator>(
name ==
"edge"));
76 }
else if (libname ==
"FirefoxCreator") {
77 m.emplace(
name, std::make_unique<FirefoxCreator>());
78 }
else if (libname ==
"BrowserCreator") {
79 m.emplace(
name, std::make_unique<BrowserCreator>(
false));
80 }
else if (!libname.empty()) {
84 search =
m.find(
name);
87 if (search !=
m.end())
88 return search->second;
90 static std::unique_ptr<RWebDisplayHandle::Creator> dummy;
95namespace Experimental {
127 gSystem->
Exec((
"taskkill /F /PID "s + std::to_string(fPid) +
" >NUL 2>NUL").c_str());
128 std::string rmdir =
"rmdir /S /Q ";
132 std::string rmdir =
"rm -rf ";
134 if (!fTmpDir.empty())
151 if (exec.find(
"$url") == std::string::npos) {
154 fExec = exec +
" $url";
156 fExec = exec +
" $url &";
160 auto pos = exec.find(
" ");
161 if (pos != std::string::npos)
162 fProg = exec.substr(0, pos);
165 fExec =
"open \'$url\'";
167 fExec =
"start $url";
169 fExec =
"xdg-open \'$url\' &";
178 if (nexttry.empty() || !fProg.empty())
183 fProg = std::regex_replace(nexttry, std::regex(
"%20"),
" ");
190 if (!check_std_paths)
195 auto pos = ProgramFiles.find(
" (x86)");
196 if (pos != std::string::npos)
197 ProgramFiles.erase(pos, 6);
198 std::string ProgramFilesx86 =
gSystem->
Getenv(
"ProgramFiles(x86)");
200 if (!ProgramFiles.empty())
201 TestProg(ProgramFiles + nexttry,
false);
202 if (!ProgramFilesx86.empty())
203 TestProg(ProgramFilesx86 + nexttry,
false);
210std::unique_ptr<RWebDisplayHandle>
218 std::cout <<
"New web window: " << url << std::endl;
219 return std::make_unique<RWebBrowserHandle>(url,
"",
"");
226 exec = fHeadlessExec;
230 exec =
"$prog $url &";
235 std::string swidth = std::to_string(args.
GetWidth() > 0 ? args.
GetWidth() : 800),
237 sposx = std::to_string(args.
GetX() >= 0 ? args.
GetX() : 0),
238 sposy = std::to_string(args.
GetY() >= 0 ? args.
GetY() : 0);
240 ProcessGeometry(exec, args);
242 std::string rmdir = MakeProfile(exec, args.
IsHeadless());
244 exec = std::regex_replace(exec, std::regex(
"\\$url"), url);
245 exec = std::regex_replace(exec, std::regex(
"\\$width"), swidth);
246 exec = std::regex_replace(exec, std::regex(
"\\$height"), sheight);
247 exec = std::regex_replace(exec, std::regex(
"\\$posx"), sposx);
248 exec = std::regex_replace(exec, std::regex(
"\\$posy"), sposy);
250 if (exec.compare(0,5,
"fork:") == 0) {
261 if (!fargs || (fargs->GetLast()<=0)) {
266 std::vector<char *> argv;
267 argv.push_back((
char *) fProg.c_str());
268 for (
Int_t n = 0;
n <= fargs->GetLast(); ++
n)
269 argv.push_back((
char *)fargs->At(
n)->GetName());
270 argv.push_back(
nullptr);
272 R__LOG_DEBUG(0,
WebGUILog()) <<
"Show web window in browser with posix_spawn:\n" << fProg <<
" " << exec;
275 int status = posix_spawn(&pid, argv[0],
nullptr,
nullptr, argv.data(),
nullptr);
283 return std::make_unique<RWebBrowserHandle>(url, rmdir, pid);
293 exec =
"wmic process call create '"s +
gSystem->
UnixPathName(fProg.c_str()) + exec +
"' | find \"ProcessId\" "s;
295 std::stringstream ss(process_id);
299 ss >> tmp >>
c >> pid;
307 return std::make_unique<RWebBrowserHandle>(url, rmdir, pid);
313 if (exec.rfind(
"&") == exec.length() - 1) {
316 exec.resize(exec.length() - 1);
318 std::vector<char *> argv;
319 std::string firstarg = fProg;
320 auto slashpos = firstarg.find_last_of(
"/\\");
321 if (slashpos != std::string::npos)
322 firstarg.erase(0, slashpos + 1);
323 argv.push_back((
char *)firstarg.c_str());
326 for (
Int_t n = 1;
n <= fargs->GetLast(); ++
n)
327 argv.push_back((
char *)fargs->At(
n)->GetName());
328 argv.push_back(
nullptr);
334 return std::make_unique<RWebBrowserHandle>(url, rmdir,
""s);
342 std::string prog = std::regex_replace(fProg, std::regex(
" "),
"\\ ");
344 std::string prog = fProg;
349 exec = std::regex_replace(exec, std::regex(
"\\$prog"), prog);
353 if (!redirect.empty()) {
354 auto p = exec.length();
355 if (exec.rfind(
"&") ==
p-1) --
p;
356 exec.insert(
p,
" >"s + redirect +
" "s);
364 if (!redirect.empty()) {
371 return std::make_unique<RWebBrowserHandle>(url, rmdir, dump_content);
387 TestProg(
"\\Microsoft\\Edge\\Application\\msedge.exe",
true);
389 TestProg(
"\\Google\\Chrome\\Application\\chrome.exe",
true);
392 TestProg(
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
396 TestProg(
"/usr/bin/chromium-browser");
397 TestProg(
"/usr/bin/chrome-browser");
398 TestProg(
"/usr/bin/google-chrome-stable");
407 fBatchExec =
gEnv->
GetValue((
fEnvPrefix +
"Batch").c_str(),
"$prog --headless --no-sandbox --no-zygote --disable-extensions --disable-gpu --disable-audio-output $geometry $url");
408 fHeadlessExec =
gEnv->
GetValue((
fEnvPrefix +
"Headless").c_str(),
"$prog --headless --no-sandbox --no-zygote --disable-extensions --disable-gpu --disable-audio-output $geometry \'$url\' --dump-dom >/dev/null &");
428 geometry.append(
"--window-position="s + std::to_string(args.
GetX() >= 0 ? args.
GetX() : 0) +
","s +
429 std::to_string(args.
GetY() >= 0 ? args.
GetY() : 0));
437 exec = std::regex_replace(exec, std::regex(
"\\$geometry"),
geometry);
446 std::string rmdir, profile_arg;
448 if (exec.find(
"$profile") == std::string::npos)
451 const char *chrome_profile =
gEnv->
GetValue((fEnvPrefix +
"Profile").c_str(),
"");
452 if (chrome_profile && *chrome_profile) {
453 profile_arg = chrome_profile;
456 std::string rnd_profile =
"root_chrome_profile_"s + std::to_string(
gRandom->
Integer(0x100000));
460 profile_arg +=
"\\"s + rnd_profile;
462 profile_arg +=
"/"s + rnd_profile;
467 exec = std::regex_replace(exec, std::regex(
"\\$profile"), profile_arg);
481 TestProg(
"\\Mozilla Firefox\\firefox.exe",
true);
484 TestProg(
"/Applications/Firefox.app/Contents/MacOS/firefox");
496 fExec =
gEnv->
GetValue(
"WebGui.FirefoxInteractive",
"$prog -no-remote $profile $url &");
498 fBatchExec =
gEnv->
GetValue(
"WebGui.FirefoxBatch",
"$prog --headless --private-window --no-remote $profile $url");
500 fExec =
gEnv->
GetValue(
"WebGui.FirefoxInteractive",
"$prog --private-window \'$url\' &");
509 std::string rmdir, profile_arg;
511 if (exec.find(
"$profile") == std::string::npos)
514 const char *ff_profile =
gEnv->
GetValue(
"WebGui.FirefoxProfile",
"");
515 const char *ff_profilepath =
gEnv->
GetValue(
"WebGui.FirefoxProfilePath",
"");
517 if (ff_profile && *ff_profile) {
518 profile_arg =
"-P "s + ff_profile;
519 }
else if (ff_profilepath && *ff_profilepath) {
520 profile_arg =
"-profile "s + ff_profilepath;
521 }
else if ((ff_randomprofile > 0) || (batch_mode && (ff_randomprofile >= 0))) {
524 std::string rnd_profile =
"root_ff_profile_"s + std::to_string(
gRandom->
Integer(0x100000));
528 profile_dir +=
"\\"s + rnd_profile;
530 profile_dir +=
"/"s + rnd_profile;
533 profile_arg =
"-profile "s + profile_dir;
539 std::ofstream user_js(profile_dir +
"/user.js", std::ios::trunc);
540 user_js <<
"user_pref(\"browser.dom.window.dump.enabled\", true);" << std::endl;
542 user_js <<
"user_pref(\"datareporting.policy.dataSubmissionPolicyAcceptedVersion\", 2);" << std::endl;
543 user_js <<
"user_pref(\"datareporting.policy.dataSubmissionPolicyNotifiedTime\", \"1635760572813\");" << std::endl;
551 exec = std::regex_replace(exec, std::regex(
"\\$profile"), profile_arg);
564 std::unique_ptr<RWebDisplayHandle> handle;
569 auto try_creator = [&](std::unique_ptr<Creator> &creator) {
570 if (!creator || !creator->IsActive())
572 handle = creator->Display(args);
573 return handle ? true :
false;
577 if (try_creator(
FindCreator(
"cef",
"libROOTCefDisplay")))
582 if (try_creator(
FindCreator(
"qt5",
"libROOTQt5WebDisplay")))
587 if (try_creator(
FindCreator(
"qt6",
"libROOTQt6WebDisplay")))
601 if (try_creator(
FindCreator(
"edge",
"ChromeCreator")))
607 if (try_creator(
FindCreator(
"chrome",
"ChromeCreator")))
612 if (try_creator(
FindCreator(
"firefox",
"FirefoxCreator")))
622 std::unique_ptr<Creator> creator = std::make_unique<BrowserCreator>(
false, args.
GetCustomExec());
623 try_creator(creator);
625 try_creator(
FindCreator(
"browser",
"BrowserCreator"));
665 std::string _fname = fname;
666 std::transform(_fname.begin(), _fname.end(), _fname.begin(), ::tolower);
668 auto EndsWith = [_fname](
const std::string &suffix) {
669 return (_fname.length() > suffix.length()) ? (0 == _fname.compare (_fname.length() - suffix.length(), suffix.length(), suffix)) :
false;
672 if (EndsWith(
".json")) {
673 std::ofstream ofs(fname);
686 jsrootsys = jsrootsysdflt.
Data();
694 std::string draw_kind;
696 if (EndsWith(
".pdf"))
698 else if (EndsWith(
"shot.png"))
700 else if (EndsWith(
".svg"))
702 else if (EndsWith(
".png"))
704 else if (EndsWith(
".jpg") || EndsWith(
".jpeg"))
706 else if (EndsWith(
".webp"))
711 if (!batch_file || !*batch_file)
712 batch_file =
"/js/files/canv_batch.htm";
721 if (filecont.empty()) {
726 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_width"), std::to_string(
width));
727 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_height"), std::to_string(
height));
729 if (strstr(jsrootsys,
"http://") || strstr(jsrootsys,
"https://") || strstr(jsrootsys,
"file://"))
730 filecont = std::regex_replace(filecont, std::regex(
"\\$jsrootsys"), jsrootsys);
732 filecont = std::regex_replace(filecont, std::regex(
"\\$jsrootsys"),
"file://"s + jsrootsys);
734 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_kind"), draw_kind);
736 filecont = std::regex_replace(filecont, std::regex(
"\\$draw_object"),
json);
739 if (draw_kind ==
"draw") {
745 dump_name =
"canvasdump";
751 fputs(
"placeholder", df);
757 static bool chrome_tmp_workaround =
false;
770 R__LOG_DEBUG(0,
WebGUILog()) <<
"Using file content_len " << filecont.length() <<
" to produce batch image " << fname;
773 tmp_name =
"canvasbody";
779 fputs(filecont.c_str(), hf);
782 html_name = tmp_name +
".html";
784 if (chrome_tmp_workaround) {
786 auto pos = html_name.
Last(
'/');
791 html_name = homedir + html_name.
Data();
795 std::ofstream ofs(html_name.
Data(), std::ofstream::out);
808 R__LOG_DEBUG(0,
WebGUILog()) <<
"Using " << html_name <<
" content_len " << filecont.length() <<
" to produce batch image " << fname;
811 TString tgtfilename = fname.c_str();
822 if (draw_kind ==
"draw") {
824 wait_file_name = tgtfilename;
826 if (EndsWith(
".pdf"))
856 if (html_name.
Length() > 0)
864 if (draw_kind !=
"draw") {
866 auto dumpcont = handle->GetContent();
871 chrome_tmp_workaround =
true;
875 if (dumpcont.length() < 100) {
880 if (draw_kind ==
"svg") {
881 auto p1 = dumpcont.find(
"<svg");
882 auto p2 = dumpcont.rfind(
"</svg>");
884 std::ofstream ofs(tgtfilename);
885 if ((p1 != std::string::npos) && (p2 != std::string::npos) && (p1 < p2)) {
886 ofs << dumpcont.substr(p1,p2-p1+6);
889 ofs <<
"Failure!!!\n" << dumpcont;
894 auto p1 = dumpcont.rfind(
";base64,");
895 auto p2 = dumpcont.rfind(
"></div>");
897 if ((p1 != std::string::npos) && (p2 != std::string::npos) && (p1 < p2)) {
899 auto base64 = dumpcont.substr(p1+8, p2-p1-9);
902 std::ofstream ofs(tgtfilename, std::ios::binary);
903 ofs.write(binary.Data(), binary.Length());
#define R__LOG_ERROR(...)
#define R__LOG_DEBUG(DEBUGLEVEL,...)
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t height
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.
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
@ kEdge
Microsoft Edge browser (Windows only)
@ kDefault
default system web browser, can not be used in batch mode
@ kLocal
either CEF or Qt5 - both runs on local display without real http server
@ kServer
indicates that ROOT runs as server and just printouts window URL, browser should be started by the us...
@ 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.
ChromeCreator(bool is_edge=false)
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.
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, const char *batch_file=nullptr)
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.
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.