aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Moe Gustavsen <richard.gustavsen@theqtcompany.com>2015-05-19 11:20:12 +0200
committerRichard Moe Gustavsen <richard.gustavsen@theqtcompany.com>2015-05-22 09:59:41 +0000
commitb8db3a1c6036e6d8ab68b5f1c01a00c289a67ab8 (patch)
tree97301552b6bc2229bbfb94440c2d08681da6fca6
parenteb2ea7098512bdc241b7fdabf5c21c229d56fc07 (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.cpp30
-rw-r--r--src/quick/items/qquickwindow_p.h3
-rw-r--r--tests/auto/quick/shared/viewtestutil.cpp4
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();
}
}