| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_svx.hxx" |
| #include "fmscriptingenv.hxx" |
| #include "svx/fmmodel.hxx" |
| |
| /** === begin UNO includes === **/ |
| #include <com/sun/star/lang/IllegalArgumentException.hpp> |
| #include <com/sun/star/script/XScriptListener.hpp> |
| #include <com/sun/star/container/XHierarchicalNameAccess.hpp> |
| #include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp> |
| #include <com/sun/star/lang/DisposedException.hpp> |
| /** === end UNO includes === **/ |
| |
| #include <tools/diagnose_ex.h> |
| #include <cppuhelper/implbase1.hxx> |
| #include <comphelper/implementationreference.hxx> |
| #include <comphelper/componentcontext.hxx> |
| #include <comphelper/processfactory.hxx> |
| #include <vcl/svapp.hxx> |
| #include <vos/mutex.hxx> |
| #include <sfx2/objsh.hxx> |
| #include <sfx2/app.hxx> |
| #include <basic/basmgr.hxx> |
| |
| #include <boost/shared_ptr.hpp> |
| |
| //........................................................................ |
| namespace svxform |
| { |
| //........................................................................ |
| |
| /** === begin UNO using === **/ |
| using ::com::sun::star::uno::Reference; |
| using ::com::sun::star::script::XEventAttacherManager; |
| using ::com::sun::star::lang::IllegalArgumentException; |
| using ::com::sun::star::script::XScriptListener; |
| using ::com::sun::star::script::ScriptEvent; |
| using ::com::sun::star::uno::RuntimeException; |
| using ::com::sun::star::lang::EventObject; |
| using ::com::sun::star::reflection::InvocationTargetException; |
| using ::com::sun::star::uno::Any; |
| using ::com::sun::star::container::XHierarchicalNameAccess; |
| using ::com::sun::star::reflection::XInterfaceMethodTypeDescription; |
| using ::com::sun::star::uno::UNO_QUERY_THROW; |
| using ::com::sun::star::lang::DisposedException; |
| using ::com::sun::star::uno::RuntimeException; |
| using ::com::sun::star::uno::Exception; |
| using ::com::sun::star::uno::Sequence; |
| using ::com::sun::star::uno::XInterface; |
| /** === end UNO using === **/ |
| |
| class FormScriptingEnvironment; |
| |
| //==================================================================== |
| //= FormScriptListener |
| //==================================================================== |
| typedef ::cppu::WeakImplHelper1 < XScriptListener |
| > FormScriptListener_Base; |
| |
| /** implements the XScriptListener interface, is used by FormScriptingEnvironment |
| */ |
| class FormScriptListener :public FormScriptListener_Base |
| { |
| private: |
| ::osl::Mutex m_aMutex; |
| ::rtl::Reference< FormScriptingEnvironment > m_pScriptExecutor; |
| |
| public: |
| FormScriptListener( const ::rtl::Reference< FormScriptingEnvironment >& _pScriptExecutor ); |
| |
| // XScriptListener |
| virtual void SAL_CALL firing( const ScriptEvent& aEvent ) throw (RuntimeException); |
| virtual Any SAL_CALL approveFiring( const ScriptEvent& aEvent ) throw (InvocationTargetException, RuntimeException); |
| // XEventListener |
| virtual void SAL_CALL disposing( const EventObject& Source ) throw (RuntimeException); |
| |
| // lifetime control |
| void SAL_CALL dispose(); |
| |
| protected: |
| ~FormScriptListener(); |
| |
| private: |
| /** determines whether calling a given method at a given listener interface can be done asynchronously |
| |
| @param _rListenerType |
| the name of the UNO type whose method is to be checked |
| @param _rMethodName |
| the name of the method at the interface determined by _rListenerType |
| |
| @return |
| <TRUE/> if and only if the method is declared <code>oneway</code>, i.e. can be called asynchronously |
| */ |
| bool impl_allowAsynchronousCall_nothrow( const ::rtl::OUString& _rListenerType, const ::rtl::OUString& _rMethodName ) const; |
| |
| /** determines whether the instance is already disposed |
| */ |
| bool impl_isDisposed_nothrow() const { return !m_pScriptExecutor.is(); } |
| |
| /** fires the given script event in a thread-safe manner |
| |
| This methods calls our script executor's doFireScriptEvent, with previously releasing the given mutex guard, |
| but ensuring that our script executor is not deleted between this release and the actual call. |
| |
| @param _rGuard |
| a clearable guard to our mutex. Must be the only active guard to our mutex. |
| @param _rEvent |
| the event to fire |
| @param _pSyncronousResult |
| a place to take a possible result of the script call. |
| |
| @precond |
| m_pScriptExecutor is not <NULL/>. |
| */ |
| void impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard& _rGuard, const ScriptEvent& _rEvent, Any* _pSyncronousResult ); |
| |
| private: |
| DECL_LINK( OnAsyncScriptEvent, ScriptEvent* ); |
| }; |
| |
| //==================================================================== |
| //= FormScriptingEnvironment |
| //==================================================================== |
| class FormScriptingEnvironment : public IFormScriptingEnvironment |
| { |
| private: |
| typedef ::comphelper::ImplementationReference< FormScriptListener, XScriptListener > ListenerImplementation; |
| |
| private: |
| ::osl::Mutex m_aMutex; |
| oslInterlockedCount m_refCount; |
| ListenerImplementation m_pScriptListener; |
| FmFormModel& m_rFormModel; |
| bool m_bDisposed; |
| |
| public: |
| FormScriptingEnvironment( FmFormModel& _rModel ); |
| virtual ~FormScriptingEnvironment(); |
| |
| // callback for FormScriptListener |
| void doFireScriptEvent( const ScriptEvent& _rEvent, Any* _pSyncronousResult ); |
| |
| // IFormScriptingEnvironment |
| virtual void registerEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager ); |
| virtual void revokeEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager ); |
| virtual void dispose(); |
| |
| // IReference |
| virtual oslInterlockedCount SAL_CALL acquire(); |
| virtual oslInterlockedCount SAL_CALL release(); |
| |
| private: |
| void impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister ); |
| |
| private: |
| FormScriptingEnvironment(); // never implemented |
| FormScriptingEnvironment( const FormScriptingEnvironment& ); // never implemented |
| FormScriptingEnvironment& operator=( const FormScriptingEnvironment& ); // never implemented |
| }; |
| |
| //==================================================================== |
| //= FormScriptListener |
| //==================================================================== |
| //-------------------------------------------------------------------- |
| FormScriptListener::FormScriptListener( const ::rtl::Reference< FormScriptingEnvironment >& _pScriptExecutor ) |
| :m_pScriptExecutor( _pScriptExecutor ) |
| { |
| } |
| |
| //-------------------------------------------------------------------- |
| FormScriptListener::~FormScriptListener() |
| { |
| } |
| |
| //-------------------------------------------------------------------- |
| bool FormScriptListener::impl_allowAsynchronousCall_nothrow( const ::rtl::OUString& _rListenerType, const ::rtl::OUString& _rMethodName ) const |
| { |
| bool bAllowAsynchronousCall = false; |
| try |
| { |
| ::comphelper::ComponentContext aContext( ::comphelper::getProcessServiceFactory() ); |
| Reference< XHierarchicalNameAccess > xTypeDescriptions( aContext.getSingleton( "com.sun.star.reflection.theTypeDescriptionManager" ), UNO_QUERY_THROW ); |
| |
| ::rtl::OUString sMethodDescription( _rListenerType ); |
| sMethodDescription += ::rtl::OUString::createFromAscii( "::" ); |
| sMethodDescription += _rMethodName; |
| |
| Reference< XInterfaceMethodTypeDescription > xMethod( xTypeDescriptions->getByHierarchicalName( sMethodDescription ), UNO_QUERY_THROW ); |
| bAllowAsynchronousCall = xMethod->isOneway(); |
| } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| } |
| return bAllowAsynchronousCall; |
| } |
| |
| //-------------------------------------------------------------------- |
| void FormScriptListener::impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard& _rGuard, const ScriptEvent& _rEvent, Any* _pSyncronousResult ) |
| { |
| OSL_PRECOND( m_pScriptExecutor.is(), "FormScriptListener::impl_doFireScriptEvent_nothrow: this will crash!" ); |
| |
| ::rtl::Reference< FormScriptingEnvironment > pExecutor( m_pScriptExecutor ); |
| _rGuard.clear(); |
| pExecutor->doFireScriptEvent( _rEvent, _pSyncronousResult ); |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL FormScriptListener::firing( const ScriptEvent& _rEvent ) throw (RuntimeException) |
| { |
| ::osl::ClearableMutexGuard aGuard( m_aMutex ); |
| static const ::rtl::OUString vbaInterOp = |
| ::rtl::OUString::createFromAscii("VBAInterop"); |
| if ( _rEvent.ScriptType.equals(vbaInterOp) ) |
| return; // not handled here |
| |
| if ( impl_isDisposed_nothrow() ) |
| return; |
| |
| if ( !impl_allowAsynchronousCall_nothrow( _rEvent.ListenerType.getTypeName(), _rEvent.MethodName ) ) |
| { |
| impl_doFireScriptEvent_nothrow( aGuard, _rEvent, NULL ); |
| return; |
| } |
| |
| acquire(); |
| Application::PostUserEvent( LINK( this, FormScriptListener, OnAsyncScriptEvent ), new ScriptEvent( _rEvent ) ); |
| } |
| |
| //-------------------------------------------------------------------- |
| Any SAL_CALL FormScriptListener::approveFiring( const ScriptEvent& _rEvent ) throw (InvocationTargetException, RuntimeException) |
| { |
| Any aResult; |
| |
| ::osl::ClearableMutexGuard aGuard( m_aMutex ); |
| if ( !impl_isDisposed_nothrow() ) |
| impl_doFireScriptEvent_nothrow( aGuard, _rEvent, &aResult ); |
| |
| return aResult; |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL FormScriptListener::disposing( const EventObject& /*Source*/ ) throw (RuntimeException) |
| { |
| // not interested in |
| } |
| |
| //-------------------------------------------------------------------- |
| void SAL_CALL FormScriptListener::dispose() |
| { |
| ::osl::MutexGuard aGuard( m_aMutex ); |
| m_pScriptExecutor = NULL; |
| } |
| |
| //-------------------------------------------------------------------- |
| IMPL_LINK( FormScriptListener, OnAsyncScriptEvent, ScriptEvent*, _pEvent ) |
| { |
| OSL_PRECOND( _pEvent != NULL, "FormScriptListener::OnAsyncScriptEvent: invalid event!" ); |
| if ( !_pEvent ) |
| return 1L; |
| |
| { |
| ::osl::ClearableMutexGuard aGuard( m_aMutex ); |
| |
| if ( !impl_isDisposed_nothrow() ) |
| impl_doFireScriptEvent_nothrow( aGuard, *_pEvent, NULL ); |
| } |
| |
| delete _pEvent; |
| // we acquired ourself immediately before posting the event |
| release(); |
| return 0L; |
| } |
| |
| //==================================================================== |
| //= FormScriptingEnvironment |
| //==================================================================== |
| //-------------------------------------------------------------------- |
| FormScriptingEnvironment::FormScriptingEnvironment( FmFormModel& _rModel ) |
| :m_refCount( 0 ) |
| ,m_pScriptListener( NULL ) |
| ,m_rFormModel( _rModel ) |
| ,m_bDisposed( false ) |
| { |
| m_pScriptListener = ListenerImplementation( new FormScriptListener( this ) ); |
| // note that this is a cyclic reference between the FormScriptListener and the FormScriptingEnvironment |
| // This cycle is broken up when our instance is disposed. |
| } |
| |
| //-------------------------------------------------------------------- |
| FormScriptingEnvironment::~FormScriptingEnvironment() |
| { |
| } |
| |
| //-------------------------------------------------------------------- |
| void FormScriptingEnvironment::impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister ) |
| { |
| ::osl::MutexGuard aGuard( m_aMutex ); |
| |
| if ( !_rxManager.is() ) |
| throw IllegalArgumentException(); |
| if ( m_bDisposed ) |
| throw DisposedException(); |
| |
| try |
| { |
| if ( _bRegister ) |
| _rxManager->addScriptListener( m_pScriptListener.getRef() ); |
| else |
| _rxManager->removeScriptListener( m_pScriptListener.getRef() ); |
| } |
| catch( const RuntimeException& ) { throw; } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| void FormScriptingEnvironment::registerEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager ) |
| { |
| impl_registerOrRevoke_throw( _rxManager, true ); |
| } |
| |
| //-------------------------------------------------------------------- |
| void FormScriptingEnvironment::revokeEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager ) |
| { |
| impl_registerOrRevoke_throw( _rxManager, false ); |
| } |
| |
| //-------------------------------------------------------------------- |
| oslInterlockedCount SAL_CALL FormScriptingEnvironment::acquire() |
| { |
| return osl_incrementInterlockedCount( &m_refCount ); |
| } |
| |
| //-------------------------------------------------------------------- |
| oslInterlockedCount SAL_CALL FormScriptingEnvironment::release() |
| { |
| if ( 0 == osl_decrementInterlockedCount( &m_refCount ) ) |
| { |
| delete this; |
| return 0; |
| } |
| return m_refCount; |
| } |
| |
| //-------------------------------------------------------------------- |
| IFormScriptingEnvironment::~IFormScriptingEnvironment() |
| { |
| } |
| |
| //-------------------------------------------------------------------- |
| namespace |
| { |
| //................................................................ |
| //. NewStyleUNOScript |
| //................................................................ |
| class SAL_NO_VTABLE IScript |
| { |
| public: |
| virtual void invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult ) = 0; |
| |
| virtual ~IScript() { } |
| }; |
| typedef ::boost::shared_ptr< IScript > PScript; |
| |
| //................................................................ |
| //. NewStyleUNOScript |
| //................................................................ |
| class NewStyleUNOScript : public IScript |
| { |
| SfxObjectShell& m_rObjectShell; |
| const ::rtl::OUString m_sScriptCode; |
| |
| public: |
| NewStyleUNOScript( SfxObjectShell& _rObjectShell, const ::rtl::OUString& _rScriptCode ) |
| :m_rObjectShell( _rObjectShell ) |
| ,m_sScriptCode( _rScriptCode ) |
| { |
| } |
| |
| // IScript |
| virtual void invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult ); |
| }; |
| |
| //................................................................ |
| void NewStyleUNOScript::invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult ) |
| { |
| Sequence< sal_Int16 > aOutArgsIndex; |
| Sequence< Any > aOutArgs; |
| |
| m_rObjectShell.CallXScript( m_sScriptCode, _rArguments, _rSynchronousResult, aOutArgsIndex, aOutArgs ); |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| void FormScriptingEnvironment::doFireScriptEvent( const ScriptEvent& _rEvent, Any* _pSyncronousResult ) |
| { |
| ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() ); |
| ::osl::ClearableMutexGuard aGuard( m_aMutex ); |
| |
| if ( m_bDisposed ) |
| return; |
| |
| // SfxObjectShellRef is good here since the model controls the lifetime of the object |
| SfxObjectShellRef xObjectShell = m_rFormModel.GetObjectShell(); |
| if( !xObjectShell.Is() ) |
| return; |
| |
| // the script to execute |
| PScript pScript; |
| |
| if ( !_rEvent.ScriptType.equalsAscii( "StarBasic" ) ) |
| { |
| pScript.reset( new NewStyleUNOScript( *xObjectShell, _rEvent.ScriptCode ) ); |
| } |
| else |
| { |
| ::rtl::OUString sScriptCode = _rEvent.ScriptCode; |
| ::rtl::OUString sMacroLocation; |
| |
| // is there a location in the script name ("application" or "document")? |
| sal_Int32 nPrefixLen = sScriptCode.indexOf( ':' ); |
| DBG_ASSERT( 0 <= nPrefixLen, "FormScriptingEnvironment::doFireScriptEvent: Basic script name in old format encountered!" ); |
| |
| if ( 0 <= nPrefixLen ) |
| { |
| // and it has such a prefix |
| sMacroLocation = sScriptCode.copy( 0, nPrefixLen ); |
| DBG_ASSERT( 0 == sMacroLocation.compareToAscii( "document" ) |
| || 0 == sMacroLocation.compareToAscii( "application" ), |
| "FormScriptingEnvironment::doFireScriptEvent: invalid (unknown) prefix!" ); |
| |
| // strip the prefix: the SfxObjectShell::CallScript knows nothing about such prefixes |
| sScriptCode = sScriptCode.copy( nPrefixLen + 1 ); |
| } |
| |
| if ( !sMacroLocation.getLength() ) |
| { |
| // legacy format: use the app-wide Basic, if it has a respective method, otherwise fall back to the doc's Basic |
| if ( SFX_APP()->GetBasicManager()->HasMacro( sScriptCode ) ) |
| sMacroLocation = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "application" ) ); |
| else |
| sMacroLocation = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "document" ) ); |
| } |
| |
| ::rtl::OUStringBuffer aScriptURI; |
| aScriptURI.appendAscii( "vnd.sun.star.script:" ); |
| aScriptURI.append( sScriptCode ); |
| aScriptURI.appendAscii( "?language=Basic" ); |
| aScriptURI.appendAscii( "&location=" ); |
| aScriptURI.append( sMacroLocation ); |
| |
| const ::rtl::OUString sScriptURI( aScriptURI.makeStringAndClear() ); |
| pScript.reset( new NewStyleUNOScript( *xObjectShell, sScriptURI ) ); |
| } |
| |
| OSL_ENSURE( pScript.get(), "FormScriptingEnvironment::doFireScriptEvent: no script to execute!" ); |
| if ( !pScript.get() ) |
| // this is an internal error in the above code |
| throw RuntimeException(); |
| |
| aGuard.clear(); |
| aSolarGuard.clear(); |
| |
| Any aIgnoreResult; |
| pScript->invoke( _rEvent.Arguments, _pSyncronousResult ? *_pSyncronousResult : aIgnoreResult ); |
| pScript.reset(); |
| |
| { |
| // object shells are not thread safe, so guard the destruction |
| ::vos::OGuard aSolarGuarsReset( Application::GetSolarMutex() ); |
| xObjectShell = NULL; |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| void FormScriptingEnvironment::dispose() |
| { |
| ::osl::MutexGuard aGuard( m_aMutex ); |
| m_bDisposed = true; |
| m_pScriptListener->dispose(); |
| } |
| |
| //-------------------------------------------------------------------- |
| PFormScriptingEnvironment createDefaultFormScriptingEnvironment( FmFormModel& _rModel ) |
| { |
| return new FormScriptingEnvironment( _rModel ); |
| } |
| |
| //........................................................................ |
| } // namespace svxform |
| //........................................................................ |
| |