21 #include "RConfigure.h"
39 using namespace std::string_literals;
44 std::map<std::string, std::unique_ptr<ROOT::Experimental::RWebDisplayHandle::Creator>> &ROOT::Experimental::RWebDisplayHandle::GetMap()
46 static std::map<std::string, std::unique_ptr<ROOT::Experimental::RWebDisplayHandle::Creator>> sMap;
56 std::unique_ptr<ROOT::Experimental::RWebDisplayHandle::Creator> &ROOT::Experimental::RWebDisplayHandle::FindCreator(
const std::string &name,
const std::string &libname)
59 auto search = m.find(name);
60 if (search == m.end()) {
62 if (libname ==
"ChromeCreator") {
63 m.emplace(name, std::make_unique<ChromeCreator>());
64 }
else if (libname ==
"FirefoxCreator") {
65 m.emplace(name, std::make_unique<FirefoxCreator>());
66 }
else if (libname ==
"BrowserCreator") {
67 m.emplace(name, std::make_unique<BrowserCreator>(
false));
68 }
else if (!libname.empty()) {
69 gSystem->Load(libname.c_str());
72 search = m.find(name);
75 if (search != m.end())
76 return search->second;
78 static std::unique_ptr<ROOT::Experimental::RWebDisplayHandle::Creator> dummy;
83 namespace Experimental {
89 class RWebBrowserHandle :
public RWebDisplayHandle {
92 typedef int browser_process_id;
94 typedef pid_t browser_process_id;
98 browser_process_id fPid;
101 RWebBrowserHandle(
const std::string &url,
const std::string &tmpdir) : RWebDisplayHandle(url), fTmpDir(tmpdir) {}
103 RWebBrowserHandle(
const std::string &url,
const std::string &tmpdir, browser_process_id pid)
104 : RWebDisplayHandle(url), fTmpDir(tmpdir), fHasPid(true), fPid(pid)
108 virtual ~RWebBrowserHandle()
112 gSystem->Exec((
"taskkill /F /PID "s + std::to_string(fPid)).c_str());
113 std::string rmdir =
"rmdir /S /Q ";
117 std::string rmdir =
"rm -rf ";
119 if (!fTmpDir.empty())
120 gSystem->Exec((rmdir + fTmpDir).c_str());
130 ROOT::Experimental::RWebDisplayHandle::BrowserCreator::BrowserCreator(
bool custom,
const std::string &exec)
135 if (exec.find(
"$url") == std::string::npos) {
138 fExec = exec +
" $url";
140 fExec = exec +
" $url &";
144 auto pos = exec.find(
" ");
145 if (pos != std::string::npos)
146 fProg = exec.substr(0, pos);
148 }
else if (gSystem->InheritsFrom(
"TMacOSXSystem")) {
149 fExec =
"open \'$url\'";
150 }
else if (gSystem->InheritsFrom(
"TWinNTSystem")) {
151 fExec =
"start $url";
153 fExec =
"xdg-open \'$url\' &";
160 void ROOT::Experimental::RWebDisplayHandle::BrowserCreator::TestProg(
const std::string &nexttry,
bool check_std_paths)
162 if (nexttry.empty() || !fProg.empty())
165 if (!gSystem->AccessPathName(nexttry.c_str(), kExecutePermission)) {
167 fProg = std::regex_replace(nexttry, std::regex(
"%20"),
" ");
174 if (!check_std_paths)
178 std::string ProgramFiles = gSystem->Getenv(
"ProgramFiles");
179 auto pos = ProgramFiles.find(
" (x86)");
180 if (pos != std::string::npos)
181 ProgramFiles.erase(pos, 6);
182 std::string ProgramFilesx86 = gSystem->Getenv(
"ProgramFiles(x86)");
184 if (!ProgramFiles.empty())
185 TestProg(ProgramFiles + nexttry,
false);
186 if (!ProgramFilesx86.empty())
187 TestProg(ProgramFilesx86 + nexttry,
false);
194 std::unique_ptr<ROOT::Experimental::RWebDisplayHandle>
195 ROOT::Experimental::RWebDisplayHandle::BrowserCreator::Display(
const RWebDisplayArgs &args)
197 std::string url = args.GetFullUrl();
202 if (args.IsHeadless())
204 else if (args.IsStandalone())
210 exec =
"$prog $url &";
216 std::string swidth = std::to_string(args.GetWidth() > 0 ? args.GetWidth() : 800),
217 sheight = std::to_string(args.GetHeight() > 0 ? args.GetHeight() : 600),
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";
241 std::unique_ptr<TObjArray> fargs(TString(exec.c_str()).Tokenize(
" "));
242 if (!fargs || (fargs->GetLast()<=0)) {
243 R__ERROR_HERE(
"WebDisplay") <<
"Fork instruction is empty";
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);
258 R__ERROR_HERE(
"WebDisplay") <<
"Fail to launch " << argv[0];
264 return std::make_unique<RWebBrowserHandle>(url, rmdir, pid);
272 if (!fProg.empty()) {
273 exec =
"wmic process call create \""s + fProg + exec;
275 R__ERROR_HERE(
"WebDisplay") <<
"No Web browser found in Program Files!";
278 exec.append(
"\" | find \"ProcessId\" ");
279 std::string process_id = gSystem->GetFromPipe(exec.c_str());
280 std::stringstream ss(process_id);
281 ss >> tmp >> c >> pid;
284 return std::make_unique<RWebBrowserHandle>(url, rmdir, pid);
291 std::vector<char *> argv;
292 std::string firstarg = fProg;
293 auto slashpos = firstarg.rfind(
"\\");
294 if (slashpos != std::string::npos)
295 firstarg.erase(0, slashpos + 1);
296 slashpos = firstarg.rfind(
"/");
297 if (slashpos != std::string::npos)
298 firstarg.erase(0, slashpos + 1);
299 argv.push_back((
char *)firstarg.c_str());
301 std::unique_ptr<TObjArray> fargs(TString(exec.c_str()).Tokenize(
" "));
302 for (Int_t n = 1; n <= fargs->GetLast(); ++n)
303 argv.push_back((
char *)fargs->At(n)->GetName());
304 argv.push_back(
nullptr);
306 R__DEBUG_HERE(
"WebDisplay") <<
"Showing web window in " << fProg <<
" with:\n" << exec;
308 _spawnv(_P_NOWAIT, fProg.c_str(), argv.data());
313 std::string prog = std::regex_replace(fProg, std::regex(
" "),
"\\ ");
315 std::string prog = fProg;
318 exec = std::regex_replace(exec, std::regex(
"\\$prog"), prog);
320 R__DEBUG_HERE(
"WebDisplay") <<
"Showing web window in browser with:\n" << exec;
322 gSystem->Exec(exec.c_str());
326 return std::make_unique<RWebBrowserHandle>(url, rmdir);
332 ROOT::Experimental::RWebDisplayHandle::ChromeCreator::ChromeCreator() : BrowserCreator(true)
334 TestProg(gEnv->GetValue(
"WebGui.Chrome",
""));
337 TestProg(
"\\Google\\Chrome\\Application\\chrome.exe",
true);
340 TestProg(
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
343 TestProg(
"/usr/bin/chromium");
344 TestProg(
"/usr/bin/chromium-browser");
345 TestProg(
"/usr/bin/chrome-browser");
349 fBatchExec = gEnv->GetValue(
"WebGui.ChromeBatch",
"fork: --headless --disable-gpu $url");
350 fExec = gEnv->GetValue(
"WebGui.ChromeInteractive",
"$prog $geometry --no-first-run --app=$url");
352 fBatchExec = gEnv->GetValue(
"WebGui.ChromeBatch",
"fork:--headless --incognito $url");
353 fExec = gEnv->GetValue(
"WebGui.ChromeInteractive",
"$prog $geometry --no-first-run --incognito --app=\'$url\' &");
357 void ROOT::Experimental::RWebDisplayHandle::ChromeCreator::ProcessGeometry(std::string &exec,
const RWebDisplayArgs &args)
359 std::string size, pos;
360 if ((args.GetWidth() > 0) || (args.GetHeight() > 0))
361 size =
"--window-size="s + std::to_string(args.GetWidth() > 0 ? args.GetWidth() : 800) +
","s +
362 std::to_string(args.GetHeight() > 0 ? args.GetHeight() : 600);
363 if ((args.GetX() >= 0) || (args.GetY() >= 0))
364 pos =
" --window-position="s + std::to_string(args.GetX() >= 0 ? args.GetX() : 0) +
","s +
365 std::to_string(args.GetY() >= 0 ? args.GetY() : 0);
367 exec = std::regex_replace(exec, std::regex(
"\\$geometry"), size + pos);
374 std::string ROOT::Experimental::RWebDisplayHandle::ChromeCreator::MakeProfile(std::string &exec,
bool)
376 std::string rmdir, profile_arg;
378 if (exec.find(
"$profile") == std::string::npos)
381 const char *chrome_profile = gEnv->GetValue(
"WebGui.ChromeProfile",
"");
382 if (chrome_profile && *chrome_profile) {
383 profile_arg = chrome_profile;
386 rmdir = profile_arg = std::string(gSystem->TempDirectory()) +
"/root_chrome_profile_"s + std::to_string(gRandom->Integer(0x100000));
389 exec = std::regex_replace(exec, std::regex(
"\\$profile"), profile_arg);
398 ROOT::Experimental::RWebDisplayHandle::FirefoxCreator::FirefoxCreator() : BrowserCreator(true)
400 TestProg(gEnv->GetValue(
"WebGui.Firefox",
""));
403 TestProg(
"\\Mozilla Firefox\\firefox.exe",
true);
406 TestProg(
"/Applications/Firefox.app/Contents/MacOS/firefox");
409 TestProg(
"/usr/bin/firefox");
415 fBatchExec = gEnv->GetValue(
"WebGui.FirefoxBatch",
"fork: -headless -no-remote $profile $url");
416 fExec = gEnv->GetValue(
"WebGui.FirefoxInteractive",
"$prog -no-remote $profile $url");
418 fBatchExec = gEnv->GetValue(
"WebGui.FirefoxBatch",
"fork:--headless --private-window --no-remote $profile $url");
419 fExec = gEnv->GetValue(
"WebGui.FirefoxInteractive",
"$prog --private-window \'$url\' &");
426 std::string ROOT::Experimental::RWebDisplayHandle::FirefoxCreator::MakeProfile(std::string &exec,
bool batch_mode)
428 std::string rmdir, profile_arg;
430 if (exec.find(
"$profile") == std::string::npos)
433 const char *ff_profile = gEnv->GetValue(
"WebGui.FirefoxProfile",
"");
434 const char *ff_profilepath = gEnv->GetValue(
"WebGui.FirefoxProfilePath",
"");
435 Int_t ff_randomprofile = gEnv->GetValue(
"WebGui.FirefoxRandomProfile", (Int_t) 0);
436 if (ff_profile && *ff_profile) {
437 profile_arg =
"-P "s + ff_profile;
438 }
else if (ff_profilepath && *ff_profilepath) {
439 profile_arg =
"-profile "s + ff_profilepath;
440 }
else if ((ff_randomprofile > 0) || (batch_mode && (ff_randomprofile >= 0))) {
443 std::string rnd_profile =
"root_ff_profile_"s + std::to_string(gRandom->Integer(0x100000));
444 std::string profile_dir = std::string(gSystem->TempDirectory()) +
"/"s + rnd_profile;
446 profile_arg =
"-profile "s + profile_dir;
448 if (gSystem->mkdir(profile_dir.c_str()) == 0) {
451 R__ERROR_HERE(
"WebDisplay") <<
"Cannot create Firefox profile directory " << profile_dir;
455 exec = std::regex_replace(exec, std::regex(
"\\$profile"), profile_arg);
467 std::unique_ptr<ROOT::Experimental::RWebDisplayHandle> ROOT::Experimental::RWebDisplayHandle::Display(
const RWebDisplayArgs &args)
469 std::unique_ptr<RWebDisplayHandle> handle;
471 auto try_creator = [&](std::unique_ptr<Creator> &creator) {
472 if (!creator || !creator->IsActive())
474 handle = creator->Display(args);
475 return handle ?
true :
false;
478 if ((args.GetBrowserKind() == RWebDisplayArgs::kLocal) || (args.GetBrowserKind() == RWebDisplayArgs::kCEF)) {
479 if (try_creator(FindCreator(
"cef",
"libROOTCefDisplay")))
483 if ((args.GetBrowserKind() == RWebDisplayArgs::kLocal) || (args.GetBrowserKind() == RWebDisplayArgs::kQt5)) {
484 if (try_creator(FindCreator(
"qt5",
"libROOTQt5WebDisplay")))
488 if (args.IsLocalDisplay()) {
489 R__ERROR_HERE(
"WebDisplay") <<
"Neither Qt5 nor CEF libraries were found to provide local display";
493 if ((args.GetBrowserKind() == RWebDisplayArgs::kNative) || (args.GetBrowserKind() == RWebDisplayArgs::kChrome)) {
494 if (try_creator(FindCreator(
"chrome",
"ChromeCreator")))
498 if ((args.GetBrowserKind() == RWebDisplayArgs::kNative) || (args.GetBrowserKind() == RWebDisplayArgs::kFirefox)) {
499 if (try_creator(FindCreator(
"firefox",
"FirefoxCreator")))
503 if ((args.GetBrowserKind() == RWebDisplayArgs::kChrome) || (args.GetBrowserKind() == RWebDisplayArgs::kFirefox)) {
504 R__ERROR_HERE(
"WebDisplay") <<
"Neither Chrome nor Firefox browser cannot be started to provide display";
508 if ((args.GetBrowserKind() == RWebDisplayArgs::kCustom)) {
509 std::unique_ptr<Creator> creator = std::make_unique<BrowserCreator>(
false, args.GetCustomExec());
510 try_creator(creator);
512 try_creator(FindCreator(
"browser",
"BrowserCreator"));
531 bool ROOT::Experimental::RWebDisplayHandle::DisplayUrl(
const std::string &url)
533 RWebDisplayArgs args;
535 args.SetStandalone(
false);
537 auto handle = Display(args);