Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RWebDisplayHandle.cxx
Go to the documentation of this file.
1// Author: Sergey Linev <s.linev@gsi.de>
2// Date: 2018-10-17
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-2019, 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
14
15#include <ROOT/RLogger.hxx>
16
17#include "RConfigure.h"
18#include "TSystem.h"
19#include "TRandom.h"
20#include "TString.h"
21#include "TObjArray.h"
22#include "THttpServer.h"
23#include "TEnv.h"
24#include "TError.h"
25#include "TROOT.h"
26#include "TBase64.h"
27#include "TBufferJSON.h"
28
29#include <fstream>
30#include <iostream>
31#include <filesystem>
32#include <memory>
33#include <regex>
34
35#ifdef _MSC_VER
36#include <process.h>
37#else
38#include <unistd.h>
39#include <stdlib.h>
40#include <signal.h>
41#include <spawn.h>
42#endif
43
44using namespace ROOT;
45using namespace std::string_literals;
46
47/** \class ROOT::RWebDisplayHandle
48\ingroup webdisplay
49
50Handle of created web-based display
51Depending from type of web display, holds handle of started browser process or other display-specific information
52to correctly stop and cleanup display.
53*/
54
55
56//////////////////////////////////////////////////////////////////////////////////////////////////
57/// Static holder of registered creators of web displays
58
59std::map<std::string, std::unique_ptr<RWebDisplayHandle::Creator>> &RWebDisplayHandle::GetMap()
60{
61 static std::map<std::string, std::unique_ptr<RWebDisplayHandle::Creator>> sMap;
62 return sMap;
63}
64
65//////////////////////////////////////////////////////////////////////////////////////////////////
66/// Search for specific browser creator
67/// If not found, try to add one
68/// \param name - creator name like ChromeCreator
69/// \param libname - shared library name where creator could be provided
70
71std::unique_ptr<RWebDisplayHandle::Creator> &RWebDisplayHandle::FindCreator(const std::string &name, const std::string &libname)
72{
73 auto &m = GetMap();
74 auto search = m.find(name);
75 if (search == m.end()) {
76
77 if (libname == "ChromeCreator") {
78 m.emplace(name, std::make_unique<ChromeCreator>(name == "edge"));
79 } else if (libname == "FirefoxCreator") {
80 m.emplace(name, std::make_unique<FirefoxCreator>());
81 } else if (libname == "BrowserCreator") {
82 m.emplace(name, std::make_unique<BrowserCreator>(false));
83 } else if (!libname.empty()) {
84 gSystem->Load(libname.c_str());
85 }
86
87 search = m.find(name); // try again
88 }
89
90 if (search != m.end())
91 return search->second;
92
93 static std::unique_ptr<RWebDisplayHandle::Creator> dummy;
94 return dummy;
95}
96
97namespace ROOT {
98
99//////////////////////////////////////////////////////////////////////////////////////////////////
100/// Specialized handle to hold information about running browser process
101/// Used to correctly cleanup all processes and temporary directories
102
104
105#ifdef _MSC_VER
106 typedef int browser_process_id;
107#else
108 typedef pid_t browser_process_id;
109#endif
110 std::string fTmpDir; ///< temporary directory to delete at the end
111 std::string fTmpFile; ///< temporary file to remove
112 bool fHasPid{false};
114
115public:
116 RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &tmpfile, const std::string &dump) :
117 RWebDisplayHandle(url), fTmpDir(tmpdir), fTmpFile(tmpfile)
118 {
119 SetContent(dump);
120 }
121
122 RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &tmpfile, browser_process_id pid)
123 : RWebDisplayHandle(url), fTmpDir(tmpdir), fTmpFile(tmpfile), fHasPid(true), fPid(pid)
124 {
125 }
126
128 {
129#ifdef _MSC_VER
130 if (fHasPid)
131 gSystem->Exec(("taskkill /F /PID "s + std::to_string(fPid) + " >NUL 2>NUL").c_str());
132 std::string rmdir = "rmdir /S /Q ", rmfile = "del /F ";
133#else
134 if (fHasPid)
135 kill(fPid, SIGKILL);
136 std::string rmdir = "rm -rf ", rmfile = "rm -f ";
137#endif
138 if (!fTmpDir.empty())
139 gSystem->Exec((rmdir + fTmpDir).c_str());
140 if (!fTmpFile.empty())
141 gSystem->Exec((rmfile + fTmpFile).c_str());
142 }
143
144};
145
146} // namespace ROOT
147
148//////////////////////////////////////////////////////////////////////////////////////////////////
149/// Class to handle starting of web-browsers like Chrome or Firefox
150
151RWebDisplayHandle::BrowserCreator::BrowserCreator(bool custom, const std::string &exec)
152{
153 if (custom) return;
154
155 if (!exec.empty()) {
156 if (exec.find("$url") == std::string::npos) {
157 fProg = exec;
158#ifdef _MSC_VER
159 fExec = exec + " $url";
160#else
161 fExec = exec + " $url &";
162#endif
163 } else {
164 fExec = exec;
165 auto pos = exec.find(" ");
166 if (pos != std::string::npos)
167 fProg = exec.substr(0, pos);
168 }
169 } else if (gSystem->InheritsFrom("TMacOSXSystem")) {
170 fExec = "open \'$url\'";
171 } else if (gSystem->InheritsFrom("TWinNTSystem")) {
172 fExec = "start $url";
173 } else {
174 fExec = "xdg-open \'$url\' &";
175 }
176}
177
178//////////////////////////////////////////////////////////////////////////////////////////////////
179/// Check if browser executable exists and can be used
180
181void RWebDisplayHandle::BrowserCreator::TestProg(const std::string &nexttry, bool check_std_paths)
182{
183 if (nexttry.empty() || !fProg.empty())
184 return;
185
186 if (!gSystem->AccessPathName(nexttry.c_str(), kExecutePermission)) {
187#ifdef R__MACOSX
188 fProg = std::regex_replace(nexttry, std::regex("%20"), " ");
189#else
190 fProg = nexttry;
191#endif
192 return;
193 }
194
195 if (!check_std_paths)
196 return;
197
198#ifdef _MSC_VER
199 std::string ProgramFiles = gSystem->Getenv("ProgramFiles");
200 auto pos = ProgramFiles.find(" (x86)");
201 if (pos != std::string::npos)
202 ProgramFiles.erase(pos, 6);
203 std::string ProgramFilesx86 = gSystem->Getenv("ProgramFiles(x86)");
204
205 if (!ProgramFiles.empty())
206 TestProg(ProgramFiles + nexttry, false);
207 if (!ProgramFilesx86.empty())
208 TestProg(ProgramFilesx86 + nexttry, false);
209#endif
210}
211
212//////////////////////////////////////////////////////////////////////////////////////////////////
213/// Display given URL in web browser
214
215std::unique_ptr<RWebDisplayHandle>
217{
218 std::string url = args.GetFullUrl();
219 if (url.empty())
220 return nullptr;
221
223 std::cout << "New web window: " << url << std::endl;
224 return std::make_unique<RWebBrowserHandle>(url, "", "", "");
225 }
226
227 std::string exec;
228 if (args.IsBatchMode())
229 exec = fBatchExec;
230 else if (args.IsHeadless())
231 exec = fHeadlessExec;
232 else if (args.IsStandalone())
233 exec = fExec;
234 else
235 exec = "$prog $url &";
236
237 if (exec.empty())
238 return nullptr;
239
240 std::string swidth = std::to_string(args.GetWidth() > 0 ? args.GetWidth() : 800),
241 sheight = std::to_string(args.GetHeight() > 0 ? args.GetHeight() : 600),
242 sposx = std::to_string(args.GetX() >= 0 ? args.GetX() : 0),
243 sposy = std::to_string(args.GetY() >= 0 ? args.GetY() : 0);
244
245 ProcessGeometry(exec, args);
246
247 std::string rmdir = MakeProfile(exec, args.IsBatchMode() || args.IsHeadless());
248
249 std::string tmpfile;
250
251 // these are secret parameters, hide them in temp file
252 if ((url.find("token=") || url.find("key=")) && !args.IsBatchMode() && !args.IsHeadless()) {
253 TString filebase = "root_start_";
254
255 auto f = gSystem->TempFileName(filebase, nullptr, ".html");
256
257 bool ferr = false;
258
259 if (!f) {
260 ferr = true;
261 } else {
262 std::string content = std::regex_replace(
263 "<!DOCTYPE html>\n"
264 "<html lang=\"en\">\n"
265 "<head>\n"
266 " <meta charset=\"utf-8\">\n"
267 " <meta http-equiv=\"refresh\" content=\"0;url=$url\"/>\n"
268 " <title>Opening ROOT widget</title>\n"
269 "</head>\n"
270 "<body>\n"
271 "<p>\n"
272 " This page should redirect you to a ROOT widget. If it doesn't,\n"
273 " <a href=\"$url\">click here to go to ROOT</a>.\n"
274 "</p>\n"
275 "</body>\n"
276 "</html>\n", std::regex("\\$url"), url);
277
278 if (fwrite(content.c_str(), 1, content.length(), f) != content.length())
279 ferr = true;
280
281 if (fclose(f) != 0)
282 ferr = true;
283
284 tmpfile = filebase.Data();
285
286 url = "file://"s + tmpfile;
287 }
288
289 if (ferr) {
290 if (!tmpfile.empty())
291 gSystem->Unlink(tmpfile.c_str());
292 R__LOG_ERROR(WebGUILog()) << "Fail to create temporary HTML file to startup widget";
293 return nullptr;
294 }
295 }
296
297 exec = std::regex_replace(exec, std::regex("\\$rootetcdir"), TROOT::GetEtcDir().Data());
298 exec = std::regex_replace(exec, std::regex("\\$url"), url);
299 exec = std::regex_replace(exec, std::regex("\\$width"), swidth);
300 exec = std::regex_replace(exec, std::regex("\\$height"), sheight);
301 exec = std::regex_replace(exec, std::regex("\\$posx"), sposx);
302 exec = std::regex_replace(exec, std::regex("\\$posy"), sposy);
303
304 if (exec.compare(0,5,"fork:") == 0) {
305 if (fProg.empty()) {
306 if (!tmpfile.empty())
307 gSystem->Unlink(tmpfile.c_str());
308 R__LOG_ERROR(WebGUILog()) << "Fork instruction without executable";
309 return nullptr;
310 }
311
312 exec.erase(0, 5);
313
314#ifndef _MSC_VER
315
316 std::unique_ptr<TObjArray> fargs(TString(exec.c_str()).Tokenize(" "));
317 if (!fargs || (fargs->GetLast()<=0)) {
318 if (!tmpfile.empty())
319 gSystem->Unlink(tmpfile.c_str());
320 R__LOG_ERROR(WebGUILog()) << "Fork instruction is empty";
321 return nullptr;
322 }
323
324 std::vector<char *> argv;
325 argv.push_back((char *) fProg.c_str());
326 for (Int_t n = 0; n <= fargs->GetLast(); ++n)
327 argv.push_back((char *)fargs->At(n)->GetName());
328 argv.push_back(nullptr);
329
330 R__LOG_DEBUG(0, WebGUILog()) << "Show web window in browser with posix_spawn:\n" << fProg << " " << exec;
331
332 pid_t pid;
333 int status = posix_spawn(&pid, argv[0], nullptr, nullptr, argv.data(), nullptr);
334 if (status != 0) {
335 if (!tmpfile.empty())
336 gSystem->Unlink(tmpfile.c_str());
337 R__LOG_ERROR(WebGUILog()) << "Fail to launch " << argv[0];
338 return nullptr;
339 }
340
341 // add processid and rm dir
342
343 return std::make_unique<RWebBrowserHandle>(url, rmdir, tmpfile, pid);
344
345#else
346
347 if (fProg.empty()) {
348 if (!tmpfile.empty())
349 gSystem->Unlink(tmpfile.c_str());
350 R__LOG_ERROR(WebGUILog()) << "No Web browser found";
351 return nullptr;
352 }
353
354 // use UnixPathName to simplify handling of backslashes
355 exec = "wmic process call create '"s + gSystem->UnixPathName(fProg.c_str()) + exec + "' | find \"ProcessId\" "s;
356 std::string process_id = gSystem->GetFromPipe(exec.c_str()).Data();
357 std::stringstream ss(process_id);
358 std::string tmp;
359 char c;
360 int pid = 0;
361 ss >> tmp >> c >> pid;
362
363 if (pid <= 0) {
364 if (!tmpfile.empty())
365 gSystem->Unlink(tmpfile.c_str());
366 R__LOG_ERROR(WebGUILog()) << "Fail to launch " << fProg;
367 return nullptr;
368 }
369
370 // add processid and rm dir
371 return std::make_unique<RWebBrowserHandle>(url, rmdir, tmpfile, pid);
372#endif
373 }
374
375#ifdef _MSC_VER
376
377 if (exec.rfind("&") == exec.length() - 1) {
378
379 // if last symbol is &, use _spawn to detach execution
380 exec.resize(exec.length() - 1);
381
382 std::vector<char *> argv;
383 std::string firstarg = fProg;
384 auto slashpos = firstarg.find_last_of("/\\");
385 if (slashpos != std::string::npos)
386 firstarg.erase(0, slashpos + 1);
387 argv.push_back((char *)firstarg.c_str());
388
389 std::unique_ptr<TObjArray> fargs(TString(exec.c_str()).Tokenize(" "));
390 for (Int_t n = 1; n <= fargs->GetLast(); ++n)
391 argv.push_back((char *)fargs->At(n)->GetName());
392 argv.push_back(nullptr);
393
394 R__LOG_DEBUG(0, WebGUILog()) << "Showing web window in " << fProg << " with:\n" << exec;
395
396 _spawnv(_P_NOWAIT, gSystem->UnixPathName(fProg.c_str()), argv.data());
397
398 return std::make_unique<RWebBrowserHandle>(url, rmdir, tmpfile, ""s);
399 }
400
401 std::string prog = "\""s + gSystem->UnixPathName(fProg.c_str()) + "\""s;
402
403#else
404
405#ifdef R__MACOSX
406 std::string prog = std::regex_replace(fProg, std::regex(" "), "\\ ");
407#else
408 std::string prog = fProg;
409#endif
410
411#endif
412
413 exec = std::regex_replace(exec, std::regex("\\$prog"), prog);
414
415 std::string redirect = args.GetRedirectOutput(), dump_content;
416
417 if (!redirect.empty()) {
418 auto p = exec.length();
419 if (exec.rfind("&") == p-1) --p;
420 exec.insert(p, " >"s + redirect + " "s);
421 }
422
423 R__LOG_DEBUG(0, WebGUILog()) << "Showing web window in browser with:\n" << exec;
424
425 gSystem->Exec(exec.c_str());
426
427 // read content of redirected output
428 if (!redirect.empty()) {
429 dump_content = THttpServer::ReadFileContent(redirect.c_str());
430
431 gSystem->Unlink(redirect.c_str());
432 }
433
434 return std::make_unique<RWebBrowserHandle>(url, rmdir, tmpfile, dump_content);
435}
436
437//////////////////////////////////////////////////////////////////////////////////////////////////
438/// Constructor
439
441{
442 fEdge = _edge;
443
444 fEnvPrefix = fEdge ? "WebGui.Edge" : "WebGui.Chrome";
445
446 TestProg(gEnv->GetValue(fEnvPrefix.c_str(), ""));
447
448 if (!fProg.empty() && !fEdge)
449 fChromeVersion = gEnv->GetValue("WebGui.ChromeVersion", -1);
450
451#ifdef _MSC_VER
452 if (fEdge)
453 TestProg("\\Microsoft\\Edge\\Application\\msedge.exe", true);
454 else
455 TestProg("\\Google\\Chrome\\Application\\chrome.exe", true);
456#endif
457#ifdef R__MACOSX
458 TestProg("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
459#endif
460#ifdef R__LINUX
461 TestProg("/usr/bin/chromium");
462 TestProg("/usr/bin/chromium-browser");
463 TestProg("/usr/bin/chrome-browser");
464 TestProg("/usr/bin/google-chrome-stable");
465 TestProg("/usr/bin/google-chrome");
466#endif
467
468#ifdef _MSC_VER
469 fBatchExec = gEnv->GetValue((fEnvPrefix + "Batch").c_str(), "$prog --headless $geometry $url");
470 fHeadlessExec = gEnv->GetValue((fEnvPrefix + "Headless").c_str(), "$prog --headless --disable-gpu $geometry \"$url\" --dump-dom &");
471 fExec = gEnv->GetValue((fEnvPrefix + "Interactive").c_str(), "$prog $geometry --new-window --app=$url &"); // & in windows mean usage of spawn
472#else
473#ifdef R__MACOSX
474 bool use_normal = true; // mac does not like new flag
475#else
476 bool use_normal = fChromeVersion < 119;
477#endif
478 if (use_normal) {
479 // old browser with standard headless mode
480 fBatchExec = gEnv->GetValue((fEnvPrefix + "Batch").c_str(), "$prog --headless --no-sandbox --no-zygote --disable-extensions --disable-gpu --disable-audio-output $geometry $url");
481 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 &");
482 } else {
483 // newer version with headless=new mode
484 fBatchExec = gEnv->GetValue((fEnvPrefix + "Batch").c_str(), "$prog --headless=new --no-sandbox --no-zygote --disable-extensions --disable-gpu --disable-audio-output $geometry $url");
485 fHeadlessExec = gEnv->GetValue((fEnvPrefix + "Headless").c_str(), "$prog --headless=new --no-sandbox --no-zygote --disable-extensions --disable-gpu --disable-audio-output $geometry \'$url\' &");
486 }
487 fExec = gEnv->GetValue((fEnvPrefix + "Interactive").c_str(), "$prog $geometry --new-window --app=\'$url\' >/dev/null 2>/dev/null &");
488#endif
489}
490
491
492//////////////////////////////////////////////////////////////////////////////////////////////////
493/// Replace $geometry placeholder with geometry settings
494/// Also RWebDisplayArgs::GetExtraArgs() are appended
495
497{
498 std::string geometry;
499 if ((args.GetWidth() > 0) && (args.GetHeight() > 0))
500 geometry = "--window-size="s + std::to_string(args.GetWidth())
501 + (args.IsHeadless() ? "x"s : ","s)
502 + std::to_string(args.GetHeight());
503
504 if (((args.GetX() >= 0) || (args.GetY() >= 0)) && !args.IsHeadless()) {
505 if (!geometry.empty()) geometry.append(" ");
506 geometry.append("--window-position="s + std::to_string(args.GetX() >= 0 ? args.GetX() : 0) + ","s +
507 std::to_string(args.GetY() >= 0 ? args.GetY() : 0));
508 }
509
510 if (!args.GetExtraArgs().empty()) {
511 if (!geometry.empty()) geometry.append(" ");
512 geometry.append(args.GetExtraArgs());
513 }
514
515 exec = std::regex_replace(exec, std::regex("\\$geometry"), geometry);
516}
517
518
519//////////////////////////////////////////////////////////////////////////////////////////////////
520/// Handle profile argument
521
522std::string RWebDisplayHandle::ChromeCreator::MakeProfile(std::string &exec, bool)
523{
524 std::string rmdir, profile_arg;
525
526 if (exec.find("$profile") == std::string::npos)
527 return rmdir;
528
529 const char *chrome_profile = gEnv->GetValue((fEnvPrefix + "Profile").c_str(), "");
530 if (chrome_profile && *chrome_profile) {
531 profile_arg = chrome_profile;
532 } else {
533 gRandom->SetSeed(0);
534 profile_arg = gSystem->TempDirectory();
535#ifdef _MSC_VER
536 char slash = '\\';
537#else
538 char slash = '/';
539#endif
540 if (!profile_arg.empty() && (profile_arg[profile_arg.length()-1] != slash))
541 profile_arg += slash;
542 profile_arg += "root_chrome_profile_"s + std::to_string(gRandom->Integer(0x100000));
543
544 rmdir = profile_arg;
545 }
546
547 exec = std::regex_replace(exec, std::regex("\\$profile"), profile_arg);
548
549 return rmdir;
550}
551
552
553//////////////////////////////////////////////////////////////////////////////////////////////////
554/// Constructor
555
557{
558 TestProg(gEnv->GetValue("WebGui.Firefox", ""));
559
560#ifdef _MSC_VER
561 TestProg("\\Mozilla Firefox\\firefox.exe", true);
562#endif
563#ifdef R__MACOSX
564 TestProg("/Applications/Firefox.app/Contents/MacOS/firefox");
565#endif
566#ifdef R__LINUX
567 TestProg("/usr/bin/firefox");
568 TestProg("/usr/bin/firefox-bin");
569#endif
570
571#ifdef _MSC_VER
572 // there is a problem when specifying the window size with wmic on windows:
573 // It gives: Invalid format. Hint: <paramlist> = <param> [, <paramlist>].
574 fBatchExec = gEnv->GetValue("WebGui.FirefoxBatch", "$prog -headless -no-remote $profile $url");
575 fHeadlessExec = gEnv->GetValue("WebGui.FirefoxHeadless", "$prog -headless -no-remote $profile $url &");
576 fExec = gEnv->GetValue("WebGui.FirefoxInteractive", "$prog -no-remote $profile $geometry $url &");
577#else
578 fBatchExec = gEnv->GetValue("WebGui.FirefoxBatch", "$prog --headless --private-window -no-remote $profile $url");
579 fHeadlessExec = gEnv->GetValue("WebGui.FirefoxHeadless", "fork:--headless --private-window -no-remote $profile $url");
580 fExec = gEnv->GetValue("WebGui.FirefoxInteractive", "$rootetcdir/runfirefox.sh $cleanup_profile $prog -no-remote $profile $geometry -url \'$url\' &");
581#endif
582}
583
584//////////////////////////////////////////////////////////////////////////////////////////////////
585/// Process window geometry for Firefox
586
588{
589 std::string geometry;
590 if ((args.GetWidth() > 0) && (args.GetHeight() > 0) && !args.IsHeadless())
591 geometry = "-width="s + std::to_string(args.GetWidth()) + " -height=" + std::to_string(args.GetHeight());
592
593 exec = std::regex_replace(exec, std::regex("\\$geometry"), geometry);
594}
595
596//////////////////////////////////////////////////////////////////////////////////////////////////
597/// Create Firefox profile to run independent browser window
598
599std::string RWebDisplayHandle::FirefoxCreator::MakeProfile(std::string &exec, bool batch_mode)
600{
601 std::string rmdir, profile_arg;
602
603 if (exec.find("$profile") == std::string::npos)
604 return rmdir;
605
606 const char *ff_profile = gEnv->GetValue("WebGui.FirefoxProfile", "");
607 const char *ff_profilepath = gEnv->GetValue("WebGui.FirefoxProfilePath", "");
608 Int_t ff_randomprofile = gEnv->GetValue("WebGui.FirefoxRandomProfile", (Int_t) 1);
609 if (ff_profile && *ff_profile) {
610 profile_arg = "-P "s + ff_profile;
611 } else if (ff_profilepath && *ff_profilepath) {
612 profile_arg = "-profile "s + ff_profilepath;
613 } else if (ff_randomprofile > 0) {
614
615 gRandom->SetSeed(0);
616 std::string profile_dir = gSystem->TempDirectory();
617
618#ifdef _MSC_VER
619 char slash = '\\';
620#else
621 char slash = '/';
622#endif
623 if (!profile_dir.empty() && (profile_dir[profile_dir.length()-1] != slash))
624 profile_dir += slash;
625 profile_dir += "root_ff_profile_"s + std::to_string(gRandom->Integer(0x100000));
626
627 profile_arg = "-profile "s + profile_dir;
628
629 if (gSystem->mkdir(profile_dir.c_str()) == 0) {
630 rmdir = profile_dir;
631
632 std::ofstream user_js(profile_dir + "/user.js", std::ios::trunc);
633 // workaround for current Firefox, without such settings it fail to close window and terminate it from batch
634 // also disable question about upload of data
635 user_js << "user_pref(\"datareporting.policy.dataSubmissionPolicyAcceptedVersion\", 2);" << std::endl;
636 user_js << "user_pref(\"datareporting.policy.dataSubmissionPolicyNotifiedTime\", \"1635760572813\");" << std::endl;
637
638 // try to ensure that window closes with last tab
639 user_js << "user_pref(\"browser.tabs.closeWindowWithLastTab\", true);" << std::endl;
640 user_js << "user_pref(\"dom.allow_scripts_to_close_windows\", true);" << std::endl;
641 user_js << "user_pref(\"browser.sessionstore.resume_from_crash\", false);" << std::endl;
642
643 if (batch_mode) {
644 // allow to dump messages to std output
645 user_js << "user_pref(\"browser.dom.window.dump.enabled\", true);" << std::endl;
646 } else {
647 // to suppress annoying privacy tab
648 user_js << "user_pref(\"datareporting.policy.firstRunURL\", \"\");" << std::endl;
649 // to use custom userChrome.css files
650 user_js << "user_pref(\"toolkit.legacyUserProfileCustomizations.stylesheets\", true);" << std::endl;
651 // do not put tabs in title
652 user_js << "user_pref(\"browser.tabs.inTitlebar\", 0);" << std::endl;
653
654 std::ofstream times_json(profile_dir + "/times.json", std::ios::trunc);
655 times_json << "{" << std::endl;
656 times_json << " \"created\": 1699968480952," << std::endl;
657 times_json << " \"firstUse\": null" << std::endl;
658 times_json << "}" << std::endl;
659 if (gSystem->mkdir((profile_dir + "/chrome").c_str()) == 0) {
660 std::ofstream style(profile_dir + "/chrome/userChrome.css", std::ios::trunc);
661 // do not show tabs
662 style << "#TabsToolbar { visibility: collapse; }" << std::endl;
663 // do not show URL
664 style << "#nav-bar, #urlbar-container, #searchbar { visibility: collapse !important; }" << std::endl;
665 }
666 }
667
668 } else {
669 R__LOG_ERROR(WebGUILog()) << "Cannot create Firefox profile directory " << profile_dir;
670 }
671 }
672
673 exec = std::regex_replace(exec, std::regex("\\$profile"), profile_arg);
674
675 if (exec.find("$cleanup_profile") != std::string::npos) {
676 if (rmdir.empty()) rmdir = "<dummy>";
677 exec = std::regex_replace(exec, std::regex("\\$cleanup_profile"), rmdir);
678 rmdir.clear(); // no need to delete directory - it will be removed by script
679 }
680
681 return rmdir;
682}
683
684///////////////////////////////////////////////////////////////////////////////////////////////////
685/// Check if http server required for display
686/// \param args - defines where and how to display web window
687
689{
693 return false;
694
695 if (!args.IsHeadless() && (args.GetBrowserKind() == RWebDisplayArgs::kOn)) {
696
697#ifdef WITH_QT6WEB
698 auto &qt6 = FindCreator("qt6", "libROOTQt6WebDisplay");
699 if (qt6 && qt6->IsActive())
700 return false;
701#endif
702#ifdef WITH_QT5WEB
703 auto &qt5 = FindCreator("qt5", "libROOTQt5WebDisplay");
704 if (qt5 && qt5->IsActive())
705 return false;
706#endif
707#ifdef WITH_CEFWEB
708 auto &cef = FindCreator("cef", "libROOTCefDisplay");
709 if (cef && cef->IsActive())
710 return false;
711#endif
712 }
713
714 return true;
715}
716
717
718///////////////////////////////////////////////////////////////////////////////////////////////////
719/// Create web display
720/// \param args - defines where and how to display web window
721/// Returns RWebDisplayHandle, which holds information of running browser application
722/// Can be used fully independent from RWebWindow classes just to show any web page
723
724std::unique_ptr<RWebDisplayHandle> RWebDisplayHandle::Display(const RWebDisplayArgs &args)
725{
726 std::unique_ptr<RWebDisplayHandle> handle;
727
729 return handle;
730
731 auto try_creator = [&](std::unique_ptr<Creator> &creator) {
732 if (!creator || !creator->IsActive())
733 return false;
734 handle = creator->Display(args);
735 return handle ? true : false;
736 };
737
738 bool handleAsLocal = (args.GetBrowserKind() == RWebDisplayArgs::kLocal) ||
739 (!args.IsHeadless() && (args.GetBrowserKind() == RWebDisplayArgs::kOn)),
740 has_qt5web = false, has_qt6web = false, has_cefweb = false;
741
742#ifdef WITH_QT5WEB
743 has_qt5web = true;
744#endif
745
746#ifdef WITH_QT6WEB
747 has_qt6web = true;
748#endif
749
750#ifdef WITH_CEFWEB
751 has_cefweb = true;
752#endif
753
754 if ((handleAsLocal && has_qt6web) || (args.GetBrowserKind() == RWebDisplayArgs::kQt6)) {
755 if (try_creator(FindCreator("qt6", "libROOTQt6WebDisplay")))
756 return handle;
757 }
758
759 if ((handleAsLocal && has_qt5web) || (args.GetBrowserKind() == RWebDisplayArgs::kQt5)) {
760 if (try_creator(FindCreator("qt5", "libROOTQt5WebDisplay")))
761 return handle;
762 }
763
764 if ((handleAsLocal && has_cefweb) || (args.GetBrowserKind() == RWebDisplayArgs::kCEF)) {
765 if (try_creator(FindCreator("cef", "libROOTCefDisplay")))
766 return handle;
767 }
768
769 if (args.IsLocalDisplay()) {
770 R__LOG_ERROR(WebGUILog()) << "Neither Qt5/6 nor CEF libraries were found to provide local display";
771 return handle;
772 }
773
774 bool handleAsNative =
776
777#ifdef _MSC_VER
778 if (handleAsNative || (args.GetBrowserKind() == RWebDisplayArgs::kEdge)) {
779 if (try_creator(FindCreator("edge", "ChromeCreator")))
780 return handle;
781 }
782#endif
783
784 if (handleAsNative || (args.GetBrowserKind() == RWebDisplayArgs::kChrome)) {
785 if (try_creator(FindCreator("chrome", "ChromeCreator")))
786 return handle;
787 }
788
789 if (handleAsNative || (args.GetBrowserKind() == RWebDisplayArgs::kFirefox)) {
790 if (try_creator(FindCreator("firefox", "FirefoxCreator")))
791 return handle;
792 }
793
796 // R__LOG_ERROR(WebGUILog()) << "Neither Chrome nor Firefox browser cannot be started to provide display";
797 return handle;
798 }
799
801 std::unique_ptr<Creator> creator = std::make_unique<BrowserCreator>(false, args.GetCustomExec());
802 try_creator(creator);
803 } else {
804 try_creator(FindCreator("browser", "BrowserCreator"));
805 }
806
807 return handle;
808}
809
810///////////////////////////////////////////////////////////////////////////////////////////////////
811/// Display provided url in configured web browser
812/// \param url - specified URL address like https://root.cern
813/// Browser can specified when starting `root --web=firefox`
814/// Returns true when browser started
815/// It is convenience method, equivalent to:
816/// ~~~
817/// RWebDisplayArgs args;
818/// args.SetUrl(url);
819/// args.SetStandalone(false);
820/// auto handle = RWebDisplayHandle::Display(args);
821/// ~~~
822
823bool RWebDisplayHandle::DisplayUrl(const std::string &url)
824{
825 RWebDisplayArgs args;
826 args.SetUrl(url);
827 args.SetStandalone(false);
828
829 auto handle = Display(args);
830
831 return !!handle;
832}
833
834///////////////////////////////////////////////////////////////////////////////////////////////////
835/// Checks if configured browser can be used for image production
836
838{
842 bool detected = false;
843
844 auto &h1 = FindCreator("chrome", "ChromeCreator");
845 if (h1 && h1->IsActive()) {
847 detected = true;
848 }
849
850 if (!detected) {
851 auto &h2 = FindCreator("firefox", "FirefoxCreator");
852 if (h2 && h2->IsActive()) {
854 detected = true;
855 }
856 }
857
858#ifdef _MSC_VER
859 if (!detected) {
860 auto &h3 = FindCreator("edge", "ChromeCreator");
861 if (h3 && h3->IsActive()) {
863 detected = true;
864 }
865 }
866#endif
867 return detected;
868 }
869
871 auto &h1 = FindCreator("chrome", "ChromeCreator");
872 return h1 && h1->IsActive();
873 }
874
876 auto &h2 = FindCreator("firefox", "FirefoxCreator");
877 return h2 && h2->IsActive();
878 }
879
880#ifdef _MSC_VER
882 auto &h3 = FindCreator("edge", "ChromeCreator");
883 return h3 && h3->IsActive();
884 }
885#endif
886
887 return true;
888}
889
890///////////////////////////////////////////////////////////////////////////////////////////////////
891/// Returns true if image production for specified browser kind is supported
892/// If browser not specified - use currently configured browser or try to test existing web browsers
893
894bool RWebDisplayHandle::CanProduceImages(const std::string &browser)
895{
896 RWebDisplayArgs args(browser);
897
898 return CheckIfCanProduceImages(args);
899}
900
901///////////////////////////////////////////////////////////////////////////////////////////////////
902/// Produce image file using JSON data as source
903/// Invokes JSROOT drawing functionality in headless browser - Google Chrome or Mozilla Firefox
904bool RWebDisplayHandle::ProduceImage(const std::string &fname, const std::string &json, int width, int height, const char *batch_file)
905{
906 return ProduceImages(fname, {json}, {width}, {height}, batch_file);
907}
908
909///////////////////////////////////////////////////////////////////////////////////////////////////
910/// Produce image file(s) using JSON data as source
911/// Invokes JSROOT drawing functionality in headless browser - Google Chrome or Mozilla Firefox
912bool RWebDisplayHandle::ProduceImages(const std::string &fname, const std::vector<std::string> &jsons, const std::vector<int> &widths, const std::vector<int> &heights, const char *batch_file)
913{
914 if (jsons.empty())
915 return false;
916
917 std::string _fname = fname;
918 std::transform(_fname.begin(), _fname.end(), _fname.begin(), ::tolower);
919 auto EndsWith = [&_fname](const std::string &suffix) {
920 return (_fname.length() > suffix.length()) ? (0 == _fname.compare(_fname.length() - suffix.length(), suffix.length(), suffix)) : false;
921 };
922
923 std::vector<std::string> fnames;
924
925 if (!EndsWith(".pdf")) {
926 bool has_quialifier = _fname.find("%") != std::string::npos;
927
928 if (!has_quialifier && (jsons.size() > 1)) {
929 _fname.insert(_fname.rfind("."), "%d");
930 has_quialifier = true;
931 }
932
933 for (unsigned n = 0; n < jsons.size(); n++) {
934 if (has_quialifier) {
935 auto expand_name = TString::Format(_fname.c_str(), (int) n);
936 fnames.emplace_back(expand_name.Data());
937 } else {
938 fnames.emplace_back(_fname);
939 }
940 }
941 }
942
943 if (EndsWith(".json")) {
944 for (unsigned n = 0; n < jsons.size(); ++n) {
945 std::ofstream ofs(fnames[n]);
946 ofs << jsons[n];
947 }
948 return true;
949 }
950
951 const char *jsrootsys = gSystem->Getenv("JSROOTSYS");
952 TString jsrootsysdflt;
953 if (!jsrootsys) {
954 jsrootsysdflt = TROOT::GetDataDir() + "/js";
955 if (gSystem->ExpandPathName(jsrootsysdflt)) {
956 R__LOG_ERROR(WebGUILog()) << "Fail to locate JSROOT " << jsrootsysdflt;
957 return false;
958 }
959 jsrootsys = jsrootsysdflt.Data();
960 }
961
962 RWebDisplayArgs args; // set default browser kind, only Chrome/Firefox/Edge or CEF/Qt5/Qt6 can be used here
963 if (!CheckIfCanProduceImages(args)) {
964 R__LOG_ERROR(WebGUILog()) << "Fail to detect supported browsers for image production";
965 return false;
966 }
967
968 auto isChromeBased = (args.GetBrowserKind() == RWebDisplayArgs::kChrome) || (args.GetBrowserKind() == RWebDisplayArgs::kEdge),
969 isFirefox = args.GetBrowserKind() == RWebDisplayArgs::kFirefox;
970
971 std::string draw_kind;
972
973 if (EndsWith(".pdf"))
974 draw_kind = "draw"; // not a JSROOT drawing but Chrome capability to create PDF out of HTML page is used
975 else if (EndsWith("shot.png") && (jsons.size() == 1))
976 draw_kind = isChromeBased ? "draw" : "png"; // using screenshot
977 else if (EndsWith(".svg"))
978 draw_kind = "svg";
979 else if (EndsWith(".png"))
980 draw_kind = "png";
981 else if (EndsWith(".jpg") || EndsWith(".jpeg"))
982 draw_kind = "jpeg";
983 else if (EndsWith(".webp"))
984 draw_kind = "webp";
985 else
986 return false;
987
988 if (!batch_file || !*batch_file)
989 batch_file = "/js/files/canv_batch.htm";
990
991 TString origin = TROOT::GetDataDir() + batch_file;
992 if (gSystem->ExpandPathName(origin)) {
993 R__LOG_ERROR(WebGUILog()) << "Fail to find " << origin;
994 return false;
995 }
996
997 auto filecont = THttpServer::ReadFileContent(origin.Data());
998 if (filecont.empty()) {
999 R__LOG_ERROR(WebGUILog()) << "Fail to read content of " << origin;
1000 return false;
1001 }
1002
1003 int max_width = 0, max_height = 0, page_margin = 10;
1004 for (auto &w : widths)
1005 if (w > max_width)
1006 max_width = w;
1007 for (auto &h : heights)
1008 if (h > max_height)
1009 max_height = h;
1010
1011 auto jsonw = TBufferJSON::ToJSON(&widths, TBufferJSON::kNoSpaces);
1012 auto jsonh = TBufferJSON::ToJSON(&heights, TBufferJSON::kNoSpaces);
1013
1014 std::string mains;
1015 for (auto &json : jsons) {
1016 mains.append(mains.empty() ? "[" : ", ");
1017 mains.append(json);
1018 }
1019 mains.append("]");
1020
1021 if (strstr(jsrootsys,"http://") || strstr(jsrootsys,"https://") || strstr(jsrootsys,"file://"))
1022 filecont = std::regex_replace(filecont, std::regex("\\$jsrootsys"), jsrootsys);
1023 else
1024 filecont = std::regex_replace(filecont, std::regex("\\$jsrootsys"), "file://"s + jsrootsys);
1025
1026 filecont = std::regex_replace(filecont, std::regex("\\$page_margin"), std::to_string(page_margin) + "px");
1027 filecont = std::regex_replace(filecont, std::regex("\\$page_width"), std::to_string(max_width + 2*page_margin) + "px");
1028 filecont = std::regex_replace(filecont, std::regex("\\$page_height"), std::to_string(max_height + 2*page_margin) + "px");
1029
1030 filecont = std::regex_replace(filecont, std::regex("\\$draw_kind"), draw_kind);
1031 filecont = std::regex_replace(filecont, std::regex("\\$draw_widths"), jsonw.Data());
1032 filecont = std::regex_replace(filecont, std::regex("\\$draw_heights"), jsonh.Data());
1033 filecont = std::regex_replace(filecont, std::regex("\\$draw_objects"), mains);
1034
1035 TString dump_name;
1036 if (draw_kind == "draw") {
1037 if (!isChromeBased) {
1038 R__LOG_ERROR(WebGUILog()) << "Creation of PDF files supported only by Chrome-based browser";
1039 return false;
1040 }
1041 } else if (isChromeBased || isFirefox) {
1042 dump_name = "canvasdump";
1043 FILE *df = gSystem->TempFileName(dump_name);
1044 if (!df) {
1045 R__LOG_ERROR(WebGUILog()) << "Fail to create temporary file for dump-dom";
1046 return false;
1047 }
1048 fputs("placeholder", df);
1049 fclose(df);
1050 }
1051
1052 // When true, place HTML file into home directory
1053 // Some Chrome installation do not allow run html code from files, created in /tmp directory
1054 static bool chrome_tmp_workaround = false;
1055
1056 TString tmp_name, html_name;
1057
1058try_again:
1059
1061 args.SetUrl(""s);
1062 args.SetPageContent(filecont);
1063
1064 tmp_name.Clear();
1065 html_name.Clear();
1066
1067 R__LOG_DEBUG(0, WebGUILog()) << "Using file content_len " << filecont.length() << " to produce batch images " << fname;
1068
1069 } else {
1070 tmp_name = "canvasbody";
1071 FILE *hf = gSystem->TempFileName(tmp_name);
1072 if (!hf) {
1073 R__LOG_ERROR(WebGUILog()) << "Fail to create temporary file for batch job";
1074 return false;
1075 }
1076 fputs(filecont.c_str(), hf);
1077 fclose(hf);
1078
1079 html_name = tmp_name + ".html";
1080
1081 if (chrome_tmp_workaround) {
1082 std::string homedir = gSystem->GetHomeDirectory();
1083 auto pos = html_name.Last('/');
1084 if (pos == kNPOS)
1085 html_name = TString::Format("/random%d.html", gRandom->Integer(1000000));
1086 else
1087 html_name.Remove(0, pos);
1088 html_name = homedir + html_name.Data();
1089 gSystem->Unlink(html_name.Data());
1090 gSystem->Unlink(tmp_name.Data());
1091
1092 std::ofstream ofs(html_name.Data(), std::ofstream::out);
1093 ofs << filecont;
1094 } else {
1095 if (gSystem->Rename(tmp_name.Data(), html_name.Data()) != 0) {
1096 R__LOG_ERROR(WebGUILog()) << "Fail to rename temp file " << tmp_name << " into " << html_name;
1097 gSystem->Unlink(tmp_name.Data());
1098 return false;
1099 }
1100 }
1101
1102 args.SetUrl("file://"s + gSystem->UnixPathName(html_name.Data()));
1103 args.SetPageContent(""s);
1104
1105 R__LOG_DEBUG(0, WebGUILog()) << "Using " << html_name << " content_len " << filecont.length() << " to produce batch images " << fname;
1106 }
1107
1108 TString wait_file_name;
1109
1110 args.SetStandalone(true);
1111 args.SetHeadless(true);
1112 args.SetBatchMode(true);
1113 args.SetSize(widths[0], heights[0]);
1114
1115 if (draw_kind == "draw") {
1116
1117 TString tgtfilename = fname.c_str();
1118 if (!gSystem->IsAbsoluteFileName(tgtfilename.Data()))
1120
1121 wait_file_name = tgtfilename;
1122
1123 if (EndsWith(".pdf"))
1124 args.SetExtraArgs("--print-to-pdf-no-header --print-to-pdf="s + gSystem->UnixPathName(tgtfilename.Data()));
1125 else
1126 args.SetExtraArgs("--screenshot="s + gSystem->UnixPathName(tgtfilename.Data()));
1127
1128 // remove target image file - we use it as detection when chrome is ready
1129 gSystem->Unlink(tgtfilename.Data());
1130
1131 } else if (isFirefox) {
1132 // firefox will use window.dump to output produced result
1133 args.SetRedirectOutput(dump_name.Data());
1134 gSystem->Unlink(dump_name.Data());
1135 } else if (isChromeBased) {
1136 // require temporary output file
1137 args.SetExtraArgs("--dump-dom");
1138 args.SetRedirectOutput(dump_name.Data());
1139
1140 // wait_file_name = dump_name;
1141
1142 gSystem->Unlink(dump_name.Data());
1143 }
1144
1145 auto handle = RWebDisplayHandle::Display(args);
1146
1147 if (!handle) {
1148 R__LOG_DEBUG(0, WebGUILog()) << "Cannot start " << args.GetBrowserName() << " to produce image " << fname;
1149 return false;
1150 }
1151
1152 // delete temporary HTML file
1153 if (html_name.Length() > 0)
1154 gSystem->Unlink(html_name.Data());
1155
1156 if (!wait_file_name.IsNull() && gSystem->AccessPathName(wait_file_name.Data())) {
1157 R__LOG_ERROR(WebGUILog()) << "Fail to produce image " << fname;
1158 return false;
1159 }
1160
1161 if (draw_kind != "draw") {
1162
1163 auto dumpcont = handle->GetContent();
1164
1165 if ((dumpcont.length() > 20) && (dumpcont.length() < 60) && !chrome_tmp_workaround && isChromeBased) {
1166 // chrome creates dummy html file with mostly no content
1167 // problem running chrome from /tmp directory, lets try work from home directory
1168 chrome_tmp_workaround = true;
1169 goto try_again;
1170 }
1171
1172 if (dumpcont.length() < 100) {
1173 R__LOG_ERROR(WebGUILog()) << "Fail to dump HTML code into " << (dump_name.IsNull() ? "CEF" : dump_name.Data());
1174 return false;
1175 }
1176
1177 if (draw_kind == "svg") {
1178
1179 std::string::size_type p = 0;
1180
1181 for (auto & fn : fnames) {
1182 auto p1 = dumpcont.find("<svg", p);
1183 auto p2 = dumpcont.find("</svg></div>", p1 + 4);
1184 p = p2 + 6;
1185 std::ofstream ofs(fn);
1186 if ((p1 != std::string::npos) && (p2 != std::string::npos) && (p1 < p2)) {
1187 ofs << dumpcont.substr(p1, p2-p1+6);
1188 ::Info("ProduceImage", "SVG file %s size %d bytes has been created", fn.c_str(), (int) (p2-p1+6));
1189 } else {
1190 R__LOG_ERROR(WebGUILog()) << "Fail to extract SVG from HTML dump " << dump_name;
1191 ofs << "Failure!!!\n" << dumpcont;
1192 return false;
1193 }
1194 }
1195 } else {
1196
1197 std::string::size_type p = 0;
1198
1199 for (auto &fn : fnames) {
1200
1201 auto p1 = dumpcont.find(";base64,", p);
1202 auto p2 = dumpcont.find("></div>", p1 + 4);
1203 p = p2 + 5;
1204
1205 if ((p1 != std::string::npos) && (p2 != std::string::npos) && (p1 < p2)) {
1206
1207 auto base64 = dumpcont.substr(p1+8, p2-p1-9);
1208 auto binary = TBase64::Decode(base64.c_str());
1209
1210 std::ofstream ofs(fn, std::ios::binary);
1211 ofs.write(binary.Data(), binary.Length());
1212
1213 ::Info("ProduceImage", "Image file %s size %d bytes has been created", fn.c_str(), (int) binary.Length());
1214 } else {
1215 R__LOG_ERROR(WebGUILog()) << "Fail to extract image from dump HTML code " << dump_name;
1216
1217 return false;
1218 }
1219 }
1220 }
1221 } else if (EndsWith(".pdf")) {
1222 ::Info("ProduceImage", "PDF file %s with %d pages has been created", fname.c_str(), (int) jsons.size());
1223 }
1224
1225 if (fnames.size() == 1)
1226 R__LOG_DEBUG(0, WebGUILog()) << "Create file " << fnames[0];
1227 else
1228 R__LOG_DEBUG(0, WebGUILog()) << "Create files " << TBufferJSON::ToJSON(&fnames, TBufferJSON::kNoSpaces);
1229
1230 return true;
1231}
1232
nlohmann::json json
#define R__LOG_ERROR(...)
Definition RLogger.hxx:362
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition RLogger.hxx:365
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define h(i)
Definition RSha256.hxx:106
constexpr Ssiz_t kNPOS
Definition RtypesCore.h:124
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition TError.cxx:218
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t width
Option_t Option_t style
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t height
char name[80]
Definition TGX11.cxx:110
R__EXTERN TRandom * gRandom
Definition TRandom.h:62
@ kExecutePermission
Definition TSystem.h:43
R__EXTERN TSystem * gSystem
Definition TSystem.h:555
Specialized handle to hold information about running browser process Used to correctly cleanup all pr...
RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &tmpfile, browser_process_id pid)
std::string fTmpDir
temporary directory to delete at the end
RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &tmpfile, const std::string &dump)
std::string fTmpFile
temporary file to remove
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
std::string GetBrowserName() const
Returns configured browser name.
EBrowserKind GetBrowserKind() const
returns configured browser kind, see EBrowserKind for supported values
const std::string & GetRedirectOutput() const
get file name to which web browser output should be redirected
void SetStandalone(bool on=true)
Set standalone mode for running browser, default on When disabled, normal browser window (or just tab...
void SetBatchMode(bool on=true)
set batch mode
RWebDisplayArgs & SetSize(int w, int h)
set preferable web window width and height
RWebDisplayArgs & SetUrl(const std::string &url)
set window url
int GetWidth() const
returns preferable web window width
RWebDisplayArgs & SetPageContent(const std::string &cont)
set window url
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
RWebDisplayArgs & SetBrowserKind(const std::string &kind)
Set browser kind as string argument.
std::string GetCustomExec() const
returns custom executable to start web browser
void SetExtraArgs(const std::string &args)
set extra command line arguments for starting web browser command
bool IsBatchMode() const
returns batch mode
bool IsHeadless() const
returns headless mode
@ kOn
web display enable, first try use embed displays like Qt or CEF, then native browsers and at the end ...
@ kFirefox
Mozilla Firefox browser.
@ kNative
either Chrome or Firefox - both support major functionality
@ 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
@ kCEF
Chromium Embedded Framework - local display with CEF libs.
@ kQt5
Qt5 QWebEngine libraries - Chromium code packed in qt5.
@ kQt6
Qt6 QWebEngine libraries - Chromium code packed in qt6.
@ kCustom
custom web browser, execution string should be provided
@ kChrome
Google Chrome browser.
@ kEdge
Microsoft Edge browser (Windows only)
void SetRedirectOutput(const std::string &fname="")
specify file name to which web browser output should be redirected
void SetHeadless(bool on=true)
set 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
bool IsLocalDisplay() const
returns true if local display like CEF or Qt5 QWebEngine should be used
std::string fBatchExec
batch execute line
std::string fHeadlessExec
headless execute line
std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args) override
Display given URL in web browser.
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.
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 &) override
Replace $geometry placeholder with geometry settings Also RWebDisplayArgs::GetExtraArgs() are appende...
std::string MakeProfile(std::string &exec, bool) override
Handle profile argument.
std::string MakeProfile(std::string &exec, bool batch) override
Create Firefox profile to run independent browser window.
void ProcessGeometry(std::string &, const RWebDisplayArgs &) override
Process window geometry for Firefox.
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 CheckIfCanProduceImages(RWebDisplayArgs &args)
Checks if configured browser can be used for image production.
static bool ProduceImages(const std::string &fname, const std::vector< std::string > &jsons, const std::vector< int > &widths, const std::vector< int > &heights, const char *batch_file=nullptr)
Produce image file(s) using JSON data as source Invokes JSROOT drawing functionality in headless brow...
void SetContent(const std::string &cont)
set content
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 CanProduceImages(const std::string &browser="")
Returns true if image production for specified browser kind is supported If browser not specified - u...
static bool NeedHttpServer(const RWebDisplayArgs &args)
Check if http server required for display.
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.
Definition TBase64.cxx:131
static TString ToJSON(const T *obj, Int_t compact=0, const char *member_name=nullptr)
Definition TBufferJSON.h:75
@ kNoSpaces
no new lines plus remove all spaces around "," and ":" symbols
Definition TBufferJSON.h:39
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:491
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".
Definition TObject.cxx:524
static const TString & GetEtcDir()
Get the sysconfig directory in the installation. Static utility function.
Definition TROOT.cxx:3038
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition TROOT.cxx:3048
virtual void SetSeed(ULong_t seed=0)
Set the random generator seed.
Definition TRandom.cxx:615
virtual UInt_t Integer(UInt_t imax)
Returns a random integer uniformly distributed on the interval [ 0, imax-1 ].
Definition TRandom.cxx:361
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:417
void Clear()
Clear string without changing its capacity.
Definition TString.cxx:1235
const char * Data() const
Definition TString.h:376
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition TString.cxx:931
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition TString.cxx:2264
Bool_t IsNull() const
Definition TString.h:414
TString & Remove(Ssiz_t pos)
Definition TString.h:685
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2378
virtual FILE * TempFileName(TString &base, const char *dir=nullptr, const char *suffix=nullptr)
Create a secure temporary file by appending a unique 6 letter string to base.
Definition TSystem.cxx:1499
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx:1274
virtual const char * Getenv(const char *env)
Get environment variable.
Definition TSystem.cxx:1665
virtual int mkdir(const char *name, Bool_t recursive=kFALSE)
Make a file system directory.
Definition TSystem.cxx:906
virtual Int_t Exec(const char *shellcmd)
Execute a command.
Definition TSystem.cxx:653
virtual int Load(const char *module, const char *entry="", Bool_t system=kFALSE)
Load a shared library.
Definition TSystem.cxx:1857
virtual const char * PrependPathName(const char *dir, TString &name)
Concatenate a directory and a file name.
Definition TSystem.cxx:1081
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
Definition TSystem.cxx:1296
virtual std::string GetHomeDirectory(const char *userName=nullptr) const
Return the user's home directory.
Definition TSystem.cxx:895
virtual const char * UnixPathName(const char *unixpathname)
Convert from a local pathname to a Unix pathname.
Definition TSystem.cxx:1063
virtual int Rename(const char *from, const char *to)
Rename a file.
Definition TSystem.cxx:1350
virtual TString GetFromPipe(const char *command)
Execute command and return output in TString.
Definition TSystem.cxx:680
virtual Bool_t IsAbsoluteFileName(const char *dir)
Return true if dir is an absolute pathname.
Definition TSystem.cxx:951
virtual const char * WorkingDirectory()
Return working directory.
Definition TSystem.cxx:871
virtual int Unlink(const char *name)
Unlink, i.e.
Definition TSystem.cxx:1381
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
Definition TSystem.cxx:1482
const Int_t n
Definition legend1.C:16
TH1F * h1
Definition legend1.C:5
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
ROOT::Experimental::RLogChannel & WebGUILog()
Log channel for WebGUI diagnostics.
TCanvas * slash()
Definition slash.C:1
TMarker m
Definition textangle.C:8