From d155e971b5ece35c75efbd7572bcbd8515f25cf8 Mon Sep 17 00:00:00 2001
From: Sergey Linev <S.Linev@gsi.de>
Date: Fri, 15 Mar 2019 17:07:06 +0100
Subject: [PATCH] webui: ensure Qt5 works with custom scheme handle

Qt5 now requires that custom "rootscheme:" registered before Qt
application created. Therefore now there is no separated handlers for
different window - single handler created once. Works now with only
THttpServer instance (like in CEF), can be enhanced later for many of
them.
---
 gui/qt5webdisplay/rootqt5.cpp              | 61 ++++++++++------------
 gui/qt5webdisplay/rooturlschemehandler.cpp | 43 ++++++++-------
 gui/qt5webdisplay/rooturlschemehandler.h   | 18 +++----
 gui/qt5webdisplay/rootwebpage.cpp          |  8 +--
 gui/qt5webdisplay/rootwebpage.h            |  4 +-
 gui/qt5webdisplay/rootwebview.cpp          |  2 +-
 gui/qt5webdisplay/rootwebview.h            |  4 +-
 7 files changed, 66 insertions(+), 74 deletions(-)

diff --git a/gui/qt5webdisplay/rootqt5.cpp b/gui/qt5webdisplay/rootqt5.cpp
index 5eca8e0c368..43a2ed5f422 100644
--- a/gui/qt5webdisplay/rootqt5.cpp
+++ b/gui/qt5webdisplay/rootqt5.cpp
@@ -6,7 +6,7 @@
 /// is welcome!
 
 /*************************************************************************
- * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
+ * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
  * All rights reserved.                                                  *
  *                                                                       *
  * For the licensing terms see $ROOTSYS/LICENSE.                         *
@@ -19,6 +19,7 @@
 #include <QThread>
 #include <QWebEngineSettings>
 #include <QWebEngineProfile>
+#include <QWebEngineUrlScheme>
 
 #include "TROOT.h"
 #include "TApplication.h"
@@ -40,10 +41,11 @@
 class TQt5Timer : public TTimer {
 public:
    TQt5Timer(Long_t milliSec, Bool_t mode) : TTimer(milliSec, mode) {}
-   virtual void Timeout()
+
+   /// timeout handler
+   /// used to process all qt5 events in main ROOT thread
+   void Timeout() override
    {
-      // timeout handler
-      // used to process all qt5 events in main ROOT thread
       QApplication::sendPostedEvents();
       QApplication::processEvents();
    }
@@ -60,11 +62,18 @@ protected:
       int qargc{1};                 ///< arg counter
       char *qargv[10];              ///< arg values
       bool fInitEngine{false};      ///< does engine was initialized
-      TQt5Timer *fTimer{nullptr};   ///< timer to process ROOT events
+      std::unique_ptr<TQt5Timer> fTimer; ///< timer to process ROOT events
+      std::unique_ptr<RootUrlSchemeHandler> fHandler; ///< specialized handler
    public:
 
       Qt5Creator() = default;
 
+      virtual ~Qt5Creator()
+      {
+         if (fHandler)
+            QWebEngineProfile::defaultProfile()->removeUrlSchemeHandler(fHandler.get());
+      }
+
       std::unique_ptr<RWebDisplayHandle> Display(const RWebDisplayArgs &args) override
       {
          if (args.IsHeadless())
@@ -77,6 +86,12 @@ protected:
                return nullptr;
             }
 
+            QWebEngineUrlScheme scheme("rootscheme");
+            scheme.setSyntax(QWebEngineUrlScheme::Syntax::HostAndPort);
+            scheme.setDefaultPort(2345);
+            scheme.setFlags(QWebEngineUrlScheme::SecureScheme);
+            QWebEngineUrlScheme::registerScheme(scheme);
+
             qargv[0] = gApplication->Argv(0);
             qargv[1] = nullptr;
             qapp = new QApplication(qargc, qargv);
@@ -90,18 +105,21 @@ protected:
          if (!fTimer) {
             Int_t interval = gEnv->GetValue("WebGui.Qt5Timer", 1);
             if (interval > 0) {
-               fTimer = new TQt5Timer(interval, kTRUE);
+               fTimer = std::make_unique<TQt5Timer>(interval, kTRUE);
                fTimer->TurnOn();
             }
          }
 
-         std::unique_ptr<RootUrlSchemeHandler> handler;
          QString fullurl = QString(args.GetUrl().c_str());
 
          // if no server provided - normal HTTP will be allowed to use
          if (args.GetHttpServer()) {
-            handler = std::make_unique<RootUrlSchemeHandler>(args.GetHttpServer(), fCounter++);
-            fullurl = handler->MakeFullUrl(fullurl);
+            if (!fHandler) {
+               fHandler = std::make_unique<RootUrlSchemeHandler>();
+               QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("rootscheme", fHandler.get());
+            }
+
+            fullurl = fHandler->MakeFullUrl(args.GetHttpServer(), fullurl);
          }
 
          QWidget *qparent = (QWidget *) args.GetDriverData();
@@ -111,7 +129,7 @@ protected:
             fullurl.append(QString(args.GetUrlOpt().c_str()));
          }
 
-         auto handle = std::make_unique<RQt5WebDisplayHandle>(fullurl.toLatin1().constData(), handler);
+         auto handle = std::make_unique<RQt5WebDisplayHandle>(fullurl.toLatin1().constData());
 
          if (args.IsHeadless()) {
             RootWebPage *page = new RootWebPage();
@@ -128,26 +146,10 @@ protected:
          return handle;
       }
 
-      virtual ~Qt5Creator()
-      {
-         if (fTimer) {
-            fTimer->TurnOff();
-            delete fTimer;
-         }
-
-      }
    };
 
-   std::unique_ptr<RootUrlSchemeHandler> fHandler;
-
 public:
-   RQt5WebDisplayHandle(const std::string &url, std::unique_ptr<RootUrlSchemeHandler> &handler)
-      : RWebDisplayHandle(url)
-   {
-      std::swap(fHandler, handler);
-      if (fHandler)
-         QWebEngineProfile::defaultProfile()->installUrlSchemeHandler(QByteArray(fHandler->GetProtocol()), fHandler.get());
-   }
+   RQt5WebDisplayHandle(const std::string &url) : RWebDisplayHandle(url) {}
 
    static void AddCreator()
    {
@@ -156,11 +158,6 @@ public:
          GetMap().emplace("qt5", std::make_unique<Qt5Creator>());
    }
 
-   virtual ~RQt5WebDisplayHandle()
-   {
-      if (fHandler)
-         QWebEngineProfile::defaultProfile()->removeUrlSchemeHandler(fHandler.get());
-   }
 };
 
 struct RQt5CreatorReg {
diff --git a/gui/qt5webdisplay/rooturlschemehandler.cpp b/gui/qt5webdisplay/rooturlschemehandler.cpp
index 8871bf9240b..2296a5eb01f 100644
--- a/gui/qt5webdisplay/rooturlschemehandler.cpp
+++ b/gui/qt5webdisplay/rooturlschemehandler.cpp
@@ -1,4 +1,4 @@
-/// \file rootqt5.cpp
+/// \file rooturlschemehandler.cpp
 /// \ingroup WebUI
 /// \author Sergey Linev <S.Linev@gsi.de>
 /// \date 2017-06-29
@@ -6,7 +6,7 @@
 /// is welcome!
 
 /*************************************************************************
- * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
+ * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
  * All rights reserved.                                                  *
  *                                                                       *
  * For the licensing terms see $ROOTSYS/LICENSE.                         *
@@ -128,14 +128,27 @@ public:
 };
 
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////
+/// Returns fully qualified URL, required to open in QWindow
 
-RootUrlSchemeHandler::RootUrlSchemeHandler(THttpServer *server, int counter)
-   : QWebEngineUrlSchemeHandler(), fServer(server)
+QString RootUrlSchemeHandler::MakeFullUrl(THttpServer *serv, const QString &url)
 {
-   fProtocol = Form("roothandler%d", counter);
+   // TODO: provide support for many servers
+   fServer = serv;
+
+   QString res = "rootscheme://root.server1";
+   res.append(url);
+   if (url.indexOf("?") < 0)
+      res.append("?");
+   else
+      res.append("&");
+
+   // TODO: with should be solved different way - maybe via replacements in main HTML page
+   res.append("platform=qt5&ws=rawlongpoll");
+   return res;
 }
 
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /// Start processing of emulated HTTP request in WebEngine scheme handler
 /// Either one reads file or redirect request to THttpServer
@@ -154,6 +167,8 @@ void RootUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request)
    QString inp_query = url.query();
    QString inp_method = request->requestMethod();
 
+   // printf("REQUEST PATH:%s QUERY:%s\n", inp_path.toLatin1().data(), inp_query.toLatin1().data());
+
    auto arg = std::make_shared<TWebGuiCallArg>(request);
 
    TString fname;
@@ -181,19 +196,3 @@ void RootUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request)
    // can process immediately - function called in main thread
    fServer->SubmitHttp(arg, kTRUE);
 }
-
-/////////////////////////////////////////////////////////////////
-/// Returns fully qualified URL, required to open in QWindow
-
-QString RootUrlSchemeHandler::MakeFullUrl(const QString &url)
-{
-   QString res = fProtocol;
-   res.append(":");
-   res.append(url);
-   if (url.indexOf("?")<0)
-      res.append("?");
-   else
-      res.append("&");
-   res.append("platform=qt5&ws=rawlongpoll");
-   return res;
-}
diff --git a/gui/qt5webdisplay/rooturlschemehandler.h b/gui/qt5webdisplay/rooturlschemehandler.h
index 2ed536d6ff0..bee192907c9 100644
--- a/gui/qt5webdisplay/rooturlschemehandler.h
+++ b/gui/qt5webdisplay/rooturlschemehandler.h
@@ -1,4 +1,4 @@
-/// \file rootwebpage.h
+/// \file rooturlschemehandler.h
 /// \ingroup WebUI
 /// \author Sergey Linev <S.Linev@gsi.de>
 /// \date 2017-06-29
@@ -6,7 +6,7 @@
 /// is welcome!
 
 /*************************************************************************
- * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
+ * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
  * All rights reserved.                                                  *
  *                                                                       *
  * For the licensing terms see $ROOTSYS/LICENSE.                         *
@@ -33,7 +33,9 @@ public:
    void reset();
 
 public slots:
+
    void onRequestDeleted(QObject *obj);
+
 };
 
 // ===============================================================
@@ -43,20 +45,12 @@ class RootUrlSchemeHandler : public QWebEngineUrlSchemeHandler {
    Q_OBJECT
 protected:
 
-   QString fProtocol;
-
    THttpServer *fServer{nullptr}; ///< server instance which should handle requests
 
 public:
-   RootUrlSchemeHandler(THttpServer *server = nullptr, int counter = 0);
-
-   virtual ~RootUrlSchemeHandler() = default;
-
-   QByteArray GetProtocol() const { return QByteArray(fProtocol.toLatin1().constData(), fProtocol.length()); }
-
-   QString MakeFullUrl(const QString &url);
+   QString MakeFullUrl(THttpServer *serv, const QString &url);
 
-   virtual void requestStarted(QWebEngineUrlRequestJob *request);
+   void requestStarted(QWebEngineUrlRequestJob *request) override;
 };
 
 
diff --git a/gui/qt5webdisplay/rootwebpage.cpp b/gui/qt5webdisplay/rootwebpage.cpp
index 5334313ffef..dda988278b5 100644
--- a/gui/qt5webdisplay/rootwebpage.cpp
+++ b/gui/qt5webdisplay/rootwebpage.cpp
@@ -6,7 +6,7 @@
 /// is welcome!
 
 /*************************************************************************
- * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
+ * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
  * All rights reserved.                                                  *
  *                                                                       *
  * For the licensing terms see $ROOTSYS/LICENSE.                         *
@@ -24,10 +24,12 @@ void RootWebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel lvl, co
    switch (lvl) {
    case InfoMessageLevel:
       if (gDebug > 0)
-         R__DEBUG_HERE("Qt") << Form("%s:%d: %s", src.toLatin1().constData(), lineNumber, message.toLatin1().constData());
+         R__DEBUG_HERE("Qt") << Form("%s:%d: %s", src.toLatin1().constData(), lineNumber,
+                                     message.toLatin1().constData());
       break;
    case WarningMessageLevel:
-      R__WARNING_HERE("Qt") << Form("%s:%d: %s", src.toLatin1().constData(), lineNumber, message.toLatin1().constData());
+      R__WARNING_HERE("Qt") << Form("%s:%d: %s", src.toLatin1().constData(), lineNumber,
+                                    message.toLatin1().constData());
       break;
    case ErrorMessageLevel:
       R__ERROR_HERE("Qt") << Form("%s:%d: %s", src.toLatin1().constData(), lineNumber, message.toLatin1().constData());
diff --git a/gui/qt5webdisplay/rootwebpage.h b/gui/qt5webdisplay/rootwebpage.h
index cb74de94852..5ec512af586 100644
--- a/gui/qt5webdisplay/rootwebpage.h
+++ b/gui/qt5webdisplay/rootwebpage.h
@@ -6,7 +6,7 @@
 /// is welcome!
 
 /*************************************************************************
- * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
+ * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
  * All rights reserved.                                                  *
  *                                                                       *
  * For the licensing terms see $ROOTSYS/LICENSE.                         *
@@ -25,7 +25,7 @@ protected:
                                          int lineNumber, const QString &sourceID);
 
 public:
-   RootWebPage(QObject *parent = 0) : QWebEnginePage(parent) {}
+   RootWebPage(QObject *parent = nullptr) : QWebEnginePage(parent) {}
    virtual ~RootWebPage() = default;
 };
 
diff --git a/gui/qt5webdisplay/rootwebview.cpp b/gui/qt5webdisplay/rootwebview.cpp
index 662ed0966c0..8820266af62 100644
--- a/gui/qt5webdisplay/rootwebview.cpp
+++ b/gui/qt5webdisplay/rootwebview.cpp
@@ -6,7 +6,7 @@
 /// is welcome!
 
 /*************************************************************************
- * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
+ * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
  * All rights reserved.                                                  *
  *                                                                       *
  * For the licensing terms see $ROOTSYS/LICENSE.                         *
diff --git a/gui/qt5webdisplay/rootwebview.h b/gui/qt5webdisplay/rootwebview.h
index ff98a4843ff..90efc42668c 100644
--- a/gui/qt5webdisplay/rootwebview.h
+++ b/gui/qt5webdisplay/rootwebview.h
@@ -6,7 +6,7 @@
 /// is welcome!
 
 /*************************************************************************
- * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
+ * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
  * All rights reserved.                                                  *
  *                                                                       *
  * For the licensing terms see $ROOTSYS/LICENSE.                         *
@@ -39,7 +39,7 @@ signals:
    void drop(QDropEvent* event);
 
 public:
-   RootWebView(QWidget *parent = 0, unsigned width = 0, unsigned height = 0);
+   RootWebView(QWidget *parent = nullptr, unsigned width = 0, unsigned height = 0);
    virtual ~RootWebView() = default;
 
    virtual QSize  sizeHint() const;
-- 
GitLab