From 75fd7231e05be6a7fcd76cd2c00f9eda0bef2f28 Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Fri, 28 Dec 2018 12:31:57 +0100 Subject: [PATCH] Support QWebEngine BrowserView, QWebkit still suppported through cmake option --- CMakeLists.txt | 13 ++- src/Mod/Web/CMakeLists.txt | 2 +- src/Mod/Web/Gui/BrowserView.cpp | 200 ++++++++++++++++++++++++++++---- src/Mod/Web/Gui/BrowserView.h | 39 ++++++- src/Mod/Web/Gui/CMakeLists.txt | 19 ++- 5 files changed, 240 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74e9692a7f..d519c9d6e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,7 @@ if (BUILD_QT5) if (FREECAD_USE_QTOPENGL_WIDGET) set(HAVE_QT5_OPENGL 1) endif() + OPTION(FREECAD_USE_QWEBKIT "Force use QWebKit instead of QWebEngine." OFF) endif() configure_file(${CMAKE_SOURCE_DIR}/src/QtOpenGL.h.cmake ${CMAKE_BINARY_DIR}/src/QtOpenGL.h) @@ -872,7 +873,17 @@ endif() find_package(Qt5Network) find_package(Qt5Concurrent) if (BUILD_WEB) - find_package(Qt5WebKitWidgets) + if (FREECAD_USE_QWEBKIT) + find_package(Qt5WebKitWidgets) + else() + find_package(Qt5WebEngineWidgets) + if (Qt5WebEngineWidgets_VERSION VERSION_LESS 5.7.0) + message(WARNING "** Minimum supported Qt5WebEngine version is 5.70!\n" + "-- Trying with QWebkit, force with FREECAD_USE_QWEBKIT=On") + + find_package(Qt5WebKitWidgets) + endif() + endif() endif() endif(BUILD_GUI) diff --git a/src/Mod/Web/CMakeLists.txt b/src/Mod/Web/CMakeLists.txt index 29f1b805b4..0a71c7c987 100644 --- a/src/Mod/Web/CMakeLists.txt +++ b/src/Mod/Web/CMakeLists.txt @@ -7,7 +7,7 @@ set(Web_Scripts ) if(BUILD_GUI) - if(Qt5WebKitWidgets_FOUND OR QT_QTWEBKIT_FOUND) + if(Qt5WebEngineWidgets_FOUND OR Qt5WebKitWidgets_FOUND OR QT_QTWEBKIT_FOUND) add_subdirectory(Gui) list (APPEND Web_Scripts InitGui.py) endif() diff --git a/src/Mod/Web/Gui/BrowserView.cpp b/src/Mod/Web/Gui/BrowserView.cpp index c4c885088a..851d05a92c 100644 --- a/src/Mod/Web/Gui/BrowserView.cpp +++ b/src/Mod/Web/Gui/BrowserView.cpp @@ -36,11 +36,6 @@ # include # include # include -# if QT_VERSION >= 0x040400 -# include -# include -# include -# endif # include # include # include @@ -52,6 +47,26 @@ # include # include # include +# include +#endif + + +#if QT_VERSION >= 0x050700 and defined(QTWEBENGINE) +#include +#include +#include +#include +#include +#include +#include +#define QWEBVIEW QWebEngineView +#define QWEBPAGE QWebEnginePage +#elif QT_VERSION >= 0x040400 and defined(QTWEBKIT) +#include +#include +#include +#define QWEBVIEW QWebView +#define QWEBPAGE QWebPage #endif #include "BrowserView.h" @@ -62,6 +77,7 @@ #include #include #include +#include #include #include @@ -71,6 +87,34 @@ using namespace WebGui; using namespace Gui; namespace WebGui { +enum WebAction { + OpenLink = 0xff +}; + +#ifdef QTWEBENGINE +class WebEngineUrlRequestInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + WebEngineUrlRequestInterceptor(BrowserView *parent) : + QWebEngineUrlRequestInterceptor(parent), + m_parent(parent) + { + } + + void interceptRequest(QWebEngineUrlRequestInfo &info) + { + if (info.navigationType() == QWebEngineUrlRequestInfo::NavigationTypeLink) { + bool blockRequest = false; + m_parent->acceptRequestUrl(info.requestUrl(), blockRequest); + info.block(blockRequest); + } + } + +private: + BrowserView *m_parent; +}; +#endif + class BrowserViewPy : public Py::PythonExtension { public: @@ -137,7 +181,7 @@ Py::Object BrowserViewPy::setHtml(const Py::Tuple& args) */ WebView::WebView(QWidget *parent) - : QWebView(parent) + : QWEBVIEW(parent) { // Increase html font size for high DPI displays QRect mainScreenSize = QApplication::desktop()->screenGeometry(); @@ -146,6 +190,17 @@ WebView::WebView(QWidget *parent) } } +#ifdef QTWEBENGINE +// implement a custom method using font minimum size +void WebView::setTextSizeMultiplier(qreal factor) +{ + QWebEngineSettings *sett = settings(); + int fontSize = sett->fontSize(QWebEngineSettings::MinimumFontSize); + fontSize = static_cast(fontSize * factor); + sett->setFontSize(QWebEngineSettings::MinimumFontSize, fontSize); +} +#else // QTWEBKIT + void WebView::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::MidButton) { @@ -155,8 +210,9 @@ void WebView::mousePressEvent(QMouseEvent *event) return; } } - QWebView::mousePressEvent(event); + QWEBVIEW::mousePressEvent(event); } +#endif void WebView::wheelEvent(QWheelEvent *event) { @@ -166,55 +222,88 @@ void WebView::wheelEvent(QWheelEvent *event) event->accept(); return; } - QWebView::wheelEvent(event); + QWEBVIEW::wheelEvent(event); } void WebView::contextMenuEvent(QContextMenuEvent *event) { +#ifdef QTWEBENGINE + const QWebEngineContextMenuData r = page()->contextMenuData(); +#else QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos()); +#endif if (!r.linkUrl().isEmpty()) { QMenu menu(this); - menu.addAction(pageAction(QWebPage::OpenLink)); + QWEBPAGE::WebAction openLink = static_cast(WebAction::OpenLink); // building a custom signal for external browser action - QSignalMapper* signalMapper = new QSignalMapper (this); + QSignalMapper* signalMapper = new QSignalMapper (&menu); signalMapper->setProperty("url", QVariant(r.linkUrl())); connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(triggerContextMenuAction(int))); QAction* extAction = menu.addAction(tr("Open in External Browser")); connect (extAction, SIGNAL(triggered()), signalMapper, SLOT(map())); - signalMapper->setMapping(extAction, QWebPage::OpenLink); + signalMapper->setMapping(extAction, openLink); QAction* newAction = menu.addAction(tr("Open in new window")); connect (newAction, SIGNAL(triggered()), signalMapper, SLOT(map())); - signalMapper->setMapping(newAction, QWebPage::OpenLinkInNewWindow); + signalMapper->setMapping(newAction, QWEBPAGE::OpenLinkInNewWindow); - menu.addAction(pageAction(QWebPage::DownloadLinkToDisk)); - menu.addAction(pageAction(QWebPage::CopyLinkToClipboard)); + menu.addAction(pageAction(QWEBPAGE::DownloadLinkToDisk)); + menu.addAction(pageAction(QWEBPAGE::CopyLinkToClipboard)); menu.exec(mapToGlobal(event->pos())); return; } - QWebView::contextMenuEvent(event); +#if QT_VERSION >= 0x050800 and defined(QTWEBENGINE) + else { // for view source + static bool firstRun = true; + if (firstRun) { + firstRun = false; + QMenu *menu = page()->createStandardContextMenu(); + QList actions = menu->actions(); + for(QAction *ac : actions) { + //qDebug() << ac->text() << QLatin1String(":") << ac->data().toString() << endl; + if (ac->data().toInt() == QWEBPAGE::ViewSource) { + QSignalMapper* signalMapper = new QSignalMapper (this); + signalMapper->setProperty("url", QVariant(r.linkUrl())); + signalMapper->setMapping(ac, QWEBPAGE::ViewSource); + connect(signalMapper, SIGNAL(mapped(int)), + this, SLOT(triggerContextMenuAction(int))); + connect (ac, SIGNAL(triggered()), signalMapper, SLOT(map())); + } + } + } + } +#endif + QWEBVIEW::contextMenuEvent(event); } void WebView::triggerContextMenuAction(int id) { QObject* s = sender(); QUrl url = s->property("url").toUrl(); + const QWEBPAGE::WebAction openLink = static_cast(WebAction::OpenLink); switch (id) { - case QWebPage::OpenLink: + case openLink: openLinkInExternalBrowser(url); break; - case QWebPage::OpenLinkInNewWindow: + case QWEBPAGE::OpenLinkInNewWindow: openLinkInNewWindow(url); break; +#ifdef QTWEBENGINE + case QWEBPAGE::ViewSource: + Q_EMIT viewSource(url); + break; +#endif default: break; } } +// ------------------------------------------------------------------------ + /* TRANSLATOR Gui::BrowserView */ /** @@ -230,7 +319,9 @@ BrowserView::BrowserView(QWidget* parent) view = new WebView(this); setCentralWidget(view); - view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); +#ifdef QTWEBKIT // QWebEngine doesn't support direct access to network + // nor rendering access + view->page()->setLinkDelegationPolicy(QWebView::DelegateAllLinks); view->page()->setForwardUnsupportedContent(true); // set our custom cookie manager @@ -246,6 +337,15 @@ BrowserView::BrowserView(QWidget* parent) QPalette palette = view->palette(); palette.setBrush(QPalette::Base, Qt::white); view->page()->setPalette(palette); +#else // QTWEBENGINE + QWebEngineProfile *profile = view->page()->profile(); + QString basePath = QLatin1String(App::Application::getUserAppDataDir().c_str()) + QLatin1String("webdata"); + profile->setPersistentStoragePath(basePath + QLatin1String("persistent")); + profile->setCachePath(basePath + QLatin1String("cache")); + + interceptLinks = new WebEngineUrlRequestInterceptor(this); + profile->setRequestInterceptor(interceptLinks); +#endif view->setAttribute(Qt::WA_OpaquePaintEvent, true); connect(view, SIGNAL(loadStarted()), @@ -254,26 +354,45 @@ BrowserView::BrowserView(QWidget* parent) this, SLOT(onLoadProgress(int))); connect(view, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool))); - connect(view, SIGNAL(linkClicked(const QUrl &)), - this, SLOT(onLinkClicked(const QUrl &))); connect(view, SIGNAL(openLinkInExternalBrowser(const QUrl &)), this, SLOT(onOpenLinkInExternalBrowser(const QUrl &))); connect(view, SIGNAL(openLinkInNewWindow(const QUrl &)), this, SLOT(onOpenLinkInNewWindow(const QUrl &))); +#ifdef QTWEBKIT + connect(view, SIGNAL(linkClicked(const QUrl &)), + this, SLOT(onLinkClicked(const QUrl &))); connect(view->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(onDownloadRequested(const QNetworkRequest &))); connect(view->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(onUnsupportedContent(QNetworkReply*))); + +#else // QTWEBENGINE + connect(view->page()->profile(), SIGNAL(downloadRequested(QWebEngineDownloadItem*)), + this, SLOT(onDownloadRequested(QWebEngineDownloadItem*))); + connect(view->page(), SIGNAL(iconChanged(const QIcon &)), + this, SLOT(setWindowIcon(const QIcon &))); + connect(view, SIGNAL(viewSource(const QUrl&)), + this, SLOT(onViewSource(const QUrl&))); +#endif } /** Destroys the object and frees any allocated resources */ BrowserView::~BrowserView() { +#ifdef QTWEBENGINE + delete interceptLinks; // cleanup not handled implicitly +#endif delete view; } +#ifdef QTWEBENGINE +void BrowserView::acceptRequestUrl(const QUrl &url, bool &blockRequest) +{ +#else void BrowserView::onLinkClicked (const QUrl & url) { + bool blockRequest; +#endif QString scheme = url.scheme(); QString host = url.host(); //QString username = url.userName(); @@ -295,17 +414,23 @@ void BrowserView::onLinkClicked (const QUrl & url) //QString fragment = url. fragment(); if (scheme==QString::fromLatin1("http") || scheme==QString::fromLatin1("https")) { +#ifdef QTWEBENGINE + blockRequest = false; +#else load(url); +#endif } // Small trick to force opening a link in an external browser: use exthttp or exthttps // Write your URL as exthttp://www.example.com else if (scheme==QString::fromLatin1("exthttp")) { exturl.setScheme(QString::fromLatin1("http")); QDesktopServices::openUrl(exturl); + blockRequest = true; } else if (scheme==QString::fromLatin1("exthttps")) { exturl.setScheme(QString::fromLatin1("https")); QDesktopServices::openUrl(exturl); + blockRequest = true; } // run scripts if not from somewhere else! if ((scheme.size() < 2 || scheme==QString::fromLatin1("file"))&& host.isEmpty()) { @@ -313,6 +438,7 @@ void BrowserView::onLinkClicked (const QUrl & url) if (fi.exists()) { QString ext = fi.completeSuffix(); if (ext == QString::fromLatin1("py")) { + blockRequest = true; try { if (!q.isEmpty()) { // encapsulate the value in quotes @@ -336,6 +462,7 @@ void BrowserView::onLinkClicked (const QUrl & url) else { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("File does not exist!"), fi.absoluteFilePath ()); + blockRequest = true; } } } @@ -346,6 +473,32 @@ bool BrowserView::chckHostAllowed(const QString& host) return host.isEmpty(); } +#ifdef QTWEBENGINE +void BrowserView::onDownloadRequested(QWebEngineDownloadItem *request) +{ + Gui::Dialog::DownloadManager::getInstance()->download(request->url()); +} + +void BrowserView::setWindowIcon(const QIcon &icon) +{ + Gui::MDIView::setWindowIcon(icon); +} + +void BrowserView::onViewSource(const QUrl &url) +{ + Q_UNUSED(url); + view->page()->toHtml([=](const QString &pageSource){ + QPlainTextEdit *editorWidget = new QPlainTextEdit {}; + App::TextDocument *txtDoc = new App::TextDocument; + TextDocumentEditorView *textDocView = new TextDocumentEditorView { + txtDoc, + editorWidget, getMainWindow()}; + editorWidget->setReadOnly(true); + editorWidget->setPlainText(pageSource); + getMainWindow()->addWindow(textDocView); + }); +} +#else void BrowserView::onDownloadRequested(const QNetworkRequest & request) { Gui::Dialog::DownloadManager::getInstance()->download(request); @@ -361,6 +514,7 @@ void BrowserView::onUnsupportedContent(QNetworkReply* reply) // then fails to load. Thus, we reload the previous url. view->reload(); } +#endif void BrowserView::load(const char* URL) { @@ -386,7 +540,9 @@ void BrowserView::load(const QUrl & url) setWindowTitle(url.host()); } +#ifdef QTWEBKIT setWindowIcon(QWebSettings::iconForUrl(url)); +#endif } void BrowserView::setHtml(const QString& HtmlCode,const QUrl & BaseUrl) @@ -394,8 +550,10 @@ void BrowserView::setHtml(const QString& HtmlCode,const QUrl & BaseUrl) if (isLoading) stop(); - view->setHtml(HtmlCode,BaseUrl); + view->setHtml(HtmlCode, BaseUrl); +#ifdef QTWEBKIT setWindowIcon(QWebSettings::iconForUrl(BaseUrl)); +#endif } void BrowserView::stop(void) diff --git a/src/Mod/Web/Gui/BrowserView.h b/src/Mod/Web/Gui/BrowserView.h index 4409888668..c05a426aa6 100644 --- a/src/Mod/Web/Gui/BrowserView.h +++ b/src/Mod/Web/Gui/BrowserView.h @@ -28,26 +28,40 @@ #include #include -#if QT_VERSION >= 0x040400 +#if QT_VERSION >= 0x050700 and defined(QTWEBENGINE) +#include +namespace WebGui { +class WebEngineUrlRequestInterceptor; +}; +#elif QT_VERSION >= 0x040400 and defined(QTWEBKIT) #include #endif -class QWebView; class QUrl; class QNetworkRequest; class QNetworkReply; namespace WebGui { +#ifdef QTWEBENGINE +class WebGuiExport WebView : public QWebEngineView +#else class WebGuiExport WebView : public QWebView +#endif { Q_OBJECT public: WebView(QWidget *parent = 0); +#ifdef QTWEBENGINE + // reimplement setTextSizeMultiplier + void setTextSizeMultiplier(qreal factor); +#endif protected: +#ifdef QTWEBKIT void mousePressEvent(QMouseEvent *event); +#endif void wheelEvent(QWheelEvent *event); void contextMenuEvent(QContextMenuEvent *event); @@ -55,8 +69,11 @@ private Q_SLOTS: void triggerContextMenuAction(int); Q_SIGNALS: - void openLinkInExternalBrowser(const QUrl& url); + void openLinkInExternalBrowser(const QUrl&); void openLinkInNewWindow(const QUrl&); +#ifdef QTWEBENGINE + void viewSource(const QUrl&); +#endif }; /** @@ -87,14 +104,25 @@ public: bool canClose(void); +#ifdef QTWEBENGINE +public Q_SLOTS: + void acceptRequestUrl(const QUrl &url, bool &blockRequest); +#endif + protected Q_SLOTS: void onLoadStarted(); void onLoadProgress(int); void onLoadFinished(bool); - void onLinkClicked (const QUrl& url); bool chckHostAllowed(const QString& host); +#ifdef QTWEBENGINE + void onDownloadRequested(QWebEngineDownloadItem *request); + void setWindowIcon(const QIcon &icon); + void onViewSource(const QUrl &url); +#else + void onLinkClicked (const QUrl& url); void onDownloadRequested(const QNetworkRequest& request); void onUnsupportedContent(QNetworkReply* reply); +#endif void onOpenLinkInExternalBrowser(const QUrl& url); void onOpenLinkInNewWindow(const QUrl&); @@ -102,6 +130,9 @@ private: WebView* view; bool isLoading; float textSizeMultiplier; +#ifdef QTWEBENGINE + WebEngineUrlRequestInterceptor *interceptLinks; +#endif }; } // namespace WebGui diff --git a/src/Mod/Web/Gui/CMakeLists.txt b/src/Mod/Web/Gui/CMakeLists.txt index 963acbd57c..273f331cbc 100644 --- a/src/Mod/Web/Gui/CMakeLists.txt +++ b/src/Mod/Web/Gui/CMakeLists.txt @@ -7,17 +7,24 @@ include_directories( ${XercesC_INCLUDE_DIRS} ) +if (Qt5WebEngineWidgets_FOUND) + add_definitions(-DQTWEBENGINE) +elseif(Qt5WebKitWidgets_FOUND or QT_QTWEBKIT_FOUND) # Qt5 and Qt4 + add_definitions(-DQTWEBKIT) +endif() + set(WebGui_LIBS FreeCADGui ) if(BUILD_QT5) - include_directories( - ${Qt5WebKitWidgets_INCLUDE_DIRS} - ) - list(APPEND WebGui_LIBS - ${Qt5WebKitWidgets_LIBRARIES} - ) + if(Qt5WebEngineWidgets_FOUND) + include_directories(${Qt5WebEngineWidgets_INCLUDE_DIRS}) + list(APPEND WebGui_LIBS ${Qt5WebEngineWidgets_LIBRARIES}) + else() + include_directories(${Qt5WebKitWidgets_INCLUDE_DIRS}) + list(APPEND WebGui_LIBS ${Qt5WebKitWidgets_LIBRARIES}) + endif() qt5_add_resources(Web_QRC_SRCS Resources/Web.qrc) else() qt4_add_resources(Web_QRC_SRCS Resources/Web.qrc)