1
mirror of https://invent.kde.org/network/falkon.git synced 2024-09-21 09:42:10 +02:00

WIP: Shared QML engine for plugins

This commit is contained in:
David Rosca 2019-04-23 10:39:12 +02:00
parent ee4dbad676
commit 7ddb3b8985
No known key found for this signature in database
GPG Key ID: EBC3FC294452C6D8
19 changed files with 258 additions and 306 deletions

View File

@ -157,11 +157,10 @@ set(SRCS ${SRCS}
plugins/plugins.cpp plugins/plugins.cpp
plugins/speeddial.cpp plugins/speeddial.cpp
plugins/ocssupport.cpp plugins/ocssupport.cpp
plugins/qml/qmlplugincontext.cpp
plugins/qml/qmlpluginloader.cpp plugins/qml/qmlpluginloader.cpp
plugins/qml/qmlplugin.cpp
plugins/qml/qmlplugins.cpp plugins/qml/qmlplugins.cpp
plugins/qml/qmlplugininterface.cpp plugins/qml/qmlplugininterface.cpp
plugins/qml/qmlengine.cpp
plugins/qml/qmlstaticdata.cpp plugins/qml/qmlstaticdata.cpp
plugins/qml/api/bookmarks/qmlbookmarktreenode.cpp plugins/qml/api/bookmarks/qmlbookmarktreenode.cpp
plugins/qml/api/bookmarks/qmlbookmarks.cpp plugins/qml/api/bookmarks/qmlbookmarks.cpp

View File

@ -25,7 +25,8 @@
#include "../config.h" #include "../config.h"
#include "desktopfile.h" #include "desktopfile.h"
#include "qml/qmlplugins.h" #include "qml/qmlplugins.h"
#include "qml/qmlplugin.h" #include "qml/qmlpluginloader.h"
#include "qml/qmlplugininterface.h"
#include <iostream> #include <iostream>
@ -61,6 +62,7 @@ Plugins::Plugins(QObject* parent)
if (!MainApplication::isTestModeEnabled()) { if (!MainApplication::isTestModeEnabled()) {
loadPythonSupport(); loadPythonSupport();
} }
QmlPlugins::registerQmlTypes();
} }
QList<Plugins::Plugin> Plugins::availablePlugins() QList<Plugins::Plugin> Plugins::availablePlugins()
@ -279,7 +281,7 @@ void Plugins::loadAvailablePlugins()
plugin = loadPythonPlugin(pluginPath); plugin = loadPythonPlugin(pluginPath);
} else if (type == QL1S("Extension/Qml")) { } else if (type == QL1S("Extension/Qml")) {
// QmlPlugin // QmlPlugin
plugin = QmlPlugin::loadPlugin(pluginPath); plugin = loadQmlPlugin(pluginPath);
} else { } else {
qWarning() << "Invalid type" << type << "of" << pluginPath << "plugin"; qWarning() << "Invalid type" << type << "of" << pluginPath << "plugin";
} }
@ -370,7 +372,7 @@ Plugins::Plugin Plugins::loadPlugin(const QString &id)
return loadPythonPlugin(name); return loadPythonPlugin(name);
case Plugin::QmlPlugin: case Plugin::QmlPlugin:
return QmlPlugin::loadPlugin(name); return loadQmlPlugin(name);
default: default:
return Plugin(); return Plugin();
@ -429,6 +431,29 @@ Plugins::Plugin Plugins::loadPythonPlugin(const QString &name)
return f(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, name);
if (fullPath.isEmpty()) {
qWarning() << "QML plugin" << name << "not found";
return Plugins::Plugin();
}
}
Plugins::Plugin plugin;
plugin.type = Plugins::Plugin::QmlPlugin;
plugin.pluginId = QSL("qml:%1").arg(QFileInfo(name).fileName());
plugin.pluginPath = fullPath;
DesktopFile desktopFile(fullPath + QSL("/metadata.desktop"));
plugin.pluginSpec = Plugins::createSpec(desktopFile);
plugin.data = QVariant::fromValue(new QmlPluginLoader(plugin));
return plugin;
}
bool Plugins::initPlugin(PluginInterface::InitState state, Plugin *plugin) bool Plugins::initPlugin(PluginInterface::InitState state, Plugin *plugin)
{ {
if (!plugin) { if (!plugin) {
@ -449,7 +474,7 @@ bool Plugins::initPlugin(PluginInterface::InitState state, Plugin *plugin)
break; break;
case Plugin::QmlPlugin: case Plugin::QmlPlugin:
QmlPlugin::initPlugin(plugin); initQmlPlugin(plugin);
break; break;
default: default:
@ -508,3 +533,24 @@ void Plugins::initPythonPlugin(Plugin *plugin)
f(plugin); f(plugin);
} }
void Plugins::initQmlPlugin(Plugin *plugin)
{
Q_ASSERT(plugin->type == Plugins::Plugin::QmlPlugin);
const QString name = plugin->pluginSpec.name;
auto qmlPluginLoader = plugin->data.value<QmlPluginLoader*>();
if (!qmlPluginLoader) {
qWarning() << "Failed to cast from data";
return;
}
qmlPluginLoader->createComponent();
if (!qmlPluginLoader->instance()) {
qWarning() << "Failed to create component for" << name << "plugin:" << qmlPluginLoader->errorString();
return;
}
plugin->instance = qobject_cast<PluginInterface*>(qmlPluginLoader->instance());
}

View File

@ -114,10 +114,12 @@ private:
Plugin loadInternalPlugin(const QString &name); Plugin loadInternalPlugin(const QString &name);
Plugin loadSharedLibraryPlugin(const QString &name); Plugin loadSharedLibraryPlugin(const QString &name);
Plugin loadPythonPlugin(const QString &name); Plugin loadPythonPlugin(const QString &name);
Plugin loadQmlPlugin(const QString &name);
bool initPlugin(PluginInterface::InitState state, Plugin *plugin); bool initPlugin(PluginInterface::InitState state, Plugin *plugin);
void initInternalPlugin(Plugin *plugin); void initInternalPlugin(Plugin *plugin);
void initSharedLibraryPlugin(Plugin *plugin); void initSharedLibraryPlugin(Plugin *plugin);
void initPythonPlugin(Plugin *plugin); void initPythonPlugin(Plugin *plugin);
void initQmlPlugin(Plugin *plugin);
void registerAvailablePlugin(const Plugin &plugin); void registerAvailablePlugin(const Plugin &plugin);

View File

@ -21,10 +21,11 @@
#include "statusbar.h" #include "statusbar.h"
#include "pluginproxy.h" #include "pluginproxy.h"
#include "qml/api/fileutils/qmlfileutils.h" #include "qml/api/fileutils/qmlfileutils.h"
#include "qml/qmlengine.h" #include "qml/qmlplugincontext.h"
#include "qml/qmlstaticdata.h" #include "qml/qmlstaticdata.h"
#include <QQuickWidget> #include <QQuickWidget>
#include <QQmlContext> #include <QQmlEngine>
QmlBrowserAction::QmlBrowserAction(QObject *parent) QmlBrowserAction::QmlBrowserAction(QObject *parent)
: QObject(parent) : QObject(parent)
@ -217,13 +218,10 @@ void QmlBrowserActionButton::setIcon(const QString &icon)
if (!m_popup) { if (!m_popup) {
return; return;
} }
auto qmlEngine = qobject_cast<QmlEngine*>(m_popup->creationContext()->engine()); #if 0
if (!qmlEngine) { QIcon qicon = QmlStaticData::instance().getIcon(m_iconUrl, QmlPluginContext::contextForObject(this)->pluginPath());
return;
}
const QString pluginPath = qmlEngine->extensionPath();
QIcon qicon = QmlStaticData::instance().getIcon(m_iconUrl, pluginPath);
AbstractButtonInterface::setIcon(qicon); AbstractButtonInterface::setIcon(qicon);
#endif
} }
void QmlBrowserActionButton::setBadgeText(const QString &badgeText) void QmlBrowserActionButton::setBadgeText(const QString &badgeText)

