macOS: work around getting invalid result from NSAlert::runModal
It is possible for an application to trigger the unexpected result of NSModalResponseContinue from our call to NSAlert::runModal, by showing and hiding a modal dialog first, and processing events explicitly. This at best only flashes the native message box, at worst it triggers an assert from our processResponse function evaluating that response value as impossible (via Q_UNREACHABLE). We should never call processResponse with NSModalResponseContinue, but instead keep the modal loop running and the dialog visible. So introduce a wrapper to NSAlert::runModal that keeps calling that method until we get a result other than NSModalResponseContinue. Writing an auto test for this failed; a simple test didn't reproduce the assert; trying to place the opening of the native message box into a lambda that would be called by the event loop (simulating the button press from the bug report's reproducer) resulted in the native message box never closing and the test blocking (and still not triggering the assert). Fixes: QTBUG-114546 Change-Id: Iab25eff55c48b103287d1881ac355e6cdd190f7a Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> (cherry picked from commit 2c458a82213ae568ad708e07ee9ed5f4e494a800) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
15de36f97b
commit
848c017165
@ -28,6 +28,7 @@ private:
|
||||
Qt::WindowModality modality() const;
|
||||
NSAlert *m_alert = nullptr;
|
||||
QEventLoop *m_eventLoop = nullptr;
|
||||
NSModalResponse runModal() const;
|
||||
void processResponse(NSModalResponse response);
|
||||
};
|
||||
|
||||
|
@ -222,7 +222,7 @@ bool QCocoaMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality w
|
||||
if (m_alert && NSApp.modalWindow != m_alert.window) {
|
||||
qCDebug(lcQpaDialogs) << "Running deferred modal" << m_alert;
|
||||
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
|
||||
processResponse([m_alert runModal]);
|
||||
processResponse(runModal());
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -230,6 +230,20 @@ bool QCocoaMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality w
|
||||
return true;
|
||||
}
|
||||
|
||||
// We shouldn't get NSModalResponseContinue as a response from NSAlert::runModal,
|
||||
// and processResponse must not be called with that value (if we are there, it's
|
||||
// too late to do anything about it.
|
||||
// However, as QTBUG-114546 shows, there are scenarios where we might get that
|
||||
// response anyway. We interpret it to keep the modal loop running, and we only
|
||||
// return if we got something else to pass to processResponse.
|
||||
NSModalResponse QCocoaMessageDialog::runModal() const
|
||||
{
|
||||
NSModalResponse response = NSModalResponseContinue;
|
||||
while (response == NSModalResponseContinue)
|
||||
response = [m_alert runModal];
|
||||
return response;
|
||||
}
|
||||
|
||||
void QCocoaMessageDialog::exec()
|
||||
{
|
||||
Q_ASSERT(m_alert);
|
||||
@ -242,7 +256,7 @@ void QCocoaMessageDialog::exec()
|
||||
} else {
|
||||
qCDebug(lcQpaDialogs) << "Running modal" << m_alert;
|
||||
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
|
||||
processResponse([m_alert runModal]);
|
||||
processResponse(runModal());
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user