Blame SOURCES/0036-Do-not-revert-properties-of-deleted-objects.patch

72ccf7
From 55324650f9e759a43dce927f823c9858574106c3 Mon Sep 17 00:00:00 2001
72ccf7
From: Alexey Edelev <alexey.edelev@qt.io>
72ccf7
Date: Tue, 12 Jan 2021 16:37:09 +0100
72ccf7
Subject: [PATCH 36/36] Do not revert properties of deleted objects
72ccf7
72ccf7
If state contains revert action of properties of deleted objects,
72ccf7
we should avoid adding them to apply list
72ccf7
72ccf7
Fixes: QTBUG-85106
72ccf7
Pick-to: 5.15
72ccf7
Change-Id: Iff57eb9958a054476096f6d951ab7390277a2b39
72ccf7
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
72ccf7
(cherry picked from commit 96763dbb105fde20431a264789ac27abfdab841c)
72ccf7
---
72ccf7
 src/quick/util/qquickstate.cpp                |  5 ++
72ccf7
 .../data/revertNullObjectBinding.qml          | 48 +++++++++++++
72ccf7
 .../quick/qquickstates/tst_qquickstates.cpp   | 68 +++++++++++++++++++
72ccf7
 3 files changed, 121 insertions(+)
72ccf7
 create mode 100644 tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml
72ccf7
72ccf7
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
72ccf7
index 71ab1f4d62..6a72754bde 100644
72ccf7
--- a/src/quick/util/qquickstate.cpp
72ccf7
+++ b/src/quick/util/qquickstate.cpp
72ccf7
@@ -635,6 +635,11 @@ void QQuickState::apply(QQuickTransition *trans, QQuickState *revert)
72ccf7
             }
72ccf7
         }
72ccf7
         if (!found) {
72ccf7
+            // If revert list contains bindings assigned to deleted objects, we need to
72ccf7
+            // prevent reverting properties of those objects.
72ccf7
+            if (d->revertList.at(ii).binding() && !d->revertList.at(ii).property().object()) {
72ccf7
+                continue;
72ccf7
+            }
72ccf7
             QVariant cur = d->revertList.at(ii).property().read();
72ccf7
             QQmlPropertyPrivate::removeBinding(d->revertList.at(ii).property());
72ccf7
 
72ccf7
diff --git a/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml b/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml
72ccf7
new file mode 100644
72ccf7
index 0000000000..dee82f52ed
72ccf7
--- /dev/null
72ccf7
+++ b/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml
72ccf7
@@ -0,0 +1,48 @@
72ccf7
+import QtQuick 2.12
72ccf7
+import Qt.test 1.0
72ccf7
+
72ccf7
+Item {
72ccf7
+    id: root
72ccf7
+    readonly property int someProp: 1234
72ccf7
+
72ccf7
+    property bool state1Active: false
72ccf7
+    property bool state2Active: false
72ccf7
+    StateGroup {
72ccf7
+        states: [
72ccf7
+            State {
72ccf7
+                id: state1
72ccf7
+                name: "state1"
72ccf7
+                when: state1Active
72ccf7
+                changes: [
72ccf7
+                    PropertyChanges {
72ccf7
+                        objectName: "propertyChanges1"
72ccf7
+                        target: ContainingObj.obj
72ccf7
+                        prop: root.someProp
72ccf7
+                    }
72ccf7
+                ]
72ccf7
+            }
72ccf7
+    ]}
72ccf7
+    StateGroup {
72ccf7
+        states: [
72ccf7
+            State {
72ccf7
+                id: state2
72ccf7
+                name: "state2"
72ccf7
+                when: state2Active
72ccf7
+                changes: [
72ccf7
+                    PropertyChanges {
72ccf7
+                        objectName: "propertyChanges2"
72ccf7
+                        target: ContainingObj.obj
72ccf7
+                        prop: 11111
72ccf7
+                    }
72ccf7
+                ]
72ccf7
+            }
72ccf7
+        ]
72ccf7
+    }
72ccf7
+
72ccf7
+    Component.onCompleted: {
72ccf7
+        state1Active = true;
72ccf7
+        state2Active = true;
72ccf7
+
72ccf7
+        ContainingObj.reset()
72ccf7
+    }
72ccf7
+}
72ccf7
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
72ccf7
index d5fea3cb28..849522454f 100644
72ccf7
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
72ccf7
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
72ccf7
@@ -79,6 +79,55 @@ private:
72ccf7
 QML_DECLARE_TYPE(MyRect)
72ccf7
 QML_DECLARE_TYPEINFO(MyRect, QML_HAS_ATTACHED_PROPERTIES)
72ccf7
 
72ccf7
+class RemovableObj : public QObject
72ccf7
+{
72ccf7
+    Q_OBJECT
72ccf7
+    Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged)
72ccf7
+
72ccf7
+public:
72ccf7
+    RemovableObj(QObject *parent) : QObject(parent), m_prop(4321) { }
72ccf7
+    int prop() const { return m_prop; }
72ccf7
+
72ccf7
+public slots:
72ccf7
+    void setProp(int prop)
72ccf7
+    {
72ccf7
+        if (m_prop == prop)
72ccf7
+            return;
72ccf7
+
72ccf7
+        m_prop = prop;
72ccf7
+        emit propChanged(m_prop);
72ccf7
+    }
72ccf7
+
72ccf7
+signals:
72ccf7
+    void propChanged(int prop);
72ccf7
+
72ccf7
+private:
72ccf7
+    int m_prop;
72ccf7
+};
72ccf7
+
72ccf7
+class ContainingObj : public QObject
72ccf7
+{
72ccf7
+    Q_OBJECT
72ccf7
+    Q_PROPERTY(RemovableObj *obj READ obj NOTIFY objChanged)
72ccf7
+    RemovableObj *m_obj;
72ccf7
+
72ccf7
+public:
72ccf7
+    ContainingObj() : m_obj(new RemovableObj(this)) { }
72ccf7
+    RemovableObj *obj() const { return m_obj; }
72ccf7
+
72ccf7
+    Q_INVOKABLE void reset()
72ccf7
+    {
72ccf7
+        if (m_obj) {
72ccf7
+            m_obj->deleteLater();
72ccf7
+        }
72ccf7
+
72ccf7
+        m_obj = new RemovableObj(this);
72ccf7
+        emit objChanged();
72ccf7
+    }
72ccf7
+signals:
72ccf7
+    void objChanged();
72ccf7
+};
72ccf7
+
72ccf7
 class tst_qquickstates : public QQmlDataTest