View File

@ -18,30 +18,24 @@
#include "qmlaction.h" #include "qmlaction.h"
#include "qztools.h" #include "qztools.h"
#include "qml/api/fileutils/qmlfileutils.h" #include "qml/api/fileutils/qmlfileutils.h"
#include "qml/qmlengine.h" #include "qml/qmlplugincontext.h"
#include "qml/qmlstaticdata.h" #include "qml/qmlstaticdata.h"
#include <QQmlEngine>
QmlAction::QmlAction(QAction *action, QmlEngine *engine, QObject *parent) QmlAction::QmlAction(QAction *action, QObject *parent)
: QObject(parent) : QObject(parent)
, m_action(action) , m_action(action)
{ {
QmlEngine *qmlEngine = qobject_cast<QmlEngine*>(engine); Q_ASSERT(m_action);
m_pluginPath = qmlEngine->extensionPath();
connect(m_action, &QAction::triggered, this, &QmlAction::triggered); connect(m_action, &QAction::triggered, this, &QmlAction::triggered);
} }
void QmlAction::setProperties(const QVariantMap &map) void QmlAction::setProperties(const QVariantMap &map)
{ {
if (!m_action) {
return;
}
for (auto it = map.cbegin(); it != map.cend(); it++) { for (auto it = map.cbegin(); it != map.cend(); it++) {
const QString key = it.key(); const QString key = it.key();
if (key == QSL("icon")) { if (key == QSL("icon")) {
QString iconPath = map.value(key).toString(); QString iconPath = map.value(key).toString();
QIcon icon = QmlStaticData::instance().getIcon(iconPath, m_pluginPath); QIcon icon = QmlStaticData::instance().getIcon(iconPath, QmlPluginContext::contextForObject(this)->pluginPath());
m_action->setIcon(icon); m_action->setIcon(icon);
} else if (key == QSL("shortcut")) { } else if (key == QSL("shortcut")) {
m_action->setShortcut(QKeySequence(map.value(key).toString())); m_action->setShortcut(QKeySequence(map.value(key).toString()));
@ -51,7 +45,7 @@ void QmlAction::setProperties(const QVariantMap &map)
} }
} }
void QmlAction::update(const QVariantMap &map) void QmlAction::update(const QVariantMap &map)
{ {
setProperties(map); setProperties(map);
} }

View File

@ -21,7 +21,7 @@
#include <QAction> #include <QAction>
#include <QVariantMap> #include <QVariantMap>
class QmlEngine; class QQmlEngine;
/** /**
* @brief The class exposing Action API to QML * @brief The class exposing Action API to QML
@ -30,7 +30,7 @@ class QmlAction : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QmlAction(QAction *action, QmlEngine *engine, QObject *parent = nullptr); explicit QmlAction(QAction *action, QObject *parent = nullptr);
void setProperties(const QVariantMap &map); void setProperties(const QVariantMap &map);
/** /**
* @brief Updates the properties of the action * @brief Updates the properties of the action
@ -46,5 +46,4 @@ Q_SIGNALS:
private: private:
QAction *m_action = nullptr; QAction *m_action = nullptr;
QString m_pluginPath;
}; };

View File

@ -18,61 +18,51 @@
#include "qmlmenu.h" #include "qmlmenu.h"
#include "qztools.h" #include "qztools.h"
#include "qml/api/fileutils/qmlfileutils.h" #include "qml/api/fileutils/qmlfileutils.h"
#include "qml/qmlengine.h" #include "qml/qmlplugincontext.h"
#include "qml/qmlstaticdata.h" #include "qml/qmlstaticdata.h"
QmlMenu::QmlMenu(QMenu *menu, QQmlEngine *engine, QObject *parent) #include <QQmlEngine>
QmlMenu::QmlMenu(QMenu *menu, QObject *parent)
: QObject(parent) : QObject(parent)
, m_menu(menu) , m_menu(menu)
{ {
QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); Q_ASSERT(m_menu);
m_engine = qobject_cast<QmlEngine*>(engine);
m_pluginPath = m_engine->extensionPath();
connect(m_menu, &QMenu::triggered, this, &QmlMenu::triggered); connect(m_menu, &QMenu::triggered, this, &QmlMenu::triggered);
} }
QmlAction *QmlMenu::addAction(const QVariantMap &map) QJSValue QmlMenu::addAction(const QVariantMap &map)
{ {
if (!m_menu) {
return nullptr;
}
QAction *action = new QAction(); QAction *action = new QAction();
QmlAction *qmlAction = new QmlAction(action, m_engine, this); QmlAction *qmlAction = new QmlAction(action, this);
QQmlEngine::setContextForObject(qmlAction, QmlPluginContext::contextForObject(this));
action->setParent(qmlAction);
qmlAction->setProperties(map); qmlAction->setProperties(map);
m_menu->addAction(action); m_menu->addAction(action);
return qmlEngine(this)->newQObject(qmlAction);
return qmlAction;
} }
QmlMenu *QmlMenu::addMenu(const QVariantMap &map) QJSValue QmlMenu::addMenu(const QVariantMap &map)
{ {
if (!m_menu) {
return nullptr;
}
QMenu *newMenu = new QMenu(); QMenu *newMenu = new QMenu();
for (auto it = map.cbegin(); it != map.cend(); it++) { for (auto it = map.cbegin(); it != map.cend(); it++) {
const QString key = it.key(); const QString key = it.key();
if (key == QSL("icon")) { if (key == QSL("icon")) {
const QString iconPath = map.value(key).toString(); const QString iconPath = map.value(key).toString();
const QIcon icon = QmlStaticData::instance().getIcon(iconPath, m_pluginPath); const QIcon icon = QmlStaticData::instance().getIcon(iconPath, QmlPluginContext::contextForObject(this)->pluginPath());
newMenu->setIcon(icon); newMenu->setIcon(icon);
continue; continue;
} }
newMenu->setProperty(key.toUtf8(), map.value(key)); newMenu->setProperty(key.toUtf8(), map.value(key));
} }
m_menu->addMenu(newMenu); m_menu->addMenu(newMenu);
QmlMenu *newQmlMenu = new QmlMenu(newMenu, m_engine, this); QmlMenu *newQmlMenu = new QmlMenu(newMenu, this);
return newQmlMenu; QQmlEngine::setContextForObject(newQmlMenu, QmlPluginContext::contextForObject(this));
connect(newQmlMenu, &QObject::destroyed, newMenu, &QObject::deleteLater);
return qmlEngine(this)->newQObject(newQmlMenu);
} }
void QmlMenu::addSeparator() void QmlMenu::addSeparator()
{ {
if (!m_menu) {
return;
}
m_menu->addSeparator(); m_menu->addSeparator();
} }

View File

@ -18,10 +18,9 @@
#pragma once #pragma once
#include "qmlaction.h" #include "qmlaction.h"
#include <QMenu>
#include <QQmlEngine>
class QmlEngine; #include <QMenu>
#include <QJSValue>
/** /**
* @brief The class exposing WebView contextmenu to QML as Menu API * @brief The class exposing WebView contextmenu to QML as Menu API
@ -30,7 +29,7 @@ class QmlMenu : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QmlMenu(QMenu *menu, QQmlEngine *engine, QObject *parent = nullptr); explicit QmlMenu(QMenu *menu, QObject *parent = nullptr);
/** /**
* @brief Adds action to menu * @brief Adds action to menu
* @param A JavaScript object containing properties for action. * @param A JavaScript object containing properties for action.
@ -38,14 +37,14 @@ public:
* and shortcut in form string. * and shortcut in form string.
* @return action of type [QmlAction](@ref QmlAction) * @return action of type [QmlAction](@ref QmlAction)
*/ */
Q_INVOKABLE QmlAction *addAction(const QVariantMap &map); Q_INVOKABLE QJSValue addAction(const QVariantMap &map);
/** /**
* @brief Adds sub-menu to menu * @brief Adds sub-menu to menu
* @param A JavaScript object containing properties of menu. * @param A JavaScript object containing properties of menu.
* The icon property must be in form of url of the path. * The icon property must be in form of url of the path.
* @return menu of type [QmlMenu](@ref QmlMenu) * @return menu of type [QmlMenu](@ref QmlMenu)
*/ */
Q_INVOKABLE QmlMenu *addMenu(const QVariantMap &map); Q_INVOKABLE QJSValue addMenu(const QVariantMap &map);
/** /**
* @brief Adds a separator to menu * @brief Adds a separator to menu
*/ */
@ -59,6 +58,4 @@ Q_SIGNALS:
private: private:
QMenu *m_menu = nullptr; QMenu *m_menu = nullptr;
QString m_pluginPath;
QmlEngine *m_engine = nullptr;
}; };

