Commit fac27b49 authored by Arjen Hiemstra's avatar Arjen Hiemstra
Browse files

QmlObject: Use std::shared_ptr to properly track the lifetime of QQmlEngine

The engine passed to QmlObject is potentially shared, either because it
comes from QmlObjectSharedEngine or because the caller is already using
it. Since raw pointers do not provide any information about that,
deprecate the raw pointer constructors and replace them with a
constructor taking a std::shared_ptr.

This allows QmlObject to properly track if its the first user of the
QQmlEngine and if so, call KDeclarative::setupEngine on it. It also
allows it to know if it is the *last* user of the engine, and in that
case properly cleanup the QNAM factory, without affecting the shared
engine case.

BUG: 451790
parent 07b1e86b
......@@ -25,7 +25,7 @@ ecm_generate_export_header(KF5Declarative
GROUP_BASE_NAME KF
VERSION ${KF_VERSION}
DEPRECATED_BASE_VERSION 0
DEPRECATION_VERSIONS 5.0 5.45 5.75 5.91
DEPRECATION_VERSIONS 5.0 5.45 5.75 5.91 5.95
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
)
......
......@@ -10,6 +10,7 @@
#include <QQmlContext>
#include <QQmlEngine>
#include <QQmlIncubator>
#include <QQmlNetworkAccessManagerFactory>
#include <QQuickItem>
#include <QTimer>
......@@ -74,7 +75,8 @@ public:
QmlObject *q;
QUrl source;
QQmlEngine *engine;
std::shared_ptr<QQmlEngine> engine;
QmlObjectIncubator incubator;
QQmlComponent *component;
QTimer *executionEndTimer;
......@@ -106,7 +108,7 @@ void QmlObjectPrivate::execute(const QUrl &source)
}
delete component;
component = new QQmlComponent(engine, q);
component = new QQmlComponent(engine.get(), q);
QObject::connect(component, &QQmlComponent::statusChanged, q, &QmlObject::statusChanged, Qt::QueuedConnection);
delete incubator.object();
......@@ -131,62 +133,64 @@ void QmlObjectPrivate::scheduleExecutionEnd()
}
QmlObject::QmlObject(QObject *parent)
// cannot do : QmlObject(new QQmlEngine(this), d->engine->rootContext(), parent)
: QObject(parent)
, d(new QmlObjectPrivate(this))
: QmlObject(nullptr, nullptr, parent)
{
d->engine = new QQmlEngine(this);
d->rootContext = d->engine->rootContext();
d->kdeclarative.setDeclarativeEngine(d->engine);
d->kdeclarative.d->qmlObj = this;
d->context = new KLocalizedContext(this);
d->rootContext->setContextObject(d->context);
KDeclarative::setupEngine(d->engine);
}
#if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 95)
QmlObject::QmlObject(QQmlEngine *engine, QObject *parent)
: QmlObject(engine, engine->rootContext(), parent)
: QmlObject(std::shared_ptr<QQmlEngine>(engine), nullptr, parent)
{
}
QmlObject::QmlObject(QQmlEngine *engine, QQmlContext *rootContext, QObject *parent)
: QmlObject(engine, rootContext, nullptr /*call setupEngine*/, parent)
: QmlObject(std::shared_ptr<QQmlEngine>(engine), rootContext, parent)
{
}
QmlObject::QmlObject(QQmlEngine *engine, QQmlContext *rootContext, QmlObject *obj, QObject *parent)
: QmlObject(std::shared_ptr<QQmlEngine>(engine), rootContext, parent)
{
Q_UNUSED(obj);
}
#endif
QmlObject::QmlObject(std::shared_ptr<QQmlEngine> engine, QQmlContext *rootContext, QObject *parent)
: QObject(parent)
, d(new QmlObjectPrivate(this))
{
if (engine) {
d->engine = engine;
} else {
d->engine = new QQmlEngine(this);
d->engine = std::make_shared<QQmlEngine>();
}
if (d->engine.use_count() <= 2) {
KDeclarative::setupEngine(d->engine.get());
}
if (rootContext) {
d->rootContext = rootContext;
} else {
d->rootContext = d->engine->rootContext();
d->rootContext = engine->rootContext();
}
d->kdeclarative.setDeclarativeEngine(d->engine);
d->kdeclarative.d->qmlObj = this;
d->context = new KLocalizedContext(this);
d->rootContext->setContextObject(d->context);
if (!obj) {
KDeclarative::setupEngine(d->engine);
}
}
QmlObject::~QmlObject()
{
// QDeclarativeNetworkAccessManagerFactory *factory = d->engine->networkAccessManagerFactory();
// d->engine->setNetworkAccessManagerFactory(0);
// delete factory;
if (d->engine.use_count() == 1) {
// QQmlEngine does not take ownership of the QNAM factory so we need to
// make sure to clean it, but only if we are the last user of the engine
// otherwise we risk resetting the factory on an engine that is still in
// use.
auto factory = d->engine->networkAccessManagerFactory();
d->engine->setNetworkAccessManagerFactory(nullptr);
delete factory;
}
delete d;
}
......@@ -241,7 +245,7 @@ bool QmlObject::isInitializationDelayed() const
QQmlEngine *QmlObject::engine()
{
return d->engine;
return d->engine.get();
}
QObject *QmlObject::rootObject() const
......@@ -324,7 +328,7 @@ void QmlObject::completeInitialization(const QVariantHash &initialProperties)
QObject *QmlObject::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantHash &initialProperties)
{
QQmlComponent *component = new QQmlComponent(d->engine, this);
QQmlComponent *component = new QQmlComponent(d->engine.get(), this);
component->loadUrl(source);
return createObjectFromComponent(component, context, initialProperties);
......
......@@ -58,8 +58,14 @@ public:
*
* @param engine a QQmlEngine we want to use
* @param parent the parent of this object
*
* @deprecated Since 5.95, Use QmlObject(std::shared_ptr<QmlEngine>, QObject*)
* instead.
*/
#if KDECLARATIVE_ENABLE_DEPRECATED_SINCE(5, 95)
KDECLARATIVE_DEPRECATED_VERSION(5, 95, "Use QmlObject(std::shared_ptr<QQmlEngine>, QmlContext*, QObject*) instead")
explicit QmlObject(QQmlEngine *engine, QObject *parent = nullptr);
#endif
/**
* Constructs a new QmlObject
......@@ -67,8 +73,28 @@ public:
* @param engine the QQmlEngine to use
* @param rootContext the root context to use for object creation
* @param parent the parent of this object
*
* @deprecated Since 5.95, Use QmlObject(std::shared_ptr<QmlEngine>, QmlContext*, QObject*)
* instead.
*/
#if KDECLARATIVE_ENABLE_DEPRECATED_SINCE(5, 95)
KDECLARATIVE_DEPRECATED_VERSION(5, 95, "Use QmlObject(std::shared_ptr<QQmlEngine>, QQmlContext*, QObject*) instead")
explicit QmlObject(QQmlEngine *engine, QQmlContext *rootContext, QObject *parent = nullptr);
#endif
/**
* Construct a new QmlObject
*
* @param engine The QQmlEngine to use. If this object is the first user of
* the engine (e.g. use_count() is 1), KDeclarative::setupEngine() will be
* called. If this is nullptr, a new engine will be created for this object
* to use.
* @param rootContext The QML context to use for object creation. If this is
* nullptr, the engine's root context will be used.
* @param parent The QObject parent for this object.
*/
explicit QmlObject(std::shared_ptr<QQmlEngine> engine, QQmlContext *rootContext = nullptr, QObject *parent = nullptr);
~QmlObject() override;
/**
......@@ -230,8 +256,15 @@ protected:
* multiple objects (such as QmlObjectSharedEngine)
* @param parent the parent of this object
* @since 5.45
*
* @deprecated Since 5.95, Use QmlObject(std::shared_ptr<QmlEngine>, QmlContext*, QObject*)
* instead. The "obj" parameter has been dropped, instead setupEngine will be
* called if this QmlObject is the first user of the engine.
*/
#if KDECLARATIVE_ENABLE_DEPRECATED_SINCE(5, 95)
KDECLARATIVE_DEPRECATED_VERSION(5, 95, "Use QmlObject(std::shared_ptr<QQmlEngine>, QQmlContext*, QObject*) instead")
explicit QmlObject(QQmlEngine *engine, QQmlContext *rootContext, QmlObject *obj, QObject *parent = nullptr);
#endif
private:
friend class QmlObjectPrivate;
......
......@@ -8,6 +8,7 @@
#include <QQmlContext>
#include <QQmlEngine>
#include <QQmlNetworkAccessManagerFactory>
#include <QDebug>
#include <kdeclarative.h>
......@@ -26,21 +27,21 @@ public:
~QmlObjectSharedEnginePrivate()
{
// when the only remaining are out two refs, reset the pointers, causing deletion
// when the refcount is 2, we are sure that the only refs are s_engine and our copy
// of engineRef
if (engineRef.use_count() == 2) {
// when the only remaining are our three refs, reset the pointers, causing deletion
// when the refcount is 3, we are sure that the only refs are s_engine, our copy
// of engineRef and m_engine in QmlObject.
if (engineRef.use_count() <= 3) {
s_engine.reset();
}
}
static QQmlEngine *engine()
static std::shared_ptr<QQmlEngine> engine()
{
if (!s_engine) {
s_engine = std::make_shared<QQmlEngine>();
KDeclarative::setupEngine(s_engine.get());
}
return s_engine.get();
return s_engine;
}
// used to delete it
......@@ -52,7 +53,7 @@ public:
std::shared_ptr<QQmlEngine> QmlObjectSharedEnginePrivate::s_engine = std::shared_ptr<QQmlEngine>();
QmlObjectSharedEngine::QmlObjectSharedEngine(QObject *parent)
: QmlObject(QmlObjectSharedEnginePrivate::engine(), new QQmlContext(QmlObjectSharedEnginePrivate::engine()), this /*don't call setupEngine*/, parent)
: QmlObject(QmlObjectSharedEnginePrivate::engine(), new QQmlContext(QmlObjectSharedEnginePrivate::engine().get()), parent)
, d(new QmlObjectSharedEnginePrivate())
{
rootContext()->setParent(this);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment