Skip to content

[webgui] testing /snap/bin/chromium #18311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmake/modules/RootConfiguration.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ find_program(PERL_EXECUTABLE perl)
set(perl ${PERL_EXECUTABLE})

find_program(CHROME_EXECUTABLE NAMES chrome.exe chromium chromium-browser chrome chrome-browser google-chrome-stable Google\ Chrome
HINTS /snap/bin
PATH_SUFFIXES "Google/Chrome/Application")
if(CHROME_EXECUTABLE)
if(WIN32)
Expand Down
7 changes: 7 additions & 0 deletions gui/webdisplay/inc/ROOT/RWebDisplayHandle.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <map>
#include <memory>
#include <vector>
#include "TString.h"

namespace ROOT {

Expand All @@ -35,6 +36,7 @@ protected:
virtual std::unique_ptr<RWebDisplayHandle> Display(const RWebDisplayArgs &args) = 0;
virtual bool IsActive() const { return true; }
virtual ~Creator() = default;
virtual bool IsSnapChromium() const { return false; }
};

class BrowserCreator : public Creator {
Expand All @@ -50,6 +52,7 @@ protected:
BrowserCreator(bool custom = true, const std::string &exec = "");
std::unique_ptr<RWebDisplayHandle> Display(const RWebDisplayArgs &args) override;
~BrowserCreator() override = default;
static FILE *TemporaryFile(TString &name, int use_home_dir = 0, const char *suffix = nullptr);
};

class SafariCreator : public BrowserCreator {
Expand All @@ -67,6 +70,7 @@ protected:
ChromeCreator(bool is_edge = false);
~ChromeCreator() override = default;
bool IsActive() const override { return !fProg.empty(); }
bool IsSnapChromium() const override { return fProg == "/snap/bin/chromium"; }
void ProcessGeometry(std::string &, const RWebDisplayArgs &) override;
std::string MakeProfile(std::string &exec, bool) override;
};
Expand Down Expand Up @@ -105,6 +109,9 @@ public:
/// resize web window - if possible
virtual bool Resize(int, int) { return false; }

/// remove file which was used to startup widget - if possible
virtual void RemoveStartupFiles() {}

static bool NeedHttpServer(const RWebDisplayArgs &args);

static std::unique_ptr<RWebDisplayHandle> Display(const RWebDisplayArgs &args);
Expand Down
117 changes: 68 additions & 49 deletions gui/webdisplay/src/RWebDisplayHandle.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,15 @@ class RWebBrowserHandle : public RWebDisplayHandle {
browser_process_id fPid;

public:
RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &tmpfile, const std::string &dump) :
RWebDisplayHandle(url), fTmpDir(tmpdir), fTmpFile(tmpfile)
RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &tmpfile,
const std::string &dump)
: RWebDisplayHandle(url), fTmpDir(tmpdir), fTmpFile(tmpfile)
{
SetContent(dump);
}

RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &tmpfile, browser_process_id pid)
RWebBrowserHandle(const std::string &url, const std::string &tmpdir, const std::string &tmpfile,
browser_process_id pid)
: RWebDisplayHandle(url), fTmpDir(tmpdir), fTmpFile(tmpfile), fHasPid(true), fPid(pid)
{
}
Expand All @@ -137,19 +139,30 @@ class RWebBrowserHandle : public RWebDisplayHandle {
{
#ifdef _MSC_VER
if (fHasPid)
gSystem->Exec(("taskkill /F /PID "s + std::to_string(fPid) + " >NUL 2>NUL").c_str());
std::string rmdir = "rmdir /S /Q ", rmfile = "del /F ";
gSystem->Exec(("taskkill /F /PID " s + std::to_string(fPid) + " >NUL 2>NUL").c_str());
std::string rmdir = "rmdir /S /Q ";
#else
if (fHasPid)
kill(fPid, SIGKILL);
std::string rmdir = "rm -rf ", rmfile = "rm -f ";
std::string rmdir = "rm -rf ";
#endif
if (!fTmpDir.empty())
gSystem->Exec((rmdir + fTmpDir).c_str());
if (!fTmpFile.empty())
gSystem->Exec((rmfile + fTmpFile).c_str());
RemoveStartupFiles();
}

void RemoveStartupFiles() override
{
#ifdef _MSC_VER
std::string rmfile = "del /F ";
#else
std::string rmfile = "rm -f ";
#endif
if (!fTmpFile.empty()) {
gSystem->Exec((rmfile + fTmpFile).c_str());
fTmpFile.clear();
}
}
};

} // namespace ROOT
Expand Down Expand Up @@ -218,6 +231,29 @@ void RWebDisplayHandle::BrowserCreator::TestProg(const std::string &nexttry, boo
#endif
}

