Fixes crash in QWidget::effectiveWinId.

Widgets are left in a transitional (and incosistent) state while being
re-parented, which caused QWidget::effectiveWinId() to crash in certain
circumstances. See patch for more details.

Auto test included.

Reviewed-by: ogoffart
(cherry picked from commit 6db0153cd7e35e4a919a76ae2aadbf2d2510bfb7)
This commit is contained in:
Bjørn Erik Nilsen 2011-05-02 12:39:45 +02:00 committed by Olivier Goffart
parent 7ee981a834
commit 1e41e3076a
3 changed files with 46 additions and 0 deletions

View File

@ -299,6 +299,7 @@ QWidgetPrivate::QWidgetPrivate(int version)
#ifndef QT_NO_IM
, inheritsInputMethodHints(0)
#endif
, inSetParent(0)
#if defined(Q_WS_X11)
, picture(0)
#elif defined(Q_WS_WIN)
@ -2599,6 +2600,22 @@ WId QWidget::effectiveWinId() const
if (id || !testAttribute(Qt::WA_WState_Created))
return id;
QWidget *realParent = nativeParentWidget();
if (!realParent && d_func()->inSetParent) {
// In transitional state. This is really just a workaround. The real problem
// is that QWidgetPrivate::setParent_sys (platform specific code) first sets
// the window id to 0 (setWinId(0)) before it sets the Qt::WA_WState_Created
// attribute to false. The correct way is to do it the other way around, and
// in that case the Qt::WA_WState_Created logic above will kick in and
// return 0 whenever the widget is in a transitional state. However, changing
// the original logic for all platforms is far more intrusive and might
// break existing applications.
// Note: The widget can only be in a transitional state when changing its
// parent -- everything else is an internal error -- hence explicitly checking
// against 'inSetParent' rather than doing an unconditional return whenever
// 'realParent' is 0 (which may cause strange artifacts and headache later).
return 0;
}
// This widget *must* have a native parent widget.
Q_ASSERT(realParent);
Q_ASSERT(realParent->internalWinId());
return realParent->internalWinId();
@ -10111,6 +10128,7 @@ void QWidget::setParent(QWidget *parent)
void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
{
Q_D(QWidget);
d->inSetParent = true;
bool resized = testAttribute(Qt::WA_Resized);
bool wasCreated = testAttribute(Qt::WA_WState_Created);
QWidget *oldtlw = window();
@ -10271,6 +10289,8 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
ancestorProxy->d_func()->embedSubWindow(this);
}
#endif
d->inSetParent = false;
}
/*!

View File

@ -770,6 +770,7 @@ public:
#ifndef QT_NO_IM
uint inheritsInputMethodHints : 1;
#endif
uint inSetParent : 1;
// *************************** Platform specific ************************************
#if defined(Q_WS_X11) // <----------------------------------------------------------- X11

View File

@ -345,6 +345,7 @@ private slots:
void immediateRepaintAfterInvalidateBuffer();
#endif
void effectiveWinId();
void effectiveWinId2();
void customDpi();
void customDpiProperty();
@ -8495,6 +8496,30 @@ void tst_QWidget::effectiveWinId()
QVERIFY(child.effectiveWinId());
}
void tst_QWidget::effectiveWinId2()
{
QWidget parent;
class MyWidget : public QWidget {
bool event(QEvent *e)
{
if (e->type() == QEvent::WinIdChange) {
// Shouldn't crash.
effectiveWinId();
}
return QWidget::event(e);
}
};
MyWidget child;
child.setParent(&parent);
parent.show();
child.setParent(0);
child.setParent(&parent);
}
class CustomWidget : public QWidget
{
public: