diff options
Diffstat (limited to 'src')
33 files changed, 76 insertions, 1676 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 902fe6f..865c3f7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,6 @@ add_subdirectory(autogen) add_subdirectory(about) add_subdirectory(cmd) -add_subdirectory(webengine) add_executable(poi bookmarks/builtin.cpp diff --git a/src/bookmarks/bookmarkswidget.cpp b/src/bookmarks/bookmarkswidget.cpp index c7bc5f0..969f19e 100644 --- a/src/bookmarks/bookmarkswidget.cpp +++ b/src/bookmarks/bookmarkswidget.cpp @@ -12,8 +12,8 @@ #include "mainwindow/mainwindow.h" #include "subwindow/subwindow.h" #include "ui_bookmarksform.h" -#include "webengine/webprofilemanager.h" -#include "webengine/webview.h" +#include "webprofilemanager.h" +#include "webview.h" #include <QFileDialog> #include <QMenu> #include <QTreeView> diff --git a/src/browser.cpp b/src/browser.cpp index de5cdcf..000fd3a 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -17,8 +17,7 @@ #include "session_json.hpp" #include "settings.h" #include "util.h" -#include "webengine/webprofile.h" -#include "webengine/webprofilemanager.h" +#include "webprofile.h" #include <QAction> #include <QLibraryInfo> #include <QPluginLoader> diff --git a/src/browser.h b/src/browser.h index 63b280b..6a92d8b 100644 --- a/src/browser.h +++ b/src/browser.h @@ -11,7 +11,7 @@ #include "smolblok.hpp" #include "smolbote/session.hpp" -#include "webengine/webprofilemanager.h" +#include "webprofilemanager.h" #include <QJsonObject> #include <QMenu> #include <QPluginLoader> diff --git a/src/mainwindow/addressbar.cpp b/src/mainwindow/addressbar.cpp index d37a6a7..6b2cfd8 100644 --- a/src/mainwindow/addressbar.cpp +++ b/src/mainwindow/addressbar.cpp @@ -7,10 +7,10 @@ */ #include "addressbar.h" +#include "configuration.h" #include "ui_addressbar.h" +#include "webview.h" #include <QShortcut> -#include "configuration.h" -#include "webengine/webview.h" AddressBar::AddressBar(QWidget *parent) : QWidget(parent) diff --git a/src/mainwindow/mainwindow.cpp b/src/mainwindow/mainwindow.cpp index 64c149c..0d4a978 100644 --- a/src/mainwindow/mainwindow.cpp +++ b/src/mainwindow/mainwindow.cpp @@ -13,9 +13,9 @@ #include "browser.h" #include "configuration.h" #include "menubar.h" -#include "webengine/webprofile.h" -#include "webengine/webprofilemanager.h" -#include "webengine/webview.h" +#include "webprofile.h" +#include "webprofilemanager.h" +#include "webview.h" #include "widgets/dockwidget.h" #include "widgets/navigationbar.h" #include "widgets/searchform.h" diff --git a/src/mainwindow/menubar.cpp b/src/mainwindow/menubar.cpp index 5fc039a..80cb3ea 100644 --- a/src/mainwindow/menubar.cpp +++ b/src/mainwindow/menubar.cpp @@ -13,8 +13,8 @@ #include "downloadswidget.h" #include "mainwindow.h" #include "subwindow/subwindow.h" -#include "webengine/webprofilemanager.h" -#include "webengine/webview.h" +#include "webprofilemanager.h" +#include "webview.h" #include "widgets/menusearch.h" #include <QDir> #include <QFileDialog> diff --git a/src/mainwindow/widgets/navigationbar.cpp b/src/mainwindow/widgets/navigationbar.cpp index 0338bb7..dcb845c 100644 --- a/src/mainwindow/widgets/navigationbar.cpp +++ b/src/mainwindow/widgets/navigationbar.cpp @@ -10,8 +10,8 @@ #include "configuration.h" #include "urllineedit.h" #include "util.h" -#include "webengine/webprofile.h" -#include "webengine/webview.h" +#include "webprofile.h" +#include "webview.h" #include <QHBoxLayout> #include <QMenu> #include <QShortcut> diff --git a/src/session/savesessiondialog.cpp b/src/session/savesessiondialog.cpp index e22ce3a..3bc4150 100644 --- a/src/session/savesessiondialog.cpp +++ b/src/session/savesessiondialog.cpp @@ -12,8 +12,8 @@ #include "session_json.hpp" #include "subwindow/subwindow.h" #include "ui_savesessiondialog.h" -#include "webengine/webprofile.h" -#include "webengine/webview.h" +#include "webprofile.h" +#include "webview.h" #include <QFileDialog> #include <QPointer> #include <QTreeWidgetItem> diff --git a/src/subwindow/subwindow.cpp b/src/subwindow/subwindow.cpp index a2d6138..60ea633 100644 --- a/src/subwindow/subwindow.cpp +++ b/src/subwindow/subwindow.cpp @@ -9,9 +9,9 @@ #include "subwindow.h" #include "browser.h" #include "configuration.h" -#include "webengine/webprofile.h" -#include "webengine/webprofilemanager.h" -#include "webengine/webview.h" +#include "webprofile.h" +#include "webprofilemanager.h" +#include "webview.h" #include <QAction> #include <QCloseEvent> #include <QHideEvent> diff --git a/src/subwindow/subwindow.h b/src/subwindow/subwindow.h index 80e8520..a7b4564 100644 --- a/src/subwindow/subwindow.h +++ b/src/subwindow/subwindow.h @@ -11,7 +11,7 @@ #include "smolbote/session.hpp" #include "tabwidget.h" -#include "webengine/webview.h" +#include "webview.h" #include <QMenu> #include <QUrl> #include <QWidget> @@ -29,7 +29,7 @@ public: explicit SubWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); explicit SubWindow(const Session::SubWindow &tab_data, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); - ~SubWindow() = default; + ~SubWindow() override = default; [[nodiscard]] Session::SubWindow serialize() const; [[nodiscard]] int currentTabIndex() const diff --git a/src/subwindow/tabwidget.cpp b/src/subwindow/tabwidget.cpp index 69f3b8a..1e1d939 100644 --- a/src/subwindow/tabwidget.cpp +++ b/src/subwindow/tabwidget.cpp @@ -8,29 +8,22 @@ #include "tabwidget.h" #include "browser.h" -#include "webengine/webview.h" +#include "subwindow.h" +#include "webprofile.h" +#include "webview.h" #include <QAction> #include <QContextMenuEvent> #include <QMenu> #include <QTabBar> -#include "webengine/webprofile.h" #include <QWebEngineHistory> -#include "subwindow.h" -inline WebView *createViewFromInfo(TabWidget::TabInformation &tab, SubWindow *parent) -{ - auto *view = new WebView(tab.profile, std::bind(&SubWindow::createView, parent, std::placeholders::_1), parent); - QDataStream stream(&tab.historyBuffer, QIODevice::ReadOnly); - stream >> *view->history(); - view->history()->goToItem(view->history()->itemAt(tab.historyIndex)); - return view; -} +const QLatin1String stylesheet("QTabBar::tab { width: 200px; }"); TabWidget::TabWidget(SubWindow *parent) : QTabWidget(parent) , m_parent(parent) { - setStyleSheet("QTabBar::tab { width: 200px; }"); + setStyleSheet(stylesheet); setTabsClosable(true); setTabBarAutoHide(true); @@ -80,29 +73,6 @@ TabWidget::~TabWidget() } } -int TabWidget::addTab(WebView *view) -{ - if(view == nullptr) { - return -1; - } - - const int idx = QTabWidget::addTab(view, view->title()); - connect(view, &WebView::titleChanged, [this, view](const QString &title) { - const int current_idx = indexOf(view); - if(current_idx != -1) { - setTabText(current_idx, title); - } - }); - connect(view, &WebView::iconChanged, [this, view](const QIcon &icon) { - const int current_idx = indexOf(view); - if(current_idx != -1) { - setTabIcon(current_idx, icon); - } - }); - tabBar()->setTabData(idx, QVariant::fromValue<SubWindow::TabData>(SubWindow::TabData{})); - return idx; -} - void TabWidget::removeTab(int index) { // deleting the widget automatically removes the tab? @@ -180,3 +150,12 @@ void TabWidget::mousePressEvent(QMouseEvent *event) QTabWidget::mousePressEvent(event); } + +WebView *TabWidget::createViewFromInfo(TabWidget::TabInformation &tab, SubWindow *parent) +{ + auto *view = new WebView(tab.profile, std::bind(&SubWindow::createView, parent, std::placeholders::_1), parent); + QDataStream stream(&tab.historyBuffer, QIODevice::ReadOnly); + stream >> *view->history(); + view->history()->goToItem(view->history()->itemAt(tab.historyIndex)); + return view; +} diff --git a/src/subwindow/tabwidget.h b/src/subwindow/tabwidget.h index de5e6fb..2a5f360 100644 --- a/src/subwindow/tabwidget.h +++ b/src/subwindow/tabwidget.h @@ -9,9 +9,17 @@ #ifndef SMOLBOTE_TABWIDGET_H #define SMOLBOTE_TABWIDGET_H -#include <QTabWidget> -#include <QQueue> #include <QBuffer> +#include <QQueue> +#include <QTabWidget> + +template <typename T> +concept c_WebView = requires(T *a) +{ + a->title(); + a->titleChanged(QString()); + a->iconChanged(QIcon()); +}; class QAction; class QMenu; @@ -24,19 +32,34 @@ class TabWidget : public QTabWidget Q_OBJECT public: - struct TabInformation - { - WebProfile *profile; - QString title; - int historyIndex; - QByteArray historyBuffer; - }; - explicit TabWidget(SubWindow *parent = nullptr); ~TabWidget() override; + template <c_WebView T> + int addTab(T *view) + { + if(view == nullptr) { + return -1; + } + + const int idx = QTabWidget::addTab(view, view->title()); + connect(view, &T::titleChanged, [this, view](const QString &title) { + const int current_idx = indexOf(view); + if(current_idx != -1) { + setTabText(current_idx, title); + } + }); + connect(view, &T::iconChanged, [this, view](const QIcon &icon) { + const int current_idx = indexOf(view); + if(current_idx != -1) { + setTabIcon(current_idx, icon); + } + }); + //tabBar()->setTabData(idx, QVariant::fromValue<SubWindow::TabData>(SubWindow::TabData{})); + return idx; + } + public slots: - int addTab(WebView *view); void removeTab(int index); int restoreLastTab(); @@ -47,6 +70,14 @@ protected: void mousePressEvent(QMouseEvent *event) override; private: + struct TabInformation { + WebProfile *profile; + QString title; + int historyIndex; + QByteArray historyBuffer; + }; + [[nodiscard]] WebView *createViewFromInfo(TabInformation &tab, SubWindow *parent); + SubWindow *m_parent; int current = -1; int previous = -1; diff --git a/src/webengine/CMakeLists.txt b/src/webengine/CMakeLists.txt deleted file mode 100644 index 156c64a..0000000 --- a/src/webengine/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -add_library(webengine INTERFACE) -target_sources(webengine INTERFACE - webprofile.h webprofile.cpp webprofilemanager.h webprofilemanager.cpp - webpage.h webpage.cpp - webview.h webview.cpp webviewcontextmenu.h webviewcontextmenu.cpp - urlinterceptor.h urlinterceptor.cpp) -target_include_directories(webengine INTERFACE ${CMAKE_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(webengine INTERFACE Qt5::WebEngineWidgets autogen fmt) - -# tests -add_executable(profile_test test/profile.cpp) -target_link_libraries(profile_test PRIVATE webengine Catch2::Catch2) -#target_sanitize(profile_test) - -add_executable(profilemanager_test test/profilemanager.cpp) -target_link_libraries(profilemanager_test PRIVATE webengine Catch2::Catch2) -#target_sanitize(profilemanager_test) - -add_test(NAME webengine_profile COMMAND profile_test) -add_test(NAME webengine_profilemanager COMMAND profilemanager_test) -set_tests_properties(webengine_profile webengine_profilemanager PROPERTIES - ENVIRONMENT "PROFILE=${CMAKE_CURRENT_SOURCE_DIR}/test/testing.profile")
\ No newline at end of file diff --git a/src/webengine/test/form.html b/src/webengine/test/form.html deleted file mode 100644 index 9d8b19e..0000000 --- a/src/webengine/test/form.html +++ /dev/null @@ -1,10 +0,0 @@ -<html> - -<head> - <title>Form completion test</title> -</head> - -<body> -<h2>Form completion test</h2> -</body> -</html> diff --git a/src/webengine/test/icon.svg b/src/webengine/test/icon.svg deleted file mode 100644 index a348cab..0000000 --- a/src/webengine/test/icon.svg +++ /dev/null @@ -1,7 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" version="1.1"> - <circle cx="150" cy="150" r="100" stroke="#000000" stroke-width="6" fill="#e60026"></circle> - <circle cx="150" cy="150" r="87" stroke="#000000" stroke-width="4" fill="#e5e4e2"></circle> - <path d="M230,150 A80,80 0 0 0 150,70 L150,150 Z" /> - <path d="M70,150 A80,80 0 0 0 150,230 L150,150 Z" /> -</svg> - diff --git a/src/webengine/test/profile.cpp b/src/webengine/test/profile.cpp deleted file mode 100644 index ae3a4e3..0000000 --- a/src/webengine/test/profile.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#define CATCH_CONFIG_RUNNER - -// clazy:excludeall=non-pod-global-static - -#include "webprofilemanager.h" -#include <QApplication> -#include <catch2/catch.hpp> - -TEST_CASE("loading profile settings") -{ - const QString search = GENERATE(as<QString>{}, "https://search.url/t=%1", "https://duckduckgo.com/?q=%1&ia=web", "aaabbbccc"); - const QUrl homepage = GENERATE(as<QUrl>{}, "https://homepage.net", "about:blank", "aaabbbccc"); - const QUrl newtab = GENERATE(as<QUrl>{}, "https://newtab.net", "about:blank", "aaabbbccc"); - - auto *settings = WebProfile::load(QString(), search, homepage, newtab); - - REQUIRE(settings != nullptr); - REQUIRE(settings->value("search").toString() == search); - REQUIRE(settings->value("homepage").toUrl() == homepage); - REQUIRE(settings->value("newtab").toUrl() == newtab); - - delete settings; -} - -SCENARIO("profile properties") -{ - const QString search{ "about:blank" }; - const QUrl homepage{ "about:blank" }; - const QUrl newtab{ "about:blank" }; - const QString id{ "id" }; - - REQUIRE(qEnvironmentVariableIsSet("PROFILE")); - - // create an empty settings object - const QString settings_path = GENERATE(as<QString>{}, QString(), qgetenv("PROFILE")); - auto *settings = WebProfile::load(settings_path, search, homepage, newtab); - // create the actual profile - auto *profile = WebProfile::load(id, settings, true); - - REQUIRE(profile != nullptr); - REQUIRE(profile->isOffTheRecord()); - - WHEN("id constant") - { - REQUIRE(profile->getId() == id); - REQUIRE(profile->property("id").toString() == id); - } - - WHEN("changing profile name") - { - const QString name = GENERATE(as<QString>{}, "a", "bb", "ccc"); - profile->setName(name); - THEN("the name changes") - { - REQUIRE(profile->name() == name); - REQUIRE(settings->value("name").toString() == name); - } - } - WHEN("changing search") - { - const QString search = GENERATE(as<QString>{}, "a", "bb", "ccc"); - profile->setSearch(search); - THEN("the search url changes") - { - REQUIRE(profile->search() == search); - REQUIRE(settings->value("search").toString() == search); - } - } - WHEN("changing homepage url") - { - const QUrl url = GENERATE(as<QUrl>{}, "a", "bb", "ccc"); - profile->setHomepage(url); - THEN("homepage changes") - { - REQUIRE(profile->homepage() == url); - REQUIRE(settings->value("homepage").toUrl() == url); - } - } - WHEN("changing newtab url") - { - const QUrl url = GENERATE(as<QUrl>{}, "a", "bb", "ccc"); - profile->setNewtab(url); - THEN("newtab changes") - { - REQUIRE(profile->newtab() == url); - REQUIRE(settings->value("newtab").toUrl() == url); - } - } - - WHEN("changing cookies") - { - auto list = profile->cookies(); - REQUIRE(list.isEmpty()); - list.append(QNetworkCookie("name", "value").toRawForm()); - profile->setCookies(list); - THEN("new cookie list gets applied") - { - // There is no event loop, so the signals cannot fire and update the cookie list - //REQUIRE(list == profile->cookies()); - } - } - - WHEN("changing headers") - { - QMap<QString, QVariant> headers; - headers.insert("Dnt", "1"); - headers.insert("unknown", "pair"); - - profile->setHeaders(headers); - THEN("headers change") - { - REQUIRE(profile->headers() == headers); - } - } - - delete settings; - delete profile; -} - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - const auto r = Catch::Session().run(argc, argv); - return r; -} diff --git a/src/webengine/test/profilemanager.cpp b/src/webengine/test/profilemanager.cpp deleted file mode 100644 index 8f6a34f..0000000 --- a/src/webengine/test/profilemanager.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#define CATCH_CONFIG_RUNNER - -// clazy:excludeall=non-pod-global-static - -#include "webprofilemanager.h" -#include <QApplication> -#include <catch2/catch.hpp> - -SCENARIO("WebProfileManager") -{ - const QString search{ "https://search.url/t=%1" }; - const QUrl homepage{ "https://homepage.net" }; - const QUrl newtab{ "https://newtab.net" }; - const QString default_id{ "default" }; - - GIVEN("an empty profile list") - { - WebProfileManager<false> profiles({}, default_id, search, homepage, newtab); - - REQUIRE(profiles.idList().count() == 1); - REQUIRE(profiles.profile(default_id) == WebProfile::defaultProfile()); - REQUIRE(profiles.profile("not-in-list") == nullptr); - - WHEN("adding a new profile") - { - const QString id{ "id" }; - auto *settings = WebProfile::load(QString(), search, homepage, newtab); - auto *profile = WebProfile::load(id, settings, true); - - THEN("doesn't add profile without settings") - { - profiles.add(id, profile, nullptr); - REQUIRE(profiles.idList().count() == 1); - } - - THEN("doesn't add settings without profile") - { - profiles.add(id, nullptr, settings); - REQUIRE(profiles.idList().count() == 1); - } - - THEN("adds new profile with settings") - { - profiles.add(id, profile, settings); - REQUIRE(profiles.idList().count() == 2); - } - } - - WHEN("moving") - { - WebProfileManager<false> other(std::move(profiles)); - THEN("moved has the same number of profiles") - { - REQUIRE(other.idList().count() == 1); - REQUIRE(other.profile(default_id) == WebProfile::defaultProfile()); - REQUIRE(other.profile("not-in-list") == nullptr); - } - } - } - - GIVEN("a number of profiles, default undefined") - { - REQUIRE(qEnvironmentVariableIsSet("PROFILE")); - - WebProfileManager<false> profiles(QString::fromLatin1(qgetenv("PROFILE")).split(';'), default_id, search, homepage, newtab); - - REQUIRE(profiles.idList().count() == 2); - REQUIRE(profiles.profile(default_id) == WebProfile::defaultProfile()); - REQUIRE(profiles.profile("testing") != nullptr); - REQUIRE(profiles.profile("not-in-list") == nullptr); - - WHEN("making global") - { - profiles.make_global(); - WebProfileManager other; - - THEN("global has the same number of profiles") - { - REQUIRE(other.idList().count() == 2); - REQUIRE(other.profile(default_id) == WebProfile::defaultProfile()); - REQUIRE(other.profile("testing") != nullptr); - REQUIRE(other.profile("not-in-list") == nullptr); - } - - THEN("walking has no nullptrs") - { - other.walk([](const QString &, WebProfile *profile, const QSettings *settings) { - REQUIRE(profile != nullptr); - REQUIRE(settings != nullptr); - }); - } - } - } - - GIVEN("a number of profiles, default defined") - { - REQUIRE(qEnvironmentVariableIsSet("PROFILE")); - - WebProfileManager<false> profiles(QString::fromLatin1(qgetenv("PROFILE")).split(';'), "testing", search, homepage, newtab); - - REQUIRE(profiles.idList().count() == 1); - REQUIRE(profiles.profile("testing") == WebProfile::defaultProfile()); - REQUIRE(profiles.profile("not-in-list") == nullptr); - - WHEN("walking") - { - profiles.walk([](const QString &, WebProfile *profile, const QSettings *settings) { - REQUIRE(profile != nullptr); - REQUIRE(settings != nullptr); - }); - } - } -} - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - const auto r = Catch::Session().run(argc, argv); - return r; -} diff --git a/src/webengine/test/sample.html b/src/webengine/test/sample.html deleted file mode 100644 index 54746d5..0000000 --- a/src/webengine/test/sample.html +++ /dev/null @@ -1,7 +0,0 @@ -<html> -<head><title>sample page</title></head> -<body> - <h2><img src="icon.svg" />This is a sample page</h2> - <iframe width="100%" height="80%" src="form.html"></iframe> -</body> -</html> diff --git a/src/webengine/test/testing.profile b/src/webengine/test/testing.profile deleted file mode 100644 index e345a3e..0000000 --- a/src/webengine/test/testing.profile +++ /dev/null @@ -1,8 +0,0 @@ -name=Test Profile -otr=true -search=https://duckduckgo.com/?q=%1&ia=web -homepage=about:blank -newtab=about:blank - -[headers] -Dnt=1 diff --git a/src/webengine/test/view.cpp b/src/webengine/test/view.cpp deleted file mode 100644 index 8aa639a..0000000 --- a/src/webengine/test/view.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#define CATCH_CONFIG_RUNNER - -// clazy:excludeall=non-pod-global-static - -#include "webprofile.h" -#include "webview.h" -#include <QApplication> -#include <QMainWindow> -#include <QTimer> -#include <QtWebEngine> -#include <catch2/catch.hpp> - -SCENARIO("WebView") -{ - const QString profile_id{ "default" }; - auto *settings = WebProfile::load(qgetenv("PROFILE"), "about:blank", QUrl{ "about:blank" }, QUrl{ "about:blank" }); - auto *profile = WebProfile::load(profile_id, settings, true); - - QMainWindow window; - auto *view = new WebView(profile, nullptr); - window.setCentralWidget(view); - window.show(); - window.resize(800, 600); - - WHEN("created") - { - THEN("using the default profile") - { - REQUIRE(view->profile() == profile); - } - THEN("serialized using default profile") - { - const auto data = view->serialize(); - REQUIRE(data.profile == profile_id); - REQUIRE(data.url.isEmpty()); - REQUIRE(!data.history.isEmpty()); - } - THEN("loading a url") - { - // block until a loadFinished signal - QEventLoop pause; - QObject::connect(view, &WebView::loadFinished, &pause, &QEventLoop::quit); - view->load(QUrl{ qgetenv("URL") }); - pause.exec(); - - REQUIRE(view->isLoaded()); - } - } - - WHEN("changing profiles") - { - const QString swap_profile_id{ "swap_profile" }; - auto *swap_settings = WebProfile::load(QString(), "about:blank", QUrl{ "about:blank" }, QUrl{ "about:blank" }); - auto *swap_profile = WebProfile::load(swap_profile_id, swap_settings, true); - - view->setProfile(swap_profile); - THEN("using the swap profile") - { - REQUIRE(view->profile() == swap_profile); - } - THEN("serialized using swap profile") - { - const auto data = view->serialize(); - REQUIRE(data.profile == swap_profile_id); - REQUIRE(data.url.isEmpty()); - REQUIRE(!data.history.isEmpty()); - } - - view->setProfile(profile); - delete swap_settings; - delete swap_profile; - } - - // cleanup - window.close(); - delete view; - delete settings; - delete profile; -} - -int main(int argc, char **argv) -{ - QtWebEngine::initialize(); - QApplication app(argc, argv); - - QTimer::singleShot(0, &app, [argc, argv, &app]() { - const auto n_failed = Catch::Session().run(argc, argv); - app.exit(n_failed); - }); - - return app.exec(); -} diff --git a/src/webengine/urlinterceptor.cpp b/src/webengine/urlinterceptor.cpp deleted file mode 100644 index 047cad4..0000000 --- a/src/webengine/urlinterceptor.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#include "urlinterceptor.h" -#include "webprofile.h" - -// test DNT on https://browserleaks.com/donottrack - -UrlRequestInterceptor::UrlRequestInterceptor(WebProfile* profile) - : QWebEngineUrlRequestInterceptor(profile) -{ - Q_CHECK_PTR(profile); - m_profile = profile; -} - -void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) -{ - for(auto *filter : qAsConst(m_profile->m_filters)) { - filter->interceptRequest(info); - } - - // set headers - for(auto i = m_profile->m_headers.constBegin(); i != m_profile->m_headers.constEnd(); ++i) { - info.setHttpHeader(i.key(), i.value()); - } -} - diff --git a/src/webengine/urlinterceptor.h b/src/webengine/urlinterceptor.h deleted file mode 100644 index eb3ce67..0000000 --- a/src/webengine/urlinterceptor.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#ifndef SMOLBOTE_URLREQUESTINTERCEPTOR_H -#define SMOLBOTE_URLREQUESTINTERCEPTOR_H - -#include <QWebEngineUrlRequestInterceptor> - -class WebProfile; -class UrlRequestInterceptor : public QWebEngineUrlRequestInterceptor -{ - friend class WebProfile; - -public: - ~UrlRequestInterceptor() override = default; - - void interceptRequest(QWebEngineUrlRequestInfo &info) override; - -protected: - explicit UrlRequestInterceptor(WebProfile *profile); - -private: - WebProfile *m_profile; -}; - -#endif // SMOLBOTE_URLREQUESTINTERCEPTOR_H diff --git a/src/webengine/webpage.cpp b/src/webengine/webpage.cpp deleted file mode 100644 index b2b19b5..0000000 --- a/src/webengine/webpage.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#include "webpage.h" -#include <QLayout> -#include <QMessageBox> -#include <QTimer> -#include <QWebEngineFullScreenRequest> -#include <QWebEngineCertificateError> - -[[nodiscard]] inline QString tr_terminationStatus(QWebEnginePage::RenderProcessTerminationStatus status) -{ - switch(status) { - case QWebEnginePage::NormalTerminationStatus: - return QObject::tr("The render process terminated normally."); - case QWebEnginePage::AbnormalTerminationStatus: - return QObject::tr("The render process terminated with with a non-zero exit status."); - case QWebEnginePage::CrashedTerminationStatus: - return QObject::tr("The render process crashed, for example because of a segmentation fault."); - case QWebEnginePage::KilledTerminationStatus: - return QObject::tr("The render process was killed, for example by SIGKILL or task manager kill."); - } - - return QObject::tr("The render process was terminated with an unknown status."); -} - -[[nodiscard]] inline QString feature_toString(QWebEnginePage::Feature feature) -{ - switch(feature) { - case QWebEnginePage::Notifications: - return QObject::tr("Notifications"); - case QWebEnginePage::Geolocation: - return QObject::tr("Geolocation"); - case QWebEnginePage::MediaAudioCapture: - return QObject::tr("Audio Capture"); - case QWebEnginePage::MediaVideoCapture: - return QObject::tr("Video Capture"); - case QWebEnginePage::MediaAudioVideoCapture: - return QObject::tr("Audio and Video Capture"); - case QWebEnginePage::MouseLock: - return QObject::tr("Mouse Lock"); - case QWebEnginePage::DesktopVideoCapture: - return QObject::tr("Desktop Video Capture"); - case QWebEnginePage::DesktopAudioVideoCapture: - return QObject::tr("Desktop Audio and Video Capture"); - } - - return QObject::tr("Unknown feature"); -} - -WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) - : QWebEnginePage(profile, parent) -{ - connect(this, &WebPage::fullScreenRequested, this, [](QWebEngineFullScreenRequest request) { - request.accept(); - }); - - connect(this, &QWebEnginePage::featurePermissionRequested, this, &WebPage::featurePermissionDialog); - connect(this, &QWebEnginePage::renderProcessTerminated, this, &WebPage::renderProcessCrashed); -} - -bool WebPage::certificateError(const QWebEngineCertificateError &certificateError) -{ - QMessageBox messageBox; - - messageBox.setWindowTitle(tr("SSL Error")); - if(certificateError.isOverridable()) - messageBox.setIcon(QMessageBox::Warning); - else - messageBox.setIcon(QMessageBox::Critical); - - messageBox.setText(tr("An SSL error has occurred on <strong>%1</strong>").arg(certificateError.url().toString())); - messageBox.setInformativeText(tr("<p>%1</p>" - "<p>This error %2 be overridden.</p>") - .arg(certificateError.errorDescription(), - certificateError.isOverridable() ? tr("can") : tr("cannot"))); - messageBox.setDetailedText(tr("Error code: %1").arg(certificateError.error())); - - if(certificateError.isOverridable()) { - messageBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Abort); - messageBox.setDefaultButton(QMessageBox::Ignore); - } else - messageBox.setStandardButtons(QMessageBox::Abort); - - auto resp = messageBox.exec(); - - return resp == QMessageBox::Ignore; -} - -void WebPage::featurePermissionDialog(const QUrl &securityOrigin, QWebEnginePage::Feature feature) -{ - QMessageBox messageBox; - - messageBox.setWindowTitle(tr("Feature permission request")); - messageBox.setIcon(QMessageBox::Question); - messageBox.setText(tr("<p>The webpage <strong>%1</strong> has requested permission to access: %2</p>" - "<p>Allow this feature?</p>") - .arg(securityOrigin.toString(), feature_toString(feature))); - - messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - messageBox.setDefaultButton(QMessageBox::No); - - if(messageBox.exec() == QMessageBox::Yes) { - setFeaturePermission(securityOrigin, feature, QWebEnginePage::PermissionGrantedByUser); - } else { - setFeaturePermission(securityOrigin, feature, QWebEnginePage::PermissionDeniedByUser); - } -} - -void WebPage::renderProcessCrashed(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode) -{ - if(terminationStatus != QWebEnginePage::NormalTerminationStatus) { - QString page = "<html><body><h1>This tab has crashed!</h1>%message%</body></html>"; - page.replace(QLatin1String("%message%"), QString("<p>%1<br>Exit code is %2.</p>" - "<p>Press <a href='%3'>here</a> to reload this tab.</p>") - .arg(tr_terminationStatus(terminationStatus), QString::number(exitCode), this->url().toEncoded())); - - QTimer::singleShot(0, this, [this, page]() { - setHtml(page.toUtf8(), url()); - }); - } -} diff --git a/src/webengine/webpage.h b/src/webengine/webpage.h deleted file mode 100644 index 91ae4f3..0000000 --- a/src/webengine/webpage.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#ifndef SMOLBOTE_WEBPAGE_H -#define SMOLBOTE_WEBPAGE_H - -#include <QWebEnginePage> - -class WebPage : public QWebEnginePage -{ - Q_OBJECT - -public: - WebPage(QWebEngineProfile *profile, QObject *parent = nullptr); - ~WebPage() override = default; - -protected: - bool certificateError(const QWebEngineCertificateError &certificateError) override; - -protected slots: - void featurePermissionDialog(const QUrl &securityOrigin, QWebEnginePage::Feature feature); - void renderProcessCrashed(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode); -}; - -#endif // SMOLBOTE_WEBPAGE_H diff --git a/src/webengine/webprofile.cpp b/src/webengine/webprofile.cpp deleted file mode 100644 index f1e71fb..0000000 --- a/src/webengine/webprofile.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#include "webprofile.h" -#include "urlinterceptor.h" -#include <QFileInfo> -#include <QSettings> -#include <QWebEngineCookieStore> -#include <QWebEngineSettings> -#include <spdlog/spdlog.h> - -static WebProfile *s_profile = nullptr; - -void WebProfile::setDefaultProfile(WebProfile *profile) -{ - s_profile = profile; -} -WebProfile *WebProfile::defaultProfile() -{ - return s_profile; -} - -QSettings *WebProfile::load(const QString &path, const QString &search, const QUrl &homepage, const QUrl &newtab) -{ - auto *settings = new QSettings(path, QSettings::IniFormat); - - if(!settings->contains("search")) { - settings->setValue("search", search); - } - if(!settings->contains("homepage")) { - settings->setValue("homepage", homepage); - } - if(!settings->contains("newtab")) { - settings->setValue("newtab", newtab); - } - - return settings; -} - -WebProfile *WebProfile::load(const QString &id, QSettings *settings, bool isOffTheRecord) -{ - WebProfile *profile = nullptr; - - if(settings->value("otr", isOffTheRecord).toBool()) { - profile = new WebProfile(id, nullptr); - } else { - profile = new WebProfile(id, id, nullptr); - } - - profile->m_name = settings->value("name", id).toString(); - connect(profile, &WebProfile::nameChanged, profile, [settings](const QString &name) { settings->setValue("name", name); }); - profile->m_search = settings->value("search", "").toString(); - connect(profile, &WebProfile::searchChanged, settings, [settings](const QString &url) { settings->setValue("search", url); }); - profile->m_homepage = settings->value("homepage", "").toUrl(); - connect(profile, &WebProfile::homepageChanged, settings, [settings](const QUrl &url) { settings->setValue("homepage", url); }); - profile->m_newtab = settings->value("newtab", "").toUrl(); - connect(profile, &WebProfile::newtabChanged, settings, [settings](const QUrl &url) { settings->setValue("newtab", url); }); - - { - settings->beginGroup("properties"); - const auto keys = settings->childKeys(); - for(const QString &key : keys) { - profile->setProperty(qUtf8Printable(key), settings->value(key)); - } - settings->endGroup(); // properties - connect(profile, &WebProfile::propertyChanged, [settings](const QString &property, const QVariant &value) { - settings->setValue("properties/" + property, value); - }); - } - { - settings->beginGroup("attributes"); - const auto keys = settings->childKeys(); - auto *s = profile->settings(); - for(const QString &key : keys) { - auto attribute = static_cast<QWebEngineSettings::WebAttribute>(key.toInt()); - s->setAttribute(attribute, settings->value(key).toBool()); - } - settings->endGroup(); - connect(profile, &WebProfile::attributeChanged, [settings](const QWebEngineSettings::WebAttribute attr, const bool value) { - settings->setValue("attributes/" + QString::number(attr), value); - }); - } - { - // headers - settings->beginGroup("headers"); - const auto keys = settings->childKeys(); - for(const QString &key : keys) { - profile->setHttpHeader(key.toLatin1(), settings->value(key).toString().toLatin1()); - } - settings->endGroup(); - connect(profile, &WebProfile::headerChanged, [settings](const QString &name, const QString &value) { - settings->setValue("headers/" + name, value); - }); - connect(profile, &WebProfile::headerRemoved, [settings](const QString &name) { - settings->remove("headers/" + name); - }); - } - return profile; -} - -// off-the-record constructor -WebProfile::WebProfile(const QString &id, QObject *parent) - : QWebEngineProfile(parent) - , m_id(id) -{ - QWebEngineProfile::setUrlRequestInterceptor(new UrlRequestInterceptor(this)); - connect(this->cookieStore(), &QWebEngineCookieStore::cookieAdded, this, [this](const QNetworkCookie &cookie) { - spdlog::debug("[{}]: +cookie {}", qUtf8Printable(m_name), qUtf8Printable(cookie.name())); - m_cookies.append(cookie); - }); - connect(this->cookieStore(), &QWebEngineCookieStore::cookieRemoved, this, [this](const QNetworkCookie &cookie) { - spdlog::debug("[{}]: -cookie {}", qUtf8Printable(m_name), qUtf8Printable(cookie.name())); - m_cookies.removeOne(cookie); - }); - cookieStore()->loadAllCookies(); -} - -// default constructor -WebProfile::WebProfile(const QString &id, const QString &storageName, QObject *parent) - : QWebEngineProfile(storageName, parent) - , m_id(id) -{ - QWebEngineProfile::setUrlRequestInterceptor(new UrlRequestInterceptor(this)); - connect(this->cookieStore(), &QWebEngineCookieStore::cookieAdded, this, [this](const QNetworkCookie &cookie) { - spdlog::debug("[{}]: +cookie {}", qUtf8Printable(m_name), qUtf8Printable(cookie.name())); - m_cookies.append(cookie); - }); - connect(this->cookieStore(), &QWebEngineCookieStore::cookieRemoved, this, [this](const QNetworkCookie &cookie) { - spdlog::debug("[{}]: -cookie {}", qUtf8Printable(m_name), qUtf8Printable(cookie.name())); - m_cookies.removeOne(cookie); - }); - cookieStore()->loadAllCookies(); -} diff --git a/src/webengine/webprofile.h b/src/webengine/webprofile.h deleted file mode 100644 index 894463f..0000000 --- a/src/webengine/webprofile.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#ifndef SMOLBOTE_WEBENGINEPROFILE_H -#define SMOLBOTE_WEBENGINEPROFILE_H - -#include <QMap> -#include <QNetworkCookie> -#include <QSettings> -#include <QString> -#include <QUrl> -#include <QVariant> -#include <QVector> -#include <QWebEngineCookieStore> -#include <QWebEngineProfile> -#include <QWebEngineSettings> - -class UrlRequestInterceptor; -class WebProfile : public QWebEngineProfile -{ - friend class UrlRequestInterceptor; - - Q_OBJECT - - Q_PROPERTY(QString id READ getId CONSTANT) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(QString search READ search WRITE setSearch NOTIFY searchChanged) - Q_PROPERTY(QUrl homepage READ homepage WRITE setHomepage NOTIFY homepageChanged) - Q_PROPERTY(QUrl newtab READ newtab WRITE setNewtab NOTIFY newtabChanged) - - // QWebEngineProfile should-be properties - Q_PROPERTY(QString cachePath READ cachePath WRITE setCachePath NOTIFY propertyChanged) - Q_PROPERTY(QString persistentStoragePath READ persistentStoragePath WRITE setPersistentStoragePath NOTIFY propertyChanged) - Q_PROPERTY(int persistentCookiesPolicy READ persistentCookiesPolicy WRITE setPersistentCookiesPolicy NOTIFY propertyChanged) - - Q_PROPERTY(QString httpAcceptLanguage READ httpAcceptLanguage WRITE setHttpAcceptLanguage NOTIFY propertyChanged) - Q_PROPERTY(int httpCacheMaximumSize READ httpCacheMaximumSize WRITE setHttpCacheMaximumSize NOTIFY propertyChanged) - Q_PROPERTY(int httpCacheType READ httpCacheType WRITE setHttpCacheType NOTIFY propertyChanged) - Q_PROPERTY(QString httpUserAgent READ httpUserAgent WRITE setHttpUserAgent NOTIFY propertyChanged) - - Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY propertyChanged) - - // more custom properties - Q_PROPERTY(QList<QVariant> cookies READ cookies WRITE setCookies NOTIFY cookiesChanged) - Q_PROPERTY(QMap<QString, QVariant> headers READ headers WRITE setHeaders NOTIFY headersChanged) - -signals: - void nameChanged(const QString &name); - void searchChanged(const QString &url); - void homepageChanged(const QUrl &url); - void newtabChanged(const QUrl &url); - - void propertyChanged(const QString &name, const QVariant &value); - void attributeChanged(QWebEngineSettings::WebAttribute attribute, bool value); - - void cookiesChanged(); - void headersChanged(); - void headerChanged(const QString &name, const QString &value); - void headerRemoved(const QString &name); - -public: - [[nodiscard]] static QSettings *load(const QString &path, const QString &search = QString(), const QUrl &homepage = QUrl(), const QUrl &newtab = QUrl()); - [[nodiscard]] static WebProfile *load(const QString &id, QSettings *settings, bool isOffTheRecord = true); - - WebProfile(const WebProfile &) = delete; - WebProfile &operator=(const WebProfile &) = delete; - WebProfile(WebProfile &&) = delete; - WebProfile &operator=(WebProfile &&) = delete; - - static WebProfile *defaultProfile(); - static void setDefaultProfile(WebProfile *profile); - - ~WebProfile() override = default; - - [[nodiscard]] QString getId() const - { - return m_id; - } - [[nodiscard]] QString name() const - { - return m_name; - } - void setName(const QString &name) - { - m_name = name; - emit nameChanged(name); - } - - [[nodiscard]] QList<QVariant> cookies() const - { - QList<QVariant> r; - for(const auto &cookie : m_cookies) { - r.append(cookie.toRawForm()); - } - return r; - } - - [[nodiscard]] QString search() const - { - return m_search; - } - void setSearch(const QString &url) - { - m_search = url; - emit searchChanged(m_search); - } - - [[nodiscard]] QUrl homepage() const - { - return m_homepage; - } - void setHomepage(const QUrl &url) - { - m_homepage = url; - emit homepageChanged(m_homepage); - } - - [[nodiscard]] QUrl newtab() const - { - return m_newtab; - } - void setNewtab(const QUrl &url) - { - m_newtab = url; - emit newtabChanged(m_newtab); - } - - void setCachePath(const QString &path) - { - QWebEngineProfile::setCachePath(path); - emit propertyChanged("cachePath", path); - } - void setPersistentStoragePath(const QString &path) - { - QWebEngineProfile::setPersistentStoragePath(path); - emit propertyChanged("persistentStoragePath", path); - } - void setPersistentCookiesPolicy(int policy) - { - QWebEngineProfile::setPersistentCookiesPolicy(static_cast<QWebEngineProfile::PersistentCookiesPolicy>(policy)); - emit propertyChanged("persistentCookiesPolicy", policy); - } - - void setHttpAcceptLanguage(const QString &httpAcceptLanguage) - { - QWebEngineProfile::setHttpAcceptLanguage(httpAcceptLanguage); - emit propertyChanged("httpAcceptLanguage", httpAcceptLanguage); - } - void setHttpCacheMaximumSize(int maxSize) - { - QWebEngineProfile::setHttpCacheMaximumSize(maxSize); - emit propertyChanged("httpCacheMaximumSize", maxSize); - } - void setHttpCacheType(int type) - { - QWebEngineProfile::setHttpCacheType(static_cast<QWebEngineProfile::HttpCacheType>(type)); - emit propertyChanged("httpCacheType", type); - } - void setHttpUserAgent(const QString &userAgent) - { - QWebEngineProfile::setHttpUserAgent(userAgent); - emit propertyChanged("httpUserAgent", userAgent); - } - void setHttpHeader(const QString &name, const QString &value) - { - m_headers[name.toLatin1()] = value.toLatin1(); - emit headerChanged(name, value); - } - void removeHttpHeader(const QString &name) - { - if(m_headers.contains(name.toLatin1())) { - m_headers.remove(name.toLatin1()); - emit headerRemoved(name); - } - } - [[nodiscard]] QMap<QString, QVariant> headers() const - { - QMap<QString, QVariant> r; - auto it = m_headers.constBegin(); - while(it != m_headers.constEnd()) { - r.insert(QString(it.key()), QVariant(it.value())); - ++it; - } - return r; - } - void setSpellCheckEnabled(bool enable) - { - QWebEngineProfile::setSpellCheckEnabled(enable); - emit propertyChanged("spellCheckEnabed", enable); - } - - void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) - { - m_filters.append(interceptor); - } - -public slots: - void setCookies(const QList<QVariant> &cookies) - { - auto *store = cookieStore(); - store->deleteAllCookies(); - for(const auto &data : cookies) { - for(auto &cookie : QNetworkCookie::parseCookies(data.toByteArray())) { - store->setCookie(cookie); - } - } - emit cookiesChanged(); - } - void setHeaders(const QMap<QString, QVariant> &headers) - { - m_headers.clear(); - auto it = headers.constBegin(); - while(it != headers.constEnd()) { - m_headers.insert(it.key().toLatin1(), it.value().toByteArray()); - ++it; - } - emit headersChanged(); - } - -protected: - // off-the-record constructor - explicit WebProfile(const QString &id, QObject *parent = nullptr); - // default constructor - explicit WebProfile(const QString &id, const QString &storageName, QObject *parent = nullptr); - - const QString m_id; - QString m_name; - QString m_search = QString("about:blank"); - QUrl m_homepage = QUrl("about:blank"); - QUrl m_newtab = QUrl("about:blank"); - - QVector<QWebEngineUrlRequestInterceptor *> m_filters; - QList<QNetworkCookie> m_cookies; - QMap<QByteArray, QByteArray> m_headers; -}; - -#endif // SMOLBOTE_WEBENGINEPROFILE_H diff --git a/src/webengine/webprofilemanager.cpp b/src/webengine/webprofilemanager.cpp deleted file mode 100644 index 5cc83f8..0000000 --- a/src/webengine/webprofilemanager.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#include "webprofilemanager.h" -#include "webprofile.h" - -static WebProfileManager<false> *s_instance = nullptr; - -template <> -void WebProfileManager<false>::make_global() -{ - if(s_instance == nullptr) { - s_instance = this; - } -} - -template <> -WebProfileManager<true>::~WebProfileManager() = default; - -template <> -WebProfileManager<false>::~WebProfileManager() -{ - for(Profile p : qAsConst(profiles)) { - if(p.selfDestruct && p.settings != nullptr) { - if(!p.ptr->isOffTheRecord()) { - if(!p.ptr->persistentStoragePath().isEmpty()) - QDir(p.ptr->persistentStoragePath()).removeRecursively(); - if(!p.ptr->cachePath().isEmpty()) - QDir(p.ptr->cachePath()).removeRecursively(); - } - const QString filename = p.settings->fileName(); - delete p.settings; - QFile::remove(filename); - } else if(p.settings != nullptr) { - p.settings->sync(); - delete p.settings; - } - } -} - -template <> -WebProfile *WebProfileManager<true>::profile(const QString &id) const -{ - return s_instance->profile(id); -} - -template <> -QStringList WebProfileManager<true>::idList() const -{ - return s_instance->idList(); -} - -template <> -void WebProfileManager<false>::walk(std::function<void(const QString &id, WebProfile *profile, QSettings *settings)> f) const -{ - for(auto iter = profiles.begin(); iter != profiles.end(); ++iter) { - f(iter.key(), iter.value().ptr, iter.value().settings); - } -} - -template <> -void WebProfileManager<true>::walk(std::function<void(const QString &id, WebProfile *profile, QSettings *settings)> f) const -{ - s_instance->walk(f); -} - -void profileMenu(QMenu *menu, const std::function<void(WebProfile *)> &callback, WebProfile *current, bool checkable) -{ - auto *group = new QActionGroup(menu); - QObject::connect(menu, &QMenu::aboutToHide, group, &QActionGroup::deleteLater); - - s_instance->walk([=](const QString &, WebProfile *profile, const QSettings *) { - auto *action = menu->addAction(profile->name(), profile, [=]() { callback(profile); }); - action->setCheckable(checkable); - action->setChecked(profile == current); - group->addAction(action); - }); -} diff --git a/src/webengine/webprofilemanager.h b/src/webengine/webprofilemanager.h deleted file mode 100644 index e5df6d5..0000000 --- a/src/webengine/webprofilemanager.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#ifndef SMOLBOTE_WEBPROFILEMANAGER_H -#define SMOLBOTE_WEBPROFILEMANAGER_H - -#include "webprofile.h" -#include <QDir> -#include <QFile> -#include <QFileInfo> -#include <QMap> -#include <QMenu> -#include <functional> - -#if defined(__clang__) -#define consumable(X) [[clang::consumable(X)]] -#define return_typestate(X) [[clang::return_typestate(X)]] -#define set_typestate(X) [[clang::set_typestate(X)]] -#define callable_when(X) [[clang::callable_when(X)]] -#define param_typestate(X) [[clang::param_typestate(X)]] -#else -#define consumable(X) -#define return_typestate(X) -#define set_typestate(X) -#define callable_when(X) -#define param_typestate(X) -#endif - -void profileMenu(QMenu *menu, const std::function<void(WebProfile *)> &callback, WebProfile *current = nullptr, bool checkable = false); - -template <bool use_global = true> -class consumable(unconsumed) WebProfileManager -{ -public: - return_typestate(unconsumed) WebProfileManager() - { - static_assert(use_global); - } - return_typestate(unconsumed) WebProfileManager(const QStringList &paths, const QString &default_id, - const QString &search = QString(), const QUrl &homepage = QUrl(), const QUrl &newtab = QUrl()) - { - static_assert(!use_global); - for(const auto &path : paths) { - const auto id = QFileInfo(path).baseName(); - Profile profile; - profile.settings = WebProfile::load(path, search, homepage, newtab); - profile.ptr = WebProfile::load(id, profile.settings, true); - profiles[id] = profile; - } - - if(!profiles.contains(default_id)) { - auto *settings = WebProfile::load(QString(), search, homepage, newtab); - profiles[default_id] = Profile{ - .settings = settings, - .ptr = WebProfile::load(default_id, settings, true), - }; - } - WebProfile::setDefaultProfile(profiles[default_id].ptr); - } - ~WebProfileManager(); - - WebProfileManager(const WebProfileManager &) = delete; - WebProfileManager &operator=(const WebProfileManager &) = delete; - - return_typestate(unconsumed) WebProfileManager(WebProfileManager<false> && other param_typestate(unconsumed)) - { - static_assert(!use_global); - profiles = std::move(other.profiles); - other.consume(); - } - WebProfileManager &operator=(WebProfileManager &&) = delete; - - callable_when(unconsumed) [[nodiscard]] WebProfile *profile(const QString &id) const - { - return profiles.value(id).ptr; - } - - callable_when(unconsumed) void add(const QString &id, WebProfile *profile, QSettings *settings) - { - if constexpr(use_global) { - return; - } - - if(profile != nullptr && settings != nullptr) { - profiles[id] = Profile{ settings, profile, false }; - } - } - - callable_when(unconsumed) void deleteProfile(const QString &id) - { - if constexpr(use_global) { - return; - } - - if(profiles.contains(id)) { - profiles[id].selfDestruct = true; - } - } - - callable_when(unconsumed) [[nodiscard]] QStringList idList() const - { - return profiles.keys(); - } - - callable_when(unconsumed) void walk(std::function<void(const QString &id, WebProfile *profile, QSettings *settings)>) const; - - callable_when(unconsumed) void make_global(); - -private: - set_typestate(consumed) void consume() {} - - struct Profile { - QSettings *settings = nullptr; - WebProfile *ptr = nullptr; - bool selfDestruct = false; - }; - - QMap<QString, Profile> profiles; -}; - -#endif // SMOLBOTE_PROFILEMANAGER_H diff --git a/src/webengine/webview.cpp b/src/webengine/webview.cpp deleted file mode 100644 index bc52102..0000000 --- a/src/webengine/webview.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#include "webview.h" -#include "webpage.h" -#include "webprofile.h" -#include "webprofilemanager.h" -#include "webviewcontextmenu.h" -#include <QContextMenuEvent> -#include <QWebEngineHistoryItem> - -WebView::WebView(QWidget *parent) - : QWebEngineView(parent) -{ - // load status and progress - connect(this, &QWebEngineView::loadStarted, this, [this]() { - m_loaded = false; - }); - connect(this, &QWebEngineView::loadFinished, this, [this]() { - m_loaded = true; - }); - - // TODO for Qt 5.15, check for fix on QTBUG 65223 - connect(this, &QWebEngineView::loadProgress, this, [this](int progress) { - if(progress == 100) { - emit loadFinished(true); - } - }); -} - -WebView::WebView(WebProfile *profile, cb_createWindow_t cb, QWidget *parent) - : WebView(parent) -{ - cb_createWindow = cb; - setProfile(profile); -} - -WebView::WebView(const Session::WebView &webview_data, cb_createWindow_t cb, QWidget *parent) - : WebView(parent) -{ - cb_createWindow = cb; - WebProfileManager profileManager; - setProfile(profileManager.profile(webview_data.profile)); - - if(!webview_data.url.isEmpty()) - load(QUrl::fromUserInput(webview_data.url)); - else { - QByteArray copy(webview_data.history); - QDataStream historyStream(©, QIODevice::ReadOnly); - historyStream >> *history(); - } -} - -void WebView::setProfile(WebProfile *profile) -{ - m_profile = (profile == nullptr) ? WebProfile::defaultProfile() : profile; - const auto url = this->url(); - setPage(new WebPage(m_profile, this)); - this->load(url); -} - -Session::WebView WebView::serialize() const -{ - QByteArray historyData; - QDataStream historyStream(&historyData, QIODevice::WriteOnly); - historyStream << *history(); - - return { profile()->getId(), QString(), historyData }; -} - -void WebView::search(const QString &term) -{ - const QString searchUrl = m_profile->search().arg(QString(QUrl::toPercentEncoding(term))); - load(searchUrl); -} - -void WebView::contextMenuEvent(QContextMenuEvent *event) -{ - auto *menu = new WebViewContextMenu(this); - //const auto ctxdata = page()->contextMenuData(); - menu->exec(event->globalPos()); -} diff --git a/src/webengine/webview.h b/src/webengine/webview.h deleted file mode 100644 index 538ffa9..0000000 --- a/src/webengine/webview.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#ifndef SMOLBOTE_WEBVIEW_H -#define SMOLBOTE_WEBVIEW_H - -#include "smolbote/session.hpp" -#include "webpage.h" -#include <QWebEngineView> -#include <functional> - -class WebProfile; -class WebViewContextMenu; -class WebView final : public QWebEngineView -{ - friend class WebViewContextMenu; - - Q_OBJECT - - explicit WebView(QWidget *parent = nullptr); - -public: - typedef std::function<WebView *(QWebEnginePage::WebWindowType)> cb_createWindow_t; - - WebView(WebProfile *profile, cb_createWindow_t cb, QWidget *parent = nullptr); - WebView(const Session::WebView &webview_data, cb_createWindow_t cb, QWidget *parent = nullptr); - ~WebView() = default; - - [[nodiscard]] WebProfile *profile() const - { - return m_profile; - } - void setProfile(WebProfile *profile); - - [[nodiscard]] Session::WebView serialize() const; - - bool isLoaded() const - { - return m_loaded; - } - -public slots: - void search(const QString &term); - -signals: - void newBookmark(const QString &title, const QUrl &url); - -protected: - WebView *createWindow(QWebEnginePage::WebWindowType type) override - { - return cb_createWindow ? cb_createWindow(type) : nullptr; - } - void contextMenuEvent(QContextMenuEvent *event) override; - -private: - cb_createWindow_t cb_createWindow; - WebProfile *m_profile = nullptr; - - bool m_loaded = false; -}; - -#endif // SMOLBOTE_WEBVIEW_H diff --git a/src/webengine/webviewcontextmenu.cpp b/src/webengine/webviewcontextmenu.cpp deleted file mode 100644 index c9d809f..0000000 --- a/src/webengine/webviewcontextmenu.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#include "webviewcontextmenu.h" -#include "webprofilemanager.h" -#include "webview.h" -#include "util.h" -#include <QContextMenuEvent> -#include <QDialog> -#include <QMenu> -#include <QSlider> -#include <QStyle> -#include <QToolButton> -#include <QVBoxLayout> -#include <QWebEngineContextMenuData> -#include <QWebEngineHistory> -#include <QWidgetAction> - -constexpr int min_width = 250; -constexpr QSize button_size(32, 32); - -inline QAction *historyAction(QWebEngineView *view, const QWebEngineHistoryItem &item) -{ - auto *action = new QAction(view); - if(item.title().isEmpty()) { - action->setText(item.url().toString()); - } else { - action->setText(QObject::tr("%1 (%2)").arg(item.title(), item.url().toString())); - } - - QObject::connect(action, &QAction::triggered, view, [view, item]() { - view->history()->goToItem(item); - }); - return action; -} - -WebViewContextMenu::WebViewContextMenu(WebView *view) - : QMenu(view) -{ - setMinimumWidth(min_width); - - auto *navButtons = new QWidgetAction(this); - - auto *buttons = new QWidget(this); - auto *buttonsLayout = new QHBoxLayout; - buttonsLayout->setContentsMargins(8, 0, 8, 0); - buttonsLayout->setSpacing(2); - - auto *backButton = new QToolButton(this); - backButton->setMinimumSize(button_size); - backButton->setEnabled(view->history()->canGoBack()); - backButton->setIcon(Util::icon<QStyle::SP_ArrowBack>()); - connect(backButton, &QToolButton::clicked, view, [this, view]() { - view->back(); - this->close(); - }); - buttonsLayout->addWidget(backButton); - - auto *forwardButton = new QToolButton(this); - forwardButton->setMinimumSize(button_size); - forwardButton->setEnabled(view->history()->canGoForward()); - forwardButton->setIcon(Util::icon<QStyle::SP_ArrowForward>()); - connect(forwardButton, &QToolButton::clicked, view, [this, view]() { - view->forward(); - this->close(); - }); - buttonsLayout->addWidget(forwardButton); - - auto *refreshButton = new QToolButton(this); - refreshButton->setMinimumSize(button_size); - refreshButton->setIcon(Util::icon<QStyle::SP_BrowserReload>()); - connect(refreshButton, &QToolButton::clicked, view, [view, this]() { - view->reload(); - this->close(); - }); - buttonsLayout->addWidget(refreshButton); - - buttonsLayout->addStretch(); - - auto *muteButton = new QToolButton(this); - muteButton->setMinimumSize(button_size); - muteButton->setCheckable(true); - muteButton->setChecked(view->page()->isAudioMuted()); - muteButton->setIcon(Util::icon<QStyle::SP_MediaVolume>()); - connect(muteButton, &QToolButton::clicked, view, [view, this](bool checked) { - view->page()->setAudioMuted(checked); - this->close(); - }); - buttonsLayout->addWidget(muteButton); - - buttons->setLayout(buttonsLayout); - navButtons->setDefaultWidget(buttons); - - this->addAction(navButtons); - this->addSeparator(); - - const auto ctxdata = view->page()->contextMenuData(); - - if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeNone) { - auto *backMenu = this->addMenu(tr("Back")); - if(!view->history()->canGoBack()) { - backMenu->setEnabled(false); - } else { - connect(backMenu, &QMenu::aboutToShow, view, [view, backMenu]() { - backMenu->clear(); - const auto backItems = view->history()->backItems(10); - for(const QWebEngineHistoryItem &item : backItems) { - backMenu->addAction(historyAction(view, item)); - } - }); - } - - auto *forwardMenu = this->addMenu(tr("Forward")); - if(!view->history()->canGoForward()) { - forwardMenu->setEnabled(false); - } else { - connect(forwardMenu, &QMenu::aboutToShow, view, [view, forwardMenu]() { - forwardMenu->clear(); - const auto forwardItems = view->history()->forwardItems(10); - for(const QWebEngineHistoryItem &item : forwardItems) { - forwardMenu->addAction(historyAction(view, item)); - } - }); - } - - connect(this->addAction(tr("Reload")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::Reload); - }); - connect(this->addAction(tr("Reload and bypass Cache")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::ReloadAndBypassCache); - }); - - this->addSeparator(); - - connect(this->addAction(tr("Select All")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::SelectAll); - }); - connect(this->addAction(tr("Clear Selection")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::Unselect); - }); - connect(this->addAction(tr("Copy to clipboard")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::Copy); - }); - - } else if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeImage) { - connect(this->addAction(tr("Copy image to clipboard")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::CopyImageToClipboard); - }); - connect(this->addAction(tr("Copy image URL to clipboard")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::CopyImageUrlToClipboard); - }); - if(!ctxdata.mediaUrl().isEmpty()) { - if(view->url() != ctxdata.mediaUrl()) { - connect(this->addAction(tr("Open image")), &QAction::triggered, view, [view, ctxdata]() { - view->load(ctxdata.mediaUrl()); - }); - connect(this->addAction(tr("Open image in new tab")), &QAction::triggered, view, [view, ctxdata]() { - view->createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.mediaUrl()); - }); - } - connect(this->addAction(tr("Save image")), &QAction::triggered, view, [view, ctxdata]() { - view->page()->download(ctxdata.mediaUrl()); - }); - } - - } else { - addMenu(view->page()->createStandardContextMenu()); - } - - if(!ctxdata.linkUrl().isEmpty()) { - this->addSeparator(); - connect(this->addAction(tr("Open link in new tab")), &QAction::triggered, view, [view, ctxdata]() { - view->createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.linkUrl()); - }); - - auto *newTabMenu = this->addMenu(tr("Open link in new tab with profile")); - profileMenu(newTabMenu, [view, ctxdata](WebProfile *profile) { - auto *v = view->createWindow(QWebEnginePage::WebBrowserTab); - v->setProfile(profile); - v->load(ctxdata.linkUrl()); - }); - - connect(this->addAction(tr("Open link in new window")), &QAction::triggered, view, [view, ctxdata]() { - view->createWindow(QWebEnginePage::WebBrowserWindow)->load(ctxdata.linkUrl()); - }); - - connect(this->addAction(tr("Copy link address")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::CopyLinkToClipboard); - }); - } - - // zoom widget - { - this->addSeparator(); - - auto *zoomSlider = new QSlider(Qt::Horizontal); - zoomSlider->setMinimum(5); - zoomSlider->setMaximum(50); - zoomSlider->setValue(static_cast<int>(view->zoomFactor() * 10)); - - auto *zoomAction = this->addAction(tr("Zoom: %1x").arg(view->zoomFactor())); - connect(zoomAction, &QAction::triggered, view, [zoomSlider]() { - zoomSlider->setValue(10); - }); - - connect(zoomSlider, &QSlider::valueChanged, view, [view, zoomAction](int value) { - zoomAction->setText(tr("Zoom: %1x").arg(static_cast<qreal>(value) / 10)); - view->setZoomFactor(static_cast<qreal>(value) / 10); - }); - - auto *zoomWidgetAction = new QWidgetAction(this); - zoomWidgetAction->setDefaultWidget(zoomSlider); - - this->addAction(zoomWidgetAction); - } - -#ifndef NDEBUG - /* - { - this->addSeparator(); - auto *autofillAction = this->addAction(tr("Autofill form")); - connect(autofillAction, &QAction::triggered, view, [view]() { - Wallet::autocompleteForm(view); - }); - }; - */ -#endif -} diff --git a/src/webengine/webviewcontextmenu.h b/src/webengine/webviewcontextmenu.h deleted file mode 100644 index 881670a..0000000 --- a/src/webengine/webviewcontextmenu.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#ifndef SMOLBOTE_WEBVIEWCONTEXTMENU_H -#define SMOLBOTE_WEBVIEWCONTEXTMENU_H - -#include <QMenu> - -class WebView; -class WebViewContextMenu : public QMenu -{ -public: - explicit WebViewContextMenu(WebView *view); -}; - -#endif // SMOLBOTE_WEBVIEWCONTEXTMENU_H |