diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2018-10-03 17:09:40 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2018-10-13 18:55:29 +0000 |
commit | 7f57472e24a45a9cb56c7742f4c031405262ef98 (patch) | |
tree | 9226093e3c34ce4f32870404a0196f1fc05d3d05 | |
parent | 1ce94fc0f5311c8d9d7dc50de239a827279edcd9 (diff) |
Warn, don't crash during nested delivery of mouse or touch presses
If during delivery of a mouse press, user code calls qApp->sendEvent()
with another mouse press, then when delivery of the nested event is
finished, we call QQuickPointerMouseEvent::reset(nullptr). Then when
delivery of the original mouse press resumes, crashes are possible
because most of the code assumes that QQuickPointerEvent::m_event is
not null during delivery.
Change-Id: Id65b1f2f64351e40d03bcd4f4d16693d616729da
Fixes: QTBUG-70898
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 8 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/data/nestedSendEvent.qml | 49 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 33 |
4 files changed, 92 insertions, 0 deletions
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 4097845ec9..b2642735f2 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -447,6 +447,8 @@ protected: Qt::MouseButton m_button = Qt::NoButton; Qt::MouseButtons m_pressedButtons; + friend class QQuickWindowPrivate; + Q_DISABLE_COPY(QQuickPointerEvent) }; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 1c44afc594..53c0231f87 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2508,6 +2508,10 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, } for (QQuickItem *item : targetItems) { + if (!event->m_event) { + qWarning("event went missing during delivery! (nested sendEvent() is not allowed)"); + break; + } if (!handlersOnly && sendFilteredPointerEvent(event, item)) { if (event->isAccepted()) { for (int i = 0; i < event->pointCount(); ++i) @@ -2521,6 +2525,10 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, // nor to any item which already had a chance to filter. if (skipDelivery.contains(item)) continue; + if (!event->m_event) { + qWarning("event went missing during delivery! (nested sendEvent() is not allowed)"); + break; + } deliverMatchingPointsToItem(item, event, handlersOnly); if (event->allPointsAccepted()) handlersOnly = true; diff --git a/tests/auto/quick/qquickmousearea/data/nestedSendEvent.qml b/tests/auto/quick/qquickmousearea/data/nestedSendEvent.qml new file mode 100644 index 0000000000..908a43b04e --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/nestedSendEvent.qml @@ -0,0 +1,49 @@ +import QtQuick 2.11 +import QtQuick.Window 2.11 +import Test 1.0 + +Window { + id: window + visible: true + width: 200 + height: 200 + + property EventSender sender: EventSender { } + + Item { + width: 200 + height: 200 + + MouseArea { + anchors.fill: parent + } + + Item { + width: 200 + height: 200 + + Rectangle { + width: 200 + height: 100 + color: "red" + + MouseArea { + anchors.fill: parent + onPressed: sender.sendMouseClick(window, 50, 50) + } + } + + Rectangle { + y: 100 + width: 200 + height: 100 + color: "yellow" + + MouseArea { + anchors.fill: parent + onPressed: sender.sendMouseClick(window, 50, 50) + } + } + } + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index aa379e834e..558ca2e759 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -98,6 +98,22 @@ private: qreal m_radius; }; +class EventSender : public QObject { + Q_OBJECT + +public: + Q_INVOKABLE void sendMouseClick(QObject* obj ,qreal x , qreal y) { + { + QMouseEvent event(QEvent::MouseButtonPress, QPointF(x , y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + qApp->sendEvent(obj, &event); + } + { + QMouseEvent event(QEvent::MouseButtonRelease, QPointF(x , y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + qApp->sendEvent(obj, &event); + } + } +}; + class tst_QQuickMouseArea: public QQmlDataTest { Q_OBJECT @@ -106,6 +122,7 @@ public: : device(nullptr) { qmlRegisterType<CircleMask>("Test", 1, 0, "CircleMask"); + qmlRegisterType<EventSender>("Test", 1, 0, "EventSender"); } private slots: @@ -165,6 +182,7 @@ private slots: void pressOneAndTapAnother_data(); void pressOneAndTapAnother(); void mask(); + void nestedEventDelivery(); private: int startDragDistance() const { @@ -2298,6 +2316,21 @@ void tst_QQuickMouseArea::mask() QCOMPARE(window.rootObject()->property("clicked").toInt(), 1); } +void tst_QQuickMouseArea::nestedEventDelivery() // QTBUG-70898 +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("nestedSendEvent.qml")); + QScopedPointer<QQuickWindow> window(qmlobject_cast<QQuickWindow *>(c.create())); + QVERIFY(window.data()); + + // Click each MouseArea and verify that it doesn't crash + QByteArray message = "event went missing during delivery! (nested sendEvent() is not allowed)"; + QTest::ignoreMessage(QtWarningMsg, message); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(50,50)); + QTest::ignoreMessage(QtWarningMsg, message); // twice though, actually + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(50,150)); +} + QTEST_MAIN(tst_QQuickMouseArea) #include "tst_qquickmousearea.moc" |