View File

@ -20,11 +20,12 @@
#include "qztools.h" #include "qztools.h"
#include "sidebar.h" #include "sidebar.h"
#include "qml/api/fileutils/qmlfileutils.h" #include "qml/api/fileutils/qmlfileutils.h"
#include "qml/qmlengine.h" #include "qml/qmlplugincontext.h"
#include "qml/qmlstaticdata.h" #include "qml/qmlstaticdata.h"
#include <QAction> #include <QAction>
#include <QQuickWidget> #include <QQuickWidget>
#include <QQmlContext> #include <QQmlEngine>
QmlSideBar::QmlSideBar(QObject *parent) QmlSideBar::QmlSideBar(QObject *parent)
: QObject(parent) : QObject(parent)
@ -137,13 +138,10 @@ QAction *QmlSideBarHelper::createMenuAction()
if (!m_item) { if (!m_item) {
return action; return action;
} }
auto qmlEngine = qobject_cast<QmlEngine*>(m_item->creationContext()->engine()); #if 0
if (qmlEngine) { const QIcon icon = QmlStaticData::instance().getIcon(m_iconUrl, QmlPluginContext::contextForObject(this)->pluginPath());
return action;
}
const QString pluginPath = qmlEngine->extensionPath();
const QIcon icon = QmlStaticData::instance().getIcon(m_iconUrl, pluginPath);
action->setIcon(icon); action->setIcon(icon);
#endif
return action; return action;
} }

View File

@ -1,43 +0,0 @@
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 Anmol Gautam <tarptaeya@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "qmlengine.h"
QmlEngine::QmlEngine(QObject *parent)
: QQmlEngine(parent)
{
}
QString QmlEngine::extensionName()
{
return m_extensionName;
}
void QmlEngine::setExtensionName(const QString &name)
{
m_extensionName = name;
}
QString QmlEngine::extensionPath()
{
return m_extensionPath;
}
void QmlEngine::setExtensionPath(const QString &path)
{
m_extensionPath = path;
}

View File

@ -1,78 +0,0 @@
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 Anmol Gautam <tarptaeya@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "qmlplugin.h"
#include "qmlplugins.h"
#include "qmlpluginloader.h"
#include "datapaths.h"
#include "desktopfile.h"
#include <QFileInfo>
#include <QDir>
QmlPlugin::QmlPlugin()
{
}
Plugins::Plugin QmlPlugin::loadPlugin(const QString &name)
{
static bool qmlSupportLoaded = false;
if (!qmlSupportLoaded) {
QmlPlugins::registerQmlTypes();
qmlSupportLoaded = true;
}
QString fullPath;
if (QFileInfo(name).isAbsolute()) {
fullPath = name;
} else {
fullPath = DataPaths::locate(DataPaths::Plugins, name);
if (fullPath.isEmpty()) {
qWarning() << "QML plugin" << name << "not found";
return Plugins::Plugin();
}
}
Plugins::Plugin plugin;
plugin.type = Plugins::Plugin::QmlPlugin;
plugin.pluginId = QSL("qml:%1").arg(QFileInfo(name).fileName());
plugin.pluginPath = fullPath;
DesktopFile desktopFile(fullPath + QSL("/metadata.desktop"));
plugin.pluginSpec = Plugins::createSpec(desktopFile);
plugin.data = QVariant::fromValue(new QmlPluginLoader(plugin.pluginSpec.name, fullPath));
return plugin;
}
void QmlPlugin::initPlugin(Plugins::Plugin *plugin)
{
Q_ASSERT(plugin->type == Plugins::Plugin::QmlPlugin);
const QString name = plugin->pluginSpec.name;
auto qmlPluginLoader = plugin->data.value<QmlPluginLoader*>();
if (!qmlPluginLoader) {
qWarning() << "Failed to cast from data";
return;
}
qmlPluginLoader->createComponent();
if (!qmlPluginLoader->instance()) {
qWarning().noquote() << "Failed to create component for" << name << "plugin:" << qmlPluginLoader->component()->errorString();
return;
}
plugin->instance = qobject_cast<PluginInterface*>(qmlPluginLoader->instance());
}

View File

@ -1,28 +0,0 @@
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 Anmol Gautam <tarptaeya@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include "plugins.h"
class QmlPlugin
{
public:
explicit QmlPlugin();
static Plugins::Plugin loadPlugin(const QString &name);
static void initPlugin(Plugins::Plugin *plugin);
};

