From 3dbc7596a3b7aca37e80f76288d3ab761ddd247a Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 8 Oct 2019 10:56:44 +0200 Subject: Drag'n'Drop: fix crash when widgets are destroyed during event handling Widgets might be destroyed when handling a dragMoveEvent, in which case the following code will operate on dangling pointers or null pointers. Use a QPointer to watch for the original event receiver to disappear, and add the necessary checks for the objects we deliver events to being null. Change-Id: I4ca2f182540ae21113f4bea4e5c569e983cc58bf Fixes: QTBUG-78907 Reviewed-by: Gatis Paeglis --- src/widgets/kernel/qwidgetwindow.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'src/widgets/kernel') diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index fbc71cd0ea..aad505ed29 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -885,7 +885,7 @@ void QWidgetWindow::handleDragEnterEvent(QDragEnterEvent *event, QWidget *widget void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event) { - auto *widget = findDnDTarget(m_widget, event->pos()); + QPointer widget = findDnDTarget(m_widget, event->pos()); if (!widget) { event->ignore(); if (m_dragTarget) { // Send DragLeave to previous @@ -908,14 +908,18 @@ void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event) QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event); m_dragTarget = nullptr; } - // Send DragEnter to new widget. - handleDragEnterEvent(static_cast(event), widget); - // Handling 'DragEnter' should suffice for the application. - translated.setDropAction(event->dropAction()); - translated.setAccepted(event->isAccepted()); - // The drag enter event is always immediately followed by a drag move event, - // see QDragEnterEvent documentation. - QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + // widget might have been deleted when handling the leaveEvent + if (widget) { + // Send DragEnter to new widget. + handleDragEnterEvent(static_cast(event), widget); + // Handling 'DragEnter' should suffice for the application. + translated.setDropAction(event->dropAction()); + translated.setAccepted(event->isAccepted()); + // The drag enter event is always immediately followed by a drag move event, + // see QDragEnterEvent documentation. + if (m_dragTarget) + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + } } event->setAccepted(translated.isAccepted()); event->setDropAction(translated.dropAction()); -- cgit v1.2.3 From 1b6db1849477be30ef0ca52c288d358b911ea1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 14 Oct 2019 15:58:50 +0200 Subject: Propagate application termination requests through QPA Instead of having each platform plugin deal with application termination in their own weird ways, we now have a QPA API to signal that the system requested the application to terminate. On the QGuiApplication side this results in a Quit event being sent to the application, which triggers the default behavior of closing all app windows, and then finally calling QCoreApplication::quit(). The quit event replaces the misuse of a close event being sent to the application. Close events are documented as being sent to windows. The close events that are sent to individual windows as part of the quit process can be ignored. This will skip the final quit() of the application, and also inform the platform that the quit was not accepted, in case that should be propagated further. In the future the logic for closing windows should be unified between the various approaches in closeAllWindows, shouldQuit, and friends. Change-Id: I0ed7f1c0d3f0bf1a755e1dd4066e1575fc3a28e1 Reviewed-by: Paul Olav Tvete --- src/widgets/kernel/qapplication.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'src/widgets/kernel') diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 3223781b63..dfa1bc23b1 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1866,22 +1866,19 @@ void QApplication::aboutQt() bool QApplication::event(QEvent *e) { Q_D(QApplication); - if(e->type() == QEvent::Close) { - QCloseEvent *ce = static_cast(e); - ce->accept(); + if (e->type() == QEvent::Quit) { closeAllWindows(); - - const QWidgetList list = topLevelWidgets(); - for (auto *w : list) { + for (auto *w : topLevelWidgets()) { if (w->isVisible() && !(w->windowType() == Qt::Desktop) && !(w->windowType() == Qt::Popup) && (!(w->windowType() == Qt::Dialog) || !w->parentWidget())) { - ce->ignore(); - break; + e->ignore(); + return true; } } - if (ce->isAccepted()) { - return true; - } + // Explicitly call QCoreApplication instead of QGuiApplication so that + // we don't let QGuiApplication close any windows we skipped earlier in + // closeAllWindows(). FIXME: Unify all this close magic through closeAllWindows. + return QCoreApplication::event(e); #ifndef Q_OS_WIN } else if (e->type() == QEvent::LocaleChange) { // on Windows the event propagation is taken care by the -- cgit v1.2.3