widgets: Don't create winId when the widget is being destroyed
When QWidget is being destroyed, its winId is cleared, and a QEvent::WinIdChange is sent. If a listener of this event reacted by calling winId() again, we might crash. A crash can be observed when this child widget is destroyed in dtor of its parent. E.g. here is a hierarchy of widgets: 1:QWidget 2:QObject 3:QWidget 4:QWidget If a listener subscribed for WinIdChange events from (4), and there is a connection to destroy (4) when (2) is destroyed. This will lead to infinite loop: 1. QWidget::~QWidget 2. QWidget::destroy 3. QWidgetPrivate::setWinId(0) 4. QCoreApplication::sendEvent(q, QEvent::WinIdChange); 5. eventFilter 6. QWidget::winId 7. QWidgetPrivate::createWinId (this=0x555555957600) at kernel/qwidget.cpp:2380 8. QWidgetPrivate::createWinId (this=0x55555596b040) at kernel/qwidget.cpp:2387 9. QWidget::create (this=0x5555558f2010, window=0, initializeWindow=true, destroyOldWindow=true) at kernel/qwidget.cpp:1163 10. QWidgetPrivate::createWinId (this=0x55555596b040) at kernel/qwidget.cpp:2387 11. QWidget::create (this=0x5555558f2010, window=0, initializeWindow=true, destroyOldWindow=true) at kernel/qwidget.cpp:1163 12. QWidgetPrivate::createWinId (this=0x55555596b040) at kernel/qwidget.cpp:2387 Fixes: QTBUG-81849 Change-Id: Ib4c33ac97d9a79c701431ae107bddfb22720ba0d Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
parent
e5408b62bd
commit
9ecc595d71
@ -2350,7 +2350,9 @@ QWidget *QWidget::find(WId id)
|
|||||||
*/
|
*/
|
||||||
WId QWidget::winId() const
|
WId QWidget::winId() const
|
||||||
{
|
{
|
||||||
if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) {
|
if (!data->in_destructor
|
||||||
|
&& (!testAttribute(Qt::WA_WState_Created) || !internalWinId()))
|
||||||
|
{
|
||||||
#ifdef ALIEN_DEBUG
|
#ifdef ALIEN_DEBUG
|
||||||
qDebug() << "QWidget::winId: creating native window for" << this;
|
qDebug() << "QWidget::winId: creating native window for" << this;
|
||||||
#endif
|
#endif
|
||||||
|
@ -409,6 +409,8 @@ private slots:
|
|||||||
void closeEvent();
|
void closeEvent();
|
||||||
void closeWithChildWindow();
|
void closeWithChildWindow();
|
||||||
|
|
||||||
|
void winIdAfterClose();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool ensureScreenSize(int width, int height);
|
bool ensureScreenSize(int width, int height);
|
||||||
|
|
||||||
@ -11261,5 +11263,54 @@ void tst_QWidget::closeWithChildWindow()
|
|||||||
QVERIFY(!childWidget->isVisible());
|
QVERIFY(!childWidget->isVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WinIdChangeSpy : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
QWidget *widget = nullptr;
|
||||||
|
WId winId = 0;
|
||||||
|
explicit WinIdChangeSpy(QWidget *w, QObject *parent = nullptr)
|
||||||
|
: QObject(parent)
|
||||||
|
, widget(w)
|
||||||
|
, winId(widget->winId())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override
|
||||||
|
{
|
||||||
|
if (obj == widget) {
|
||||||
|
if (event->type() == QEvent::WinIdChange) {
|
||||||
|
winId = widget->winId();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QWidget::winIdAfterClose()
|
||||||
|
{
|
||||||
|
auto widget = new QWidget;
|
||||||
|
auto notifier = new QObject(widget);
|
||||||
|
auto deleteWidget = new QWidget(new QWidget(widget));
|
||||||
|
auto spy = new WinIdChangeSpy(deleteWidget);
|
||||||
|
deleteWidget->installEventFilter(spy);
|
||||||
|
connect(notifier, &QObject::destroyed, [&] { delete deleteWidget; });
|
||||||
|
|
||||||
|
widget->setAttribute(Qt::WA_NativeWindow);
|
||||||
|
widget->windowHandle()->create();
|
||||||
|
widget->show();
|
||||||
|
|
||||||
|
QVERIFY(QTest::qWaitForWindowExposed(widget));
|
||||||
|
QVERIFY(spy->winId);
|
||||||
|
|
||||||
|
widget->windowHandle()->close();
|
||||||
|
delete widget;
|
||||||
|
|
||||||
|
QCOMPARE(spy->winId, WId(0));
|
||||||
|
delete spy;
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QWidget)
|
QTEST_MAIN(tst_QWidget)
|
||||||
#include "tst_qwidget.moc"
|
#include "tst_qwidget.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user