View File

@ -0,0 +1,58 @@
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2019 David Rosca <nowrep@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "qmlplugincontext.h"
#include <QtQml>
#include <QFileInfo>
QmlPluginContext::QmlPluginContext(const Plugins::Plugin &plugin, QQmlEngine *engine, QObject *parent)
: QQmlContext(engine, parent)
, m_plugin(plugin)
{
}
QString QmlPluginContext::pluginPath() const
{
return m_plugin.pluginPath;
}
QString QmlPluginContext::pluginName() const
{
return QFileInfo(m_plugin.pluginPath).fileName();
}
Plugins::Plugin QmlPluginContext::plugin() const
{
return m_plugin;
}
// static
QmlPluginContext *QmlPluginContext::contextForObject(const QObject *object)
{
QQmlContext *c = qmlContext(object);
while (c) {
QmlPluginContext *p = qobject_cast<QmlPluginContext*>(c);
if (p) {
return p;
}
c = c->parentContext();
}
qCritical() << "Failed to get plugin context for object" << object;
Q_UNREACHABLE();
return nullptr;
}

View File

@ -1,6 +1,6 @@
/* ============================================================ /* ============================================================
* Falkon - Qt web browser * Falkon - Qt web browser
* Copyright (C) 2018 Anmol Gautam <tarptaeya@gmail.com> * Copyright (C) 2019 David Rosca <nowrep@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -17,18 +17,24 @@
* ============================================================ */ * ============================================================ */
#pragma once #pragma once
#include <QQmlEngine> #include <QQmlContext>
class QmlEngine : public QQmlEngine #include "plugins.h"
class QmlPluginContext : public QQmlContext
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QmlEngine(QObject *parent = nullptr); explicit QmlPluginContext(const Plugins::Plugin &plugin, QQmlEngine *engine, QObject *parent = nullptr);
QString extensionName();
void setExtensionName(const QString &name); QString pluginPath() const;
QString extensionPath(); QString pluginName() const;
void setExtensionPath(const QString &path);
Plugins::Plugin plugin() const;
static QmlPluginContext *contextForObject(const QObject *object);
private: private:
QString m_extensionName; Plugins::Plugin m_plugin;
QString m_extensionPath;
}; };

View File

