diff options
author | Thomas Hartmann <thomas.hartmann@qt.io> | 2018-11-16 18:52:56 +0100 |
---|---|---|
committer | Thomas Hartmann <thomas.hartmann@qt.io> | 2018-12-04 08:09:59 +0000 |
commit | 70199a43a978649d87c5b2815808c4a04902c6e0 (patch) | |
tree | 453be3f4c601ff4dc84cfd728d11591fef17c1f9 | |
parent | 98b6f8eee9affce6df8be137068e49f88becb9ce (diff) |
Fix for SpinBox crash in Qt Quick Designer
The crash was not 100% reliable and depends on the order in the hash().
Something in beginDeferred() has a side effect on deferData->bindings
and an element gets deleted. This causes a crash while iterating (++it).
Therefore we do a copy of the hash.
I added a regression test. The test did only crash for SpinBox and it did
only crash roughly half the time.
Task-number: QTBUG-71942
Change-Id: I339e0a4382f97db44f6ff2e9f07f2be7278d1e24
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r-- | src/quicktemplates2/qquickdeferredexecute.cpp | 5 | ||||
-rw-r--r-- | tests/auto/auto.pro | 1 | ||||
-rw-r--r-- | tests/auto/designer/designer.pro | 9 | ||||
-rw-r--r-- | tests/auto/designer/tst_designer.cpp | 161 |
4 files changed, 174 insertions, 2 deletions
diff --git a/src/quicktemplates2/qquickdeferredexecute.cpp b/src/quicktemplates2/qquickdeferredexecute.cpp index b298a81d..800dcedb 100644 --- a/src/quicktemplates2/qquickdeferredexecute.cpp +++ b/src/quicktemplates2/qquickdeferredexecute.cpp @@ -76,8 +76,9 @@ static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &pro for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) { QQmlData::DeferredData *deferData = *dit; - auto range = deferData->bindings.equal_range(propertyIndex); - if (range.first == deferData->bindings.end()) + auto bindings = deferData->bindings; + auto range = bindings.equal_range(propertyIndex); + if (range.first == bindings.end()) continue; QQmlComponentPrivate::ConstructionState *state = new QQmlComponentPrivate::ConstructionState; diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index d4e0c604..d528b848 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -5,6 +5,7 @@ SUBDIRS += \ controls \ cursor \ customization \ + designer \ focus \ font \ palette \ diff --git a/tests/auto/designer/designer.pro b/tests/auto/designer/designer.pro new file mode 100644 index 00000000..68fbc747 --- /dev/null +++ b/tests/auto/designer/designer.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +TARGET = tst_designer + +QT += quick quick-private quickcontrols2 testlib +CONFIG += testcase +macos:CONFIG -= app_bundle + +SOURCES += \ + $$PWD/tst_designer.cpp diff --git a/tests/auto/designer/tst_designer.cpp b/tests/auto/designer/tst_designer.cpp new file mode 100644 index 00000000..2c67c2c6 --- /dev/null +++ b/tests/auto/designer/tst_designer.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest> +#include <QtQuick> + +#include <QtQuickControls2> +#include <QQmlComponent> +#include <QDir> + +#include <private/qquickdesignersupportitems_p.h> + +class tst_Designer : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void test_controls(); + void test_controls_data(); +}; + + +void tst_Designer::initTestCase() +{ +} + +void doComponentCompleteRecursive(QObject *object) +{ + if (object) { + QQuickItem *item = qobject_cast<QQuickItem*>(object); + + if (item && DesignerSupport::isComponentComplete(item)) + return; + + DesignerSupport::emitComponentCompleteSignalForAttachedProperty(qobject_cast<QQuickItem*>(object)); + + QList<QObject*> childList = object->children(); + + if (item) { + for (QQuickItem *childItem : item->childItems()) { + if (!childList.contains(childItem)) + childList.append(childItem); + } + } + + for (QObject *child : childList) + doComponentCompleteRecursive(child); + + if (item) { + static_cast<QQmlParserStatus*>(item)->componentComplete(); + } else { + QQmlParserStatus *qmlParserStatus = dynamic_cast< QQmlParserStatus*>(object); + if (qmlParserStatus) + qmlParserStatus->componentComplete(); + } + } +} + + +void tst_Designer::test_controls() +{ + QFETCH(QString, type); + + const QByteArray before("import QtQuick 2.10\n" + "import QtQuick.Controls 2.3\n" + "Item {\n"); + + QByteArray source = before; + source.append(type); + + const QByteArray after(" {" + "}\n" + "}\n"); + + source.append(after); + + QQmlEngine engine; + QQmlComponent component(&engine); + + { + ComponentCompleteDisabler disableComponentComplete; + component.setData(source, QUrl::fromLocalFile(QDir::current().absolutePath())); + } + + QObject *root = component.create(); + QVERIFY(root); + doComponentCompleteRecursive(root); +} + +void tst_Designer::test_controls_data() +{ + QTest::addColumn<QString>("type"); + + QTest::newRow("type") << "SpinBox"; + QTest::newRow("type") << "Switch"; + QTest::newRow("type") << "ComboBox"; + QTest::newRow("type") << "CheckBox"; + QTest::newRow("type") << "Button"; + QTest::newRow("type") << "DelayButton"; + QTest::newRow("type") << "Dial"; + QTest::newRow("type") << "Frame"; + QTest::newRow("type") << "GroupBox"; + QTest::newRow("type") << "Label"; + QTest::newRow("type") << "Page"; + QTest::newRow("type") << "Pane"; + QTest::newRow("type") << "ProgressBar"; + QTest::newRow("type") << "RadioButton"; + QTest::newRow("type") << "RangeSlider"; + QTest::newRow("type") << "RoundButton"; + QTest::newRow("type") << "ScrollView"; + QTest::newRow("type") << "Slider"; + QTest::newRow("type") << "StackView"; + QTest::newRow("type") << "SwipeView"; + QTest::newRow("type") << "Switch"; + QTest::newRow("type") << "TabBar"; + QTest::newRow("type") << "TabButton"; + QTest::newRow("type") << "TextArea"; + QTest::newRow("type") << "TextField"; + QTest::newRow("type") << "ToolBar"; + QTest::newRow("type") << "ToolButton"; + QTest::newRow("type") << "Tumbler"; +} + +QTEST_MAIN(tst_Designer) + +#include "tst_designer.moc" |