diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | plugins/webengine/CMakeLists.txt | 12 | ||||
-rw-r--r-- | plugins/webengine/WebEngine.json | 4 | ||||
-rw-r--r-- | plugins/webengine/webengineplugin.cpp (renamed from src/rview.cpp) | 7 | ||||
-rw-r--r-- | plugins/webengine/webengineplugin.hpp | 23 | ||||
-rw-r--r-- | plugins/webengine/webview.cpp (renamed from src/webengine/webview.cpp) | 0 | ||||
-rw-r--r-- | plugins/webengine/webview.h (renamed from src/webengine/webview.h) | 2 | ||||
-rw-r--r-- | src/CMakeLists.txt | 9 | ||||
-rw-r--r-- | src/application.cpp | 37 | ||||
-rw-r--r-- | src/application.h | 4 | ||||
-rw-r--r-- | src/main.cpp | 11 | ||||
-rw-r--r-- | src/plugins/CMakeLists.txt | 7 | ||||
-rw-r--r-- | src/plugins/pluginloader.cpp | 17 | ||||
-rw-r--r-- | src/plugins/pluginloader.h | 25 | ||||
-rw-r--r-- | src/plugins/rplugininterface.hpp | 23 | ||||
-rw-r--r-- | src/plugins/rview.hpp (renamed from src/rview.h) | 2 | ||||
-rw-r--r-- | src/plugins/test/pluginloader.cpp | 40 |
17 files changed, 212 insertions, 12 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 025c40e0..ee184c79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ feature_summary(WHAT ALL) ADD_SUBDIRECTORY( src ) ADD_SUBDIRECTORY( icons ) +add_subdirectory(plugins/webengine) # ================================================================================ diff --git a/plugins/webengine/CMakeLists.txt b/plugins/webengine/CMakeLists.txt new file mode 100644 index 00000000..8a11bc76 --- /dev/null +++ b/plugins/webengine/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(WebEnginePlugin MODULE + webengineplugin.cpp webengineplugin.hpp + webview.cpp webview.h + ${PROJECT_SOURCE_DIR}/src/plugins/rplugininterface.hpp + ${PROJECT_SOURCE_DIR}/src/plugins/rview.hpp +) +target_include_directories(WebEnginePlugin PRIVATE ${PROJECT_SOURCE_DIR}/src/plugins) +target_link_libraries(WebEnginePlugin PUBLIC Qt6::Core Qt6::WebEngineWidgets) + +if(TESTING) + add_test(NAME "load_plugin WebEnginePlugin" COMMAND load_plugin $<TARGET_FILE:WebEnginePlugin>) +endif() diff --git a/plugins/webengine/WebEngine.json b/plugins/webengine/WebEngine.json new file mode 100644 index 00000000..03e2112c --- /dev/null +++ b/plugins/webengine/WebEngine.json @@ -0,0 +1,4 @@ +{ + "name": "WebEngine", + "schemes": [ "file", "http", "https" ] +} diff --git a/src/rview.cpp b/plugins/webengine/webengineplugin.cpp index 2da3096c..cf6811dc 100644 --- a/src/rview.cpp +++ b/plugins/webengine/webengineplugin.cpp @@ -4,9 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-only * Copyright (C) 2022 aqua <aqua@iserlohn-fortress.net> * ============================================================ - * Description: View Interface + * Description: WebEngine plugin View factory * ============================================================ */ -#include "rview.h" +#include "webengineplugin.hpp" +#include "webview.h" -rView::rView(const QUrl &url, QWidget *parent) : QWidget(parent) {} +rView *WebEnginePlugin::view(const QUrl &url) { return new WebView(url, nullptr); } diff --git a/plugins/webengine/webengineplugin.hpp b/plugins/webengine/webengineplugin.hpp new file mode 100644 index 00000000..3ee63ff8 --- /dev/null +++ b/plugins/webengine/webengineplugin.hpp @@ -0,0 +1,23 @@ +/* ============================================================ + * The rekonq project + * ============================================================ + * SPDX-License-Identifier: GPL-3.0-only + * Copyright (C) 2022 aqua <aqua@iserlohn-fortress.net> + * ============================================================ + * Description: WebEngine plugin View factory + * ============================================================ */ + +#pragma once + +#include <rplugininterface.hpp> + +class WebEnginePlugin final : public RekonqPluginInterface { + Q_OBJECT + Q_PLUGIN_METADATA(IID RekonqPluginInterface_iid FILE "WebEngine.json") + Q_INTERFACES(RekonqPluginInterface) + +public: + WebEnginePlugin() = default; + + rView *view(const QUrl &url) override; +}; diff --git a/src/webengine/webview.cpp b/plugins/webengine/webview.cpp index 1aef01e2..1aef01e2 100644 --- a/src/webengine/webview.cpp +++ b/plugins/webengine/webview.cpp diff --git a/src/webengine/webview.h b/plugins/webengine/webview.h index b838c5ac..0b8d0dae 100644 --- a/src/webengine/webview.h +++ b/plugins/webengine/webview.h @@ -9,7 +9,7 @@ #pragma once -#include "rview.h" +#include "rview.hpp" class QWebEngineView; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4aa141c9..5f154e3f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ ### ------- sub dirs ------- ADD_SUBDIRECTORY( data ) +add_subdirectory(plugins) ### ------- SETTING REKONQ FILES.. @@ -216,15 +217,17 @@ ADD_DEFINITIONS ( ${KDE4_DEFINITIONS} ) add_executable(rekonq main.cpp application.cpp application.h # ---------------------------------------- - rview.cpp rview.h webengine/webview.cpp webengine/webview.h + plugins/rplugininterface.hpp plugins/rview.hpp ) ### --------------- TARGETTING LINK LIBRARIES... target_link_libraries(rekonq - Qt6::Widgets Qt6::WebEngineWidgets - SingleApplication::SingleApplication) + Qt6::Widgets + SingleApplication::SingleApplication + pluginloader +) # Nepomuk optional target link libraries IF(SOPRANO_FOUND AND NepomukCore_FOUND) diff --git a/src/application.cpp b/src/application.cpp index 54a7db88..9e34e5e2 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -10,8 +10,9 @@ * ============================================================ */ #include "application.h" -#include "rview.h" -#include "webengine/webview.h" +#include "plugins/pluginloader.h" +#include "plugins/rplugininterface.hpp" +#include <QPluginLoader> // --------------------------------------------------------------------------------------------------------------- // Ctor and Dtor @@ -42,9 +43,29 @@ Application::~Application() // Destroy all web apps for (auto *webApp : m_webApps) delete webApp; + + // Unload all plugins + for (auto *plugin : m_plugins) { + plugin->unload(); + delete plugin; + } } // --------------------------------------------------------------------------------------------------------------- +// Plugin management + +bool Application::registerPlugin(const QString &path) +{ + auto *loader = new PluginLoader(path); + if (!loader->load()) { + delete loader; + return false; + } + + m_plugins.append(loader); + return true; +} +// --------------------------------------------------------------------------------------------------------------- /* int Application::newInstance() { @@ -388,7 +409,17 @@ void Application::setWindowInfo(RekonqWindow *w) rView *Application::newWebApp(const QUrl &url) { - auto *view = new WebView(url); + RekonqPluginInterface *interface = nullptr; + for (auto *plugin : m_plugins) + if (plugin->hasScheme(url.scheme())) { + interface = plugin->interface(); + break; + } + + if (interface == nullptr) return nullptr; + + auto *view = interface->view(url); + Q_CHECK_PTR(view); // tab->installEventFilter(this); m_webApps.append(view); diff --git a/src/application.h b/src/application.h index 761bc89b..407a8681 100644 --- a/src/application.h +++ b/src/application.h @@ -18,6 +18,7 @@ // Forward Declarations class rView; +class PluginLoader; // class RekonqWindow; // class WebTab; @@ -38,6 +39,8 @@ public: Application(int &argc, char *argv[]); ~Application() override; + bool registerPlugin(const QString &path); + // int newInstance(); static Application *instance() { return (qobject_cast<Application *>(QCoreApplication::instance())); } @@ -107,6 +110,7 @@ private slots: // void pageCreated(WebPage *); private: + QList<PluginLoader *> m_plugins; // RekonqWindowList m_rekonqWindows; QList<rView *> m_webApps; }; diff --git a/src/main.cpp b/src/main.cpp index 20c30c68..6db6f36c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,9 @@ static const char *description = "A lightweight Web Browser based on Qt WebEngin int main(int argc, char **argv) { + // When loading QtWebEngine from a plugin, set Qt::AA_ShareOpenGLContexts using QCoreApplication::setAttribute + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); + Application app(argc, argv); // Set application data QCoreApplication::setApplicationName(QL1S("rekonq")); @@ -34,13 +37,19 @@ int main(int argc, char **argv) QCommandLineOption options_incognito("incognito", QCoreApplication::translate("main", "Open in incognito mode")); QCommandLineOption options_webapp("webapp", QCoreApplication::translate("main", "Open URL as web app (in a simple window)")); - parser.addOptions({options_incognito, options_webapp}); + QCommandLineOption options_plugin({"l", "load"}, QCoreApplication::translate("main", "Add plugin to load path"), + "path"); + parser.addOptions({options_incognito, options_webapp, options_plugin}); // Define the positional arguments parser.addPositionalArgument("URL", QCoreApplication::translate("main", "Location to open")); parser.process(app); + if (parser.isSet(options_plugin)) { + for (const auto &plugin : parser.values(options_plugin)) app.registerPlugin(plugin); + } + const auto positionalArguments = parser.positionalArguments(); if (parser.isSet(options_webapp)) positionalArguments.isEmpty() ? app.newWebApp() : app.newWebApp(QUrl::fromUserInput(positionalArguments.first())); diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt new file mode 100644 index 00000000..01b13ae1 --- /dev/null +++ b/src/plugins/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(pluginloader STATIC pluginloader.cpp pluginloader.h) +target_link_libraries(pluginloader PUBLIC Qt6::Core Qt6::Widgets) + +if(TESTING) + add_executable(load_plugin test/pluginloader.cpp) + target_link_libraries(load_plugin GTest::gtest GTest::gtest_main Qt6::Core) +endif() diff --git a/src/plugins/pluginloader.cpp b/src/plugins/pluginloader.cpp new file mode 100644 index 00000000..ad5dbc14 --- /dev/null +++ b/src/plugins/pluginloader.cpp @@ -0,0 +1,17 @@ +/* ============================================================ + * The rekonq project + * ============================================================ + * SPDX-License-Identifier: GPL-3.0-only + * Copyright (C) 2022 aqua <aqua@iserlohn-fortress.net> + * ============================================================ + * Description: rekonq plugin loader + * ============================================================ */ + +#include "pluginloader.h" +#include <QJsonArray> + +PluginLoader::PluginLoader(const QString &fileName, QObject *parent) : QPluginLoader(fileName, parent) +{ + const auto metadata = metaData()["MetaData"].toObject(); + for (const auto &value : metadata["schemes"].toArray()) { m_schemes.append(value.toString()); } +} diff --git a/src/plugins/pluginloader.h b/src/plugins/pluginloader.h new file mode 100644 index 00000000..f764da9f --- /dev/null +++ b/src/plugins/pluginloader.h @@ -0,0 +1,25 @@ +/* ============================================================ + * The rekonq project + * ============================================================ + * SPDX-License-Identifier: GPL-3.0-only + * Copyright (C) 2022 aqua <aqua@iserlohn-fortress.net> + * ============================================================ + * Description: rekonq plugin loader + * ============================================================ */ + +#pragma once + +#include "rplugininterface.hpp" +#include <QPluginLoader> + +class PluginLoader : public QPluginLoader { + +public: + explicit PluginLoader(const QString &path, QObject *parent = nullptr); + [[nodiscard]] bool hasScheme(const QString &scheme) { return m_schemes.contains(scheme); } + + [[nodiscard]] RekonqPluginInterface *interface() { return qobject_cast<RekonqPluginInterface *>(instance()); } + +private: + QStringList m_schemes; +}; diff --git a/src/plugins/rplugininterface.hpp b/src/plugins/rplugininterface.hpp new file mode 100644 index 00000000..16186591 --- /dev/null +++ b/src/plugins/rplugininterface.hpp @@ -0,0 +1,23 @@ +/* ============================================================ + * The rekonq project + * ============================================================ + * SPDX-License-Identifier: GPL-3.0-only + * Copyright (C) 2022 aqua <aqua@iserlohn-fortress.net> + * ============================================================ + * Description: rekonq plugin interface + * ============================================================ */ + +#pragma once + +#include "rview.hpp" +#include <QtPlugin> + +class RekonqPluginInterface : public QObject { + Q_OBJECT + +public: + virtual rView *view(const QUrl &url) = 0; +}; + +#define RekonqPluginInterface_iid "rekonq.3.RekongPluginInterface" +Q_DECLARE_INTERFACE(RekonqPluginInterface, RekonqPluginInterface_iid) diff --git a/src/rview.h b/src/plugins/rview.hpp index 4f4cc897..71bf5ddf 100644 --- a/src/rview.h +++ b/src/plugins/rview.hpp @@ -16,7 +16,7 @@ class rView : public QWidget { Q_OBJECT public: - explicit rView(const QUrl &url = QUrl(), QWidget *parent = nullptr); + explicit rView(const QUrl &url = QUrl(), QWidget *parent = nullptr) : QWidget(parent) {} signals: void iconChanged(const QIcon &); diff --git a/src/plugins/test/pluginloader.cpp b/src/plugins/test/pluginloader.cpp new file mode 100644 index 00000000..83b30c40 --- /dev/null +++ b/src/plugins/test/pluginloader.cpp @@ -0,0 +1,40 @@ +#include <QJsonArray> +#include <QPluginLoader> +#include <gtest/gtest.h> +#include <iostream> + +char *pluginPath; + +class PluginLoaderTest : public ::testing::Test { + +protected: + void SetUp() override { loader = new QPluginLoader; } + void TearDown() override { delete loader; } + + QPluginLoader *loader = nullptr; +}; + +TEST_F(PluginLoaderTest, InterfaceTest) +{ + // Ensure plugin loads + loader->setFileName(pluginPath); + EXPECT_TRUE(loader->load()) << "For plugin: " << pluginPath << '\n' << qUtf8Printable(loader->errorString()); + + // Ensure plugin has metadata + const auto metadata = loader->metaData()["MetaData"].toObject(); + EXPECT_TRUE(metadata.contains("name")); + EXPECT_FALSE(metadata["name"].toString().isEmpty()); + EXPECT_TRUE(metadata.contains("schemes")); + EXPECT_FALSE(metadata["schemes"].toArray().isEmpty()); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " [path to plugin]" << std::endl; + return -1; + } + pluginPath = argv[1]; + return RUN_ALL_TESTS(); +} |