@ -31,15 +31,18 @@
#include "api/tabs/qmltab.h" #include "api/tabs/qmltab.h"
#include "webpage.h" #include "webpage.h"
#include "qztools.h" #include "qztools.h"
#include "qml/qmlengine.h" #include "qml/qmlplugincontext.h"
#include <QDebug> #include <QDebug>
#include <QQuickWidget> #include <QQuickWidget>
#include <QDialog> #include <QDialog>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QQmlEngine>
QmlPluginInterface::QmlPluginInterface() QmlPluginInterface::QmlPluginInterface()
: m_qmlReusableTab(new QmlTab()) : m_qmlReusableTab(new QmlTab())
{ {
// QQmlEngine::setContextForObject(m_qmlReusableTab, QmlPluginContext::contextForObject(this));
} }
QmlPluginInterface::~QmlPluginInterface() QmlPluginInterface::~QmlPluginInterface()
@ -95,11 +98,13 @@ void QmlPluginInterface::populateWebViewMenu(QMenu *menu, WebView *webview, cons
return; return;
} }
QmlMenu *qmlMenu = new QmlMenu(menu, m_engine); QmlMenu *qmlMenu = new QmlMenu(menu);
QQmlEngine::setContextForObject(qmlMenu, QmlPluginContext::contextForObject(this));
QmlWebHitTestResult *qmlWebHitTestResult = new QmlWebHitTestResult(webHitTestResult); QmlWebHitTestResult *qmlWebHitTestResult = new QmlWebHitTestResult(webHitTestResult);
QQmlEngine::setContextForObject(qmlWebHitTestResult, QmlPluginContext::contextForObject(this));
QJSValueList args; QJSValueList args;
args.append(m_engine->newQObject(qmlMenu)); args.append(qmlEngine(this)->newQObject(qmlMenu));
args.append(m_engine->newQObject(qmlWebHitTestResult)); args.append(qmlEngine(this)->newQObject(qmlWebHitTestResult));
m_populateWebViewMenu.call(args); m_populateWebViewMenu.call(args);
menu->addSeparator(); menu->addSeparator();
} }
@ -125,9 +130,10 @@ bool QmlPluginInterface::mouseDoubleClick(Qz::ObjectName type, QObject *obj, QMo
return false; return false;
} }
auto qmlMouseEvent = new QmlMouseEvent(event); auto qmlMouseEvent = new QmlMouseEvent(event);
QQmlEngine::setContextForObject(qmlMouseEvent, QmlPluginContext::contextForObject(this));
QJSValueList args; QJSValueList args;
args.append(QmlQzObjects::ObjectName(type)); args.append(QmlQzObjects::ObjectName(type));
args.append(m_engine->newQObject(qmlMouseEvent)); args.append(qmlEngine(this)->newQObject(qmlMouseEvent));
m_mouseDoubleClick.call(args); m_mouseDoubleClick.call(args);
qmlMouseEvent->clear(); qmlMouseEvent->clear();
return false; return false;
@ -140,9 +146,10 @@ bool QmlPluginInterface::mousePress(Qz::ObjectName type, QObject *obj, QMouseEve
return false; return false;
} }
auto qmlMouseEvent = new QmlMouseEvent(event); auto qmlMouseEvent = new QmlMouseEvent(event);
QQmlEngine::setContextForObject(qmlMouseEvent, QmlPluginContext::contextForObject(this));
QJSValueList args; QJSValueList args;
args.append(QmlQzObjects::ObjectName(type)); args.append(QmlQzObjects::ObjectName(type));
args.append(m_engine->newQObject(qmlMouseEvent)); args.append(qmlEngine(this)->newQObject(qmlMouseEvent));
m_mousePress.call(args); m_mousePress.call(args);
qmlMouseEvent->clear(); qmlMouseEvent->clear();
return false; return false;
@ -155,9 +162,10 @@ bool QmlPluginInterface::mouseRelease(Qz::ObjectName type, QObject *obj, QMouseE
return false; return false;
} }
auto qmlMouseEvent = new QmlMouseEvent(event); auto qmlMouseEvent = new QmlMouseEvent(event);
QQmlEngine::setContextForObject(qmlMouseEvent, QmlPluginContext::contextForObject(this));
QJSValueList args; QJSValueList args;
args.append(QmlQzObjects::ObjectName(type)); args.append(QmlQzObjects::ObjectName(type));
args.append(m_engine->newQObject(qmlMouseEvent)); args.append(qmlEngine(this)->newQObject(qmlMouseEvent));
m_mouseRelease.call(args); m_mouseRelease.call(args);
qmlMouseEvent->clear(); qmlMouseEvent->clear();
return false; return false;
@ -170,9 +178,10 @@ bool QmlPluginInterface::mouseMove(Qz::ObjectName type, QObject *obj, QMouseEven
return false; return false;
} }
auto qmlMouseEvent = new QmlMouseEvent(event); auto qmlMouseEvent = new QmlMouseEvent(event);
QQmlEngine::setContextForObject(qmlMouseEvent, QmlPluginContext::contextForObject(this));
QJSValueList args; QJSValueList args;
args.append(QmlQzObjects::ObjectName(type)); args.append(QmlQzObjects::ObjectName(type));
args.append(m_engine->newQObject(qmlMouseEvent)); args.append(qmlEngine(this)->newQObject(qmlMouseEvent));
m_mouseMove.call(args); m_mouseMove.call(args);
qmlMouseEvent->clear(); qmlMouseEvent->clear();
return false; return false;
@ -185,9 +194,10 @@ bool QmlPluginInterface::wheelEvent(Qz::ObjectName type, QObject *obj, QWheelEve
return false; return false;
} }
auto qmlWheelEvent = new QmlWheelEvent(event); auto qmlWheelEvent = new QmlWheelEvent(event);
QQmlEngine::setContextForObject(qmlWheelEvent, QmlPluginContext::contextForObject(this));
QJSValueList args; QJSValueList args;
args.append(QmlQzObjects::ObjectName(type)); args.append(QmlQzObjects::ObjectName(type));
args.append(m_engine->newQObject(qmlWheelEvent)); args.append(qmlEngine(this)->newQObject(qmlWheelEvent));
m_wheelEvent.call(args); m_wheelEvent.call(args);
qmlWheelEvent->clear(); qmlWheelEvent->clear();
return false; return false;
@ -200,9 +210,10 @@ bool QmlPluginInterface::keyPress(Qz::ObjectName type, QObject *obj, QKeyEvent *
return false; return false;
} }
auto qmlKeyEvent = new QmlKeyEvent(event); auto qmlKeyEvent = new QmlKeyEvent(event);
QQmlEngine::setContextForObject(qmlKeyEvent, QmlPluginContext::contextForObject(this));
QJSValueList args; QJSValueList args;
args.append(QmlQzObjects::ObjectName(type)); args.append(QmlQzObjects::ObjectName(type));
args.append(m_engine->newQObject(qmlKeyEvent)); args.append(qmlEngine(this)->newQObject(qmlKeyEvent));
m_keyPress.call(args); m_keyPress.call(args);
qmlKeyEvent->clear(); qmlKeyEvent->clear();
return false; return false;
@ -215,9 +226,10 @@ bool QmlPluginInterface::keyRelease(Qz::ObjectName type, QObject *obj, QKeyEvent
return false; return false;
} }
auto qmlKeyEvent = new QmlKeyEvent(event); auto qmlKeyEvent = new QmlKeyEvent(event);
QQmlEngine::setContextForObject(qmlKeyEvent, QmlPluginContext::contextForObject(this));
QJSValueList args; QJSValueList args;
args.append(QmlQzObjects::ObjectName(type)); args.append(QmlQzObjects::ObjectName(type));
args.append(m_engine->newQObject(qmlKeyEvent)); args.append(qmlEngine(this)->newQObject(qmlKeyEvent));
m_keyRelease.call(args); m_keyRelease.call(args);
qmlKeyEvent->clear(); qmlKeyEvent->clear();
return false; return false;
@ -230,7 +242,7 @@ bool QmlPluginInterface::acceptNavigationRequest(WebPage *page, const QUrl &url,
} }
m_qmlReusableTab->setWebPage(page); m_qmlReusableTab->setWebPage(page);
QJSValueList args; QJSValueList args;
args.append(m_engine->newQObject(m_qmlReusableTab)); args.append(qmlEngine(this)->newQObject(m_qmlReusableTab));
args.append(QString::fromUtf8(url.toEncoded())); args.append(QString::fromUtf8(url.toEncoded()));
args.append(type); args.append(type);
args.append(isMainFrame); args.append(isMainFrame);
@ -267,16 +279,6 @@ void QmlPluginInterface::setTestPlugin(const QJSValue &testPlugin)
m_testPlugin = testPlugin; m_testPlugin = testPlugin;
} }
void QmlPluginInterface::setEngine(QQmlEngine *engine)
{
m_engine = engine;
}
void QmlPluginInterface::setName(const QString &name)
{
m_name = name;
}
QJSValue QmlPluginInterface::readPopulateWebViewMenu() const QJSValue QmlPluginInterface::readPopulateWebViewMenu() const
{ {
return m_populateWebViewMenu; return m_populateWebViewMenu;

View File

@ -53,8 +53,6 @@ public:
void init(InitState state, const QString &settingsPath) override; void init(InitState state, const QString &settingsPath) override;
void unload() override; void unload() override;
bool testPlugin() override; bool testPlugin() override;
void setEngine(QQmlEngine *engine);
void setName(const QString &name);
void populateWebViewMenu(QMenu *menu, WebView *webview, const WebHitTestResult &webHitTestResult) override; void populateWebViewMenu(QMenu *menu, WebView *webview, const WebHitTestResult &webHitTestResult) override;
void showSettings(QWidget *parent = nullptr) override; void showSettings(QWidget *parent = nullptr) override;
@ -74,7 +72,6 @@ Q_SIGNALS:
void qmlPluginUnloaded(); void qmlPluginUnloaded();
private: private:
QQmlEngine *m_engine = nullptr;
QString m_name; QString m_name;
QJSValue m_init; QJSValue m_init;
QJSValue m_unload; QJSValue m_unload;

View File

@ -16,42 +16,30 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */ * ============================================================ */
#include "qmlpluginloader.h" #include "qmlpluginloader.h"
#include "qmlengine.h" #include "qmlplugincontext.h"
#include <QQmlContext> #include "qmlplugininterface.h"
#include <QDir>
#include "../config.h" #include "../config.h"
#include <QDir>
#include <QFileInfo>
#include <QQmlEngine>
#include <QQmlComponent>
#if HAVE_LIBINTL #if HAVE_LIBINTL
#include "qml/api/i18n/qmli18n.h" #include "qml/api/i18n/qmli18n.h"
#endif #endif
QmlPluginLoader::QmlPluginLoader(const QString &name, const QString &path) Q_GLOBAL_STATIC(QQmlEngine, s_engine)
QmlPluginLoader::QmlPluginLoader(const Plugins::Plugin &plugin)
: QObject()
, m_plugin(plugin)
{ {
m_name = name;
m_path = path;
initEngineAndComponent();
} }
void QmlPluginLoader::createComponent() QString QmlPluginLoader::errorString() const
{ {
m_interface = qobject_cast<QmlPluginInterface*>(m_component->create(m_component->creationContext())); return m_component ? m_component->errorString() : QString();
if (!m_interface) {
return;
}
m_interface->setEngine(m_engine);
m_interface->setName(m_name);
connect(m_interface, &QmlPluginInterface::qmlPluginUnloaded, this, [this] {
delete m_component;
delete m_engine;
initEngineAndComponent();
});
}
QQmlComponent *QmlPluginLoader::component() const
{
return m_component;
} }
QmlPluginInterface *QmlPluginLoader::instance() const QmlPluginInterface *QmlPluginLoader::instance() const
@ -59,19 +47,39 @@ QmlPluginInterface *QmlPluginLoader::instance() const
return m_interface; return m_interface;
} }
void QmlPluginLoader::createComponent()
{
initEngineAndComponent();
m_interface = qobject_cast<QmlPluginInterface*>(m_component->create(m_context));
if (!m_interface) {
qWarning() << "Failed to create QmlPluginInterface!";
return;
}
connect(m_interface, &QmlPluginInterface::qmlPluginUnloaded, this, [this] {
m_component->deleteLater();
m_component = nullptr;
m_context->deleteLater();
m_context = nullptr;
});
}
void QmlPluginLoader::initEngineAndComponent() void QmlPluginLoader::initEngineAndComponent()
{ {
m_engine = new QmlEngine(); if (m_component) {
m_component = new QQmlComponent(m_engine, QDir(m_path).filePath(QStringLiteral("main.qml"))); return;
m_engine->setExtensionPath(m_path); }
m_engine->setExtensionName(m_name);
m_component = new QQmlComponent(s_engine(), QDir(m_plugin.pluginPath).filePath(QStringLiteral("main.qml")), this);
m_context = new QmlPluginContext(m_plugin, s_engine(), this);
#if HAVE_LIBINTL #if HAVE_LIBINTL
auto i18n = new QmlI18n(m_name); auto i18n = new QmlI18n(QFileInfo(m_plugin.pluginPath).fileName());
m_engine->globalObject().setProperty(QSL("__falkon_i18n"), m_engine->newQObject(i18n)); s_engine()->globalObject().setProperty(QSL("__falkon_i18n"), s_engine()->newQObject(i18n));
m_engine->evaluate(QSL("i18n = function (s) { return __falkon_i18n.i18n(s) };")); s_engine()->evaluate(QSL("i18n = function (s) { return __falkon_i18n.i18n(s) };"));
m_engine->evaluate(QSL("i18np = function (s1, s2) { return __falkon_i18n.i18np(s1, s2) }")); s_engine()->evaluate(QSL("i18np = function (s1, s2) { return __falkon_i18n.i18np(s1, s2) }"));
#else #else
m_engine->evaluate(QSL("i18n = function (s) { return s; };")); s_engine()->evaluate(QSL("i18n = function (s) { return s; };"));
m_engine->evaluate(QSL("i18np = function (s1, s2) { return s1; }")); s_engine()->evaluate(QSL("i18np = function (s1, s2) { return s1; }"));
#endif #endif
} }

