Commit 951cb9c3 authored by Frank Reininghaus's avatar Frank Reininghaus
Browse files

Prevent crashes caused by nested event loops run when renaming inline

When renaming inline and starting a drag or invoking the context menu,
a nested event loop will be run. If the role editor loses focus and
emits roleEditingFinished(), we must prevent that deleteLater() is
called because this would delete the role editor inside a nested event
loop which is run from one of its own functions. We would get a crash
when returning from that event loop otherwise.

BUG: 308018
BUG: 309421
FIXED-IN: 4.9.4
parent cc069353
......@@ -609,7 +609,10 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const
this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant)));
disconnect(m_roleEditor, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)),
this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant)));
m_roleEditor->deleteLater();
// Do not delete the role editor using deleteLater() because we might be
// inside a nested event loop which has been started by one of its event
// handlers (contextMenuEvent() or drag&drop inside mouseMoveEvent()).
m_roleEditor->deleteWhenIdle();
m_roleEditor = 0;
}
return;
......@@ -1274,7 +1277,11 @@ void KStandardItemListWidget::closeRoleEditor()
this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant)));
disconnect(m_roleEditor, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)),
this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant)));
m_roleEditor->deleteLater();
// Do not delete the role editor using deleteLater() because we might be
// inside a nested event loop which has been started by one of its event
// handlers (contextMenuEvent() or drag&drop inside mouseMoveEvent()).
m_roleEditor->deleteWhenIdle();
m_roleEditor = 0;
}
......
......@@ -26,7 +26,9 @@ KItemListRoleEditor::KItemListRoleEditor(QWidget *parent) :
KTextEdit(parent),
m_index(0),
m_role(),
m_blockFinishedSignal(false)
m_blockFinishedSignal(false),
m_eventHandlingLevel(0),
m_deleteAfterEventHandling(false)
{
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
......@@ -64,6 +66,20 @@ QByteArray KItemListRoleEditor::role() const
return m_role;
}
void KItemListRoleEditor::deleteWhenIdle()
{
if (m_eventHandlingLevel > 0) {
// We are handling an event at the moment. It could be that we
// are in a nested event loop run by contextMenuEvent() or a
// call of mousePressEvent() which results in drag&drop.
// -> do not call deleteLater() to prevent a crash when we
// return from the nested event loop.
m_deleteAfterEventHandling = true;
} else {
deleteLater();
}
}
bool KItemListRoleEditor::eventFilter(QObject* watched, QEvent* event)
{
if (watched == parentWidget() && event->type() == QEvent::Resize) {
......@@ -75,13 +91,42 @@ bool KItemListRoleEditor::eventFilter(QObject* watched, QEvent* event)
bool KItemListRoleEditor::event(QEvent* event)
{
++m_eventHandlingLevel;
if (event->type() == QEvent::FocusOut) {
QFocusEvent* focusEvent = static_cast<QFocusEvent*>(event);
if (focusEvent->reason() != Qt::PopupFocusReason) {
emitRoleEditingFinished();
}
}
return KTextEdit::event(event);
const int result = KTextEdit::event(event);
--m_eventHandlingLevel;
if (m_deleteAfterEventHandling && m_eventHandlingLevel == 0) {
// Schedule this object for deletion and make sure that we do not try
// to deleteLater() again when the DeferredDelete event is received.
deleteLater();
m_deleteAfterEventHandling = false;
}
return result;
}
bool KItemListRoleEditor::viewportEvent(QEvent* event)
{
++m_eventHandlingLevel;
const bool result = KTextEdit::viewportEvent(event);
--m_eventHandlingLevel;
if (m_deleteAfterEventHandling && m_eventHandlingLevel == 0) {
// Schedule this object for deletion and make sure that we do not try
// to deleteLater() again when the DeferredDelete event is received.
deleteLater();
m_deleteAfterEventHandling = false;
}
return result;
}
void KItemListRoleEditor::keyPressEvent(QKeyEvent* event)
......
......@@ -47,6 +47,15 @@ public:
void setRole(const QByteArray& role);
QByteArray role() const;
/**
* Calls deleteLater() if no event is being handled at the moment.
* Otherwise, the deletion is deferred until the event handling is
* finished. This prevents that the deletion happens inside a nested
* event loop which might be run in contextMenuEvent() or
* mouseMoveEvent() because this would probably cause a crash.
*/
void deleteWhenIdle();
virtual bool eventFilter(QObject* watched, QEvent* event);
signals:
......@@ -55,6 +64,7 @@ signals:
protected:
virtual bool event(QEvent* event);
virtual bool viewportEvent(QEvent* event);
virtual void keyPressEvent(QKeyEvent* event);
private slots:
......@@ -75,6 +85,8 @@ private:
int m_index;
QByteArray m_role;
bool m_blockFinishedSignal;
int m_eventHandlingLevel;
bool m_deleteAfterEventHandling;
};
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment