summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2018-10-03 17:09:40 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2018-10-13 18:55:29 +0000
commit7f57472e24a45a9cb56c7742f4c031405262ef98 (patch)
tree9226093e3c34ce4f32870404a0196f1fc05d3d05
parent1ce94fc0f5311c8d9d7dc50de239a827279edcd9 (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.h2
-rw-r--r--src/quick/items/qquickwindow.cpp8
-rw-r--r--tests/auto/quick/qquickmousearea/data/nestedSendEvent.qml49
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp33
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"