Resolves: tdf#88985 block app from exiting during macro execution
but stop basic execution on the exit attempt, and then resend exit at a safe
place when basic execution has stopped
Change-Id: I77c43acffa0b82e8125dcb3b10ad9bf0d6dd26c3
diff --git a/framework/inc/services/desktop.hxx b/framework/inc/services/desktop.hxx
index e8fd001..6e0c215 100644
--- a/framework/inc/services/desktop.hxx
+++ b/framework/inc/services/desktop.hxx
@@ -428,6 +428,18 @@ class Desktop : private cppu::BaseMutex,
*/
css::uno::Reference< css::frame::XTerminateListener > m_xQuickLauncher;
/** special terminate listener active when a macro is executing.
* Because basic runs Application::Yield internally the application may quit
* while running inside the internal basic event loop. So all the basic
* infrastructure may be deleted while the call is executing, leading to
* a varient of crashes. So this special terminate listener will
* veto the current quit attempt, stop basic execution, which will
* cause the inner event loop to quit, and on return to the outer normal
* application event loop then resend the quit attempt.
* So these implementation must be a special terminate listener too .-(
*/
css::uno::Reference< css::frame::XTerminateListener > m_xStarBasicQuitGuard;
/** special terminate listener which loads images asynchronous for current open documents.
* Because internally it uses blocking system APIs... it can't be guaranteed that
* running jobs can be cancelled successfully if the corresponding document will be closed...
diff --git a/framework/source/services/desktop.cxx b/framework/source/services/desktop.cxx
index 64e7e64..0ec0bce 100644
--- a/framework/source/services/desktop.cxx
+++ b/framework/source/services/desktop.cxx
@@ -171,6 +171,7 @@ Desktop::Desktop( const css::uno::Reference< css::uno::XComponentContext >& xCon
, m_xDispatchRecorderSupplier( )
, m_xPipeTerminator ( )
, m_xQuickLauncher ( )
, m_xStarBasicQuitGuard ( )
, m_xSWThreadManager ( )
, m_xSfxTerminator ( )
, m_xTitleNumberGenerator ( )
@@ -214,6 +215,7 @@ sal_Bool SAL_CALL Desktop::terminate()
css::uno::Reference< css::frame::XTerminateListener > xPipeTerminator = m_xPipeTerminator;
css::uno::Reference< css::frame::XTerminateListener > xQuickLauncher = m_xQuickLauncher;
css::uno::Reference< css::frame::XTerminateListener > xStarBasicQuitGuard = m_xStarBasicQuitGuard;
css::uno::Reference< css::frame::XTerminateListener > xSWThreadManager = m_xSWThreadManager;
css::uno::Reference< css::frame::XTerminateListener > xSfxTerminator = m_xSfxTerminator;
@@ -275,6 +277,12 @@ sal_Bool SAL_CALL Desktop::terminate()
lCalledTerminationListener.push_back( xQuickLauncher );
}
if ( xStarBasicQuitGuard.is() )
{
xStarBasicQuitGuard->queryTermination( aEvent );
lCalledTerminationListener.push_back( xStarBasicQuitGuard );
}
if ( xSWThreadManager.is() )
{
xSWThreadManager->queryTermination( aEvent );
@@ -322,6 +330,9 @@ sal_Bool SAL_CALL Desktop::terminate()
xQuickLauncher->notifyTermination( aEvent );
}
if ( xStarBasicQuitGuard.is() )
xStarBasicQuitGuard->notifyTermination( aEvent );
if ( xSWThreadManager.is() )
xSWThreadManager->notifyTermination( aEvent );
@@ -395,6 +406,11 @@ void SAL_CALL Desktop::addTerminateListener( const css::uno::Reference< css::fra
m_xQuickLauncher = xListener;
return;
}
if( sImplementationName == "com.sun.star.comp.svx.StarBasicQuitGuard" )
{
m_xStarBasicQuitGuard = xListener;
return;
}
if( sImplementationName == "com.sun.star.util.comp.FinalThreadManager" )
{
m_xSWThreadManager = xListener;
@@ -436,6 +452,12 @@ void SAL_CALL Desktop::removeTerminateListener( const css::uno::Reference< css::
return;
}
if( sImplementationName == "com.sun.star.comp.svx.StarBasicQuitGuard" )
{
m_xStarBasicQuitGuard.clear();
return;
}
if( sImplementationName == "com.sun.star.util.comp.FinalThreadManager" )
{
m_xSWThreadManager.clear();
@@ -1068,6 +1090,7 @@ void SAL_CALL Desktop::disposing()
m_xPipeTerminator.clear();
m_xQuickLauncher.clear();
m_xStarBasicQuitGuard.clear();
m_xSWThreadManager.clear();
m_xSfxTerminator.clear();
m_xCommandOptions.reset();
diff --git a/svx/source/form/fmscriptingenv.cxx b/svx/source/form/fmscriptingenv.cxx
index 3cc284e..b79d421 100644
--- a/svx/source/form/fmscriptingenv.cxx
+++ b/svx/source/form/fmscriptingenv.cxx
@@ -22,14 +22,18 @@
#include "fmscriptingenv.hxx"
#include "svx/fmmodel.hxx"
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/script/XScriptListener.hpp>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XTerminateListener.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/EventObject.hpp>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/script/XScriptListener.hpp>
#include <tools/diagnose_ex.h>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/compbase.hxx>
#include <comphelper/processfactory.hxx>
#include <vcl/svapp.hxx>
#include <osl/mutex.hxx>
@@ -764,6 +768,111 @@ namespace svxform
m_pScriptExecutor = nullptr;
}
// tdf#88985 If LibreOffice tries to exit during the execution of a macro
// then: detect the effort, stop basic execution, block until the macro
// returns due to that stop, then restart the quit. This avoids the app
// exiting and destroying itself until the macro is parked at a safe place
// to do that.
class QuitGuard
{
private:
class TerminateListener : public cppu::WeakComponentImplHelper<css::frame::XTerminateListener,
css::lang::XServiceInfo>
{
private:
css::uno::Reference<css::frame::XDesktop2> m_xDesktop;
osl::Mutex maMutex;
bool mbQuitBlocked;
public:
// XTerminateListener
virtual void SAL_CALL queryTermination(const css::lang::EventObject& /*rEvent*/)
throw(css::frame::TerminationVetoException, css::uno::RuntimeException, std::exception) override
{
mbQuitBlocked = true;
StarBASIC::Stop();
throw css::frame::TerminationVetoException();
}
virtual void SAL_CALL notifyTermination(const css::lang::EventObject& /*rEvent*/)
throw(css::uno::RuntimeException, std::exception) override
{
mbQuitBlocked = false;
}
using cppu::WeakComponentImplHelperBase::disposing;
virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent)
throw(css::uno::RuntimeException, std::exception) override
{
const bool bShutDown = (rEvent.Source == m_xDesktop);
if (bShutDown && m_xDesktop.is())
{
m_xDesktop->removeTerminateListener(this);
m_xDesktop.clear();
}
}
// XServiceInfo
virtual OUString SAL_CALL getImplementationName()
throw (css::uno::RuntimeException, std::exception) override
{
return OUString("com.sun.star.comp.svx.StarBasicQuitGuard");
}
virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName)
throw (css::uno::RuntimeException, std::exception) override
{
return cppu::supportsService(this, ServiceName);
}
virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames()
throw (css::uno::RuntimeException, std::exception) override
{
css::uno::Sequence<OUString> aSeq { "com.sun.star.svx.StarBasicQuitGuard" };
return aSeq;
}
public:
TerminateListener()
: cppu::WeakComponentImplHelper<css::frame::XTerminateListener,
css::lang::XServiceInfo>(maMutex)
, mbQuitBlocked(false)
{
}
void start()
{
css::uno::Reference<css::uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
m_xDesktop = css::frame::Desktop::create(xContext);
m_xDesktop->addTerminateListener(this);
}
void stop()
{
if (!m_xDesktop.is())
return;
m_xDesktop->removeTerminateListener(this);
if (mbQuitBlocked)
m_xDesktop->terminate();
}
};
TerminateListener* mpListener;
css::uno::Reference<css::frame::XTerminateListener> mxLifeCycle;
public:
QuitGuard()
: mpListener(new TerminateListener)
, mxLifeCycle(mpListener)
{
mpListener->start();
}
~QuitGuard()
{
mpListener->stop();
}
};
IMPL_LINK_TYPED( FormScriptListener, OnAsyncScriptEvent, void*, p, void )
{
@@ -776,7 +885,10 @@ namespace svxform
::osl::ClearableMutexGuard aGuard( m_aMutex );
if ( !impl_isDisposed_nothrow() )
{
QuitGuard aQuitGuard;
impl_doFireScriptEvent_nothrow( aGuard, *_pEvent, nullptr );
}
}
delete _pEvent;