72ccf7
 {
72ccf7
     Q_OBJECT
72ccf7
@@ -140,12 +189,20 @@ private slots:
72ccf7
     void duplicateStateName();
72ccf7
     void trivialWhen();
72ccf7
     void parentChangeCorrectReversal();
72ccf7
+    void revertNullObjectBinding();
72ccf7
 };
72ccf7
 
72ccf7
 void tst_qquickstates::initTestCase()
72ccf7
 {
72ccf7
     QQmlDataTest::initTestCase();
72ccf7
     qmlRegisterType<MyRect>("Qt.test", 1, 0, "MyRectangle");
72ccf7
+    qmlRegisterSingletonType<ContainingObj>(
72ccf7
+            "Qt.test", 1, 0, "ContainingObj", [](QQmlEngine *engine, QJSEngine *) {
72ccf7
+                static ContainingObj instance;
72ccf7
+                engine->setObjectOwnership(&instance, QQmlEngine::CppOwnership);
72ccf7
+                return &instance;
72ccf7
+            });
72ccf7
+    qmlRegisterUncreatableType<RemovableObj>("Qt.test", 1, 0, "RemovableObj", "Uncreatable");
72ccf7
 }
72ccf7
 
72ccf7
 QByteArray tst_qquickstates::fullDataPath(const QString &path) const
72ccf7
@@ -1692,6 +1749,17 @@ void tst_qquickstates::parentChangeCorrectReversal()
72ccf7
     QCOMPARE(oldX, stayingRectX.read().toDouble());
72ccf7
 }
72ccf7
 
72ccf7
+void tst_qquickstates::revertNullObjectBinding()
72ccf7
+{
72ccf7
+    QQmlEngine engine;
72ccf7
+
72ccf7
+    QQmlComponent c(&engine, testFileUrl("revertNullObjectBinding.qml"));
72ccf7
+    QScopedPointer<QObject> root { c.create() };
72ccf7
+    QVERIFY(root);
72ccf7
+    QTest::qWait(10);
72ccf7
+    QQmlProperty state2Active(root.get(), "state2Active");
72ccf7
+    state2Active.write(false);
72ccf7
+}
72ccf7
 
72ccf7
 QTEST_MAIN(tst_qquickstates)
72ccf7
 
72ccf7
-- 
72ccf7
2.31.1
72ccf7