summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2025-11-13 13:48:37 +0100
committerSimon Rettberg2025-11-13 14:00:03 +0100
commit2c3b026349349729141ca46bd9d292327da2dfe2 (patch)
treeaf0650430c311a319bda68c89b4fd39036a9ec2c
parentFix: error page not showing HTTP error codes, but "Unknown Error" (diff)
downloadslxbrowser-master.tar.gz
slxbrowser-master.tar.xz
slxbrowser-master.zip
Port to QtWebEngine (thx Junie), bump to C++20HEADmaster
-rw-r--r--CMakeLists.txt52
-rw-r--r--src/Makefile256
-rw-r--r--src/main.cpp8
-rw-r--r--src/nam.cpp70
-rw-r--r--src/nam.h38
-rw-r--r--src/slxbrowser.cpp290
-rw-r--r--src/slxbrowser.h25
-rw-r--r--src/webview.cpp89
-rw-r--r--src/webview.h47
9 files changed, 283 insertions, 592 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f188f66..b71df00 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,58 +1,44 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.11)
# project name
project(slxbrowser)
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 20)
set(CMAKE_BUILD_TYPE Debug)
-set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall -Wextra -Werror -Wno-multichar -std=c++11")
-set(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wno-multichar -std=c++11")
+set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall -Wextra -Werror -Wno-multichar -std=c++20")
+set(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wno-multichar -std=c++20")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
-file(GLOB_RECURSE SLXBROWSER_SOURCES src/*.cpp)
-#file(GLOB_RECURSE SLXBROWSER_MOC_HEADERS src/*.h)
-#file(GLOB_RECURSE SLXBROWSER_UIS src/ui/*.ui)
-#file(GLOB_RECURSE SLXBROWSER_RESOURCES src/*.qrc)
+set(SLXBROWSER_HEADERS
+ src/nam.h
+ src/slxbrowser.h
+ src/webview.h
+)
-#include_directories(${CMAKE_CURRENT_BINARY_DIR} ./)
+set(SLXBROWSER_SOURCES
+ src/main.cpp
+ src/nam.cpp
+ src/slxbrowser.cpp
+ src/webview.cpp
+)
#
-# Qt4
+# Qt5
#
-find_package(Qt5WebKitWidgets)
-
-#if(Qt5_FOUND)
-# message(STATUS "Qt5 found")
-#else(Qt5_FOUND)
-# message(FATAL_ERROR "Qt5 not found")
-#endif(Qt5_FOUND)
-
-#set(QT_USE_QTXML TRUE)
-#set(QT_USE_QTSVG TRUE)
-#set(QT_USE_QTNETWORK TRUE)
-#set(QT_USE_QTWEBKIT TRUE)
-
-#include(${QT_USE_FILE})
-
-#QT4_ADD_RESOURCES(SLXBROWSER_RC_SOURCES ${SLXBROWSER_RESOURCES})
-#QT5_WRAP_UI(SLXBROWSER_UI_HEADERS ${SLXBROWSER_UIS})
-#QT5_WRAP_CPP(SLXBROWSER_MOC_SOURCES ${SLXBROWSER_MOC_HEADERS})
+find_package(Qt5 COMPONENTS WebEngineWidgets REQUIRED)
#
# build slxbrowser
#
add_executable(slxbrowser
+ ${SLXBROWSER_HEADERS}
${SLXBROWSER_SOURCES}
-# ${SLXBROWSER_MOC_SOURCES}
-# ${SLXBROWSER_UI_HEADERS}
-# ${SLXBROWSER_RC_SOURCES}
-# ${SLXBROWSER_QMS}
)
target_link_libraries(slxbrowser
- Qt5::WebKitWidgets
+ Qt5::WebEngineWidgets
)
install(TARGETS slxbrowser RUNTIME DESTINATION bin)
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644
index 4fd1610..0000000
--- a/src/Makefile
+++ /dev/null
@@ -1,256 +0,0 @@
-# CMAKE generated file: DO NOT EDIT!
-# Generated by "Unix Makefiles" Generator, CMake Version 3.5
-
-# Default target executed when no arguments are given to make.
-default_target: all
-
-.PHONY : default_target
-
-# Allow only one "make -f Makefile2" at a time, but pass parallelism.
-.NOTPARALLEL:
-
-
-#=============================================================================
-# Special targets provided by cmake.
-
-# Disable implicit rules so canonical targets will work.
-.SUFFIXES:
-
-
-# Remove some rules from gmake that .SUFFIXES does not remove.
-SUFFIXES =
-
-.SUFFIXES: .hpux_make_needs_suffix_list
-
-
-# Suppress display of executed commands.
-$(VERBOSE).SILENT:
-
-
-# A target that is always out of date.
-cmake_force:
-
-.PHONY : cmake_force
-
-#=============================================================================
-# Set environment variables for the build.
-
-# The shell in which to execute make rules.
-SHELL = /bin/sh
-
-# The CMake executable.
-CMAKE_COMMAND = /usr/bin/cmake
-
-# The command to remove a file.
-RM = /usr/bin/cmake -E remove -f
-
-# Escaping for special characters.
-EQUALS = =
-
-# The top-level source directory on which CMake was run.
-CMAKE_SOURCE_DIR = /home/sr/dev/openslx/slxbrowser
-
-# The top-level build directory on which CMake was run.
-CMAKE_BINARY_DIR = /home/sr/dev/openslx/slxbrowser/src
-
-#=============================================================================
-# Targets provided globally by CMake.
-
-# Special rule for the target install
-install: preinstall
- @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
- /usr/bin/cmake -P cmake_install.cmake
-.PHONY : install
-
-# Special rule for the target install
-install/fast: preinstall/fast
- @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
- /usr/bin/cmake -P cmake_install.cmake
-.PHONY : install/fast
-
-# Special rule for the target list_install_components
-list_install_components:
- @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\""
-.PHONY : list_install_components
-
-# Special rule for the target list_install_components
-list_install_components/fast: list_install_components
-
-.PHONY : list_install_components/fast
-
-# Special rule for the target rebuild_cache
-rebuild_cache:
- @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
- /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
-.PHONY : rebuild_cache
-
-# Special rule for the target rebuild_cache
-rebuild_cache/fast: rebuild_cache
-
-.PHONY : rebuild_cache/fast
-
-# Special rule for the target install/strip
-install/strip: preinstall
- @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
- /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
-.PHONY : install/strip
-
-# Special rule for the target install/strip
-install/strip/fast: install/strip
-
-.PHONY : install/strip/fast
-
-# Special rule for the target install/local
-install/local: preinstall
- @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
- /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
-.PHONY : install/local
-
-# Special rule for the target install/local
-install/local/fast: install/local
-
-.PHONY : install/local/fast
-
-# Special rule for the target edit_cache
-edit_cache:
- @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..."
- /usr/bin/ccmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
-.PHONY : edit_cache
-
-# Special rule for the target edit_cache
-edit_cache/fast: edit_cache
-
-.PHONY : edit_cache/fast
-
-# The main all target
-all: cmake_check_build_system
- $(CMAKE_COMMAND) -E cmake_progress_start /home/sr/dev/openslx/slxbrowser/src/CMakeFiles /home/sr/dev/openslx/slxbrowser/src/CMakeFiles/progress.marks
- $(MAKE) -f CMakeFiles/Makefile2 all
- $(CMAKE_COMMAND) -E cmake_progress_start /home/sr/dev/openslx/slxbrowser/src/CMakeFiles 0
-.PHONY : all
-
-# The main clean target
-clean:
- $(MAKE) -f CMakeFiles/Makefile2 clean
-.PHONY : clean
-
-# The main clean target
-clean/fast: clean
-
-.PHONY : clean/fast
-
-# Prepare targets for installation.
-preinstall: all
- $(MAKE) -f CMakeFiles/Makefile2 preinstall
-.PHONY : preinstall
-
-# Prepare targets for installation.
-preinstall/fast:
- $(MAKE) -f CMakeFiles/Makefile2 preinstall
-.PHONY : preinstall/fast
-
-# clear depends
-depend:
- $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
-.PHONY : depend
-
-#=============================================================================
-# Target rules for targets named slxbrowser
-
-# Build rule for target.
-slxbrowser: cmake_check_build_system
- $(MAKE) -f CMakeFiles/Makefile2 slxbrowser
-.PHONY : slxbrowser
-
-# fast build rule for target.
-slxbrowser/fast:
- $(MAKE) -f CMakeFiles/slxbrowser.dir/build.make CMakeFiles/slxbrowser.dir/build
-.PHONY : slxbrowser/fast
-
-CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.o: CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.o
-
-.PHONY : CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.o
-
-# target to build an object file
-CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.o:
- $(MAKE) -f CMakeFiles/slxbrowser.dir/build.make CMakeFiles/slxbrowser.dir/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.o
-.PHONY : CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.o
-
-CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.i: CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.i
-
-.PHONY : CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.i
-
-# target to preprocess a source file
-CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.i:
- $(MAKE) -f CMakeFiles/slxbrowser.dir/build.make CMakeFiles/slxbrowser.dir/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.i
-.PHONY : CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.i
-
-CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.s: CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.s
-
-.PHONY : CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.s
-
-# target to generate assembly for a file
-CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.s:
- $(MAKE) -f CMakeFiles/slxbrowser.dir/build.make CMakeFiles/slxbrowser.dir/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.s
-.PHONY : CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp.s
-
-main.o: main.cpp.o
-
-.PHONY : main.o
-
-# target to build an object file
-main.cpp.o:
- $(MAKE) -f CMakeFiles/slxbrowser.dir/build.make CMakeFiles/slxbrowser.dir/main.cpp.o
-.PHONY : main.cpp.o
-
-main.i: main.cpp.i
-
-.PHONY : main.i
-
-# target to preprocess a source file
-main.cpp.i:
- $(MAKE) -f CMakeFiles/slxbrowser.dir/build.make CMakeFiles/slxbrowser.dir/main.cpp.i
-.PHONY : main.cpp.i
-
-main.s: main.cpp.s
-
-.PHONY : main.s
-
-# target to generate assembly for a file
-main.cpp.s:
- $(MAKE) -f CMakeFiles/slxbrowser.dir/build.make CMakeFiles/slxbrowser.dir/main.cpp.s
-.PHONY : main.cpp.s
-
-# Help Target
-help:
- @echo "The following are some of the valid targets for this Makefile:"
- @echo "... all (the default if no target is provided)"
- @echo "... clean"
- @echo "... depend"
- @echo "... install"
- @echo "... list_install_components"
- @echo "... rebuild_cache"
- @echo "... slxbrowser"
- @echo "... install/strip"
- @echo "... install/local"
- @echo "... edit_cache"
- @echo "... CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.o"
- @echo "... CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.i"
- @echo "... CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.s"
- @echo "... main.o"
- @echo "... main.i"
- @echo "... main.s"
-.PHONY : help
-
-
-
-#=============================================================================
-# Special targets to cleanup operation of make.
-
-# Special rule to run CMake to check the build system integrity.
-# No rule that depends on this can have commands that come from listfiles
-# because they might be regenerated.
-cmake_check_build_system:
- $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
-.PHONY : cmake_check_build_system
-
diff --git a/src/main.cpp b/src/main.cpp
index 4d3b6e8..f9659d8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,11 +8,11 @@
class KeyHandler : public QObject
{
public:
- KeyHandler(SlxBrowser *win) : QObject(), _win(win) {}
- bool eventFilter(QObject *obj, QEvent *event)
+ explicit KeyHandler(SlxBrowser *win) : _win(win) {}
+ bool eventFilter(QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::KeyPress) {
- QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
+ auto *keyEvent = dynamic_cast<QKeyEvent*>(event);
if ( keyEvent->key() == Qt::Key_Q && (keyEvent->modifiers() & Qt::ControlModifier))
exit(0);
_win->activity();
@@ -86,7 +86,7 @@ QStringList loadUrlList(const QString &file)
QFile textFile(file);
if (!textFile.open(QFile::ReadOnly)) {
QTextStream(stdout) << "Cannot open URL list\n";
- return QStringList();
+ return {};
}
QTextStream textStream(&textFile);
while (true)
diff --git a/src/nam.cpp b/src/nam.cpp
index 798a8e6..3f2c252 100644
--- a/src/nam.cpp
+++ b/src/nam.cpp
@@ -1,49 +1,33 @@
#include "nam.h"
-#include <QCoreApplication>
#include <QDebug>
-SlxDisabledNetworkReply::SlxDisabledNetworkReply(QObject *parent, const QNetworkRequest &req,
- QNetworkAccessManager::Operation op)
- : QNetworkReply(parent)
+void SlxRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info)
{
- setRequest(req);
- setUrl(req.url());
- setOperation(op);
- setFinished(true);
- qRegisterMetaType<QNetworkReply::NetworkError>();
- QString msg = QCoreApplication::translate("QNetworkAccessManager",
- "Network access is disabled.");
- setError(UnknownNetworkError, msg);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
- QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
-}
-
-QNetworkReply* SlxNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
- const QNetworkRequest &req, QIODevice *outgoingData)
-{
- const QUrl url(req.url());
- //qDebug() << url;
- bool ok;
- if (url.isLocalFile()) {
- ok = true;
- } else if (url.scheme() == QLatin1String("qrc") || url.scheme() == QLatin1String("data")) {
- ok = true;
- } else if (url.host().isEmpty()) {
- ok = true;
- } else {
- auto str = url.toDisplayString(QUrl::NormalizePathSegments);
- if (_white.isValid() && _white.match(str).hasMatch()) {
- ok = true;
- } else if (_black.isValid() && _black.match(str).hasMatch()) {
- ok = false;
- } else {
- ok = true;
- }
- }
- if (!ok) {
- return new SlxDisabledNetworkReply(this, req, op);
- }
- return QNetworkAccessManager::createRequest(op, req, outgoingData);
+ const QUrl url = info.requestUrl();
+ bool ok = true;
+ if (url.isLocalFile()) {
+ ok = true;
+ } else if (url.scheme() == QLatin1String("qrc") || url.scheme() == QLatin1String("data")) {
+ ok = true;
+ } else if (url.host().isEmpty()) {
+ ok = true;
+ } else if (!url.isValid()) {
+ ok = false;
+ } else {
+ auto str = url.toDisplayString(QUrl::NormalizePathSegments);
+ if (_white.isValid() && _white.match(str).hasMatch()) {
+ ok = true;
+ } else if (_black.isValid() && _black.match(str).hasMatch()) {
+ ok = false;
+ } else {
+ ok = true;
+ }
+ }
+ if (!ok) {
+ info.block(true);
+ emit urlBlocked(url);
+ } else if (!QUrl::fromUserInput(url.toString()).isValid()) {
+ emit unsupportedScheme(url);
+ }
}
diff --git a/src/nam.h b/src/nam.h
index ec2bb17..8044aaf 100644
--- a/src/nam.h
+++ b/src/nam.h
@@ -1,38 +1,30 @@
#ifndef NAM_H_
#define NAM_H_
-#include <QNetworkAccessManager>
#include <QRegularExpression>
-#include <QNetworkReply>
+#include <QtWebEngineCore/QWebEngineUrlRequestInterceptor>
+#include <QtWebEngineCore/QWebEngineUrlRequestInfo>
/**
- * Block certain requests based on URL
+ * Block certain requests based on URL via QtWebEngine interceptor
*/
-class SlxNetworkAccessManager : public QNetworkAccessManager
+class SlxRequestInterceptor : public QWebEngineUrlRequestInterceptor
{
-Q_OBJECT
+ Q_OBJECT
public:
- SlxNetworkAccessManager(QRegularExpression blackList, QRegularExpression whiteList, QObject *parent = nullptr)
- : QNetworkAccessManager(parent), _black(blackList), _white(whiteList) {}
-protected:
- QNetworkReply* createRequest(QNetworkAccessManager::Operation op,
- const QNetworkRequest &originalReq, QIODevice *outgoingData = nullptr) override;
+ SlxRequestInterceptor(const QRegularExpression& blackList, const QRegularExpression& whiteList, QObject *parent = nullptr)
+ : QWebEngineUrlRequestInterceptor(parent), _black(blackList), _white(whiteList) {}
+ void interceptRequest(QWebEngineUrlRequestInfo &info) override;
+
+signals:
+ void urlBlocked(const QUrl &url);
+ void unsupportedScheme(const QUrl &url);
+
private:
- QRegularExpression _black, _white;
+ QRegularExpression _black, _white;
};
-class SlxDisabledNetworkReply : public QNetworkReply
-{
- Q_OBJECT
-public:
- SlxDisabledNetworkReply(QObject *parent, const QNetworkRequest &req,
- QNetworkAccessManager::Operation op);
-
- ~SlxDisabledNetworkReply() {}
- void abort() override { }
-protected:
- qint64 readData(char *, qint64) override { return -1; }
-};
+// no reply subclass needed with WebEngine
#endif
diff --git a/src/slxbrowser.cpp b/src/slxbrowser.cpp
index 8eceea0..3bb1161 100644
--- a/src/slxbrowser.cpp
+++ b/src/slxbrowser.cpp
@@ -1,24 +1,29 @@
#include "slxbrowser.h"
#include "webview.h"
#include "nam.h"
-#include <QtWebKitWidgets>
-#include <QWebPage>
-#include <QNetworkReply>
-#include <QSslConfiguration>
+#include <QtWebEngineWidgets/QWebEngineSettings>
+#include <QtWebEngineWidgets/QWebEnginePage>
+#include <QtWebEngineWidgets/QWebEngineProfile>
+#include <QtWebEngineWidgets/QWebEngineHistory>
#include <QProgressBar>
#include <QDateTime>
+#include <QVBoxLayout>
+#include <QMessageBox>
+#include <QRegularExpression>
+#include <QDebug>
+#include <utility>
static QRegularExpression urlListToRegExp(const QStringList &list);
SlxBrowser::SlxBrowser(BrowserSettings settings)
: QMainWindow(nullptr),
- _settings(settings),
+ _settings(std::move(settings)),
_unsupportedUri(false),
- _blockedSite(false),
- _lastPageLoad(0),
- _activity(false),
- _lastActivity(0),
- _pageValid(false)
+ _blockedSite(false),
+ _lastPageLoad(0),
+ _activity(false),
+ _lastActivity(0),
+ _pageValid(false)
{
_settings.reloadInterval *= 1000;
if (_settings.zoom <= 0) {
@@ -32,16 +37,17 @@ SlxBrowser::SlxBrowser(BrowserSettings settings)
} else if (_settings.maximized) {
this->showMaximized();
}
- QWebSettings::globalSettings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
- //QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
- QWidget *w = new QWidget;
- QLayout *l = new QVBoxLayout;
- _browser = new WebView;
- _browser->setZoomFactor(float(_settings.zoom) * .01f);
- _progress = new QProgressBar;
- _progress->hide();
- l->addWidget(_browser);
- l->addWidget(_progress);
+ // Enable local storage in WebEngine
+ _browser = new WebView;
+ _browser->page()->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
+ _browser->setIgnoreSslErrors(_settings.ignoreSslErrors);
+ auto *w = new QWidget;
+ QLayout *l = new QVBoxLayout;
+ _browser->setZoomFactor(float(_settings.zoom) * .01f);
+ _progress = new QProgressBar;
+ _progress->hide();
+ l->addWidget(_browser);
+ l->addWidget(_progress);
l->setMargin(0);
l->setSpacing(0);
l->setContentsMargins(0,0,0,0);
@@ -52,180 +58,117 @@ SlxBrowser::SlxBrowser(BrowserSettings settings)
_reset.setSingleShot(true);
connect(&_reset, &QTimer::timeout, this, &SlxBrowser::reloadInitial);
- connect(_browser, &WebView::loadStarted, this, &SlxBrowser::loadStarted);
- connect(_browser, &WebView::loadFinished, this, &SlxBrowser::loadFinished);
- connect(_browser, &WebView::loadProgress, this, &SlxBrowser::loadProgress);
- //
- QWebPage *page = _browser->page();
- QNetworkAccessManager *nam;
- if (_settings.whiteList.isEmpty() && _settings.blackList.isEmpty()) {
- nam = new QNetworkAccessManager(this);
- } else {
- if (_settings.blackList.isEmpty()) {
- _settings.blackList << "*";
- }
- // Just to be safe
- _settings.whiteList << _settings.url;
- nam = new SlxNetworkAccessManager(urlListToRegExp(_settings.blackList), urlListToRegExp(_settings.whiteList));
- }
- connect(nam, &QNetworkAccessManager::sslErrors, this, &SlxBrowser::sslErrors);
- connect(nam, &QNetworkAccessManager::finished, this, &SlxBrowser::requestFinished);
- page->setNetworkAccessManager(nam);
- page->mainFrame()->load(_settings.url);
- //
- _browser->show();
+ connect(_browser, &WebView::loadStarted, this, &SlxBrowser::loadStarted);
+ connect(_browser, &WebView::loadFinished, this, &SlxBrowser::loadFinished);
+ connect(_browser, &WebView::loadProgress, this, &SlxBrowser::loadProgress);
+ // URL filtering via interceptor
+ if (!_settings.whiteList.isEmpty() || !_settings.blackList.isEmpty()) {
+ if (_settings.blackList.isEmpty()) {
+ _settings.blackList << "*";
+ }
+ _settings.whiteList << _settings.url; // ensure initial URL allowed
+ auto *interceptor = new SlxRequestInterceptor(urlListToRegExp(_settings.blackList), urlListToRegExp(_settings.whiteList), this);
+ connect(interceptor, &SlxRequestInterceptor::urlBlocked, this, [this](const QUrl&){
+ _blockedSite = true;
+ });
+ connect(interceptor, &SlxRequestInterceptor::unsupportedScheme, this, [this](const QUrl&){
+ _unsupportedUri = true;
+ });
+ _browser->page()->profile()->setUrlRequestInterceptor(interceptor);
+ }
+ _browser->load(QUrl(_settings.url));
+ //
+ _browser->show();
}
SlxBrowser::~SlxBrowser()
-{
-}
+= default;
void SlxBrowser::loadStarted()
{
- _pageValid = false;
- _reset.stop();
- if (_settings.reloadInterval > 0) {
- _reset.start(_settings.reloadInterval + 5000);
- }
- _normalError.clear();
- _sslErrors.clear();
- _progress->setValue(0);
- _progress->show();
+ _pageValid = false;
+ _reset.stop();
+ if (_settings.reloadInterval > 0) {
+ _reset.start(_settings.reloadInterval + 5000);
+ }
+ _progress->setValue(0);
+ _progress->show();
}
void SlxBrowser::maybeTriggerBack()
{
- auto elems = _browser->page()->mainFrame()->documentElement().findAll("*");
- if (elems.count() > 10)
- return;
- QStringList want{{ "HEAD", "TITLE", "H1", "HR", "A", "PRE" }};
- for (auto x : elems) {
- if (x.tagName() != want.first())
- continue;
- want.pop_front();
- if (want.isEmpty())
- break;
- }
- if (want.isEmpty()) {
- // Was probably a JS redirect, go back before displaying the error
- _browser->page()->triggerAction(QWebPage::Back);
- }
+ // Best-effort: if there is a back entry, go back
+ if (_browser->page()->history()->canGoBack())
+ _browser->back();
}
void SlxBrowser::loadFinished(bool ok)
{
- _progress->hide();
- bool abortedDl = _browser->wasAbortedDownload();
- if (!abortedDl && !ok) {
- if (_unsupportedUri) {
- QMessageBox::warning(this, QString::fromUtf8("Denied"),
- QString::fromUtf8("This URL type is not supported.\n\n"
- "Diese Art Link wird nicht unterstützt.\n\n"
- "(z.B. Mail)"));
- } else if (_blockedSite) {
- maybeTriggerBack();
- QTimer::singleShot(10, [=]() {
- maybeTriggerBack();
- QMessageBox::warning(this, QString::fromUtf8("Denied"),
- QString::fromUtf8("Target URL not allowed.\n\n"
- "Dieser Link führt auf eine nicht erlaubte Seite."));
- });
- } else {
- _browser->blockSignals(true);
- _browser->page()->mainFrame()->setHtml("<html><body style='background:blue;color:white'><br><br>"
- "<center><h1>Page Load Error</h1><div id='content'></div><br>"
- "<p><a style='color:white' href='#' onclick='window.history.back()'>Back</a></p>"
- "</center></body></html>");
- _browser->blockSignals(false);
- QWebElement el = _browser->page()->mainFrame()->documentElement().findFirst("#content");
- QString str;
- if (!_sslErrors.empty()) {
- str.append("SSL Errors:\n");
- for (QSslError err : _sslErrors) {
- str.append(err.errorString());
- str.append('\n');
- }
- } else if (!_normalError.isEmpty()) {
- str.append("Load Error:\n");
- str.append(_normalError);
- } else {
- str.append("Unknown Error");
- }
- str.append("\n\n\n\n" + QDateTime::currentDateTime().toString());
- el.setPlainText(str);
- _pageValid = false;
- if (_settings.reloadInterval > 0) {
- _reset.start(qMin(30000, _settings.reloadInterval));
- } else {
- _reset.start(30000);
- }
- }
- _sslErrors.clear();
- _normalError.clear();
- } else {
- _pageValid = true;
- if (_settings.reloadInterval > 0) {
- _reset.start(qMax(_settings.reloadInterval / 20, 1000));
- }
- }
+ _progress->hide();
+ bool abortedDl = _browser->wasAbortedDownload();
+ if (!abortedDl && !ok) {
+ if (_unsupportedUri) {
+ QMessageBox::warning(this, QString::fromUtf8("Denied"),
+ QString::fromUtf8("This URL type is not supported.\n\n"
+ "Diese Art Link wird nicht unterstützt.\n\n"
+ "(z.B. Mail)"));
+ } else if (_blockedSite) {
+ maybeTriggerBack();
+ QTimer::singleShot(10, [this]() {
+ QMessageBox::warning(this, QString::fromUtf8("Denied"),
+ QString::fromUtf8("Target URL not allowed.\n\n"
+ "Dieser Link führt auf eine nicht erlaubte Seite."));
+ });
+ } else {
+ QString html = QString::fromUtf8(
+ "<html><body style='background:blue;color:white'><br><br>"
+ "<center><h1>Page Load Error</h1><div>%1</div><br>"
+ "<p><a style='color:white' href='#' onclick='history.back()'>Back</a></p>"
+ "</center></body></html>")
+ .arg(QDateTime::currentDateTime().toString());
+ _browser->setHtml(html);
+ _pageValid = false;
+ if (_settings.reloadInterval > 0) {
+ _reset.start(qMin(30000, _settings.reloadInterval));
+ } else {
+ _reset.start(30000);
+ }
+ }
+ } else {
+ _pageValid = true;
+ if (_settings.reloadInterval > 0) {
+ _reset.start(qMax(_settings.reloadInterval / 20, 1000));
+ }
+ }
_unsupportedUri = false;
- _blockedSite = false;
- _lastPageLoad = QDateTime::currentMSecsSinceEpoch();;
+ _blockedSite = false;
+ _lastPageLoad = QDateTime::currentMSecsSinceEpoch();;
}
void SlxBrowser::loadProgress(int progress)
{
- _progress->setValue(progress);
-}
-
-void SlxBrowser::sslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
-{
- if (_settings.ignoreSslErrors) {
- reply->ignoreSslErrors();
- return;
- }
- for (const auto& err : errors) {
- qDebug() << "SSL:" << err;
- }
- _sslErrors.append(errors);
-}
-
-void SlxBrowser::requestFinished(QNetworkReply *reply)
-{
- if (reply->error() == QNetworkReply::ProtocolUnknownError) {
- _unsupportedUri = true;
- } else if (reply->error() == QNetworkReply::UnknownNetworkError) {
- _blockedSite = true;
- } else {
- _normalError = reply->errorString();
- if (_normalError.isEmpty()) {
- int ec = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (ec >= 400) {
- _normalError = QString::asprintf("HTTP ERROR %d", ec);
- }
- }
- }
+ _progress->setValue(progress);
}
void SlxBrowser::reloadInitial()
{
- if (_pageValid) {
- if (_activity) {
- _lastActivity = QDateTime::currentMSecsSinceEpoch();
- _activity = false;
- _reset.start(qMax(_settings.reloadInterval / 20, 1000));
- return;
- }
- qint64 now = QDateTime::currentMSecsSinceEpoch();
- if (now - qMax(_lastActivity, _lastPageLoad) > _settings.reloadInterval) {
- _browser->page()->mainFrame()->load(_settings.url);
- } else {
- _reset.start(qMax(_settings.reloadInterval / 20, 1000));
- }
- } else {
- _browser->page()->mainFrame()->load(_settings.url);
- }
+ if (_pageValid) {
+ if (_activity) {
+ _lastActivity = QDateTime::currentMSecsSinceEpoch();
+ _activity = false;
+ _reset.start(qMax(_settings.reloadInterval / 20, 1000));
+ return;
+ }
+ qint64 now = QDateTime::currentMSecsSinceEpoch();
+ if (now - qMax(_lastActivity, _lastPageLoad) > _settings.reloadInterval) {
+ _browser->load(QUrl(_settings.url));
+ } else {
+ _reset.start(qMax(_settings.reloadInterval / 20, 1000));
+ }
+ } else {
+ _browser->load(QUrl(_settings.url));
+ }
}
static QRegularExpression urlListToRegExp(const QStringList &list)
@@ -233,10 +176,9 @@ static QRegularExpression urlListToRegExp(const QStringList &list)
// We search in the escaped string, so actually look for \*\* and \*
// Capture char before that because it must not be another backslash, as that
// means the star was already escaped in the list.
- // Since these are C strings there are some additional backslashes here.
- static const QRegularExpression STARSTAR("(^|[^\\\\])\\\\\\*\\\\\\*");
- static const QRegularExpression STAR("(^|[^\\\\])\\\\\\*");
- static const QRegularExpression QUEST("(^|[^\\\\])\\\\\\?");
+ static const QRegularExpression STARSTAR(R"((^|[^\\])\\\*\\\*)");
+ static const QRegularExpression STAR(R"((^|[^\\])\\\*)");
+ static const QRegularExpression QUEST(R"((^|[^\\])\\\?)");
static const QString STARSTAR_REP("\\1.*");
static const QString STAR_REP("\\1[^/]*");
static const QString QUEST_REP("\\1.?");
diff --git a/src/slxbrowser.h b/src/slxbrowser.h
index ba95f6b..cdde7af 100644
--- a/src/slxbrowser.h
+++ b/src/slxbrowser.h
@@ -3,13 +3,10 @@
#include <QMainWindow>
#include <QList>
-#include <QSslError>
#include <QTimer>
-class QNetworkReply;
class WebView;
class QProgressBar;
-class QNetworkReply;
struct BrowserSettings
{
@@ -27,31 +24,27 @@ class SlxBrowser : public QMainWindow
Q_OBJECT
public:
SlxBrowser(BrowserSettings settings);
- virtual ~SlxBrowser();
+ ~SlxBrowser() override;
void activity() { _activity = true; }
private slots:
void loadStarted();
void loadFinished(bool ok);
void loadProgress(int progress);
- void sslErrors(QNetworkReply * reply, const QList<QSslError> & errors);
- void requestFinished(QNetworkReply *reply);
- void reloadInitial();
- void maybeTriggerBack();
+ void reloadInitial();
+ void maybeTriggerBack();
private:
BrowserSettings _settings;
bool _unsupportedUri;
bool _blockedSite;
WebView *_browser;
- QProgressBar *_progress;
- QTimer _reset;
- QList<QSslError> _sslErrors;
- QString _normalError;
- qint64 _lastPageLoad;
- bool _activity;
- qint64 _lastActivity;
- bool _pageValid;
+ QProgressBar *_progress;
+ QTimer _reset;
+ qint64 _lastPageLoad;
+ bool _activity;
+ qint64 _lastActivity;
+ bool _pageValid;
};
#endif /* SLXBROWSER_H_ */
diff --git a/src/webview.cpp b/src/webview.cpp
index bbed1cf..bd2861a 100644
--- a/src/webview.cpp
+++ b/src/webview.cpp
@@ -1,54 +1,81 @@
#include "webview.h"
-#include <QWebFrame>
-#include <QNetworkReply>
#include <QMessageBox>
#include <QTimer>
+#include <QtWebEngineWidgets/QWebEngineProfile>
+#include <QtWebEngineWidgets/QWebEnginePage>
+#include <QMenu>
+#include <QContextMenuEvent>
WebView::WebView(QWidget* parent)
- : QWebView(parent),
- _timer(new QTimer(this)),
- _abortedDownload(false) {
- _timer->setSingleShot(true);
- connect(page(), SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
- page()->setForwardUnsupportedContent(true);
- page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
- connect(page(), SIGNAL(unsupportedContent(QNetworkReply*)),this,SLOT(unsupportedContent(QNetworkReply*)));
- connect(page(), SIGNAL(downloadRequested(QNetworkRequest)),this,SLOT(downloadRequest(QNetworkRequest)));
- connect(_timer, SIGNAL(timeout()), this, SLOT(downloadDeniedMessage()));
+ : QWebEngineView(parent),
+ _timer(new QTimer(this)),
+ _abortedDownload(false) {
+ _timer->setSingleShot(true);
+ // Use custom page to manage SSL errors and window behavior
+ _customPage = new CustomPage(this);
+ setPage(_customPage);
+ // Cancel all downloads and show message
+ connect(page()->profile(), &QWebEngineProfile::downloadRequested,
+ this, &WebView::onDownloadRequested);
+ connect(page(), &QWebEnginePage::windowCloseRequested,
+ this, &WebView::windowCloseRequested);
+ connect(_timer, &QTimer::timeout, this, &WebView::downloadDeniedMessage);
+}
+
+void WebView::setIgnoreSslErrors(bool on)
+{
+ if (_customPage)
+ _customPage->setIgnoreSslErrors(on);
}
void WebView::windowCloseRequested()
{
- // If we have an old URL stored on the stack, navigate back to it, otherwise we return and nothing happens
- if (_urls.empty())
- return;
- QUrl url = _urls.pop();
- page()->mainFrame()->load(url);
+ // If we have an old URL stored on the stack, navigate back to it
+ if (_urls.empty())
+ return;
+ QUrl url = _urls.pop();
+ this->load(url);
}
-QWebView* WebView::createWindow(QWebPage::WebWindowType)
+
+void WebView::contextMenuEvent(QContextMenuEvent* ev)
{
- // Remember current URL, then return the current Web View so no new window opens
- _urls.push(this->url());
- return this;
+ // Build the default menu
+ QMenu* menu = page()->createStandardContextMenu();
+
+ // Find and remove/hide the "View source" action
+ QAction* viewSource = page()->action(QWebEnginePage::ViewSource);
+ if (viewSource) {
+ viewSource->setVisible(false);
+ viewSource->setEnabled(false);
+ }
+
+ // Show the customized menu
+ menu->exec(ev->globalPos());
+ menu->deleteLater();
+ ev->accept();
}
-void WebView::unsupportedContent(QNetworkReply* rep)
+QWebEngineView* WebView::createWindow(QWebEnginePage::WebWindowType)
{
- _abortedDownload = true;
- rep->abort();
- rep->deleteLater();
- _timer->start(1);
+ // Remember current URL, then load into same view (ignore creating new window)
+ _urls.push(this->url());
+ // Return this view so the request is loaded here
+ return this;
}
-void WebView::downloadRequest(QNetworkRequest)
+void WebView::onDownloadRequested(QWebEngineDownloadItem* item)
{
- _timer->start(1);
+ if (!item)
+ return;
+ _abortedDownload = true;
+ item->cancel();
+ _timer->start(1);
}
void WebView::downloadDeniedMessage()
{
- QMessageBox::warning(this->parentWidget(), QString::fromUtf8("Denied"),
- QString::fromUtf8("The requested action triggered a download, which is not allowed.\n\n"
- "Diese Aktion löst einen Download aus, was nicht erlaubt ist."));
+ QMessageBox::warning(this->parentWidget(), QString::fromUtf8("Denied"),
+ QString::fromUtf8("The requested action triggered a download, which is not allowed.\n\n"
+ "Diese Aktion löst einen Download aus, was nicht erlaubt ist."));
}
diff --git a/src/webview.h b/src/webview.h
index 9d7e28d..43e293e 100644
--- a/src/webview.h
+++ b/src/webview.h
@@ -2,8 +2,12 @@
#define WEBVIEW_H_
#include <QStack>
-#include <QWebView>
-#include <QNetworkRequest>
+#include <QUrl>
+#include <QtWebEngineWidgets/QWebEngineView>
+#include <QtWebEngineWidgets/QWebEnginePage>
+#include <QtWebEngineWidgets/QWebEngineProfile>
+#include <QtWebEngineWidgets/QWebEngineDownloadItem>
+#include <QtWebEngineWidgets/QWebEngineCertificateError>
class QNetworkReply;
class QTimer;
@@ -12,11 +16,29 @@ class QTimer;
* Make sure pages that want to load in a new tab are actually loaded in the same page,
* and remember the previous URL in case the "new tab" requests to be closed.
*/
-class WebView : public QWebView
+class CustomPage : public QWebEnginePage
+{
+ Q_OBJECT
+public:
+ explicit CustomPage(QObject *parent = nullptr) : QWebEnginePage(parent) {}
+ void setIgnoreSslErrors(bool on) { _ignoreSslErrors = on; }
+protected:
+ bool certificateError(const QWebEngineCertificateError &error) override {
+ if (_ignoreSslErrors) {
+ return true;
+ }
+ return QWebEnginePage::certificateError(error);
+ }
+private:
+ bool _ignoreSslErrors{false};
+};
+
+class WebView : public QWebEngineView
{
Q_OBJECT
public:
- WebView(QWidget* parent = NULL);
+ explicit WebView(QWidget* parent = nullptr);
+ void setIgnoreSslErrors(bool on);
bool wasAbortedDownload() {
bool r = _abortedDownload;
_abortedDownload = false;
@@ -24,18 +46,19 @@ public:
}
protected:
- QWebView *createWindow(QWebPage::WebWindowType);
+ QWebEngineView *createWindow(QWebEnginePage::WebWindowType) override;
+ void contextMenuEvent(QContextMenuEvent *ev) override;
protected slots:
- void windowCloseRequested();
- void unsupportedContent(QNetworkReply*);
- void downloadRequest(QNetworkRequest);
- void downloadDeniedMessage();
+ void windowCloseRequested();
+ void onDownloadRequested(QWebEngineDownloadItem* item);
+ void downloadDeniedMessage();
private:
- QStack<QUrl> _urls;
- QTimer *_timer;
- bool _abortedDownload;
+ QStack<QUrl> _urls;
+ QTimer *_timer;
+ bool _abortedDownload;
+ CustomPage *_customPage{nullptr};
};
#endif