diff options
-rw-r--r-- | src/qml/types/qqmldelegatemodel.cpp | 10 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel_p_p.h | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquickrepeater/data/invalidContextCrash.qml | 7 | ||||
-rw-r--r-- | tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp | 43 |
4 files changed, 56 insertions, 6 deletions
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 4591d42710..f7ce1c8fad 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -903,7 +903,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bo if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); return 0; - } else if (!m_context->isValid()) { + } else if (!m_context || !m_context->isValid()) { return 0; } @@ -946,7 +946,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bo cacheItem->incubationTask->index[i] = it.index[i]; QQmlContextData *ctxt = new QQmlContextData; - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context)); + ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context.data())); ctxt->contextObject = cacheItem; cacheItem->contextData = ctxt; @@ -1409,7 +1409,7 @@ void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, void QQmlDelegateModelPrivate::emitChanges() { - if (m_transaction || !m_complete || !m_context->isValid()) + if (m_transaction || !m_complete || !m_context || !m_context->isValid()) return; m_transaction = true; @@ -1594,7 +1594,7 @@ QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::ValueRef object, int groups) { - if (!m_context->isValid()) + if (!m_context || !m_context->isValid()) return false; QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1); @@ -2433,7 +2433,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) return QQmlV4Handle(QV4::Encode::undefined()); QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (!model->m_context->isValid()) { + if (!model->m_context || !model->m_context->isValid()) { return QQmlV4Handle(QV4::Encode::undefined()); } else if (index < 0 || index >= model->m_compositor.count(d->group)) { qmlInfo(this) << tr("get: index out of range"); diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index d64f641a1b..cf7a719455 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -303,7 +303,7 @@ public: QQmlListCompositor m_compositor; QQmlComponent *m_delegate; QQmlDelegateModelItemMetaType *m_cacheMetaType; - QQmlContext *m_context; + QPointer<QQmlContext> m_context; QQmlDelegateModelParts *m_parts; QQmlDelegateModelGroupEmitterList m_pendingParts; diff --git a/tests/auto/quick/qquickrepeater/data/invalidContextCrash.qml b/tests/auto/quick/qquickrepeater/data/invalidContextCrash.qml new file mode 100644 index 0000000000..4a822cf25a --- /dev/null +++ b/tests/auto/quick/qquickrepeater/data/invalidContextCrash.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +Item { + Repeater { + model: badModel + delegate: Item {} + } +} diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp index 9fb76f9584..2dc5a65d7d 100644 --- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp +++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp @@ -79,6 +79,7 @@ private slots: void initParent(); void dynamicModelCrash(); void visualItemModelCrash(); + void invalidContextCrash(); }; class TestObject : public QObject @@ -744,6 +745,48 @@ void tst_QQuickRepeater::visualItemModelCrash() delete window; } +class BadModel : public QAbstractListModel +{ +public: + ~BadModel() + { + beginResetModel(); + endResetModel(); + } + + QVariant data(const QModelIndex &, int) const { return QVariant(); } + int rowCount(const QModelIndex &) const { return 0; } +}; + + +void tst_QQuickRepeater::invalidContextCrash() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("invalidContextCrash.qml")); + + BadModel* model = new BadModel; + engine.rootContext()->setContextProperty("badModel", model); + + QScopedPointer<QObject> root(component.create()); + QCOMPARE(root->children().count(), 1); + QObject *repeater = root->children().first(); + + // Make sure the model comes first in the child list, so it will be + // deleted first and then the repeater. During deletion the QML context + // has been deleted already and is invalid. + model->setParent(root.data()); + repeater->setParent(0); + repeater->setParent(root.data()); + + QCOMPARE(root->children().count(), 2); + QVERIFY(root->children().at(0) == model); + QVERIFY(root->children().at(1) == repeater); + + // Delete the root object, which will invalidate/delete the QML context + // and then delete the child QObjects, which may try to access the context. + root.reset(0); +} + QTEST_MAIN(tst_QQuickRepeater) #include "tst_qquickrepeater.moc" |