Fix hover event crash

It was possible for a hover event to be sent to a QQuickItem that has
already been scheduled to be deleted (through deleteLater()). This
lead to an access on an already-freed object when the leave event
is generated. This change ensures that the item is part of a scene
before generating the hover enter event.

Task-number: QTBUG-32771

Change-Id: I69adb6bbd0ae52c70a6bda4e6c918b7671549a4c
Reviewed-by: Alan Alpert <aalpert@blackberry.com>
This commit is contained in:
Josh Faust 2013-08-01 16:51:03 -06:00 committed by The Qt Project
parent 602bec1286
commit b00a120d4d
3 changed files with 65 additions and 1 deletions

View File

@ -1414,6 +1414,7 @@ void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, bool &accepted)
{
Q_Q(QQuickWindow);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
@ -1463,7 +1464,13 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
for (int i = startIdx; i >= 0; i--) {
QQuickItem *itemToHover = itemsToHover[i];
if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover);
// The item may be about to be deleted or reparented to another window
// due to another hover event delivered in this function. If that is the
// case, sending a hover event here will cause a crash or other bad
// behavior when the leave event is generated. Checking
// itemToHoverPrivate->window here prevents that case.
if (itemToHoverPrivate->window == q && itemToHoverPrivate->hoverEnabled) {
hoverItems.prepend(itemToHover);
sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
}

View File

@ -0,0 +1,36 @@
import QtQuick 2.0
import QtQuick.Window 2.0 as Window
Window.Window {
width: 200
height: 200
color: "#00FF00"
Column {
Rectangle {
objectName: 'item1'
color: 'red'
width: 100
height: 100
MouseArea {
id: area
anchors.fill: parent
hoverEnabled: true
}
}
Loader {
objectName: 'item2'
width: 100
height: 100
active: area.containsMouse
sourceComponent: Rectangle {
color: 'blue'
MouseArea {
anchors.fill: parent
hoverEnabled: true
}
}
}
}
}

View File

@ -328,6 +328,8 @@ private slots:
void blockClosing();
void crashWhenHoverItemDeleted();
#ifndef QT_NO_CURSOR
void cursor();
#endif
@ -1487,6 +1489,25 @@ void tst_qquickwindow::blockClosing()
QTRY_VERIFY(!window->isVisible());
}
void tst_qquickwindow::crashWhenHoverItemDeleted()
{
// QTBUG-32771
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("hoverCrash.qml"));
QQuickWindow* window = qobject_cast<QQuickWindow *>(component.create());
QVERIFY(window);
window->show();
QTest::qWaitForWindowExposed(window);
// Simulate a move from the first rectangle to the second. Crash will happen in here
// Moving instantaneously from (0, 99) to (0, 102) does not cause the crash
for (int i = 99; i < 102; ++i)
{
QTest::mouseMove(window, QPoint(0, i));
}
}
QTEST_MAIN(tst_qquickwindow)
#include "tst_qquickwindow.moc"