Logo ROOT  
Reference Guide
gui_handler.cxx
Go to the documentation of this file.
1/// \file gui_handler.cxx
2// Author: Sergey Linev <S.Linev@gsi.de>
3// Date: 2017-06-29
4// Warning: This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome!
5
6/*************************************************************************
7 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14#if !defined(_MSC_VER)
15#pragma GCC diagnostic ignored "-Wunused-parameter"
16#pragma GCC diagnostic ignored "-Wshadow"
17#endif
18
19#include "gui_handler.h"
20#include "simple_app.h"
21
22#include <sstream>
23#include <string>
24
25#include "include/base/cef_bind.h"
26#include "include/cef_app.h"
27#include "include/cef_version.h"
28#include "include/views/cef_browser_view.h"
29#include "include/views/cef_window.h"
30#include "include/wrapper/cef_closure_task.h"
31#include "include/wrapper/cef_helpers.h"
32#include "include/cef_parser.h"
33#include "include/wrapper/cef_stream_resource_handler.h"
34
35#include "TEnv.h"
36#include "TUrl.h"
37#include "THttpServer.h"
38#include "THttpCallArg.h"
39#include "TSystem.h"
40#include "TBase64.h"
41#include <ROOT/RLogger.hxx>
42
43
45{
46 static ROOT::Experimental::RLogChannel sChannel("ROOT.CefWebDisplay");
47 return sChannel;
48}
49
50
51GuiHandler::GuiHandler(bool use_views) : fUseViews(use_views), is_closing_(false)
52{
53 fConsole = gEnv->GetValue("WebGui.Console", (int)0);
54
55 // see https://bitbucket.org/chromiumembedded/cef-project/src/master/examples/resource_manager/?at=master for demo
56 // one probably can avoid to use scheme handler and just redirect requests
57 fResourceManager = new CefResourceManager();
58}
59
60void GuiHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString &title)
61{
62 CEF_REQUIRE_UI_THREAD();
63
64 if (fUseViews) {
65 // Set the title of the window using the Views framework.
66 CefRefPtr<CefBrowserView> browser_view = CefBrowserView::GetForBrowser(browser);
67 if (browser_view) {
68 CefRefPtr<CefWindow> window = browser_view->GetWindow();
69 if (window) window->SetTitle(title);
70 }
71 } else {
72 // Set the title of the window using platform APIs.
73 PlatformTitleChange(browser, title);
74 }
75}
76
77void GuiHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
78{
79 CEF_REQUIRE_UI_THREAD();
80
81 // Add to the list of existing browsers.
82 fBrowserList.emplace_back(browser);
83}
84
85bool GuiHandler::DoClose(CefRefPtr<CefBrowser> browser)
86{
87 CEF_REQUIRE_UI_THREAD();
88
89 // Closing the main window requires special handling. See the DoClose()
90 // documentation in the CEF header for a detailed description of this
91 // process.
92 if (fBrowserList.size() == 1) {
93 // Set a flag to indicate that the window close should be allowed.
94 is_closing_ = true;
95 }
96
97 // Allow the close. For windowed browsers this will result in the OS close
98 // event being sent.
99 return false;
100}
101
102void GuiHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
103{
104 CEF_REQUIRE_UI_THREAD();
105
106 // Remove from the list of existing browsers.
107 auto bit = fBrowserList.begin();
108 for (; bit != fBrowserList.end(); ++bit) {
109 if ((*bit)->IsSame(browser)) {
110 fBrowserList.erase(bit);
111 break;
112 }
113 }
114
115 if (fBrowserList.empty()) {
116
117 // All browser windows have closed. Quit the application message loop.
118
119 CefQuitMessageLoop();
120 }
121}
122
123// Returns a data: URI with the specified contents.
124std::string GuiHandler::GetDataURI(const std::string& data, const std::string& mime_type)
125{
126 return "data:" + mime_type + ";base64," +
127 CefURIEncode(CefBase64Encode(data.data(), data.size()), false)
128 .ToString();
129}
130
131
132void GuiHandler::OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode,
133 const CefString &errorText, const CefString &failedUrl)
134{
135 CEF_REQUIRE_UI_THREAD();
136
137 // Don't display an error for downloaded files.
138 if (errorCode == ERR_ABORTED)
139 return;
140
141 // Display a load error message.
142 std::stringstream ss;
143 ss << "<html><body bgcolor=\"white\">"
144 "<h2>Failed to load URL "
145 << failedUrl.ToString().substr(0,100) << " with error " << errorText.ToString() << " (" << errorCode
146 << ").</h2></body></html>";
147 // frame->LoadURL(GetDataURI(ss.str(), "text/html"));
148
149 printf("Fail to load URL %s\n", failedUrl.ToString().substr(0,100).c_str());
150}
151
152void GuiHandler::CloseAllBrowsers(bool force_close)
153{
154 if (!CefCurrentlyOn(TID_UI)) {
155 // Execute on the UI thread.
156#if CEF_VERSION_MAJOR < 95
157 CefPostTask(TID_UI, base::Bind(&GuiHandler::CloseAllBrowsers, this, force_close));
158#else
159 CefPostTask(TID_UI, base::BindOnce(&GuiHandler::CloseAllBrowsers, this, force_close));
160#endif
161 return;
162 }
163
164 if (fBrowserList.empty())
165 return;
166
167 for (auto &br : fBrowserList)
168 br->GetHost()->CloseBrowser(force_close);
169}
170
171bool GuiHandler::OnConsoleMessage(CefRefPtr<CefBrowser> browser,
172 cef_log_severity_t level,
173 const CefString &message, const CefString &source,
174 int line)
175{
176 std::string src = source.ToString().substr(0,100);
177
178 switch (level) {
179 case LOGSEVERITY_WARNING:
180 if (fConsole > -1)
181 R__LOG_WARNING(CefWebDisplayLog()) << Form("CEF: %s:%d: %s", src.c_str(), line, message.ToString().c_str());
182 break;
183 case LOGSEVERITY_ERROR:
184 if (fConsole > -2)
185 R__LOG_ERROR(CefWebDisplayLog()) << Form("CEF: %s:%d: %s", src.c_str(), line, message.ToString().c_str());
186 break;
187 default:
188 if (fConsole > 0)
189 R__LOG_DEBUG(0, CefWebDisplayLog()) << Form("CEF: %s:%d: %s", src.c_str(), line, message.ToString().c_str());
190 break;
191 }
192
193 return true;
194}
195
197 CefRefPtr<CefBrowser> browser,
198 CefRefPtr<CefFrame> frame,
199 CefRefPtr<CefRequest> request,
200 CefRefPtr<CefResourceLoadCallBack> callback) {
201 CEF_REQUIRE_IO_THREAD();
202
203 // std::string url = request->GetURL().ToString();
204 // printf("OnBeforeResourceLoad url %s\n", url.c_str());
205
206 return fResourceManager->OnBeforeResourceLoad(browser, frame, request,
207 callback);
208}
209
210
211
213protected:
214
215 CefRefPtr<CefCallback> fCallBack{nullptr};
216
217public:
218 explicit TCefHttpCallArg() = default;
219
220 /** provide WS kind */
221 const char *GetWSKind() const override { return "longpoll"; }
222
223 /** provide WS platform */
224 const char *GetWSPlatform() const override { return "cef3"; }
225
226 void AssignCallback(CefRefPtr<CefCallback> cb) { fCallBack = cb; }
227
228 // this is callback from HTTP server
229 void HttpReplied() override
230 {
231 if (IsFile()) {
232 // send file
233 std::string file_content = THttpServer::ReadFileContent((const char *)GetContent());
234 SetContent(std::move(file_content));
235 }
236
237 fCallBack->Continue(); // we have result and can continue with processing
238 }
239};
240
241
243public:
244 // QWebEngineUrlRequestJob *fRequest;
245
247 std::shared_ptr<TCefHttpCallArg> fArg;
248
250
251 explicit TGuiResourceHandler(THttpServer *serv, bool dummy = false)
252 {
253 fServer = serv;
254
255 if (!dummy)
256 fArg = std::make_shared<TCefHttpCallArg>();
257 }
258
260
261 void Cancel() override { CEF_REQUIRE_IO_THREAD(); }
262
263 bool ProcessRequest(CefRefPtr<CefRequest> request, CefRefPtr<CefCallback> callback) override
264 {
265 CEF_REQUIRE_IO_THREAD();
266
267 if (fArg) {
268 fArg->AssignCallback(callback);
270 } else {
271 callback->Continue();
272 }
273
274 return true;
275 }
276
277 void GetResponseHeaders(CefRefPtr<CefResponse> response, int64 &response_length, CefString &redirectUrl) override
278 {
279 CEF_REQUIRE_IO_THREAD();
280
281 if (!fArg || fArg->Is404()) {
282 response->SetMimeType("text/html");
283 response->SetStatus(404);
284 response_length = 0;
285 } else {
286 response->SetMimeType(fArg->GetContentType());
287 response->SetStatus(200);
288 response_length = fArg->GetContentLength();
289
290 if (fArg->NumHeader() > 0) {
291 // printf("******* Response with extra headers\n");
292 CefResponse::HeaderMap headers;
293 for (Int_t n = 0; n < fArg->NumHeader(); ++n) {
294 TString name = fArg->GetHeaderName(n);
295 TString value = fArg->GetHeader(name.Data());
296 headers.emplace(CefString(name.Data()), CefString(value.Data()));
297 // printf(" header %s %s\n", name.Data(), value.Data());
298 }
299 response->SetHeaderMap(headers);
300 }
301// if (strstr(fArg->GetQuery(),"connection="))
302// printf("Reply %s %s %s len: %d %s\n", fArg->GetPathName(), fArg->GetFileName(), fArg->GetQuery(),
303// fArg->GetContentLength(), (const char *) fArg->GetContent() );
304 }
305 // DCHECK(!fArg->Is404());
306 }
307
308 bool ReadResponse(void *data_out, int bytes_to_read, int &bytes_read, CefRefPtr<CefCallback> callback) override
309 {
310 CEF_REQUIRE_IO_THREAD();
311
312 if (!fArg) return false;
313
314 bytes_read = 0;
315
316 if (fTransferOffset < fArg->GetContentLength()) {
317 char *data_ = (char *)fArg->GetContent();
318 // Copy the next block of data into the buffer.
319 int transfer_size = fArg->GetContentLength() - fTransferOffset;
320 if (transfer_size > bytes_to_read)
321 transfer_size = bytes_to_read;
322 memcpy(data_out, data_ + fTransferOffset, transfer_size);
323 fTransferOffset += transfer_size;
324
325 bytes_read = transfer_size;
326 }
327
328 // if content fully copied - can release reference, object will be cleaned up
329 if (fTransferOffset >= fArg->GetContentLength())
330 fArg.reset();
331
332 return bytes_read > 0;
333 }
334
337};
338
339
340CefRefPtr<CefResourceHandler> GuiHandler::GetResourceHandler(
341 CefRefPtr<CefBrowser> browser,
342 CefRefPtr<CefFrame> frame,
343 CefRefPtr<CefRequest> request) {
344 CEF_REQUIRE_IO_THREAD();
345
346 std::string addr = request->GetURL().ToString();
347 std::string prefix = "http://rootserver.local";
348
349 if (addr.compare(0, prefix.length(), prefix) != 0)
350 return fResourceManager->GetResourceHandler(browser, frame, request);
351
352 int indx = std::stoi(addr.substr(prefix.length(), addr.find("/", prefix.length()) - prefix.length()));
353
354 if ((indx < 0) || (indx >= (int) fServers.size()) || !fServers[indx]) {
355 R__LOG_ERROR(CefWebDisplayLog()) << "No THttpServer with index " << indx;
356 return nullptr;
357 }
358
359 THttpServer *serv = fServers[indx];
360 if (serv->IsZombie()) {
361 fServers[indx] = nullptr;
362 R__LOG_ERROR(CefWebDisplayLog()) << "THttpServer with index " << indx << " is zombie now";
363 return nullptr;
364 }
365
366 TUrl url(addr.c_str());
367
368 const char *inp_path = url.GetFile();
369
370 TString inp_query = url.GetOptions();
371
372 TString fname;
373
374 if (serv->IsFileRequested(inp_path, fname)) {
375 // process file - no need for special requests handling
376
377 // when file not exists - return nullptr
378 if (gSystem->AccessPathName(fname.Data()))
379 return new TGuiResourceHandler(serv, true);
380
381 const char *mime = THttpServer::GetMimeType(fname.Data());
382
383 CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForFile(fname.Data());
384
385 // Constructor for HTTP status code 200 and no custom response headers.
386 // There’s also a version of the constructor for custom status code and response headers.
387 return new CefStreamResourceHandler(mime, stream);
388 }
389
390 std::string inp_method = request->GetMethod().ToString();
391
392 // printf("REQUEST METHOD %s\n", inp_method.c_str());
393
394 TGuiResourceHandler *handler = new TGuiResourceHandler(serv);
395 handler->fArg->SetMethod(inp_method.c_str());
396 handler->fArg->SetPathAndFileName(inp_path);
397 handler->fArg->SetTopName("webgui");
398
399 if (inp_method == "POST") {
400
401 CefRefPtr< CefPostData > post_data = request->GetPostData();
402
403 if (!post_data) {
404 R__LOG_ERROR(CefWebDisplayLog()) << "FATAL - NO POST DATA in CEF HANDLER!!!";
405 exit(1);
406 } else {
407 CefPostData::ElementVector elements;
408 post_data->GetElements(elements);
409 size_t sz = 0, off = 0;
410 for (unsigned n = 0; n < elements.size(); ++n)
411 sz += elements[n]->GetBytesCount();
412 std::string data;
413 data.resize(sz);
414
415 for (unsigned n = 0; n < elements.size(); ++n) {
416 sz = elements[n]->GetBytes(elements[n]->GetBytesCount(), (char *)data.data() + off);
417 off += sz;
418 }
419 handler->fArg->SetPostData(std::move(data));
420 }
421 } else if (inp_query.Index("&post=") != kNPOS) {
422 Int_t pos = inp_query.Index("&post=");
423 TString buf = TBase64::Decode(inp_query.Data() + pos + 6);
424 handler->fArg->SetPostData(std::string(buf.Data()));
425 inp_query.Resize(pos);
426 }
427
428 handler->fArg->SetQuery(inp_query.Data());
429
430 // just return handler
431 return handler;
432}
433
434///////////////////////////////////////////////////////////////////////////////////////////////////
435/// Generate URL for batch page
436/// Uses file:/// prefix to let access JSROOT scripts placed on file system
437/// Register provider for that page in resource manager
438
439std::string GuiHandler::AddBatchPage(const std::string &cont)
440{
441 std::string url = "file:///batch_page";
442 url.append(std::to_string(fBatchPageCount++));
443 url.append(".html");
444
445 fResourceManager->AddContentProvider(url, cont, "text/html", 0, std::string());
446 return url;
447}
448
449///////////////////////////////////////////////////////////////////////////////////////////////////
450/// Generate URL for RWebWindow page
451/// Register server instance and assign it with the index
452/// Produced URL only works inside CEF and does not represent real HTTP address
453
454std::string GuiHandler::MakePageUrl(THttpServer *serv, const std::string &addr)
455{
456 unsigned indx = 0;
457 while ((indx < fServers.size()) && (fServers[indx] != serv)) indx++;
458 if (indx >= fServers.size()) {
459 fServers.emplace_back(serv);
460 indx = fServers.size() - 1;
461 }
462
463 return std::string("http://rootserver.local") + std::to_string(indx) + addr;
464}
465
#define R__LOG_WARNING(...)
Definition: RLogger.hxx:363
#define R__LOG_ERROR(...)
Definition: RLogger.hxx:362
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition: RLogger.hxx:365
const Ssiz_t kNPOS
Definition: RtypesCore.h:124
R__EXTERN TEnv * gEnv
Definition: TEnv.h:170
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
char name[80]
Definition: TGX11.cxx:110
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition: TString.cxx:2447
R__EXTERN TSystem * gSystem
Definition: TSystem.h:559
const char * mime_type
Definition: civetweb.c:8026
CefRefPtr< CefResourceManager > fResourceManager
Definition: gui_handler.h:131
bool is_closing_
Definition: gui_handler.h:60
int fConsole
! console parameter, assigned via WebGui.Console rootrc parameter
Definition: gui_handler.h:54
GuiHandler(bool use_views=false)
Definition: gui_handler.cxx:51
std::vector< THttpServer * > fServers
Definition: gui_handler.h:58
virtual void OnBeforeClose(CefRefPtr< CefBrowser > browser) override
virtual CefRefPtr< CefResourceHandler > GetResourceHandler(CefRefPtr< CefBrowser > browser, CefRefPtr< CefFrame > frame, CefRefPtr< CefRequest > request) override
virtual bool DoClose(CefRefPtr< CefBrowser > browser) override
Definition: gui_handler.cxx:85
BrowserList fBrowserList
Definition: gui_handler.h:56
void CloseAllBrowsers(bool force_close)
int fBatchPageCount
Definition: gui_handler.h:132
void PlatformTitleChange(CefRefPtr< CefBrowser > browser, const CefString &title)
virtual void OnLoadError(CefRefPtr< CefBrowser > browser, CefRefPtr< CefFrame > frame, ErrorCode errorCode, const CefString &errorText, const CefString &failedUrl) override
static std::string GetDataURI(const std::string &data, const std::string &mime_type)
bool fUseViews
! if view framework is used, required for true headless mode
Definition: gui_handler.h:53
std::string AddBatchPage(const std::string &cont)
Generate URL for batch page Uses file:/// prefix to let access JSROOT scripts placed on file system R...
std::string MakePageUrl(THttpServer *serv, const std::string &addr)
Generate URL for RWebWindow page Register server instance and assign it with the index Produced URL o...
virtual bool OnConsoleMessage(CefRefPtr< CefBrowser > browser, cef_log_severity_t level, const CefString &message, const CefString &source, int line) override
virtual void OnTitleChange(CefRefPtr< CefBrowser > browser, const CefString &title) override
Definition: gui_handler.cxx:60
virtual void OnAfterCreated(CefRefPtr< CefBrowser > browser) override
Definition: gui_handler.cxx:77
virtual cef_return_value_t OnBeforeResourceLoad(CefRefPtr< CefBrowser > browser, CefRefPtr< CefFrame > frame, CefRefPtr< CefRequest > request, CefRefPtr< CefResourceLoadCallBack > callback) override
A log configuration for a channel, e.g.
Definition: RLogger.hxx:101
static TString Decode(const char *data)
Decode a base64 string date into a generic TString.
Definition: TBase64.cxx:131
void AssignCallback(CefRefPtr< CefCallback > cb)
void HttpReplied() override
virtual method to inform object that http request is processed Normally condition is notified and wai...
const char * GetWSPlatform() const override
provide WS platform
CefRefPtr< CefCallback > fCallBack
const char * GetWSKind() const override
provide WS kind
TCefHttpCallArg()=default
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition: TEnv.cxx:491
bool ProcessRequest(CefRefPtr< CefRequest > request, CefRefPtr< CefCallback > callback) override
DISALLOW_COPY_AND_ASSIGN(TGuiResourceHandler)
std::shared_ptr< TCefHttpCallArg > fArg
IMPLEMENT_REFCOUNTING(TGuiResourceHandler)
bool ReadResponse(void *data_out, int bytes_to_read, int &bytes_read, CefRefPtr< CefCallback > callback) override
virtual ~TGuiResourceHandler()
TGuiResourceHandler(THttpServer *serv, bool dummy=false)
THttpServer * fServer
void GetResponseHeaders(CefRefPtr< CefResponse > response, int64 &response_length, CefString &redirectUrl) override
void Cancel() override
const void * GetContent() const
Definition: THttpCallArg.h:240
void SetContent(const char *cont)
Set content as text.
Bool_t IsFile() const
Definition: THttpCallArg.h:232
Bool_t IsFileRequested(const char *uri, TString &res) const
Check if file is requested, thread safe.
Bool_t SubmitHttp(std::shared_ptr< THttpCallArg > arg, Bool_t can_run_immediately=kFALSE)
Submit HTTP request.
static char * ReadFileContent(const char *filename, Int_t &len)
Reads content of file from the disk.
static const char * GetMimeType(const char *path)
Guess mime type base on file extension.
R__ALWAYS_INLINE Bool_t IsZombie() const
Definition: TObject.h:149
Basic string class.
Definition: TString.h:136
const char * Data() const
Definition: TString.h:369
void Resize(Ssiz_t n)
Resize the string. Truncate or add blanks as necessary.
Definition: TString.cxx:1120
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:639
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
This class represents a WWW compatible URL.
Definition: TUrl.h:33
const char * GetFile() const
Definition: TUrl.h:69
const char * GetOptions() const
Definition: TUrl.h:71
TLine * line
ROOT::Experimental::RLogChannel & CefWebDisplayLog()
Definition: gui_handler.cxx:44
const Int_t n
Definition: legend1.C:16