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:
parent
602bec1286
commit
b00a120d4d
|
@ -1414,6 +1414,7 @@ void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
|
||||||
bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
|
bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
|
||||||
Qt::KeyboardModifiers modifiers, bool &accepted)
|
Qt::KeyboardModifiers modifiers, bool &accepted)
|
||||||
{
|
{
|
||||||
|
Q_Q(QQuickWindow);
|
||||||
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
|
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
|
||||||
|
|
||||||
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
|
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
|
||||||
|
@ -1463,7 +1464,13 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
|
||||||
|
|
||||||
for (int i = startIdx; i >= 0; i--) {
|
for (int i = startIdx; i >= 0; i--) {
|
||||||
QQuickItem *itemToHover = itemsToHover[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);
|
hoverItems.prepend(itemToHover);
|
||||||
sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
|
sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -328,6 +328,8 @@ private slots:
|
||||||
|
|
||||||
void blockClosing();
|
void blockClosing();
|
||||||
|
|
||||||
|
void crashWhenHoverItemDeleted();
|
||||||
|
|
||||||
#ifndef QT_NO_CURSOR
|
#ifndef QT_NO_CURSOR
|
||||||
void cursor();
|
void cursor();
|
||||||
#endif
|
#endif
|
||||||
|
@ -1487,6 +1489,25 @@ void tst_qquickwindow::blockClosing()
|
||||||
QTRY_VERIFY(!window->isVisible());
|
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)
|
QTEST_MAIN(tst_qquickwindow)
|
||||||
|
|
||||||
#include "tst_qquickwindow.moc"
|
#include "tst_qquickwindow.moc"
|
||||||
|
|
Loading…
Reference in New Issue