View File

@ -17,30 +17,33 @@
* ============================================================ */ * ============================================================ */
#pragma once #pragma once
#include <QQmlEngine> #include <QObject>
#include <QQmlComponent>
#include "qmlplugininterface.h"
#include "plugins.h" #include "plugins.h"
class QmlEngine; class QQmlComponent;
class QmlPluginContext;
class QmlPluginInterface;
class QmlPluginLoader : public QObject class QmlPluginLoader : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QmlPluginLoader(const QString &name, const QString &path); explicit QmlPluginLoader(const Plugins::Plugin &plugin);
void createComponent();
QQmlComponent *component() const; QString errorString() const;
QmlPluginInterface *instance() const; QmlPluginInterface *instance() const;
void createComponent();
private: private:
QString m_path; void initEngineAndComponent();
QString m_name;
QmlEngine *m_engine = nullptr; Plugins::Plugin m_plugin;
QmlPluginContext *m_context = nullptr;
QQmlComponent *m_component = nullptr; QQmlComponent *m_component = nullptr;
QmlPluginInterface *m_interface = nullptr; QmlPluginInterface *m_interface = nullptr;
void initEngineAndComponent();
}; };
Q_DECLARE_METATYPE(QmlPluginLoader *) Q_DECLARE_METATYPE(QmlPluginLoader *)

