diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 81cc5c8ad..694b9764a 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -154,6 +154,8 @@ set(SRCS ${SRCS} plugins/pluginproxy.cpp plugins/plugins.cpp plugins/speeddial.cpp + plugins/qml/qmlplugins.cpp + plugins/qml/qmlplugininterface.cpp popupwindow/popuplocationbar.cpp popupwindow/popupstatusbarmessage.cpp popupwindow/popupwebview.cpp diff --git a/src/lib/plugins/plugins.cpp b/src/lib/plugins/plugins.cpp index 7a360bcc3..c0c32afb0 100644 --- a/src/lib/plugins/plugins.cpp +++ b/src/lib/plugins/plugins.cpp @@ -24,10 +24,13 @@ #include "adblock/adblockplugin.h" #include "../config.h" #include "desktopfile.h" +#include "qml/qmlplugins.h" #include #include #include +#include +#include Plugins::Plugins(QObject* parent) : QObject(parent) @@ -39,6 +42,8 @@ Plugins::Plugins(QObject* parent) if (!MainApplication::isTestModeEnabled()) { loadPythonSupport(); } + + loadQmlSupport(); } QList Plugins::getAvailablePlugins() @@ -113,6 +118,7 @@ PluginSpec Plugins::createSpec(const DesktopFile &metaData) spec.description = metaData.comment(); spec.version = metaData.value(QSL("X-Falkon-Version")).toString(); spec.author = QSL("%1 <%2>").arg(metaData.value(QSL("X-Falkon-Author")).toString(), metaData.value(QSL("X-Falkon-Email")).toString()); + spec.entryPoint = metaData.value(QSL("X-Falkon-EntryPoint")).toString(); spec.hasSettings = metaData.value(QSL("X-Falkon-Settings")).toBool(); const QString iconName = metaData.icon(); @@ -204,6 +210,24 @@ void Plugins::loadAvailablePlugins() } } } + + // QmlPlugin + for (QString dir: dirs) { + // qml plugins will be loaded from subdirectory qml + dir.append(QSL("/qml")); + const auto qmlDirs = QDir(dir).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QFileInfo &info : qmlDirs) { + Plugin plugin = loadQmlPlugin(info.absoluteFilePath()); + if (plugin.type == Plugin::Invalid) { + continue; + } + if (plugin.pluginSpec.name.isEmpty()) { + qWarning() << "Invalid plugin spec of" << info.absoluteFilePath() << "plugin"; + continue; + } + registerAvailablePlugin(plugin); + } + } } void Plugins::registerAvailablePlugin(const Plugin &plugin) @@ -244,6 +268,11 @@ void Plugins::loadPythonSupport() } } +void Plugins::loadQmlSupport() +{ + QmlPlugins::registerQmlTypes(); +} + Plugins::Plugin Plugins::loadPlugin(const QString &id) { QString name; @@ -258,6 +287,8 @@ Plugins::Plugin Plugins::loadPlugin(const QString &id) type = Plugin::SharedLibraryPlugin; } else if (t == QL1S("python")) { type = Plugin::PythonPlugin; + } else if (t == QL1S("qml")) { + type = Plugin::QmlPlugin; } name = id.mid(colon + 1); } else { @@ -275,6 +306,9 @@ Plugins::Plugin Plugins::loadPlugin(const QString &id) case Plugin::PythonPlugin: return loadPythonPlugin(name); + case Plugin::QmlPlugin: + return loadQmlPlugin(name); + default: return Plugin(); } @@ -340,6 +374,36 @@ Plugins::Plugin Plugins::loadPythonPlugin(const QString &name) return f(name); } +Plugins::Plugin Plugins::loadQmlPlugin(const QString &name) +{ + QString fullPath; + if (QFileInfo(name).isAbsolute()) { + fullPath = name; + } else { + fullPath = DataPaths::locate(DataPaths::Plugins, QSL("qml/") + name); + if (fullPath.isEmpty()) { + qWarning() << "Plugin" << name << "not found"; + return Plugin(); + } + } + + Plugin plugin; + plugin.type = Plugin::QmlPlugin; + plugin.pluginId = QSL("qml:%1").arg(QFileInfo(name).fileName()); + plugin.pluginSpec = createSpec(DesktopFile(fullPath + QSL("/metadata.desktop"))); + + QQmlEngine* engine = new QQmlEngine(); + QQmlComponent component(engine, QDir(fullPath).filePath(plugin.pluginSpec.entryPoint)); + plugin.qmlComponentInstance = qobject_cast(component.create()); + + if (!plugin.qmlComponentInstance) { + qWarning() << "Loading" << fullPath << "failed:" << component.errorString(); + return Plugin(); + } + + return plugin; +} + bool Plugins::initPlugin(PluginInterface::InitState state, Plugin *plugin) { if (!plugin) { @@ -359,6 +423,10 @@ bool Plugins::initPlugin(PluginInterface::InitState state, Plugin *plugin) initPythonPlugin(plugin); break; + case Plugin::QmlPlugin: + initQmlPlugin(plugin); + break; + default: return false; } @@ -409,3 +477,11 @@ void Plugins::initPythonPlugin(Plugin *plugin) f(plugin); } + +void Plugins::initQmlPlugin(Plugin *plugin) +{ + Q_ASSERT(plugin->type == Plugin::QmlPlugin); + + plugin->qmlComponentInstance->setName(plugin->pluginSpec.name); + plugin->instance = qobject_cast(plugin->qmlComponentInstance); +} diff --git a/src/lib/plugins/plugins.h b/src/lib/plugins/plugins.h index bfd77b5f6..a8c58caa4 100644 --- a/src/lib/plugins/plugins.h +++ b/src/lib/plugins/plugins.h @@ -24,6 +24,7 @@ #include "qzcommon.h" #include "plugininterface.h" +#include "qml/qmlplugininterface.h" class QLibrary; class QPluginLoader; @@ -36,6 +37,7 @@ struct PluginSpec { QString author; QString version; QPixmap icon; + QString entryPoint; bool hasSettings = false; bool operator==(const PluginSpec &other) const { @@ -55,7 +57,8 @@ public: Invalid = 0, InternalPlugin, SharedLibraryPlugin, - PythonPlugin + PythonPlugin, + QmlPlugin }; Type type = Invalid; QString pluginId; @@ -69,6 +72,9 @@ public: QString libraryPath; QPluginLoader *pluginLoader = nullptr; + // QmlPlugin + QmlPluginInterface *qmlComponentInstance = nullptr; + // Other QVariant data; @@ -109,14 +115,17 @@ Q_SIGNALS: private: void loadPythonSupport(); + void loadQmlSupport(); Plugin loadPlugin(const QString &id); Plugin loadInternalPlugin(const QString &name); Plugin loadSharedLibraryPlugin(const QString &name); Plugin loadPythonPlugin(const QString &name); + Plugin loadQmlPlugin(const QString &name); bool initPlugin(PluginInterface::InitState state, Plugin *plugin); void initInternalPlugin(Plugin *plugin); void initSharedLibraryPlugin(Plugin *plugin); void initPythonPlugin(Plugin *plugin); + void initQmlPlugin(Plugin *plugin); void registerAvailablePlugin(const Plugin &plugin); diff --git a/src/lib/plugins/qml/qmlplugininterface.cpp b/src/lib/plugins/qml/qmlplugininterface.cpp new file mode 100644 index 000000000..30efc23d1 --- /dev/null +++ b/src/lib/plugins/qml/qmlplugininterface.cpp @@ -0,0 +1,100 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2018 Anmol Gautam +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#include "qmlplugininterface.h" +#include + +QmlPluginInterface::QmlPluginInterface() +{ +} + +void QmlPluginInterface::init(InitState state, const QString &settingsPath) +{ + if (m_jsInit.isCallable()) { + QJSValueList args; + args.append(state); + args.append(settingsPath); + m_jsInit.call(args); + } else { + qWarning() << "Unable to call init on" << m_name << "plugin"; + } +} + +DesktopFile QmlPluginInterface::metaData() const +{ + return DesktopFile(); +} + +void QmlPluginInterface::unload() +{ + if (m_jsUnload.isCallable()) { + m_jsUnload.call(); + } else { + qWarning() << "Unable to call unload on" << m_name << "plugin"; + } +} + +bool QmlPluginInterface::testPlugin() +{ + if (m_jsTestPlugin.isCallable()) { + QJSValue ret = m_jsTestPlugin.call(); + return ret.toBool(); + } else { + qWarning() << "Unable to call testPlugin on" << m_name << "plugin"; + return false; + } +} + +QString QmlPluginInterface::name() +{ + return m_name; +} + +void QmlPluginInterface::setName(const QString &name) +{ + m_name = name; +} + +QJSValue QmlPluginInterface::jsInit() +{ + return m_jsInit; +} + +void QmlPluginInterface::setJsInit(const QJSValue &init) +{ + m_jsInit = init; +} + +QJSValue QmlPluginInterface::jsUnload() +{ + return m_jsUnload; +} + +void QmlPluginInterface::setJsUnload(const QJSValue &unload) +{ + m_jsUnload = unload; +} + +QJSValue QmlPluginInterface::jsTestPlugin() +{ + return m_jsTestPlugin; +} + +void QmlPluginInterface::setJsTestPlugin(const QJSValue &testPlugin) +{ + m_jsTestPlugin = testPlugin; +} diff --git a/src/lib/plugins/qml/qmlplugininterface.h b/src/lib/plugins/qml/qmlplugininterface.h new file mode 100644 index 000000000..cd350389c --- /dev/null +++ b/src/lib/plugins/qml/qmlplugininterface.h @@ -0,0 +1,60 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2018 Anmol Gautam +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#ifndef QMLPLUGININTERFACE_H +#define QMLPLUGININTERFACE_H + +#include +#include + +#include "desktopfile.h" +#include "plugininterface.h" + +class QmlPluginInterface : public QObject, public PluginInterface +{ + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_ENUMS(InitState) + Q_PROPERTY(QString __name__ READ name CONSTANT) + Q_PROPERTY(QJSValue init READ jsInit WRITE setJsInit) + Q_PROPERTY(QJSValue unload READ jsUnload WRITE setJsUnload) + Q_PROPERTY(QJSValue testPlugin READ jsTestPlugin WRITE setJsTestPlugin) + +public: + explicit QmlPluginInterface(); + DesktopFile metaData() const; + void init(InitState state, const QString &settingsPath); + void unload(); + bool testPlugin(); + + QString name(); + void setName(const QString &name); + QJSValue jsInit(); + void setJsInit(const QJSValue &init); + QJSValue jsUnload(); + void setJsUnload(const QJSValue &unload); + QJSValue jsTestPlugin(); + void setJsTestPlugin(const QJSValue &testPlugin); + +private: + QString m_name; + QJSValue m_jsInit; + QJSValue m_jsUnload; + QJSValue m_jsTestPlugin; +}; + +#endif // QMLPLUGININTERFACE_H diff --git a/src/lib/plugins/qml/qmlplugins.cpp b/src/lib/plugins/qml/qmlplugins.cpp new file mode 100644 index 000000000..50f774133 --- /dev/null +++ b/src/lib/plugins/qml/qmlplugins.cpp @@ -0,0 +1,33 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2018 Anmol Gautam +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#include "qmlplugins.h" +#include "qmlplugininterface.h" + +#include + +// static +void QmlPlugins::registerQmlTypes() +{ + registerQmlPluginInterface(); +} + +// private static +void QmlPlugins::registerQmlPluginInterface() +{ + qmlRegisterType("org.kde.falkon", 1, 0, "PluginInterface"); +} diff --git a/src/lib/plugins/qml/qmlplugins.h b/src/lib/plugins/qml/qmlplugins.h new file mode 100644 index 000000000..4abb1afd6 --- /dev/null +++ b/src/lib/plugins/qml/qmlplugins.h @@ -0,0 +1,28 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2018 Anmol Gautam +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#ifndef QMLPLUGINS_H +#define QMLPLUGINS_H + +class QmlPlugins +{ + static void registerQmlPluginInterface(); +public: + static void registerQmlTypes(); +}; + +#endif // QMLPLUGINS_H