diff options
author | Richard Moe Gustavsen <richard.gustavsen@theqtcompany.com> | 2015-05-19 11:20:12 +0200 |
---|---|---|
committer | Richard Moe Gustavsen <richard.gustavsen@theqtcompany.com> | 2015-05-22 09:59:41 +0000 |
commit | b8db3a1c6036e6d8ab68b5f1c01a00c289a67ab8 (patch) | |
tree | 97301552b6bc2229bbfb94440c2d08681da6fca6 | |
parent | eb2ea7098512bdc241b7fdabf5c21c229d56fc07 (diff) |
qquickwindow: ensure we delete the correct delayed touch event after delivery
Delivering a delayed touch event from QQuickWindow can cause the event loop
to recurse (e.g if it starts a drag'n'drop). This again can cause new touch
events to be delivered to QQuickWindow, and new delayed touch events to be
stored.
This results in the following:
(1) Receive new touch press event in QQuickWindow, and set
delayedTouch to be a copy of it
(2) Deliver delayedTouch to items. This can cause an event loop
recursion.
(3) While inside the recursion, QQuickWindow receives another new
touch press event. We then redeliver and delete the current
delayedTouch event created in (1), and set delayedTouch to be
a copy of the new event.
(4) Later we return back from (2), and try to access
delayedTouch (or actually a reference to the touchpoints inside
it, qquickwindow.cpp:1958). Since the event was deleted in (3), we
have a crash.
This patch will ensure that we set delayedTouch to 0 before delivering
it (so it cannot be redelivered), and that we safely delete it afterwards
when it goes out of scope. By converting delayedTouch to a QScopedPointer
we also ensure that the event is not leaked upon destruction.
Task-number: QTBUG-45877
Change-Id: Ic372a39a0eb127abfd12cec2d51b3743ad83194d
Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 30 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 3 | ||||
-rw-r--r-- | tests/auto/quick/shared/viewtestutil.cpp | 4 |
3 files changed, 18 insertions, 19 deletions
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 0b1c79567f..84b585e3b5 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -467,8 +467,6 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) animationController = new QQuickAnimatorController(q); - delayedTouch = 0; - QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection); QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection); QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection); @@ -1835,6 +1833,15 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) return true; } +void QQuickWindowPrivate::deliverDelayedTouchEvent() +{ + // Deliver and delete delayedTouch. + // Set delayedTouch to 0 before delivery to avoid redelivery in case of + // event loop recursions (e.g if it the touch starts a dnd session). + QScopedPointer<QTouchEvent> e(delayedTouch.take()); + reallyDeliverTouchEvent(e.data()); +} + static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); // check what kind of touch we have (begin/update) and @@ -1854,7 +1861,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) && ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) == 0)) { // we can only compress something that isn't a press or release if (!delayedTouch) { - delayedTouch = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()); + delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); delayedTouch->setTimestamp(event->timestamp()); if (renderControl) QQuickRenderControlPrivate::get(renderControl)->maybeUpdate(); @@ -1900,19 +1907,14 @@ void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) } // merging wasn't possible, so deliver the delayed event first, and then delay this one - reallyDeliverTouchEvent(delayedTouch); - delete delayedTouch; - delayedTouch = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()); + deliverDelayedTouchEvent(); + delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); delayedTouch->setTimestamp(event->timestamp()); return; } } else { - if (delayedTouch) { - // deliver the delayed touch first - reallyDeliverTouchEvent(delayedTouch); - delete delayedTouch; - delayedTouch = 0; - } + if (delayedTouch) + deliverDelayedTouchEvent(); reallyDeliverTouchEvent(event); } } @@ -1920,9 +1922,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) void QQuickWindowPrivate::flushDelayedTouchEvent() { if (delayedTouch) { - reallyDeliverTouchEvent(delayedTouch); - delete delayedTouch; - delayedTouch = 0; + deliverDelayedTouchEvent(); // Touch events which constantly start animations (such as a behavior tracking // the mouse point) need animations to start. diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index a61c0b0346..605a36fb1d 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -151,6 +151,7 @@ public: void deliverTouchEvent(QTouchEvent *); void reallyDeliverTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); + void deliverDelayedTouchEvent(); void flushDelayedTouchEvent(); bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered); @@ -219,7 +220,7 @@ public: QSGRenderLoop *windowManager; QQuickRenderControl *renderControl; QQuickAnimatorController *animationController; - QTouchEvent *delayedTouch; + QScopedPointer<QTouchEvent> delayedTouch; int touchRecursionGuard; QQuickCustomRenderStage *customRenderStage; diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index 24e565012e..5b9111d448 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -350,9 +350,7 @@ namespace QQuickTouchUtils { QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); if (!wd || !wd->delayedTouch) return; - wd->reallyDeliverTouchEvent(wd->delayedTouch); - delete wd->delayedTouch; - wd->delayedTouch = 0; + wd->deliverDelayedTouchEvent(); } } |