diff --git a/.appveyor.yml b/.appveyor.yml index eafee7cf..89c797b9 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,7 +7,7 @@ branches: - stable - /release\/.*/ image: - - Visual Studio 2015 + - Visual Studio 2017 configuration: Release platform: - x86 @@ -19,9 +19,9 @@ environment: init: - ps: |- if ($Env:PLATFORM -eq "x64") { - $Env:QT_ROOT = "C:\Qt\5.11\msvc2015_64" + $Env:QT_ROOT = "C:\Qt\5.12\msvc2017_64" } else { - $Env:QT_ROOT = "C:\Qt\5.11\msvc2015" + $Env:QT_ROOT = "C:\Qt\5.12\msvc2017" } $Env:Path += ";${Env:QT_ROOT}\bin;C:\Program Files\7-Zip" @@ -29,15 +29,9 @@ install: - ps: |- # Install vcpkg toolchain. $Env:VCPKG_ROOT = "c:\projects\vcpkg-export" - $vcpkgArchive = "vcpkg-export-20181014.4-${Env:PLATFORM}.7z" + $vcpkgArchive = "vcpkg-export-20200420.6-${Env:PLATFORM}.7z" appveyor DownloadFile "https://dl.bintray.com/zealdocs/windows-ci/$vcpkgArchive" 7z x $vcpkgArchive -o"${Env:VCPKG_ROOT}" - - # Install QtWebKit. - $qtWebKitArchiveBaseName = "qtwebkit-5.212.0_72cfbd7-qt5111-msvc2015-${Env:PLATFORM}" - appveyor Downloadfile "https://dl.bintray.com/zealdocs/windows-ci/$qtWebKitArchiveBaseName.zip" - 7z x "$qtWebKitArchiveBaseName.zip" - Get-ChildItem ".\$qtWebKitArchiveBaseName" | Copy -Destination ${Env:QT_ROOT} -Recurse -Force before_build: - ps: |- Write-Output "Source directory: ${Env:APPVEYOR_BUILD_FOLDER}" @@ -46,7 +40,7 @@ before_build: Set-Location -Path dist # Prepare .sln with CMake: - $cmakeGenerator = "Visual Studio 14 2015" + $cmakeGenerator = "Visual Studio 15 2017" if ($Env:PLATFORM -eq "x64") { $cmakeGenerator += " Win64" } diff --git a/.github/workflows/build-check.yml b/.github/workflows/build-check.yml index e38e04e0..e7ec38a6 100644 --- a/.github/workflows/build-check.yml +++ b/.github/workflows/build-check.yml @@ -27,11 +27,11 @@ jobs: cmake \ extra-cmake-modules \ libarchive-dev \ - libqt5webkit5-dev \ libqt5x11extras5-dev \ libsqlite3-dev \ libxcb-keysyms1-dev \ - qt5-default + qt5-default \ + qtwebengine5-dev - name: Configure run: | @@ -59,12 +59,12 @@ jobs: cmake extra-cmake-modules libappindicator-dev libarchive-dev - libqt5webkit5-dev libqt5x11extras5-dev libsqlite3-dev libxcb-keysyms1-dev ninja-build qtbase5-dev + qtwebengine5-dev - name: Upload AppImage uses: actions/upload-artifact@v2-preview diff --git a/README.md b/README.md index 2d6b075d..702c77ba 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ After installing Zeal, go to *Tools->Docsets*, select the ones you want, and cli ### Required dependencies * [CMake](https://cmake.org/). -* [Qt](https://www.qt.io/) version 5.9.5 or above. Required module: Qt WebKit Widgets. +* [Qt](https://www.qt.io/) version 5.9.5 or above. Required module: Qt WebEngine Widgets. * [libarchive](http://libarchive.org/). * [SQLite](https://sqlite.org/). * X11 platforms only: Qt X11 Extras and `xcb-util-keysyms`. diff --git a/pkg/appimage/appimage-amd64.yml b/pkg/appimage/appimage-amd64.yml index 455dc1d3..6c9a2fe4 100644 --- a/pkg/appimage/appimage-amd64.yml +++ b/pkg/appimage/appimage-amd64.yml @@ -40,7 +40,7 @@ AppDir: - libqt5x11extras5 - libqt5concurrent5 - libqt5webchannel5 - - libqt5webkit5 + - libqt5webengine5 - libqt5widgets5 - qtwayland5 # Other dependencies. diff --git a/src/app/resources/browser/404.html b/src/app/resources/browser/404.html index ff25e636..170877b3 100644 --- a/src/app/resources/browser/404.html +++ b/src/app/resources/browser/404.html @@ -61,6 +61,15 @@ + + + diff --git a/src/app/resources/browser/assets/css/darkmode.css b/src/app/resources/browser/assets/css/darkmode.css index 97a64f07..806bffd3 100644 --- a/src/app/resources/browser/assets/css/darkmode.css +++ b/src/app/resources/browser/assets/css/darkmode.css @@ -1,11 +1,11 @@ /* Dark mode (see #466). */ /* TODO: Check if we need -webkit-filter for old Qt WebKit. */ + html { - -webkit-filter: invert() hue-rotate(180deg) contrast(70%); - filter: invert() hue-rotate(180deg) contrast(70%); + background-color: #262626 !important; + filter: invert(1) hue-rotate(180deg) contrast(70%) !important; } html img { - -webkit-filter: invert() hue-rotate(180deg) contrast(120%); - filter: invert() hue-rotate(180deg) contrast(120%); + filter: invert(1) hue-rotate(180deg) contrast(120%) !important; } diff --git a/src/app/resources/browser/welcome.html b/src/app/resources/browser/welcome.html index 40216377..cc6a2d5d 100644 --- a/src/app/resources/browser/welcome.html +++ b/src/app/resources/browser/welcome.html @@ -73,8 +73,13 @@ + diff --git a/src/libs/browser/CMakeLists.txt b/src/libs/browser/CMakeLists.txt index e260a68b..4ebb34c5 100644 --- a/src/libs/browser/CMakeLists.txt +++ b/src/libs/browser/CMakeLists.txt @@ -1,11 +1,13 @@ add_library(Browser STATIC searchtoolbar.cpp + urlrequestinterceptor.cpp webbridge.cpp webcontrol.cpp + webpage.cpp webview.cpp ) target_link_libraries(Browser) -find_package(Qt5 COMPONENTS WebKitWidgets REQUIRED) -target_link_libraries(Browser Qt5::WebKitWidgets) +find_package(Qt5 COMPONENTS WebEngineWidgets WebChannel REQUIRED) +target_link_libraries(Browser Qt5::WebEngineWidgets Qt5::WebChannel) diff --git a/src/libs/browser/searchtoolbar.cpp b/src/libs/browser/searchtoolbar.cpp index 6c102ce4..af826610 100644 --- a/src/libs/browser/searchtoolbar.cpp +++ b/src/libs/browser/searchtoolbar.cpp @@ -29,12 +29,12 @@ #include #include #include -#include -#include +#include +#include using namespace Zeal::Browser; -SearchToolBar::SearchToolBar(QWebView *webView, QWidget *parent) +SearchToolBar::SearchToolBar(QWebEngineView *webView, QWidget *parent) : QWidget(parent) , m_webView(webView) { @@ -172,8 +172,8 @@ void SearchToolBar::findNext() return; } - QWebPage::FindFlags ff = QWebPage::FindWrapsAroundDocument; - ff.setFlag(QWebPage::FindCaseSensitively, m_matchCaseButton->isChecked()); + QWebEnginePage::FindFlags ff; + ff.setFlag(QWebEnginePage::FindCaseSensitively, m_matchCaseButton->isChecked()); m_webView->findText(m_lineEdit->text(), ff); } @@ -183,15 +183,15 @@ void SearchToolBar::findPrevious() return; } - QWebPage::FindFlags ff = QWebPage::FindWrapsAroundDocument; - ff.setFlag(QWebPage::FindCaseSensitively, m_matchCaseButton->isChecked()); - ff.setFlag(QWebPage::FindBackward); + QWebEnginePage::FindFlags ff; + ff.setFlag(QWebEnginePage::FindCaseSensitively, m_matchCaseButton->isChecked()); + ff.setFlag(QWebEnginePage::FindBackward); m_webView->findText(m_lineEdit->text(), ff); } void SearchToolBar::hideHighlight() { - m_webView->findText(QString(), QWebPage::HighlightAllOccurrences); + m_webView->findText(QString()); } void SearchToolBar::updateHighlight() @@ -199,8 +199,8 @@ void SearchToolBar::updateHighlight() hideHighlight(); if (m_highlightAllButton->isChecked()) { - QWebPage::FindFlags ff = QWebPage::HighlightAllOccurrences; - ff.setFlag(QWebPage::FindCaseSensitively, m_matchCaseButton->isChecked()); + QWebEnginePage::FindFlags ff; + ff.setFlag(QWebEnginePage::FindCaseSensitively, m_matchCaseButton->isChecked()); m_webView->findText(m_lineEdit->text(), ff); } } diff --git a/src/libs/browser/searchtoolbar.h b/src/libs/browser/searchtoolbar.h index 38e2cb78..917b88a8 100644 --- a/src/libs/browser/searchtoolbar.h +++ b/src/libs/browser/searchtoolbar.h @@ -27,7 +27,7 @@ class QLineEdit; class QToolButton; -class QWebView; +class QWebEngineView; namespace Zeal { namespace Browser { @@ -37,7 +37,7 @@ class SearchToolBar final : public QWidget Q_OBJECT Q_DISABLE_COPY(SearchToolBar) public: - explicit SearchToolBar(QWebView *webView, QWidget *parent = nullptr); + explicit SearchToolBar(QWebEngineView *webView, QWidget *parent = nullptr); void setText(const QString &text); void activate(); @@ -63,7 +63,7 @@ private: QToolButton *m_highlightAllButton = nullptr; QToolButton *m_matchCaseButton = nullptr; - QWebView *m_webView = nullptr; + QWebEngineView *m_webView = nullptr; }; } // namespace Browser diff --git a/src/libs/browser/urlrequestinterceptor.cpp b/src/libs/browser/urlrequestinterceptor.cpp new file mode 100644 index 00000000..f40d980d --- /dev/null +++ b/src/libs/browser/urlrequestinterceptor.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Oleg Shparber +** Copyright (C) 2019 Kay Gawlik +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#include "urlrequestinterceptor.h" + +#include +#include +#include + +using namespace Zeal::Browser; + +UrlRequestInterceptor::UrlRequestInterceptor(QObject *parent) + : QWebEngineUrlRequestInterceptor(parent) +{ +} + +void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) +{ + QUrl requestUrl = info.requestUrl(); + QUrl firstPartyUrl = info.firstPartyUrl(); + + if (!firstPartyUrl.isValid()) { + return; + } + + bool firstPartyUrlLocal = Core::NetworkAccessManager::isLocalUrl(firstPartyUrl); + bool requestUrlLocal = Core::NetworkAccessManager::isLocalUrl(requestUrl); + + if (firstPartyUrlLocal && requestUrlLocal) { + return; + } + + if (firstPartyUrlLocal != requestUrlLocal) { + blockRequest(info); + return; + } + + Core::Settings::ExternalLinkPolicy linkPolicy = Core::Application::instance()->settings()->externalLinkPolicy; + switch (info.resourceType()) { + case QWebEngineUrlRequestInfo::ResourceTypeMainFrame: + if (linkPolicy != Core::Settings::ExternalLinkPolicy::Open + && linkPolicy != Core::Settings::ExternalLinkPolicy::Ask) { + blockRequest(info); + } + break; + case QWebEngineUrlRequestInfo::ResourceTypeSubFrame: + if (linkPolicy != Core::Settings::ExternalLinkPolicy::Open) { + blockRequest(info); + } + break; + default: + break; + } +} + +void UrlRequestInterceptor::blockRequest(QWebEngineUrlRequestInfo &info) +{ + info.block(true); +} diff --git a/src/libs/browser/urlrequestinterceptor.h b/src/libs/browser/urlrequestinterceptor.h new file mode 100644 index 00000000..72be71c0 --- /dev/null +++ b/src/libs/browser/urlrequestinterceptor.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Oleg Shparber +** Copyright (C) 2019 Kay Gawlik +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#ifndef ZEAL_BROWSER_URLREQUESTINTERCEPTOR_H +#define ZEAL_BROWSER_URLREQUESTINTERCEPTOR_H + +#include + +namespace Zeal { +namespace Browser { + +class UrlRequestInterceptor final : public QWebEngineUrlRequestInterceptor +{ + Q_OBJECT + Q_DISABLE_COPY(UrlRequestInterceptor) +public: + UrlRequestInterceptor(QObject *parent = nullptr); + void interceptRequest(QWebEngineUrlRequestInfo &info) override; + +private: + void blockRequest(QWebEngineUrlRequestInfo &info); +}; + +} // namespace Browser +} // namespace Zeal + +#endif // ZEAL_BROWSER_URLREQUESTINTERCEPTOR_H diff --git a/src/libs/browser/webcontrol.cpp b/src/libs/browser/webcontrol.cpp index bdf8c7a6..2b1b6e7b 100644 --- a/src/libs/browser/webcontrol.cpp +++ b/src/libs/browser/webcontrol.cpp @@ -31,9 +31,10 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include using namespace Zeal::Browser; @@ -47,15 +48,15 @@ WebControl::WebControl(QWidget *parent) m_webView = new WebView(); setFocusProxy(m_webView); - connect(m_webView->page(), &QWebPage::linkHovered, this, [this](const QString &link) { + connect(m_webView->page(), &QWebEnginePage::linkHovered, this, [this](const QString &link) { if (Core::NetworkAccessManager::isLocalUrl(QUrl(link))) { return; } - setToolTip(link); + m_webView->setToolTip(link); }); - connect(m_webView, &QWebView::titleChanged, this, &WebControl::titleChanged); - connect(m_webView, &QWebView::urlChanged, this, &WebControl::urlChanged); + connect(m_webView, &QWebEngineView::titleChanged, this, &WebControl::titleChanged); + connect(m_webView, &QWebEngineView::urlChanged, this, &WebControl::urlChanged); layout->addWidget(m_webView); @@ -94,15 +95,17 @@ void WebControl::resetZoom() void WebControl::setJavaScriptEnabled(bool enabled) { - m_webView->page()->settings()->setAttribute(QWebSettings::JavascriptEnabled, enabled); + m_webView->page()->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, enabled); } void WebControl::setWebBridgeObject(const QString &name, QObject *object) { - connect(m_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared, - this, [=]() { - m_webView->page()->mainFrame()->addToJavaScriptWindowObject(name, object); - }); + QWebEnginePage *page = m_webView->page(); + + QWebChannel *channel = new QWebChannel(page); + channel->registerObject(name, object); + + page->setWebChannel(channel); } void WebControl::load(const QUrl &url) @@ -157,7 +160,7 @@ QUrl WebControl::url() const return m_webView->url(); } -QWebHistory *WebControl::history() const +QWebEngineHistory *WebControl::history() const { return m_webView->history(); } diff --git a/src/libs/browser/webcontrol.h b/src/libs/browser/webcontrol.h index d0fc377d..87a211a0 100644 --- a/src/libs/browser/webcontrol.h +++ b/src/libs/browser/webcontrol.h @@ -26,7 +26,7 @@ #include -class QWebHistory; +class QWebEngineHistory; namespace Zeal { namespace Browser { @@ -49,7 +49,7 @@ public: QString title() const; QUrl url() const; - QWebHistory *history() const; + QWebEngineHistory *history() const; void restoreHistory(const QByteArray &array); QByteArray saveHistory() const; diff --git a/src/libs/browser/webpage.cpp b/src/libs/browser/webpage.cpp new file mode 100644 index 00000000..55920d6a --- /dev/null +++ b/src/libs/browser/webpage.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Oleg Shparber +** Copyright (C) 2019 Kay Gawlik +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#include "webpage.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { +constexpr char DarkModeCssUrl[] = "qrc:///browser/assets/css/darkmode.css"; +constexpr char HighlightOnNavigateCssUrl[] = "qrc:///browser/assets/css/highlight.css"; +} + +using namespace Zeal::Browser; + +WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) + : QWebEnginePage (profile, parent) +{ + auto settings = Core::Application::instance()->settings(); + + if (settings->darkModeEnabled) { + insertCssWithJS(DarkModeCssUrl); + setBackgroundColor(QColor::fromRgb(26, 26, 26)); + } + + if (settings->highlightOnNavigateEnabled) { + insertCssWithJS(HighlightOnNavigateCssUrl); + } + + if (QFileInfo::exists(settings->customCssFile)) { + insertCssWithJS(settings->customCssFile); + } +} + +bool WebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) +{ + if (Core::NetworkAccessManager::isLocalUrl(url)) { + return true; + } + + switch (Core::Application::instance()->settings()->externalLinkPolicy) { + case Core::Settings::ExternalLinkPolicy::Open: + break; + case Core::Settings::ExternalLinkPolicy::Ask: { + QMessageBox mb; + mb.setIcon(QMessageBox::Question); + mb.setText(tr("How do you want to open the external link?
URL: %1") + .arg(url.toString())); + + + QCheckBox *checkBox = new QCheckBox("Do ¬ ask again"); + mb.setCheckBox(checkBox); + + QPushButton *openInBrowserButton = mb.addButton(tr("Open in &Desktop Browser"), QMessageBox::ActionRole); + QPushButton *openInZealButton = mb.addButton(tr("Open in &Zeal"), QMessageBox::ActionRole); + mb.addButton(QMessageBox::Cancel); + + mb.setDefaultButton(openInBrowserButton); + + if (mb.exec() == QMessageBox::Cancel) { + return false; + } + + if (mb.clickedButton() == openInZealButton) { + if (checkBox->isChecked()) { + Core::Application::instance()->settings()->externalLinkPolicy + = Core::Settings::ExternalLinkPolicy::Open; + Core::Application::instance()->settings()->save(); + } + + return true; + } + + if (mb.clickedButton() == openInBrowserButton) { + if (checkBox->isChecked()) { + Core::Application::instance()->settings()->externalLinkPolicy + = Core::Settings::ExternalLinkPolicy::OpenInSystemBrowser; + Core::Application::instance()->settings()->save(); + } + + QDesktopServices::openUrl(url); + return false; + } + + break; + } + case Core::Settings::ExternalLinkPolicy::OpenInSystemBrowser: + QDesktopServices::openUrl(url); + return false; + } + + return false; +} + +void WebPage::insertCssWithJS(const QString &cssUrl) +{ + QString cssInjectCode = QLatin1String( + "var head = document.getElementsByTagName('head')[0];" + "var link = document.createElement('link');" + "link.rel = 'stylesheet';" + "link.type = 'text/css';" + "link.href = '%1';" + "link.media = 'all';" + "head.appendChild(link);"); + + QWebEngineScript injectCssScript; + injectCssScript.setSourceCode(cssInjectCode.arg(cssUrl)); + injectCssScript.setInjectionPoint(QWebEngineScript::DocumentReady); + scripts().insert(injectCssScript); +} diff --git a/src/libs/browser/webpage.h b/src/libs/browser/webpage.h new file mode 100644 index 00000000..41f51990 --- /dev/null +++ b/src/libs/browser/webpage.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Oleg Shparber +** Copyright (C) 2019 Kay Gawlik +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#ifndef ZEAL_BROWSER_WEBPAGE_H +#define ZEAL_BROWSER_WEBPAGE_H + +#include + +namespace Zeal { +namespace Browser { + +class WebPage final : public QWebEnginePage +{ + Q_OBJECT + Q_DISABLE_COPY(WebPage) +public: + WebPage(QWebEngineProfile *profile, QObject *parent = nullptr); + +protected: + bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) override; + +private: + void insertCssWithJS(const QString &cssUrl); +}; + +} // namespace Browser +} // namespace Zeal + +#endif // ZEAL_BROWSER_WEBPAGE_H diff --git a/src/libs/browser/webview.cpp b/src/libs/browser/webview.cpp index 309d86b7..bb9c3c40 100644 --- a/src/libs/browser/webview.cpp +++ b/src/libs/browser/webview.cpp @@ -23,7 +23,9 @@ #include "webview.h" +#include "urlrequestinterceptor.h" #include "webcontrol.h" +#include "webpage.h" #include #include @@ -31,22 +33,30 @@ #include #include +#include #include #include #include #include #include #include +#include +#include #include -#include using namespace Zeal::Browser; WebView::WebView(QWidget *parent) - : QWebView(parent) + : QWebEngineView(parent) { - page()->setNetworkAccessManager(Core::Application::instance()->networkManager()); + UrlRequestInterceptor *requestInterceptor = new UrlRequestInterceptor(this); + QWebEngineProfile *profile = new QWebEngineProfile(this); + profile->setRequestInterceptor(requestInterceptor); + QWebEnginePage *p = new WebPage(profile, this); + setPage(p); setZoomLevel(defaultZoomLevel()); + + QApplication::instance()->installEventFilter(this); } int WebView::zoomLevel() const @@ -101,7 +111,7 @@ void WebView::resetZoom() setZoomLevel(defaultZoomLevel()); } -QWebView *WebView::createWindow(QWebPage::WebWindowType type) +QWebEngineView *WebView::createWindow(QWebEnginePage::WebWindowType type) { Q_UNUSED(type) return Core::Application::instance()->mainWindow()->createTab()->webControl()->m_webView; @@ -109,11 +119,11 @@ QWebView *WebView::createWindow(QWebPage::WebWindowType type) void WebView::contextMenuEvent(QContextMenuEvent *event) { - const QWebHitTestResult hitTestResult = hitTestContent(event->pos()); + QWebEnginePage *p = page(); + const QWebEngineContextMenuData& contextData = p->contextMenuData(); - // Return standard menu for input fields. - if (hitTestResult.isContentEditable()) { - QWebView::contextMenuEvent(event); + if (!contextData.isValid()) { + QWebEngineView::contextMenuEvent(event); return; } @@ -123,15 +133,15 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) m_contextMenu->deleteLater(); } - auto m_contextMenu = new QMenu(this); + m_contextMenu = new QMenu(this); - const QUrl linkUrl = hitTestResult.linkUrl(); + QUrl linkUrl = contextData.linkUrl(); if (linkUrl.isValid()) { const QString scheme = linkUrl.scheme(); if (scheme != QLatin1String("javascript")) { m_contextMenu->addAction(tr("Open Link in New Tab"), this, [this]() { - triggerPageAction(QWebPage::WebAction::OpenLinkInNewWindow); + triggerPageAction(QWebEnginePage::WebAction::OpenLinkInNewWindow); }); } @@ -142,16 +152,17 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) }); } - m_contextMenu->addAction(pageAction(QWebPage::CopyLinkToClipboard)); + m_contextMenu->addAction(pageAction(QWebEnginePage::CopyLinkToClipboard)); } } - if (hitTestResult.isContentSelected()) { + const QString selectedText = contextData.selectedText(); + if (!selectedText.isEmpty()) { if (!m_contextMenu->isEmpty()) { m_contextMenu->addSeparator(); } - m_contextMenu->addAction(pageAction(QWebPage::Copy)); + m_contextMenu->addAction(pageAction(QWebEnginePage::Copy)); } if (!linkUrl.isValid() && url().scheme() != QLatin1String("qrc")) { @@ -159,8 +170,8 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) m_contextMenu->addSeparator(); } - m_contextMenu->addAction(pageAction(QWebPage::Back)); - m_contextMenu->addAction(pageAction(QWebPage::Forward)); + m_contextMenu->addAction(pageAction(QWebEnginePage::Back)); + m_contextMenu->addAction(pageAction(QWebEnginePage::Forward)); m_contextMenu->addSeparator(); m_contextMenu->addAction(tr("Open Page in Desktop Browser"), this, [this]() { @@ -186,105 +197,10 @@ void WebView::mousePressEvent(QMouseEvent *event) forward(); event->accept(); return; - case Qt::LeftButton: - case Qt::MiddleButton: { - m_clickedLink.clear(); - - const QUrl clickedLink = hitTestContent(event->pos()).linkUrl(); - if (clickedLink.isValid() && clickedLink.scheme() != QLatin1String("javascript")) { - m_clickedLink = clickedLink; - } - - break; - } default: break; } - - QWebView::mousePressEvent(event); -} - -void WebView::mouseReleaseEvent(QMouseEvent *event) -{ - if (m_clickedLink.isEmpty() - || (event->button() != Qt::LeftButton && event->button() != Qt::MiddleButton)) { - QWebView::mouseReleaseEvent(event); - return; - } - - const QUrl clickedLink = hitTestContent(event->pos()).linkUrl(); - if (!clickedLink.isValid() || clickedLink != m_clickedLink - || clickedLink.scheme() == QLatin1String("javascript")) { - QWebView::mouseReleaseEvent(event); - return; - } - - if (!Core::NetworkAccessManager::isLocalUrl(clickedLink)) { - switch (Core::Application::instance()->settings()->externalLinkPolicy) { - case Core::Settings::ExternalLinkPolicy::Open: - break; - case Core::Settings::ExternalLinkPolicy::Ask: { - QScopedPointer mb(new QMessageBox()); - mb->setIcon(QMessageBox::Question); - mb->setText(tr("How do you want to open the external link?
URL: %1") - .arg(clickedLink.toString())); - - auto checkBox = new QCheckBox("Do ¬ ask again"); - mb->setCheckBox(checkBox); - - QPushButton *openInBrowserButton = mb->addButton(tr("Open in &Desktop Browser"), - QMessageBox::ActionRole); - QPushButton *openInZealButton = mb->addButton(tr("Open in &Zeal"), - QMessageBox::ActionRole); - mb->addButton(QMessageBox::Cancel); - - mb->setDefaultButton(openInBrowserButton); - - if (mb->exec() == QMessageBox::Cancel) { - event->accept(); - return; - } - - if (mb->clickedButton() == openInZealButton) { - if (checkBox->isChecked()) { - Core::Application::instance()->settings()->externalLinkPolicy - = Core::Settings::ExternalLinkPolicy::Open; - Core::Application::instance()->settings()->save(); - } - - break; - } - - if (mb->clickedButton() == openInBrowserButton) { - if (checkBox->isChecked()) { - Core::Application::instance()->settings()->externalLinkPolicy - = Core::Settings::ExternalLinkPolicy::OpenInSystemBrowser; - Core::Application::instance()->settings()->save(); - } - } - } - case Core::Settings::ExternalLinkPolicy::OpenInSystemBrowser: - QDesktopServices::openUrl(clickedLink); - event->accept(); - return; - } - } - - switch (event->button()) { - case Qt::LeftButton: - if (!(event->modifiers() & Qt::ControlModifier || event->modifiers() & Qt::ShiftModifier)) { - QWebView::mouseReleaseEvent(event); - return; - } - case Qt::MiddleButton: - createWindow(QWebPage::WebBrowserWindow)->load(clickedLink); - event->accept(); - return; - default: - break; - } - - QWebView::mouseReleaseEvent(event); + QWebEngineView::mousePressEvent(event); } void WebView::wheelEvent(QWheelEvent *event) @@ -305,10 +221,36 @@ void WebView::wheelEvent(QWheelEvent *event) return; } - QWebView::wheelEvent(event); + QWebEngineView::wheelEvent(event); } -QWebHitTestResult WebView::hitTestContent(const QPoint &pos) const +bool WebView::eventFilter(QObject *watched, QEvent *event) { - return page()->mainFrame()->hitTestContent(pos); + // https://forum.qt.io/topic/54233/how-to-capture-mouse-events-in-webengineview + if (watched->parent() != this) { + return false; + } + + switch (event->type()) { + case QEvent::MouseButtonPress: { + QMouseEvent *mevent = static_cast(event); + switch (mevent->button()) { + case Qt::BackButton: + back(); + event->accept(); + return true; + case Qt::ForwardButton: + forward(); + event->accept(); + return true; + default: + break; + } + break; + } + default: + break; + } + + return false; } diff --git a/src/libs/browser/webview.h b/src/libs/browser/webview.h index 597d3eeb..bc9f3f63 100644 --- a/src/libs/browser/webview.h +++ b/src/libs/browser/webview.h @@ -24,12 +24,12 @@ #ifndef ZEAL_BROWSER_WEBVIEW_H #define ZEAL_BROWSER_WEBVIEW_H -#include +#include namespace Zeal { namespace Browser { -class WebView final : public QWebView +class WebView final : public QWebEngineView { Q_OBJECT Q_DISABLE_COPY(WebView) @@ -39,6 +39,8 @@ public: int zoomLevel() const; void setZoomLevel(int level); + bool eventFilter(QObject *watched, QEvent *event) override; + static const QVector &availableZoomLevels(); static int defaultZoomLevel(); @@ -51,15 +53,12 @@ signals: void zoomLevelChanged(); protected: - QWebView *createWindow(QWebPage::WebWindowType type) override; + QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override; void contextMenuEvent(QContextMenuEvent *event) override; void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private: - QWebHitTestResult hitTestContent(const QPoint &pos) const; - QMenu *m_contextMenu = nullptr; QUrl m_clickedLink; int m_zoomLevel = 0; diff --git a/src/libs/core/CMakeLists.txt b/src/libs/core/CMakeLists.txt index 3976f714..31cba24d 100644 --- a/src/libs/core/CMakeLists.txt +++ b/src/libs/core/CMakeLists.txt @@ -13,8 +13,8 @@ add_library(Core STATIC target_link_libraries(Core Registry Ui) -find_package(Qt5 COMPONENTS Network WebKit Widgets REQUIRED) -target_link_libraries(Core Qt5::Network Qt5::WebKit Qt5::Widgets) +find_package(Qt5 COMPONENTS Network WebEngine Widgets REQUIRED) +target_link_libraries(Core Qt5::Network Qt5::WebEngine Qt5::Widgets) find_package(LibArchive REQUIRED) include_directories(${LibArchive_INCLUDE_DIRS}) diff --git a/src/libs/core/settings.cpp b/src/libs/core/settings.cpp index 79c1c5d8..a9a328a6 100644 --- a/src/libs/core/settings.cpp +++ b/src/libs/core/settings.cpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include namespace { // Configuration file groups @@ -53,9 +53,8 @@ Settings::Settings(QObject *parent) qRegisterMetaTypeStreamOperators("ExternalLinkPolicy"); // Enable local storage due to https://github.com/zealdocs/zeal/issues/872. - QWebSettings *webSettings = QWebSettings::globalSettings(); - webSettings->setLocalStoragePath(Application::cacheLocation() + QLatin1String("/localStorage")); - webSettings->setAttribute(QWebSettings::LocalStorageEnabled, true); + QWebEngineSettings *webSettings = QWebEngineSettings::globalSettings(); + webSettings->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); load(); } @@ -92,18 +91,18 @@ void Settings::load() settings->beginGroup(GroupContent); // Fonts - QWebSettings *webSettings = QWebSettings::globalSettings(); + QWebEngineSettings *webSettings = QWebEngineSettings::globalSettings(); serifFontFamily = settings->value(QStringLiteral("serif_font_family"), - webSettings->fontFamily(QWebSettings::SerifFont)).toString(); + webSettings->fontFamily(QWebEngineSettings::SerifFont)).toString(); sansSerifFontFamily = settings->value(QStringLiteral("sans_serif_font_family"), - webSettings->fontFamily(QWebSettings::SansSerifFont)).toString(); + webSettings->fontFamily(QWebEngineSettings::SansSerifFont)).toString(); fixedFontFamily = settings->value(QStringLiteral("fixed_font_family"), - webSettings->fontFamily(QWebSettings::FixedFont)).toString(); + webSettings->fontFamily(QWebEngineSettings::FixedFont)).toString(); - static const QMap fontFamilies = { - {QStringLiteral("sans-serif"), QWebSettings::SansSerifFont}, - {QStringLiteral("serif"), QWebSettings::SerifFont}, - {QStringLiteral("monospace"), QWebSettings::FixedFont} + static const QMap fontFamilies = { + {QStringLiteral("sans-serif"), QWebEngineSettings::SansSerifFont}, + {QStringLiteral("serif"), QWebEngineSettings::SerifFont}, + {QStringLiteral("monospace"), QWebEngineSettings::FixedFont} }; defaultFontFamily = settings->value(QStringLiteral("default_font_family"), @@ -114,23 +113,23 @@ void Settings::load() defaultFontFamily = QStringLiteral("serif"); } - webSettings->setFontFamily(QWebSettings::SansSerifFont, sansSerifFontFamily); - webSettings->setFontFamily(QWebSettings::SerifFont, serifFontFamily); - webSettings->setFontFamily(QWebSettings::FixedFont, fixedFontFamily); + webSettings->setFontFamily(QWebEngineSettings::SansSerifFont, sansSerifFontFamily); + webSettings->setFontFamily(QWebEngineSettings::SerifFont, serifFontFamily); + webSettings->setFontFamily(QWebEngineSettings::FixedFont, fixedFontFamily); const QString defaultFontFamilyResolved = webSettings->fontFamily(fontFamilies.value(defaultFontFamily)); - webSettings->setFontFamily(QWebSettings::StandardFont, defaultFontFamilyResolved); + webSettings->setFontFamily(QWebEngineSettings::StandardFont, defaultFontFamilyResolved); defaultFontSize = settings->value(QStringLiteral("default_font_size"), - webSettings->fontSize(QWebSettings::DefaultFontSize)).toInt(); + webSettings->fontSize(QWebEngineSettings::DefaultFontSize)).toInt(); defaultFixedFontSize = settings->value(QStringLiteral("default_fixed_font_size"), - webSettings->fontSize(QWebSettings::DefaultFixedFontSize)).toInt(); + webSettings->fontSize(QWebEngineSettings::DefaultFixedFontSize)).toInt(); minimumFontSize = settings->value(QStringLiteral("minimum_font_size"), - webSettings->fontSize(QWebSettings::MinimumFontSize)).toInt(); + webSettings->fontSize(QWebEngineSettings::MinimumFontSize)).toInt(); - webSettings->setFontSize(QWebSettings::DefaultFontSize, defaultFontSize); - webSettings->setFontSize(QWebSettings::DefaultFixedFontSize, defaultFixedFontSize); - webSettings->setFontSize(QWebSettings::MinimumFontSize, minimumFontSize); + webSettings->setFontSize(QWebEngineSettings::DefaultFontSize, defaultFontSize); + webSettings->setFontSize(QWebEngineSettings::DefaultFixedFontSize, defaultFixedFontSize); + webSettings->setFontSize(QWebEngineSettings::MinimumFontSize, minimumFontSize); darkModeEnabled = settings->value(QStringLiteral("dark_mode"), false).toBool(); highlightOnNavigateEnabled = settings->value(QStringLiteral("highlight_on_navigate"), true).toBool(); diff --git a/src/libs/ui/CMakeLists.txt b/src/libs/ui/CMakeLists.txt index 5a8da7e4..ecd1ce39 100644 --- a/src/libs/ui/CMakeLists.txt +++ b/src/libs/ui/CMakeLists.txt @@ -23,5 +23,5 @@ add_library(Ui STATIC target_link_libraries(Ui Browser Sidebar QxtGlobalShortcut Widgets Registry) -find_package(Qt5 COMPONENTS WebKitWidgets REQUIRED) -target_link_libraries(Ui Qt5::WebKitWidgets) +find_package(Qt5 COMPONENTS WebEngineWidgets REQUIRED) +target_link_libraries(Ui Qt5::WebEngineWidgets) diff --git a/src/libs/ui/browsertab.cpp b/src/libs/ui/browsertab.cpp index 609fab79..9f618151 100644 --- a/src/libs/ui/browsertab.cpp +++ b/src/libs/ui/browsertab.cpp @@ -39,7 +39,7 @@ #include #include #include -#include +#include using namespace Zeal; using namespace Zeal::WidgetUi; @@ -83,11 +83,11 @@ BrowserTab::BrowserTab(QWidget *parent) auto backMenu = new QMenu(m_backButton); connect(backMenu, &QMenu::aboutToShow, this, [this, backMenu]() { backMenu->clear(); - QWebHistory *history = m_webControl->history(); - QList items = history->backItems(10); + QWebEngineHistory *history = m_webControl->history(); + QList items = history->backItems(10); for (auto it = items.crbegin(); it != items.crend(); ++it) { const QIcon icon = docsetIcon(it->url()); - const QWebHistoryItem item = *it; + const QWebEngineHistoryItem item = *it; backMenu->addAction(icon, it->title(), this, [=](bool) { history->goToItem(item); }); } }); @@ -105,9 +105,9 @@ BrowserTab::BrowserTab(QWidget *parent) auto forwardMenu = new QMenu(m_forwardButton); connect(forwardMenu, &QMenu::aboutToShow, this, [this, forwardMenu]() { forwardMenu->clear(); - QWebHistory *history = m_webControl->history(); + QWebEngineHistory *history = m_webControl->history(); const auto forwardItems = history->forwardItems(10); - for (const QWebHistoryItem &item : forwardItems) { + for (const QWebEngineHistoryItem &item : forwardItems) { const QIcon icon = docsetIcon(item.url()); forwardMenu->addAction(icon, item.title(), this, [=](bool) { history->goToItem(item); }); } diff --git a/src/libs/ui/mainwindow.cpp b/src/libs/ui/mainwindow.cpp index de1f53a1..8115858c 100644 --- a/src/libs/ui/mainwindow.cpp +++ b/src/libs/ui/mainwindow.cpp @@ -41,23 +41,17 @@ #include #include -#include #include #include #include #include #include #include -#include +#include using namespace Zeal; using namespace Zeal::WidgetUi; -namespace { -constexpr char DarkModeCssUrl[] = ":/browser/assets/css/darkmode.css"; -constexpr char HighlightOnNavigateCssUrl[] = ":/browser/assets/css/highlight.css"; -} // namespace - MainWindow::MainWindow(Core::Application *app, QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) @@ -531,34 +525,8 @@ void MainWindow::applySettings() else removeTrayIcon(); - // Content - QByteArray ba = QByteArrayLiteral("body { background-color: white; }"); - if (m_settings->darkModeEnabled) { - QScopedPointer file(new QFile(DarkModeCssUrl)); - if (file->open(QIODevice::ReadOnly)) { - ba += file->readAll(); - } - } - - if (m_settings->highlightOnNavigateEnabled) { - QScopedPointer file(new QFile(HighlightOnNavigateCssUrl)); - if (file->open(QIODevice::ReadOnly)) { - ba += file->readAll(); - } - } - - if (QFileInfo::exists(m_settings->customCssFile)) { - QScopedPointer file(new QFile(m_settings->customCssFile)); - if (file->open(QIODevice::ReadOnly)) { - ba += file->readAll(); - } - } - - const QString cssUrl = QLatin1String("data:text/css;charset=utf-8;base64,") + ba.toBase64(); - QWebSettings::globalSettings()->setUserStyleSheetUrl(QUrl(cssUrl)); - - QWebSettings::globalSettings()->setAttribute(QWebSettings::ScrollAnimatorEnabled, - m_settings->isSmoothScrollingEnabled); + QWebEngineSettings::globalSettings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, + m_settings->isSmoothScrollingEnabled); } void MainWindow::toggleWindow() diff --git a/src/libs/ui/settingsdialog.cpp b/src/libs/ui/settingsdialog.cpp index a1dc7f1c..1115f60b 100644 --- a/src/libs/ui/settingsdialog.cpp +++ b/src/libs/ui/settingsdialog.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include using namespace Zeal; using namespace Zeal::WidgetUi; @@ -41,9 +41,9 @@ namespace { constexpr int AvailableFontSizes[] = {9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 40, 44, 48, 56, 64, 72}; -constexpr QWebSettings::FontFamily BasicFontFamilies[] = {QWebSettings::SerifFont, - QWebSettings::SansSerifFont, - QWebSettings::FixedFont}; +constexpr QWebEngineSettings::FontFamily BasicFontFamilies[] = {QWebEngineSettings::SerifFont, + QWebEngineSettings::SansSerifFont, + QWebEngineSettings::FixedFont}; } // namespace SettingsDialog::SettingsDialog(QWidget *parent) @@ -80,51 +80,51 @@ SettingsDialog::SettingsDialog(QWidget *parent) // Disable global shortcut settings if not supported. ui->globalHotKeyGroupBox->setEnabled(QxtGlobalShortcut::isSupported()); - QWebSettings *webSettings = QWebSettings::globalSettings(); + QWebEngineSettings *webSettings = QWebEngineSettings::globalSettings(); // Avoid casting in each connect. auto currentIndexChangedSignal = static_cast(&QComboBox::currentIndexChanged); - auto syncStandardFont = [this, webSettings](QWebSettings::FontFamily fontFamily, + auto syncStandardFont = [this, webSettings](QWebEngineSettings::FontFamily fontFamily, const QFont &font) { const int index = ui->defaultFontComboBox->currentIndex(); if (BasicFontFamilies[index] == fontFamily) { - webSettings->setFontFamily(QWebSettings::StandardFont, font.family()); + webSettings->setFontFamily(QWebEngineSettings::StandardFont, font.family()); } }; connect(ui->defaultFontComboBox, currentIndexChangedSignal, this, [webSettings](int index) { const QString fontFamily = webSettings->fontFamily(BasicFontFamilies[index]); - webSettings->setFontFamily(QWebSettings::StandardFont, fontFamily); + webSettings->setFontFamily(QWebEngineSettings::StandardFont, fontFamily); }); connect(ui->serifFontComboBox, &QFontComboBox::currentFontChanged, this, [webSettings, syncStandardFont](const QFont &font) { - webSettings->setFontFamily(QWebSettings::SerifFont, font.family()); - syncStandardFont(QWebSettings::SerifFont, font); + webSettings->setFontFamily(QWebEngineSettings::SerifFont, font.family()); + syncStandardFont(QWebEngineSettings::SerifFont, font); }); connect(ui->sansSerifFontComboBox, &QFontComboBox::currentFontChanged, this, [webSettings, syncStandardFont](const QFont &font) { - webSettings->setFontFamily(QWebSettings::SansSerifFont, font.family()); - syncStandardFont(QWebSettings::SansSerifFont, font); + webSettings->setFontFamily(QWebEngineSettings::SansSerifFont, font.family()); + syncStandardFont(QWebEngineSettings::SansSerifFont, font); }); connect(ui->fixedFontComboBox, &QFontComboBox::currentFontChanged, this, [webSettings, syncStandardFont](const QFont &font) { - webSettings->setFontFamily(QWebSettings::FixedFont, font.family()); - syncStandardFont(QWebSettings::FixedFont, font); + webSettings->setFontFamily(QWebEngineSettings::FixedFont, font.family()); + syncStandardFont(QWebEngineSettings::FixedFont, font); }); connect(ui->fontSizeComboBox, currentIndexChangedSignal, this, [webSettings](int index) { - webSettings->setFontSize(QWebSettings::DefaultFontSize, AvailableFontSizes[index]); + webSettings->setFontSize(QWebEngineSettings::DefaultFontSize, AvailableFontSizes[index]); }); connect(ui->fixedFontSizeComboBox, currentIndexChangedSignal, this, [webSettings](int index) { - webSettings->setFontSize(QWebSettings::DefaultFixedFontSize, AvailableFontSizes[index]); + webSettings->setFontSize(QWebEngineSettings::DefaultFixedFontSize, AvailableFontSizes[index]); }); connect(ui->minFontSizeComboBox, currentIndexChangedSignal, this, [webSettings](int index) { const int fontSize = index == 0 ? 0 : AvailableFontSizes[index-1]; - webSettings->setFontSize(QWebSettings::MinimumFontSize, fontSize); + webSettings->setFontSize(QWebEngineSettings::MinimumFontSize, fontSize); }); loadSettings();