//////////////////////////////////////////////////////////////////////////////////////////////////
/// Create temporary file for web display
/// Normally gSystem->TempFileName() method used to create file in default temporary directory
/// For snap chromium use of default temp directory is not always possible therefore one switches to home directory
/// But one checks if default temp directory modified and already points to /home folder

FILE *RWebDisplayHandle::BrowserCreator::TemporaryFile(TString &name, int use_home_dir, const char *suffix)
{
std::string dirname;
if (use_home_dir > 0) {
if (use_home_dir == 1) {
const char *tmp_dir = gSystem->TempDirectory();
if (tmp_dir && (strncmp(tmp_dir, "/home", 5) == 0))
use_home_dir = 0;
else if (!tmp_dir || (strncmp(tmp_dir, "/tmp", 4) == 0))
use_home_dir = 2;
}

if (use_home_dir > 1)
dirname = gSystem->GetHomeDirectory();
}
return gSystem->TempFileName(name, use_home_dir > 1 ? dirname.c_str() : nullptr, suffix);
}

static void DummyTimeOutHandler(int /* Sig */) {}

Expand Down Expand Up @@ -265,7 +301,7 @@ RWebDisplayHandle::BrowserCreator::Display(const RWebDisplayArgs &args)
if (((url.find("token=") != std::string::npos) || (url.find("key=") != std::string::npos)) && !args.IsBatchMode() && !args.IsHeadless()) {
TString filebase = "root_start_";

auto f = gSystem->TempFileName(filebase, nullptr, ".html");
auto f = TemporaryFile(filebase, IsSnapChromium() ? 1 : 0, ".html");

bool ferr = false;

Expand Down Expand Up @@ -587,6 +623,7 @@ RWebDisplayHandle::ChromeCreator::ChromeCreator(bool _edge) : BrowserCreator(tru
TestProg("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
#endif
#ifdef R__LINUX
TestProg("/snap/bin/chromium"); // test snap before to detect it properly
TestProg("/usr/bin/chromium");
TestProg("/usr/bin/chromium-browser");
TestProg("/usr/bin/chrome-browser");
Expand Down Expand Up @@ -1156,13 +1193,26 @@ bool RWebDisplayHandle::ProduceImages(const std::vector<std::string> &fnames, co
return false;
}

auto isChromeBased = (args.GetBrowserKind() == RWebDisplayArgs::kChrome) || (args.GetBrowserKind() == RWebDisplayArgs::kEdge),
auto isChrome = (args.GetBrowserKind() == RWebDisplayArgs::kChrome),
isChromeBased = isChrome || (args.GetBrowserKind() == RWebDisplayArgs::kEdge),
isFirefox = args.GetBrowserKind() == RWebDisplayArgs::kFirefox;

std::vector<std::string> draw_kinds;
bool use_browser_draw = false, can_optimize_json = false;
int use_home_dir = 0;
TString jsonkind;

// Some Chrome installation do not allow run html code from files, created in /tmp directory
// When during session such failures happened, force usage of home directory from the beginning
static int chrome_tmp_workaround = 0;

if (isChrome) {
use_home_dir = chrome_tmp_workaround;
auto &h1 = FindCreator("chrome", "ChromeCreator");
if (h1 && h1->IsActive() && h1->IsSnapChromium() && (use_home_dir == 0))
use_home_dir = 1;
}

if (fmts[0] == "s.png") {
if (!isChromeBased && !isFirefox) {
R__LOG_ERROR(WebGUILog()) << "Direct png image creation supported only by Chrome and Firefox browsers";
Expand Down Expand Up @@ -1248,10 +1298,11 @@ bool RWebDisplayHandle::ProduceImages(const std::vector<std::string> &fnames, co
filecont = std::regex_replace(filecont, std::regex("\\$draw_heights"), jsonh.Data());
filecont = std::regex_replace(filecont, std::regex("\\$draw_objects"), mains);

TString dump_name;
TString dump_name, html_name;

if (!use_browser_draw && (isChromeBased || isFirefox)) {
dump_name = "canvasdump";
FILE *df = gSystem->TempFileName(dump_name);
FILE *df = BrowserCreator::TemporaryFile(dump_name, use_home_dir);
if (!df) {
R__LOG_ERROR(WebGUILog()) << "Fail to create temporary file for dump-dom";
return false;
Expand All @@ -1260,58 +1311,26 @@ bool RWebDisplayHandle::ProduceImages(const std::vector<std::string> &fnames, co
fclose(df);
}

// When true, place HTML file into home directory
// Some Chrome installation do not allow run html code from files, created in /tmp directory
static bool chrome_tmp_workaround = false;

TString tmp_name, html_name;

try_again:

if ((args.GetBrowserKind() == RWebDisplayArgs::kCEF) || (args.GetBrowserKind() == RWebDisplayArgs::kQt6)) {
args.SetUrl(""s);
args.SetPageContent(filecont);

tmp_name.Clear();
html_name.Clear();

R__LOG_DEBUG(0, WebGUILog()) << "Using file content_len " << filecont.length() << " to produce batch images ";

} else {
tmp_name = "canvasbody";
FILE *hf = gSystem->TempFileName(tmp_name);
html_name = "canvasbody";
FILE *hf = BrowserCreator::TemporaryFile(html_name, use_home_dir, ".html");
if (!hf) {
R__LOG_ERROR(WebGUILog()) << "Fail to create temporary file for batch job";
return false;
}
fputs(filecont.c_str(), hf);
fclose(hf);

html_name = tmp_name + ".html";

if (chrome_tmp_workaround) {
std::string homedir = gSystem->GetHomeDirectory();
auto pos = html_name.Last('/');
if (pos == kNPOS) {
TRandom3 rnd;
rnd.SetSeed(0);
html_name = TString::Format("/random%d.html", rnd.Integer(1000000));
} else
html_name.Remove(0, pos);
html_name = homedir + html_name.Data();
gSystem->Unlink(html_name.Data());
gSystem->Unlink(tmp_name.Data());

std::ofstream ofs(html_name.Data(), std::ofstream::out);
ofs << filecont;
} else {
if (gSystem->Rename(tmp_name.Data(), html_name.Data()) != 0) {
R__LOG_ERROR(WebGUILog()) << "Fail to rename temp file " << tmp_name << " into " << html_name;
gSystem->Unlink(tmp_name.Data());
return false;
}
}

args.SetUrl("file://"s + gSystem->UnixPathName(html_name.Data()));
args.SetPageContent(""s);

Expand Down Expand Up @@ -1385,11 +1404,11 @@ bool RWebDisplayHandle::ProduceImages(const std::vector<std::string> &fnames, co
} else {
auto dumpcont = handle->GetContent();

if ((dumpcont.length() > 20) && (dumpcont.length() < 60) && !chrome_tmp_workaround && isChromeBased) {
if ((dumpcont.length() > 20) && (dumpcont.length() < 60) && (use_home_dir < 2) && isChrome) {
// chrome creates dummy html file with mostly no content
// problem running chrome from /tmp directory, lets try work from home directory

chrome_tmp_workaround = true;
R__LOG_INFO(WebGUILog()) << "Use home directory for running chrome in batch, set TMPDIR for preferable temp directory";
chrome_tmp_workaround = use_home_dir = 2;
goto try_again;
}

Expand Down
5 changes: 4 additions & 1 deletion gui/webdisplay/src/RWebWindow.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ bool RWebWindow::ProcessWS(THttpCallArg &arg)

for (auto &conn : fPendingConn)
if (_CanTrustIn(conn, key, ntry, is_remote, true /* test_first_time */))
return true;
return true;

return false;
}
Expand Down Expand Up @@ -860,6 +860,9 @@ bool RWebWindow::ProcessWS(THttpCallArg &arg)
// preserve key for longpoll or when with session key used for HMAC hash of messages
// conn->fKey.clear();
conn->ResetStamps();
// remove files which are required for startup
if (conn->fDisplayHandle)
conn->fDisplayHandle->RemoveStartupFiles();
if (conn->fWasFirst)
fConn.emplace(fConn.begin(), conn);
else
Expand Down
Loading