From 7ddb3b8985649cd27bc52855112b76a56fd0a76e Mon Sep 17 00:00:00 2001 From: David Rosca Date: Tue, 23 Apr 2019 10:39:12 +0200 Subject: [PATCH] WIP: Shared QML engine for plugins --- src/lib/CMakeLists.txt | 3 +- src/lib/plugins/plugins.cpp | 54 ++++++++++++- src/lib/plugins/plugins.h | 2 + .../api/browseraction/qmlbrowseraction.cpp | 14 ++-- src/lib/plugins/qml/api/menus/qmlaction.cpp | 16 ++-- src/lib/plugins/qml/api/menus/qmlaction.h | 5 +- src/lib/plugins/qml/api/menus/qmlmenu.cpp | 42 ++++------ src/lib/plugins/qml/api/menus/qmlmenu.h | 13 ++- .../plugins/qml/api/sidebar/qmlsidebar.cpp | 14 ++-- src/lib/plugins/qml/qmlengine.cpp | 43 ---------- src/lib/plugins/qml/qmlplugin.cpp | 78 ------------------ src/lib/plugins/qml/qmlplugin.h | 28 ------- src/lib/plugins/qml/qmlplugincontext.cpp | 58 ++++++++++++++ .../qml/{qmlengine.h => qmlplugincontext.h} | 26 +++--- src/lib/plugins/qml/qmlplugininterface.cpp | 46 ++++++----- src/lib/plugins/qml/qmlplugininterface.h | 3 - src/lib/plugins/qml/qmlpluginloader.cpp | 80 ++++++++++--------- src/lib/plugins/qml/qmlpluginloader.h | 27 ++++--- src/lib/plugins/qml/qmlplugins.cpp | 12 ++- 19 files changed, 258 insertions(+), 306 deletions(-) delete mode 100644 src/lib/plugins/qml/qmlengine.cpp delete mode 100644 src/lib/plugins/qml/qmlplugin.cpp delete mode 100644 src/lib/plugins/qml/qmlplugin.h create mode 100644 src/lib/plugins/qml/qmlplugincontext.cpp rename src/lib/plugins/qml/{qmlengine.h => qmlplugincontext.h} (64%) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 74775e351..1561f4daa 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -157,11 +157,10 @@ set(SRCS ${SRCS} plugins/plugins.cpp plugins/speeddial.cpp plugins/ocssupport.cpp + plugins/qml/qmlplugincontext.cpp plugins/qml/qmlpluginloader.cpp - plugins/qml/qmlplugin.cpp plugins/qml/qmlplugins.cpp plugins/qml/qmlplugininterface.cpp - plugins/qml/qmlengine.cpp plugins/qml/qmlstaticdata.cpp plugins/qml/api/bookmarks/qmlbookmarktreenode.cpp plugins/qml/api/bookmarks/qmlbookmarks.cpp diff --git a/src/lib/plugins/plugins.cpp b/src/lib/plugins/plugins.cpp index 03f422647..45af02812 100644 --- a/src/lib/plugins/plugins.cpp +++ b/src/lib/plugins/plugins.cpp @@ -25,7 +25,8 @@ #include "../config.h" #include "desktopfile.h" #include "qml/qmlplugins.h" -#include "qml/qmlplugin.h" +#include "qml/qmlpluginloader.h" +#include "qml/qmlplugininterface.h" #include @@ -61,6 +62,7 @@ Plugins::Plugins(QObject* parent) if (!MainApplication::isTestModeEnabled()) { loadPythonSupport(); } + QmlPlugins::registerQmlTypes(); } QList Plugins::availablePlugins() @@ -279,7 +281,7 @@ void Plugins::loadAvailablePlugins() plugin = loadPythonPlugin(pluginPath); } else if (type == QL1S("Extension/Qml")) { // QmlPlugin - plugin = QmlPlugin::loadPlugin(pluginPath); + plugin = loadQmlPlugin(pluginPath); } else { qWarning() << "Invalid type" << type << "of" << pluginPath << "plugin"; } @@ -370,7 +372,7 @@ Plugins::Plugin Plugins::loadPlugin(const QString &id) return loadPythonPlugin(name); case Plugin::QmlPlugin: - return QmlPlugin::loadPlugin(name); + return loadQmlPlugin(name); default: return Plugin(); @@ -429,6 +431,29 @@ 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, 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) { if (!plugin) { @@ -449,7 +474,7 @@ bool Plugins::initPlugin(PluginInterface::InitState state, Plugin *plugin) break; case Plugin::QmlPlugin: - QmlPlugin::initPlugin(plugin); + initQmlPlugin(plugin); break; default: @@ -508,3 +533,24 @@ void Plugins::initPythonPlugin(Plugin *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(); + 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(qmlPluginLoader->instance()); +} diff --git a/src/lib/plugins/plugins.h b/src/lib/plugins/plugins.h index 7481c2df2..00d406914 100644 --- a/src/lib/plugins/plugins.h +++ b/src/lib/plugins/plugins.h @@ -114,10 +114,12 @@ private: 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/api/browseraction/qmlbrowseraction.cpp b/src/lib/plugins/qml/api/browseraction/qmlbrowseraction.cpp index 8ab4f250a..ea32601c6 100644 --- a/src/lib/plugins/qml/api/browseraction/qmlbrowseraction.cpp +++ b/src/lib/plugins/qml/api/browseraction/qmlbrowseraction.cpp @@ -21,10 +21,11 @@ #include "statusbar.h" #include "pluginproxy.h" #include "qml/api/fileutils/qmlfileutils.h" -#include "qml/qmlengine.h" +#include "qml/qmlplugincontext.h" #include "qml/qmlstaticdata.h" + #include -#include +#include QmlBrowserAction::QmlBrowserAction(QObject *parent) : QObject(parent) @@ -217,13 +218,10 @@ void QmlBrowserActionButton::setIcon(const QString &icon) if (!m_popup) { return; } - auto qmlEngine = qobject_cast(m_popup->creationContext()->engine()); - if (!qmlEngine) { - return; - } - const QString pluginPath = qmlEngine->extensionPath(); - QIcon qicon = QmlStaticData::instance().getIcon(m_iconUrl, pluginPath); +#if 0 + QIcon qicon = QmlStaticData::instance().getIcon(m_iconUrl, QmlPluginContext::contextForObject(this)->pluginPath()); AbstractButtonInterface::setIcon(qicon); +#endif } void QmlBrowserActionButton::setBadgeText(const QString &badgeText) diff --git a/src/lib/plugins/qml/api/menus/qmlaction.cpp b/src/lib/plugins/qml/api/menus/qmlaction.cpp index 4a410f01e..c80c46e08 100644 --- a/src/lib/plugins/qml/api/menus/qmlaction.cpp +++ b/src/lib/plugins/qml/api/menus/qmlaction.cpp @@ -18,30 +18,24 @@ #include "qmlaction.h" #include "qztools.h" #include "qml/api/fileutils/qmlfileutils.h" -#include "qml/qmlengine.h" +#include "qml/qmlplugincontext.h" #include "qml/qmlstaticdata.h" -#include -QmlAction::QmlAction(QAction *action, QmlEngine *engine, QObject *parent) +QmlAction::QmlAction(QAction *action, QObject *parent) : QObject(parent) , m_action(action) { - QmlEngine *qmlEngine = qobject_cast(engine); - m_pluginPath = qmlEngine->extensionPath(); + Q_ASSERT(m_action); connect(m_action, &QAction::triggered, this, &QmlAction::triggered); } void QmlAction::setProperties(const QVariantMap &map) { - if (!m_action) { - return; - } - for (auto it = map.cbegin(); it != map.cend(); it++) { const QString key = it.key(); if (key == QSL("icon")) { 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); } else if (key == QSL("shortcut")) { 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); } diff --git a/src/lib/plugins/qml/api/menus/qmlaction.h b/src/lib/plugins/qml/api/menus/qmlaction.h index 05fbf611d..6eff89d95 100644 --- a/src/lib/plugins/qml/api/menus/qmlaction.h +++ b/src/lib/plugins/qml/api/menus/qmlaction.h @@ -21,7 +21,7 @@ #include #include -class QmlEngine; +class QQmlEngine; /** * @brief The class exposing Action API to QML @@ -30,7 +30,7 @@ class QmlAction : public QObject { Q_OBJECT public: - explicit QmlAction(QAction *action, QmlEngine *engine, QObject *parent = nullptr); + explicit QmlAction(QAction *action, QObject *parent = nullptr); void setProperties(const QVariantMap &map); /** * @brief Updates the properties of the action @@ -46,5 +46,4 @@ Q_SIGNALS: private: QAction *m_action = nullptr; - QString m_pluginPath; }; diff --git a/src/lib/plugins/qml/api/menus/qmlmenu.cpp b/src/lib/plugins/qml/api/menus/qmlmenu.cpp index 39d99688c..19a87da1f 100644 --- a/src/lib/plugins/qml/api/menus/qmlmenu.cpp +++ b/src/lib/plugins/qml/api/menus/qmlmenu.cpp @@ -18,61 +18,51 @@ #include "qmlmenu.h" #include "qztools.h" #include "qml/api/fileutils/qmlfileutils.h" -#include "qml/qmlengine.h" +#include "qml/qmlplugincontext.h" #include "qml/qmlstaticdata.h" -QmlMenu::QmlMenu(QMenu *menu, QQmlEngine *engine, QObject *parent) +#include + +QmlMenu::QmlMenu(QMenu *menu, QObject *parent) : QObject(parent) , m_menu(menu) { - QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); - - m_engine = qobject_cast(engine); - m_pluginPath = m_engine->extensionPath(); + Q_ASSERT(m_menu); 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(); - 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); m_menu->addAction(action); - - return qmlAction; + return qmlEngine(this)->newQObject(qmlAction); } -QmlMenu *QmlMenu::addMenu(const QVariantMap &map) +QJSValue QmlMenu::addMenu(const QVariantMap &map) { - if (!m_menu) { - return nullptr; - } - QMenu *newMenu = new QMenu(); for (auto it = map.cbegin(); it != map.cend(); it++) { const QString key = it.key(); if (key == QSL("icon")) { 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); continue; } newMenu->setProperty(key.toUtf8(), map.value(key)); } m_menu->addMenu(newMenu); - QmlMenu *newQmlMenu = new QmlMenu(newMenu, m_engine, this); - return newQmlMenu; + QmlMenu *newQmlMenu = new QmlMenu(newMenu, this); + QQmlEngine::setContextForObject(newQmlMenu, QmlPluginContext::contextForObject(this)); + connect(newQmlMenu, &QObject::destroyed, newMenu, &QObject::deleteLater); + return qmlEngine(this)->newQObject(newQmlMenu); } void QmlMenu::addSeparator() { - if (!m_menu) { - return; - } - m_menu->addSeparator(); } diff --git a/src/lib/plugins/qml/api/menus/qmlmenu.h b/src/lib/plugins/qml/api/menus/qmlmenu.h index def353c87..402baa000 100644 --- a/src/lib/plugins/qml/api/menus/qmlmenu.h +++ b/src/lib/plugins/qml/api/menus/qmlmenu.h @@ -18,10 +18,9 @@ #pragma once #include "qmlaction.h" -#include -#include -class QmlEngine; +#include +#include /** * @brief The class exposing WebView contextmenu to QML as Menu API @@ -30,7 +29,7 @@ class QmlMenu : public QObject { Q_OBJECT public: - explicit QmlMenu(QMenu *menu, QQmlEngine *engine, QObject *parent = nullptr); + explicit QmlMenu(QMenu *menu, QObject *parent = nullptr); /** * @brief Adds action to menu * @param A JavaScript object containing properties for action. @@ -38,14 +37,14 @@ public: * and shortcut in form string. * @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 * @param A JavaScript object containing properties of menu. * The icon property must be in form of url of the path. * @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 */ @@ -59,6 +58,4 @@ Q_SIGNALS: private: QMenu *m_menu = nullptr; - QString m_pluginPath; - QmlEngine *m_engine = nullptr; }; diff --git a/src/lib/plugins/qml/api/sidebar/qmlsidebar.cpp b/src/lib/plugins/qml/api/sidebar/qmlsidebar.cpp index 247cf0f65..aa20411b3 100644 --- a/src/lib/plugins/qml/api/sidebar/qmlsidebar.cpp +++ b/src/lib/plugins/qml/api/sidebar/qmlsidebar.cpp @@ -20,11 +20,12 @@ #include "qztools.h" #include "sidebar.h" #include "qml/api/fileutils/qmlfileutils.h" -#include "qml/qmlengine.h" +#include "qml/qmlplugincontext.h" #include "qml/qmlstaticdata.h" + #include #include -#include +#include QmlSideBar::QmlSideBar(QObject *parent) : QObject(parent) @@ -137,13 +138,10 @@ QAction *QmlSideBarHelper::createMenuAction() if (!m_item) { return action; } - auto qmlEngine = qobject_cast(m_item->creationContext()->engine()); - if (qmlEngine) { - return action; - } - const QString pluginPath = qmlEngine->extensionPath(); - const QIcon icon = QmlStaticData::instance().getIcon(m_iconUrl, pluginPath); +#if 0 + const QIcon icon = QmlStaticData::instance().getIcon(m_iconUrl, QmlPluginContext::contextForObject(this)->pluginPath()); action->setIcon(icon); +#endif return action; } diff --git a/src/lib/plugins/qml/qmlengine.cpp b/src/lib/plugins/qml/qmlengine.cpp deleted file mode 100644 index 131d5b2a4..000000000 --- a/src/lib/plugins/qml/qmlengine.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* ============================================================ -* 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 "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; -} diff --git a/src/lib/plugins/qml/qmlplugin.cpp b/src/lib/plugins/qml/qmlplugin.cpp deleted file mode 100644 index 043b204a1..000000000 --- a/src/lib/plugins/qml/qmlplugin.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* ============================================================ -* 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 "qmlplugin.h" -#include "qmlplugins.h" -#include "qmlpluginloader.h" -#include "datapaths.h" -#include "desktopfile.h" - -#include -#include - -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(); - 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(qmlPluginLoader->instance()); -} diff --git a/src/lib/plugins/qml/qmlplugin.h b/src/lib/plugins/qml/qmlplugin.h deleted file mode 100644 index 7956de547..000000000 --- a/src/lib/plugins/qml/qmlplugin.h +++ /dev/null @@ -1,28 +0,0 @@ -/* ============================================================ -* 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 . -* ============================================================ */ -#pragma once - -#include "plugins.h" - -class QmlPlugin -{ -public: - explicit QmlPlugin(); - static Plugins::Plugin loadPlugin(const QString &name); - static void initPlugin(Plugins::Plugin *plugin); -}; diff --git a/src/lib/plugins/qml/qmlplugincontext.cpp b/src/lib/plugins/qml/qmlplugincontext.cpp new file mode 100644 index 000000000..3585f628c --- /dev/null +++ b/src/lib/plugins/qml/qmlplugincontext.cpp @@ -0,0 +1,58 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2019 David Rosca +* +* 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 "qmlplugincontext.h" + +#include +#include + +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(c); + if (p) { + return p; + } + c = c->parentContext(); + } + qCritical() << "Failed to get plugin context for object" << object; + Q_UNREACHABLE(); + return nullptr; +} diff --git a/src/lib/plugins/qml/qmlengine.h b/src/lib/plugins/qml/qmlplugincontext.h similarity index 64% rename from src/lib/plugins/qml/qmlengine.h rename to src/lib/plugins/qml/qmlplugincontext.h index 064774c97..a723ead94 100644 --- a/src/lib/plugins/qml/qmlengine.h +++ b/src/lib/plugins/qml/qmlplugincontext.h @@ -1,6 +1,6 @@ /* ============================================================ * Falkon - Qt web browser -* Copyright (C) 2018 Anmol Gautam +* Copyright (C) 2019 David Rosca * * 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 @@ -17,18 +17,24 @@ * ============================================================ */ #pragma once -#include +#include -class QmlEngine : public QQmlEngine +#include "plugins.h" + +class QmlPluginContext : public QQmlContext { Q_OBJECT + public: - explicit QmlEngine(QObject *parent = nullptr); - QString extensionName(); - void setExtensionName(const QString &name); - QString extensionPath(); - void setExtensionPath(const QString &path); + explicit QmlPluginContext(const Plugins::Plugin &plugin, QQmlEngine *engine, QObject *parent = nullptr); + + QString pluginPath() const; + QString pluginName() const; + + Plugins::Plugin plugin() const; + + static QmlPluginContext *contextForObject(const QObject *object); + private: - QString m_extensionName; - QString m_extensionPath; + Plugins::Plugin m_plugin; }; diff --git a/src/lib/plugins/qml/qmlplugininterface.cpp b/src/lib/plugins/qml/qmlplugininterface.cpp index 9363d15ef..35435404b 100644 --- a/src/lib/plugins/qml/qmlplugininterface.cpp +++ b/src/lib/plugins/qml/qmlplugininterface.cpp @@ -31,15 +31,18 @@ #include "api/tabs/qmltab.h" #include "webpage.h" #include "qztools.h" -#include "qml/qmlengine.h" +#include "qml/qmlplugincontext.h" + #include #include #include #include +#include QmlPluginInterface::QmlPluginInterface() : m_qmlReusableTab(new QmlTab()) { + // QQmlEngine::setContextForObject(m_qmlReusableTab, QmlPluginContext::contextForObject(this)); } QmlPluginInterface::~QmlPluginInterface() @@ -95,11 +98,13 @@ void QmlPluginInterface::populateWebViewMenu(QMenu *menu, WebView *webview, cons return; } - QmlMenu *qmlMenu = new QmlMenu(menu, m_engine); + QmlMenu *qmlMenu = new QmlMenu(menu); + QQmlEngine::setContextForObject(qmlMenu, QmlPluginContext::contextForObject(this)); QmlWebHitTestResult *qmlWebHitTestResult = new QmlWebHitTestResult(webHitTestResult); + QQmlEngine::setContextForObject(qmlWebHitTestResult, QmlPluginContext::contextForObject(this)); QJSValueList args; - args.append(m_engine->newQObject(qmlMenu)); - args.append(m_engine->newQObject(qmlWebHitTestResult)); + args.append(qmlEngine(this)->newQObject(qmlMenu)); + args.append(qmlEngine(this)->newQObject(qmlWebHitTestResult)); m_populateWebViewMenu.call(args); menu->addSeparator(); } @@ -125,9 +130,10 @@ bool QmlPluginInterface::mouseDoubleClick(Qz::ObjectName type, QObject *obj, QMo return false; } auto qmlMouseEvent = new QmlMouseEvent(event); + QQmlEngine::setContextForObject(qmlMouseEvent, QmlPluginContext::contextForObject(this)); QJSValueList args; args.append(QmlQzObjects::ObjectName(type)); - args.append(m_engine->newQObject(qmlMouseEvent)); + args.append(qmlEngine(this)->newQObject(qmlMouseEvent)); m_mouseDoubleClick.call(args); qmlMouseEvent->clear(); return false; @@ -140,9 +146,10 @@ bool QmlPluginInterface::mousePress(Qz::ObjectName type, QObject *obj, QMouseEve return false; } auto qmlMouseEvent = new QmlMouseEvent(event); + QQmlEngine::setContextForObject(qmlMouseEvent, QmlPluginContext::contextForObject(this)); QJSValueList args; args.append(QmlQzObjects::ObjectName(type)); - args.append(m_engine->newQObject(qmlMouseEvent)); + args.append(qmlEngine(this)->newQObject(qmlMouseEvent)); m_mousePress.call(args); qmlMouseEvent->clear(); return false; @@ -155,9 +162,10 @@ bool QmlPluginInterface::mouseRelease(Qz::ObjectName type, QObject *obj, QMouseE return false; } auto qmlMouseEvent = new QmlMouseEvent(event); + QQmlEngine::setContextForObject(qmlMouseEvent, QmlPluginContext::contextForObject(this)); QJSValueList args; args.append(QmlQzObjects::ObjectName(type)); - args.append(m_engine->newQObject(qmlMouseEvent)); + args.append(qmlEngine(this)->newQObject(qmlMouseEvent)); m_mouseRelease.call(args); qmlMouseEvent->clear(); return false; @@ -170,9 +178,10 @@ bool QmlPluginInterface::mouseMove(Qz::ObjectName type, QObject *obj, QMouseEven return false; } auto qmlMouseEvent = new QmlMouseEvent(event); + QQmlEngine::setContextForObject(qmlMouseEvent, QmlPluginContext::contextForObject(this)); QJSValueList args; args.append(QmlQzObjects::ObjectName(type)); - args.append(m_engine->newQObject(qmlMouseEvent)); + args.append(qmlEngine(this)->newQObject(qmlMouseEvent)); m_mouseMove.call(args); qmlMouseEvent->clear(); return false; @@ -185,9 +194,10 @@ bool QmlPluginInterface::wheelEvent(Qz::ObjectName type, QObject *obj, QWheelEve return false; } auto qmlWheelEvent = new QmlWheelEvent(event); + QQmlEngine::setContextForObject(qmlWheelEvent, QmlPluginContext::contextForObject(this)); QJSValueList args; args.append(QmlQzObjects::ObjectName(type)); - args.append(m_engine->newQObject(qmlWheelEvent)); + args.append(qmlEngine(this)->newQObject(qmlWheelEvent)); m_wheelEvent.call(args); qmlWheelEvent->clear(); return false; @@ -200,9 +210,10 @@ bool QmlPluginInterface::keyPress(Qz::ObjectName type, QObject *obj, QKeyEvent * return false; } auto qmlKeyEvent = new QmlKeyEvent(event); + QQmlEngine::setContextForObject(qmlKeyEvent, QmlPluginContext::contextForObject(this)); QJSValueList args; args.append(QmlQzObjects::ObjectName(type)); - args.append(m_engine->newQObject(qmlKeyEvent)); + args.append(qmlEngine(this)->newQObject(qmlKeyEvent)); m_keyPress.call(args); qmlKeyEvent->clear(); return false; @@ -215,9 +226,10 @@ bool QmlPluginInterface::keyRelease(Qz::ObjectName type, QObject *obj, QKeyEvent return false; } auto qmlKeyEvent = new QmlKeyEvent(event); + QQmlEngine::setContextForObject(qmlKeyEvent, QmlPluginContext::contextForObject(this)); QJSValueList args; args.append(QmlQzObjects::ObjectName(type)); - args.append(m_engine->newQObject(qmlKeyEvent)); + args.append(qmlEngine(this)->newQObject(qmlKeyEvent)); m_keyRelease.call(args); qmlKeyEvent->clear(); return false; @@ -230,7 +242,7 @@ bool QmlPluginInterface::acceptNavigationRequest(WebPage *page, const QUrl &url, } m_qmlReusableTab->setWebPage(page); QJSValueList args; - args.append(m_engine->newQObject(m_qmlReusableTab)); + args.append(qmlEngine(this)->newQObject(m_qmlReusableTab)); args.append(QString::fromUtf8(url.toEncoded())); args.append(type); args.append(isMainFrame); @@ -267,16 +279,6 @@ void QmlPluginInterface::setTestPlugin(const QJSValue &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 { return m_populateWebViewMenu; diff --git a/src/lib/plugins/qml/qmlplugininterface.h b/src/lib/plugins/qml/qmlplugininterface.h index bc2251f04..aafbeea36 100644 --- a/src/lib/plugins/qml/qmlplugininterface.h +++ b/src/lib/plugins/qml/qmlplugininterface.h @@ -53,8 +53,6 @@ public: void init(InitState state, const QString &settingsPath) override; void unload() override; bool testPlugin() override; - void setEngine(QQmlEngine *engine); - void setName(const QString &name); void populateWebViewMenu(QMenu *menu, WebView *webview, const WebHitTestResult &webHitTestResult) override; void showSettings(QWidget *parent = nullptr) override; @@ -74,7 +72,6 @@ Q_SIGNALS: void qmlPluginUnloaded(); private: - QQmlEngine *m_engine = nullptr; QString m_name; QJSValue m_init; QJSValue m_unload; diff --git a/src/lib/plugins/qml/qmlpluginloader.cpp b/src/lib/plugins/qml/qmlpluginloader.cpp index e036cdf86..1f18dea43 100644 --- a/src/lib/plugins/qml/qmlpluginloader.cpp +++ b/src/lib/plugins/qml/qmlpluginloader.cpp @@ -16,42 +16,30 @@ * along with this program. If not, see . * ============================================================ */ #include "qmlpluginloader.h" -#include "qmlengine.h" -#include -#include +#include "qmlplugincontext.h" +#include "qmlplugininterface.h" #include "../config.h" +#include +#include +#include +#include + #if HAVE_LIBINTL #include "qml/api/i18n/qmli18n.h" #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(m_component->create(m_component->creationContext())); - - 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; + return m_component ? m_component->errorString() : QString(); } QmlPluginInterface *QmlPluginLoader::instance() const @@ -59,19 +47,39 @@ QmlPluginInterface *QmlPluginLoader::instance() const return m_interface; } +void QmlPluginLoader::createComponent() +{ + initEngineAndComponent(); + + m_interface = qobject_cast(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() { - m_engine = new QmlEngine(); - m_component = new QQmlComponent(m_engine, QDir(m_path).filePath(QStringLiteral("main.qml"))); - m_engine->setExtensionPath(m_path); - m_engine->setExtensionName(m_name); + if (m_component) { + return; + } + + 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 - auto i18n = new QmlI18n(m_name); - m_engine->globalObject().setProperty(QSL("__falkon_i18n"), m_engine->newQObject(i18n)); - m_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) }")); + auto i18n = new QmlI18n(QFileInfo(m_plugin.pluginPath).fileName()); + s_engine()->globalObject().setProperty(QSL("__falkon_i18n"), s_engine()->newQObject(i18n)); + s_engine()->evaluate(QSL("i18n = function (s) { return __falkon_i18n.i18n(s) };")); + s_engine()->evaluate(QSL("i18np = function (s1, s2) { return __falkon_i18n.i18np(s1, s2) }")); #else - m_engine->evaluate(QSL("i18n = function (s) { return s; };")); - m_engine->evaluate(QSL("i18np = function (s1, s2) { return s1; }")); + s_engine()->evaluate(QSL("i18n = function (s) { return s; };")); + s_engine()->evaluate(QSL("i18np = function (s1, s2) { return s1; }")); #endif } diff --git a/src/lib/plugins/qml/qmlpluginloader.h b/src/lib/plugins/qml/qmlpluginloader.h index 881ab1e02..528eef8fd 100644 --- a/src/lib/plugins/qml/qmlpluginloader.h +++ b/src/lib/plugins/qml/qmlpluginloader.h @@ -17,30 +17,33 @@ * ============================================================ */ #pragma once -#include -#include +#include -#include "qmlplugininterface.h" #include "plugins.h" -class QmlEngine; +class QQmlComponent; + +class QmlPluginContext; +class QmlPluginInterface; class QmlPluginLoader : public QObject { Q_OBJECT public: - explicit QmlPluginLoader(const QString &name, const QString &path); - void createComponent(); - QQmlComponent *component() const; + explicit QmlPluginLoader(const Plugins::Plugin &plugin); + + QString errorString() const; QmlPluginInterface *instance() const; + + void createComponent(); + private: - QString m_path; - QString m_name; - QmlEngine *m_engine = nullptr; + void initEngineAndComponent(); + + Plugins::Plugin m_plugin; + QmlPluginContext *m_context = nullptr; QQmlComponent *m_component = nullptr; QmlPluginInterface *m_interface = nullptr; - - void initEngineAndComponent(); }; Q_DECLARE_METATYPE(QmlPluginLoader *) diff --git a/src/lib/plugins/qml/qmlplugins.cpp b/src/lib/plugins/qml/qmlplugins.cpp index fd9c29536..647e83a77 100644 --- a/src/lib/plugins/qml/qmlplugins.cpp +++ b/src/lib/plugins/qml/qmlplugins.cpp @@ -17,7 +17,7 @@ * ============================================================ */ #include "qmlplugins.h" #include "qmlplugininterface.h" -#include "qmlengine.h" +#include "qmlplugincontext.h" #include "api/bookmarks/qmlbookmarktreenode.h" #include "api/bookmarks/qmlbookmarks.h" #include "api/topsites/qmlmostvisitedurl.h" @@ -116,15 +116,17 @@ void QmlPlugins::registerQmlTypes() qmlRegisterSingletonType(url, majorVersion, minorVersion, "Notifications", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { Q_UNUSED(scriptEngine) - QmlEngine *qmlEngine = qobject_cast(engine); +#if 0 + auto context = qobject_cast(engine); if (!qmlEngine) { qWarning() << "Unable to cast QQmlEngine * to QmlEngine *"; return nullptr; } QString filePath = qmlEngine->extensionPath(); +#endif auto *object = new QmlNotifications(); - object->setPluginPath(filePath); + object->setPluginPath(QString()); return object; }); @@ -202,13 +204,15 @@ void QmlPlugins::registerQmlTypes() qmlRegisterSingletonType(url, majorVersion, minorVersion, "FileUtils", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { Q_UNUSED(scriptEngine) +#if 0 QmlEngine *qmlEngine = qobject_cast(engine); if (!qmlEngine) { qWarning() << "Unable to cast QQmlEngine * to QmlEngine *"; return nullptr; } QString filePath = qmlEngine->extensionPath(); - return new QmlFileUtils(filePath); +#endif + return new QmlFileUtils(QString()); }); qmlRegisterUncreatableType(url, majorVersion, minorVersion, "Enums", QSL("Unable to register type: Enums"));