From: Debian Qt/KDE Maintainers Date: Sun, 26 Feb 2023 20:32:58 +0000 (+0000) Subject: QQuickItem: avoid emitting signals during destruction X-Git-Tag: archive/raspbian/5.15.8+dfsg-3+rpi1^2~7 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=f7225c058e1b2de6314bd6b7903158a10c0f7142;p=qtdeclarative-opensource-src.git QQuickItem: avoid emitting signals during destruction Origin: upstream, https://code.qt.io/cgit/qt/qtdeclarative.git/commit/?id=74873324bdf33997 Last-Update: 2023-02-26 If a QQuickItem is in the QQuickItem destructor, then it is both unsafe and unnecessary to emit property change notifications. Connected code can no longer rely on the state of the emitting object - if it was originally a subclass of QQuickItem, then those subclass destructors will already have run. And the QQuickItem destructor will also have partially run, leaving the object in an undefined state. Add a flag that we set to true at the top of ~QQuickItem, and don't emit visibleChildrenChanged, parentChanged, visibleChanged, and childrenChanged for items that are partially destroyed already. [ChangeLog][Qt Quick][QQuickItem] QQuickItem no longer emits change notifications for the parent, children, visible, and visibleChildren properties while it is being destroyed. Gbp-Pq: Name qquickitem_no_signals_on_destruction.patch --- diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 75f145781..e023801fd 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2326,6 +2326,7 @@ QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent) QQuickItem::~QQuickItem() { Q_D(QQuickItem); + d->inDestructor = true; if (d->windowRefCount > 1) d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow(). @@ -2689,9 +2690,8 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) const bool wasVisible = isVisible(); op->removeChild(this); - if (wasVisible) { + if (wasVisible && !op->inDestructor) emit oldParentItem->visibleChildrenChanged(); - } } else if (d->window) { QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this); } @@ -2768,8 +2768,9 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) d->itemChange(ItemParentHasChanged, d->parentItem); - emit parentChanged(d->parentItem); - if (isVisible() && d->parentItem) + if (!d->inDestructor) + emit parentChanged(d->parentItem); + if (isVisible() && d->parentItem && !QQuickItemPrivate::get(d->parentItem)->inDestructor) emit d->parentItem->visibleChildrenChanged(); } @@ -2965,7 +2966,8 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) itemChange(QQuickItem::ItemChildRemovedChange, child); - emit q->childrenChanged(); + if (!inDestructor) + emit q->childrenChanged(); } void QQuickItemPrivate::refWindow(QQuickWindow *c) @@ -3194,6 +3196,7 @@ QQuickItemPrivate::QQuickItemPrivate() , touchEnabled(false) #endif , hasCursorHandler(false) + , inDestructor(false) , dirtyAttributes(0) , nextDirtyItem(nullptr) , prevDirtyItem(nullptr) @@ -6106,9 +6109,11 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) QAccessible::updateAccessibility(&ev); } #endif - emit q->visibleChanged(); - if (childVisibilityChanged) - emit q->visibleChildrenChanged(); + if (!inDestructor) { + emit q->visibleChanged(); + if (childVisibilityChanged) + emit q->visibleChildrenChanged(); + } return true; // effective visibility DID change } diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 841d91bb4..ade8fb61f 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -472,6 +472,7 @@ public: bool replayingPressEvent:1; bool touchEnabled:1; bool hasCursorHandler:1; + quint32 inDestructor:1; // has entered ~QQuickItem enum DirtyType { TransformOrigin = 0x00000001, diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index c8f251dbe..b8d5c44b3 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -131,6 +131,8 @@ private slots: void grab(); + void signalsOnDestruction(); + private: QQmlEngine engine; bool qt_tab_all_widgets() { @@ -3520,6 +3522,52 @@ void tst_QQuickItem::isAncestorOf() QVERIFY(!sub2.isAncestorOf(&sub2)); } +/* + Items that are getting destroyed should not emit property change notifications. +*/ +void tst_QQuickItem::signalsOnDestruction() +{ + QQuickWindow window; + window.show(); + + // visual children, but not QObject children + std::unique_ptr parent(new QQuickItem(window.contentItem())); + std::unique_ptr child(new QQuickItem); + std::unique_ptr grandChild(new QQuickItem); + + QSignalSpy childrenSpy(parent.get(), &QQuickItem::childrenChanged); + QSignalSpy visibleChildrenSpy(parent.get(), &QQuickItem::visibleChildrenChanged); + QSignalSpy childParentSpy(child.get(), &QQuickItem::parentChanged); + QSignalSpy childChildrenSpy(child.get(), &QQuickItem::childrenChanged); + QSignalSpy childVisibleChildrenSpy(child.get(), &QQuickItem::visibleChanged); + QSignalSpy grandChildParentSpy(grandChild.get(), &QQuickItem::parentChanged); + + child->setParentItem(parent.get()); + QCOMPARE(childrenSpy.count(), 1); + QCOMPARE(visibleChildrenSpy.count(), 1); + QCOMPARE(childParentSpy.count(), 1); + QCOMPARE(childChildrenSpy.count(), 0); + QCOMPARE(childVisibleChildrenSpy.count(), 0); + + grandChild->setParentItem(child.get()); + QCOMPARE(childrenSpy.count(), 1); + QCOMPARE(visibleChildrenSpy.count(), 1); + QCOMPARE(childParentSpy.count(), 1); + QCOMPARE(childChildrenSpy.count(), 1); + QCOMPARE(childVisibleChildrenSpy.count(), 0); + QCOMPARE(grandChildParentSpy.count(), 1); + + parent.reset(); + + QVERIFY(!child->parentItem()); + QVERIFY(grandChild->parentItem()); + QCOMPARE(childrenSpy.count(), 1); + QCOMPARE(visibleChildrenSpy.count(), 1); + QCOMPARE(childParentSpy.count(), 2); + QCOMPARE(grandChildParentSpy.count(), 1); +} + + QTEST_MAIN(tst_QQuickItem) #include "tst_qquickitem.moc"