View File

@ -17,7 +17,7 @@
* ============================================================ */ * ============================================================ */
#include "qmlplugins.h" #include "qmlplugins.h"
#include "qmlplugininterface.h" #include "qmlplugininterface.h"
#include "qmlengine.h" #include "qmlplugincontext.h"
#include "api/bookmarks/qmlbookmarktreenode.h" #include "api/bookmarks/qmlbookmarktreenode.h"
#include "api/bookmarks/qmlbookmarks.h" #include "api/bookmarks/qmlbookmarks.h"
#include "api/topsites/qmlmostvisitedurl.h" #include "api/topsites/qmlmostvisitedurl.h"
@ -116,15 +116,17 @@ void QmlPlugins::registerQmlTypes()
qmlRegisterSingletonType<QmlNotifications>(url, majorVersion, minorVersion, "Notifications", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { qmlRegisterSingletonType<QmlNotifications>(url, majorVersion, minorVersion, "Notifications", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
Q_UNUSED(scriptEngine) Q_UNUSED(scriptEngine)
QmlEngine *qmlEngine = qobject_cast<QmlEngine*>(engine); #if 0
auto context = qobject_cast<QmlEngine*>(engine);
if (!qmlEngine) { if (!qmlEngine) {
qWarning() << "Unable to cast QQmlEngine * to QmlEngine *"; qWarning() << "Unable to cast QQmlEngine * to QmlEngine *";
return nullptr; return nullptr;
} }
QString filePath = qmlEngine->extensionPath(); QString filePath = qmlEngine->extensionPath();
#endif
auto *object = new QmlNotifications(); auto *object = new QmlNotifications();
object->setPluginPath(filePath); object->setPluginPath(QString());
return object; return object;
}); });
@ -202,13 +204,15 @@ void QmlPlugins::registerQmlTypes()
qmlRegisterSingletonType<QmlFileUtils>(url, majorVersion, minorVersion, "FileUtils", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { qmlRegisterSingletonType<QmlFileUtils>(url, majorVersion, minorVersion, "FileUtils", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
Q_UNUSED(scriptEngine) Q_UNUSED(scriptEngine)
#if 0
QmlEngine *qmlEngine = qobject_cast<QmlEngine*>(engine); QmlEngine *qmlEngine = qobject_cast<QmlEngine*>(engine);
if (!qmlEngine) { if (!qmlEngine) {
qWarning() << "Unable to cast QQmlEngine * to QmlEngine *"; qWarning() << "Unable to cast QQmlEngine * to QmlEngine *";
return nullptr; return nullptr;
} }
QString filePath = qmlEngine->extensionPath(); QString filePath = qmlEngine->extensionPath();
return new QmlFileUtils(filePath); #endif
return new QmlFileUtils(QString());
}); });
qmlRegisterUncreatableType<QmlEnums>(url, majorVersion, minorVersion, "Enums", QSL("Unable to register type: Enums")); qmlRegisterUncreatableType<QmlEnums>(url, majorVersion, minorVersion, "Enums", QSL("Unable to register type: Enums"));