diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2019-02-19 19:45:36 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2019-02-26 11:09:45 +0000 |
commit | 6618e4aed563c99c03904c9cf1e6e272d05315e3 (patch) | |
tree | 4cf2e508e6e9659c4c2c449efa5ac2fcb2b7bd2c | |
parent | ea17082234d9921c1fdf34a1f34fa28eeb7faf88 (diff) |
Don't crash when passive grabber deleted before exclusive gr. released
In this scenario, a DragHandler is inside an Item in a Loader, under
a MouseArea, which unloads the loader on press. So on press, the
DragHandler acquires a passive grab, then the MouseArea acquires the
exclusive grab, then the DragHandler is destroyed along with its parent
when the Loader is unloaded. On release,
QQuickEventPoint::setGrabberItem(nullptr) was sending an
onGrabChanged(passiveGrabber, OverrideGrabPassive, this) notification.
That was questionable: the handler was not just then getting its grab
overridden, but rather un-overridden, because the exclusive grab
was being released. It's also a good idea to check for null pointers,
since m_passiveGrabbers is a collection of QPointers already,
so we can tell when a passive grabber is deleted dynamically.
It can also be reproduced with MultiPointTouchArea just as with
MouseArea, so the test is written that way for convenience, because
we have tst_multipointtoucharea_interop already. It doesn't really
matter which handler has the passive grab, or which item has the
exclusive grab that's being relinquished.
Fixes: QTBUG-73819
Change-Id: Ic605efa2143a1d849be095dcb88d6c38d7d2ee19
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
3 files changed, 88 insertions, 2 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 2eaab164a0..c43eab6b8a 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -864,8 +864,11 @@ void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(grabber->window()); windowPriv->sendUngrabEvent(oldGrabberItem, windowPriv->isDeliveringTouchAsMouse()); } - for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) - passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this); + if (grabber) { + for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) + if (passiveGrabber) + passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this); + } } } diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml new file mode 100644 index 0000000000..1bd20c6bcb --- /dev/null +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 + +Item { + width: 640 + height: 480 + + Loader { + id: loader + + width: 480 + height: 480 + + sourceComponent: Rectangle { + id: item2 + anchors.fill: parent + color: "blue" + + DragHandler{} + } + } + + Rectangle { + color: "yellow" + width: 180 + height: 180 + + MultiPointTouchArea { + anchors.fill: parent + touchPoints: [ + TouchPoint { onPressedChanged: loader.sourceComponent = undefined } + ] + } + } +} + + diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp index b994e0fc07..bf582b820b 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp @@ -55,6 +55,7 @@ private slots: void touchDrag(); void touchesThenPinch(); + void unloadHandlerWithPassiveGrab(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); @@ -282,6 +283,24 @@ void tst_MptaInterop::touchesThenPinch() QTRY_COMPARE(mptaReleasedSpy.count(), 1); } +void tst_MptaInterop::unloadHandlerWithPassiveGrab() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "unloadHandlerOnPress.qml"); + QQuickView * window = windowPtr.data(); + + QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>(); + QVERIFY(handler); + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>(); + QVERIFY(mpta); + + QPoint point(90, 90); + QTest::mousePress(window, Qt::LeftButton, 0, point); + QCOMPARE(window->mouseGrabberItem(), mpta); + QTRY_VERIFY(handler.isNull()); // it got unloaded + QTest::mouseRelease(window, Qt::LeftButton, 0, point); // QTBUG-73819: don't crash +} + QTEST_MAIN(tst_MptaInterop) #include "tst_multipointtoucharea_interop.moc" |