diff --git a/.appveyor.yml b/.appveyor.yml index eafee7cf3..89c797b96 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 e38e04e09..e7ec38a64 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 2d6b075d9..702c77ba6 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 455dc1d3a..6c9a2fe4e 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/assets/css/darkmode.css b/src/app/resources/browser/assets/css/darkmode.css index 97a64f072..866fa33d6 100644 --- a/src/app/resources/browser/assets/css/darkmode.css +++ b/src/app/resources/browser/assets/css/darkmode.css @@ -1,11 +1,28 @@ /* Dark mode (see #466). */ /* TODO: Check if we need -webkit-filter for old Qt WebKit. */ + +body { + background-color: white !important; + transition-duration: 0s !important; +} + html { - -webkit-filter: invert() hue-rotate(180deg) contrast(70%); - filter: invert() hue-rotate(180deg) contrast(70%); + filter: invert(1) hue-rotate(180deg) contrast(70%) !important; + background-color: #262626 !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; +} + +::-webkit-scrollbar { + width: 15px; + height: 15px; +} +::-webkit-scrollbar-track-piece { + background-color: black; +} +::-webkit-scrollbar-thumb:vertical { + height: 30px; + background-color: #0A4C95; } diff --git a/src/app/resources/browser/welcome.html b/src/app/resources/browser/welcome.html index 40216377a..f6e5c507b 100644 --- a/src/app/resources/browser/welcome.html +++ b/src/app/resources/browser/welcome.html @@ -8,6 +8,7 @@ + @@ -74,7 +75,11 @@ diff --git a/src/libs/browser/CMakeLists.txt b/src/libs/browser/CMakeLists.txt index a267d1a80..eccc59238 100644 --- a/src/libs/browser/CMakeLists.txt +++ b/src/libs/browser/CMakeLists.txt @@ -3,9 +3,11 @@ add_library(Browser STATIC searchtoolbar.cpp webbridge.cpp webview.cpp + webpage.cpp + urlrequestinterceptor.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 6c102ce4f..af8266104 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 38e2cb789..917b88a8a 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 @@ class SearchToolBar final : public QWidget 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 000000000..8ff8d3ea5 --- /dev/null +++ b/src/libs/browser/urlrequestinterceptor.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** 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 + +namespace Zeal { +namespace 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 = !isExternalUrl(firstPartyUrl); + bool requestUrlLocal = !isExternalUrl(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; + } +} + +bool UrlRequestInterceptor::isExternalUrl(const QUrl &url) +{ + if (url.isLocalFile() || url.scheme() == QStringLiteral("qrc")) { + return false; + } + + if (url.host().startsWith(QStringLiteral("127."))) { + return false; + } + + return true; +} + + +void UrlRequestInterceptor::blockRequest(QWebEngineUrlRequestInfo &info) +{ + qDebug() << info.firstPartyUrl() << ": Block request " << info.requestUrl(); + info.block(true); +} + +}} + diff --git a/src/libs/browser/urlrequestinterceptor.h b/src/libs/browser/urlrequestinterceptor.h new file mode 100644 index 000000000..43661a2b6 --- /dev/null +++ b/src/libs/browser/urlrequestinterceptor.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_URLREQUESTINTERCEPTOR_H +#define ZEAL_BROWSER_URLREQUESTINTERCEPTOR_H + +#include + +namespace Zeal { +namespace Browser { + +class UrlRequestInterceptor : public QWebEngineUrlRequestInterceptor +{ + Q_OBJECT +public: + UrlRequestInterceptor(QObject *parent); + void interceptRequest(QWebEngineUrlRequestInfo &info) override; + + static bool isExternalUrl(const QUrl &url); + +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 546124f7d..fec23b5e1 100644 --- a/src/libs/browser/webcontrol.cpp +++ b/src/libs/browser/webcontrol.cpp @@ -27,10 +27,11 @@ #include "webview.h" #include +#include +#include +#include +#include #include -#include -#include -#include using namespace Zeal::Browser; @@ -44,14 +45,14 @@ 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 (link.startsWith(QLatin1String("file:")) || link.startsWith(QLatin1String("qrc:"))) 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); @@ -90,15 +91,15 @@ 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); + page->setWebChannel(channel); + channel->registerObject(name, object); } void WebControl::load(const QUrl &url) @@ -153,7 +154,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 d0fc377d4..5eece19d7 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 @@ class WebControl final : public QWidget QString title() const; QUrl url() const; - QWebHistory *history() const; + QWebEngineHistory *history() const; void restoreHistory(const QByteArray &array); QByteArray saveHistory() const; @@ -78,7 +78,7 @@ public slots: private: friend class WebView; - WebView *m_webView = nullptr; + WebView *m_webView = nullptr; SearchToolBar *m_searchToolBar = nullptr; }; diff --git a/src/libs/browser/webpage.cpp b/src/libs/browser/webpage.cpp new file mode 100644 index 000000000..6417b1a39 --- /dev/null +++ b/src/libs/browser/webpage.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** 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 + +namespace { +constexpr char DarkModeCssUrl[] = "qrc:///browser/assets/css/darkmode.css"; +constexpr char HighlightOnNavigateCssUrl[] = "qrc:///browser/assets/css/highlight.css"; +} + +namespace Zeal { +namespace Browser { + +namespace +{ +bool isLocalFile(const QUrl &url) +{ + if (url.isLocalFile() || url.scheme() == QStringLiteral("qrc")) { + return true; + } + + if (url.host().startsWith(QStringLiteral("127."))) { + return true; + } + + return false; +} +} + +WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) + : QWebEnginePage (profile, parent) +{ + Zeal::Core::Settings* 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 (isLocalFile(url)) { + lastUrlWasExternal = false; + return true; + } + + // Dont ask for every external link. + if (lastUrlWasExternal) { + 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(); + } + lastUrlWasExternal = true; + 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 = R"( + 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 000000000..39186d6d7 --- /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 : public QWebEnginePage +{ + Q_OBJECT +public: + WebPage(QWebEngineProfile *profile, QObject *parent); + +protected: + bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) override; + +private: + bool lastUrlWasExternal = false; + 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 0f389475b..3d00ffcfc 100644 --- a/src/libs/browser/webview.cpp +++ b/src/libs/browser/webview.cpp @@ -24,6 +24,8 @@ #include "webview.h" #include "webcontrol.h" +#include "urlrequestinterceptor.h" +#include "webpage.h" #include #include @@ -37,15 +39,24 @@ #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 @@ -100,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; @@ -108,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; } @@ -122,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); }); } @@ -141,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")) { @@ -158,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]() { @@ -185,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 (isExternalUrl(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) @@ -304,23 +221,35 @@ void WebView::wheelEvent(QWheelEvent *event) return; } - QWebView::wheelEvent(event); -} - -QWebHitTestResult WebView::hitTestContent(const QPoint &pos) const -{ - return page()->mainFrame()->hitTestContent(pos); + QWebEngineView::wheelEvent(event); } -bool WebView::isExternalUrl(const QUrl &url) +bool WebView::eventFilter(QObject *watched, QEvent *event) { - if (url.isLocalFile() || url.scheme() == QStringLiteral("qrc")) { + // https://forum.qt.io/topic/54233/how-to-capture-mouse-events-in-webengineview + if(watched->parent() != this) return false; - } - if (url.host().startsWith(QStringLiteral("127."))) { - 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 true; + return false; } diff --git a/src/libs/browser/webview.h b/src/libs/browser/webview.h index d8e280fb0..4707b07c0 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) @@ -42,6 +42,7 @@ class WebView final : public QWebView static const QVector &availableZoomLevels(); static int defaultZoomLevel(); + bool eventFilter(QObject *watched, QEvent *event) override; public slots: void zoomIn(); void zoomOut(); @@ -51,17 +52,12 @@ public slots: 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; - - static bool isExternalUrl(const QUrl &url); - 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 3976f7143..31cba24d1 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 79c1c5d82..bb78a4064 100644 --- a/src/libs/core/settings.cpp +++ b/src/libs/core/settings.cpp @@ -25,13 +25,14 @@ #include "application.h" #include +#include #include #include #include #include #include #include -#include +#include namespace { // Configuration file groups @@ -53,9 +54,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 +92,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 +114,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 5a8da7e44..ecd1ce395 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 609fab790..9f618151d 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 de1f53a1d..8115858c8 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 a1dc7f1c2..1115f60bd 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();