diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2014-01-03 10:38:33 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-01-06 10:40:08 +0100 |
commit | 39540124dd0900e0c99dcda8c0ebdf4f3cea8d5e (patch) | |
tree | 8fb7174b6523945101f7bd5177b973a745b357de | |
parent | e3232bce40ddefd3b4dfa57a923b15cb47f051bf (diff) |
Fix interaction of QQuickItems with the garbage collector
The QObject ownership of QQuickItem objects is not accessible / mutable
in QML, because the parent property maps to the (dynamic) visual parent.
There are use-cases of creating QQuickItem objects without a QObject parent and
to support this, the visual parent needs to mark its visual children in order
to provide intuitive semantics.
[ChangeLog][QtQuick][Import Behavior Changes] A QQuick Item is now strongly
referenced by its visual parent item, so it doesn't require a QObject parent to
stay alive.
Task-number: QTBUG-35913
Change-Id: Ief2d40ac76298a0cf241ca73ff654c4ecfa12748
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r-- | src/quick/items/qquickitem.cpp | 11 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 3 | ||||
-rw-r--r-- | src/quick/items/qquickview.cpp | 27 | ||||
-rw-r--r-- | src/quick/items/qquickview_p.h | 22 | ||||
-rw-r--r-- | tests/auto/quick/qquickitem/data/visualParentOwnership.qml | 14 | ||||
-rw-r--r-- | tests/auto/quick/qquickitem/tst_qquickitem.cpp | 65 |
6 files changed, 142 insertions, 0 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8c94b9dd5b..72b0e588ec 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -6478,6 +6478,17 @@ void QQuickItemPrivate::incrementCursorCount(int delta) #endif } +void QQuickItemPrivate::markObjects(QV4::ExecutionEngine *e) +{ + Q_Q(QQuickItem); + QQmlData *ddata = QQmlData::get(q); + if (ddata) + ddata->jsWrapper.markOnce(e); + + foreach (QQuickItem *child, childItems) + QQuickItemPrivate::get(child)->markObjects(e); +} + #ifndef QT_NO_CURSOR /*! diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index ef5c63e40f..96cb9e8843 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -583,6 +583,9 @@ public: virtual void mirrorChange() {} void incrementCursorCount(int delta); + + // recursive helper to let a visual parent mark its visual children + void markObjects(QV4::ExecutionEngine *e); }; /* diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index d1fe489dcb..61f0f9bec2 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -56,6 +56,26 @@ QT_BEGIN_NAMESPACE +DEFINE_MANAGED_VTABLE(QQuickRootItemMarker); + +QQuickRootItemMarker::QQuickRootItemMarker(QQuickViewPrivate *view) + : QV4::Object(QQmlEnginePrivate::getV4Engine(view->engine.data())) + , view(view) +{ + setVTable(&static_vtbl); +} + +void QQuickRootItemMarker::markObjects(QV4::Managed *that, QV4::ExecutionEngine *e) +{ + QQuickItem *root = static_cast<QQuickRootItemMarker*>(that)->view->root; + if (root) { + QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(root); + rootPrivate->markObjects(e); + } + + QV4::Object::markObjects(that, e); +} + void QQuickViewPrivate::init(QQmlEngine* e) { Q_Q(QQuickView); @@ -68,6 +88,13 @@ void QQuickViewPrivate::init(QQmlEngine* e) if (!engine.data()->incubationController()) engine.data()->setIncubationController(q->incubationController()); + { + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine.data()); + QV4::Scope scope(v4); + QV4::Scoped<QQuickRootItemMarker> v(scope, new (v4->memoryManager) QQuickRootItemMarker(this)); + rootItemMarker = v; + } + if (QQmlDebugService::isDebuggingEnabled()) QQmlInspectorService::instance()->addView(q); } diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h index 170c93a6cf..e18b45dfbe 100644 --- a/src/quick/items/qquickview_p.h +++ b/src/quick/items/qquickview_p.h @@ -51,12 +51,17 @@ #include <QtCore/QWeakPointer> #include <QtQml/qqmlengine.h> +#include <private/qv4object_p.h> #include "qquickwindow_p.h" #include "qquickitemchangelistener_p.h" QT_BEGIN_NAMESPACE +namespace QV4 { +struct ExecutionEngine; +} + class QQmlContext; class QQmlError; class QQuickItem; @@ -94,6 +99,23 @@ public: QQuickView::ResizeMode resizeMode; QSize initialSize; QElapsedTimer frameTimer; + QV4::PersistentValue rootItemMarker; +}; + +struct QQuickRootItemMarker : public QV4::Object +{ + Q_MANAGED + + QQuickRootItemMarker(QQuickViewPrivate *view); + + static void destroy(Managed *that) + { + static_cast<QQuickRootItemMarker*>(that)->~QQuickRootItemMarker(); + } + + static void markObjects(Managed *that, QV4::ExecutionEngine *e); + + QQuickViewPrivate *view; }; QT_END_NAMESPACE diff --git a/tests/auto/quick/qquickitem/data/visualParentOwnership.qml b/tests/auto/quick/qquickitem/data/visualParentOwnership.qml new file mode 100644 index 0000000000..644d14ba43 --- /dev/null +++ b/tests/auto/quick/qquickitem/data/visualParentOwnership.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +Item { + Component { + id: factory + Item {} + } + + property Item keepAliveProperty; + + function createItemWithoutParent() { + return factory.createObject(/*parent*/ null); + } +} diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index ad3c4fc208..f4f2374183 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -49,6 +49,7 @@ #include <qpa/qwindowsysteminterface.h> #include <QDebug> #include <QTimer> +#include <QQmlEngine> #include "../../shared/util.h" class TestItem : public QQuickItem @@ -167,6 +168,8 @@ private slots: void acceptedMouseButtons(); + void visualParentOwnership(); + private: enum PaintOrderOp { @@ -1754,6 +1757,68 @@ void tst_qquickitem::acceptedMouseButtons() QCOMPARE(item.releaseCount, 3); } +static void gc(QQmlEngine &engine) +{ + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); +} + +void tst_qquickitem::visualParentOwnership() +{ + QQuickView view; + view.setSource(testFileUrl("visualParentOwnership.qml")); + + QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject()); + QVERIFY(root); + + QVariant newObject; + { + QVERIFY(QMetaObject::invokeMethod(root, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject))); + QPointer<QQuickItem> newItem = qvariant_cast<QQuickItem*>(newObject); + QVERIFY(!newItem.isNull()); + + QVERIFY(!newItem->parent()); + QVERIFY(!newItem->parentItem()); + + newItem->setParentItem(root); + + gc(*view.engine()); + + QVERIFY(!newItem.isNull()); + newItem->setParentItem(0); + + gc(*view.engine()); + QVERIFY(newItem.isNull()); + } + { + QVERIFY(QMetaObject::invokeMethod(root, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject))); + QPointer<QQuickItem> firstItem = qvariant_cast<QQuickItem*>(newObject); + QVERIFY(!firstItem.isNull()); + + firstItem->setParentItem(root); + + QVERIFY(QMetaObject::invokeMethod(root, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject))); + QPointer<QQuickItem> secondItem = qvariant_cast<QQuickItem*>(newObject); + QVERIFY(!firstItem.isNull()); + + secondItem->setParentItem(firstItem); + + gc(*view.engine()); + + delete firstItem; + + root->setProperty("keepAliveProperty", newObject); + + gc(*view.engine()); + QVERIFY(!secondItem.isNull()); + + root->setProperty("keepAliveProperty", QVariant()); + + gc(*view.engine()); + QVERIFY(secondItem.isNull()); + } +} QTEST_MAIN(tst_qquickitem) |