xref: /aoo42x/main/svx/source/form/fmscriptingenv.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 #include "fmscriptingenv.hxx"
31 #include "svx/fmmodel.hxx"
32 
33 /** === begin UNO includes === **/
34 #include <com/sun/star/lang/IllegalArgumentException.hpp>
35 #include <com/sun/star/script/XScriptListener.hpp>
36 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
37 #include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp>
38 #include <com/sun/star/lang/DisposedException.hpp>
39 /** === end UNO includes === **/
40 
41 #include <tools/diagnose_ex.h>
42 #include <cppuhelper/implbase1.hxx>
43 #include <comphelper/implementationreference.hxx>
44 #include <comphelper/componentcontext.hxx>
45 #include <comphelper/processfactory.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vos/mutex.hxx>
48 #include <sfx2/objsh.hxx>
49 #include <sfx2/app.hxx>
50 #include <basic/basmgr.hxx>
51 
52 #include <boost/shared_ptr.hpp>
53 
54 //........................................................................
55 namespace svxform
56 {
57 //........................................................................
58 
59 	/** === begin UNO using === **/
60     using ::com::sun::star::uno::Reference;
61     using ::com::sun::star::script::XEventAttacherManager;
62     using ::com::sun::star::lang::IllegalArgumentException;
63     using ::com::sun::star::script::XScriptListener;
64     using ::com::sun::star::script::ScriptEvent;
65     using ::com::sun::star::uno::RuntimeException;
66     using ::com::sun::star::lang::EventObject;
67     using ::com::sun::star::reflection::InvocationTargetException;
68     using ::com::sun::star::uno::Any;
69     using ::com::sun::star::container::XHierarchicalNameAccess;
70     using ::com::sun::star::reflection::XInterfaceMethodTypeDescription;
71     using ::com::sun::star::uno::UNO_QUERY_THROW;
72     using ::com::sun::star::lang::DisposedException;
73     using ::com::sun::star::uno::RuntimeException;
74     using ::com::sun::star::uno::Exception;
75     using ::com::sun::star::uno::Sequence;
76     using ::com::sun::star::uno::XInterface;
77 	/** === end UNO using === **/
78 
79     class FormScriptingEnvironment;
80 
81     //====================================================================
82 	//= FormScriptListener
83 	//====================================================================
84     typedef ::cppu::WeakImplHelper1 <   XScriptListener
85                                     >   FormScriptListener_Base;
86 
87     /** implements the XScriptListener interface, is used by FormScriptingEnvironment
88     */
89     class FormScriptListener    :public FormScriptListener_Base
90     {
91     private:
92         ::osl::Mutex                                            m_aMutex;
93         ::rtl::Reference< FormScriptingEnvironment >            m_pScriptExecutor;
94 
95     public:
96         FormScriptListener( const ::rtl::Reference< FormScriptingEnvironment >& _pScriptExecutor );
97 
98         // XScriptListener
99         virtual void SAL_CALL firing( const ScriptEvent& aEvent ) throw (RuntimeException);
100         virtual Any SAL_CALL approveFiring( const ScriptEvent& aEvent ) throw (InvocationTargetException, RuntimeException);
101         // XEventListener
102         virtual void SAL_CALL disposing( const EventObject& Source ) throw (RuntimeException);
103 
104         // lifetime control
105         void SAL_CALL dispose();
106 
107     protected:
108         ~FormScriptListener();
109 
110     private:
111         /** determines whether calling a given method at a given listener interface can be done asynchronously
112 
113             @param _rListenerType
114                 the name of the UNO type whose method is to be checked
115             @param _rMethodName
116                 the name of the method at the interface determined by _rListenerType
117 
118             @return
119                 <TRUE/> if and only if the method is declared <code>oneway</code>, i.e. can be called asynchronously
120         */
121         bool    impl_allowAsynchronousCall_nothrow( const ::rtl::OUString& _rListenerType, const ::rtl::OUString& _rMethodName ) const;
122 
123         /** determines whether the instance is already disposed
124         */
125         bool    impl_isDisposed_nothrow() const { return !m_pScriptExecutor.is(); }
126 
127         /** fires the given script event in a thread-safe manner
128 
129             This methods calls our script executor's doFireScriptEvent, with previously releasing the given mutex guard,
130             but ensuring that our script executor is not deleted between this release and the actual call.
131 
132             @param _rGuard
133                 a clearable guard to our mutex. Must be the only active guard to our mutex.
134             @param _rEvent
135                 the event to fire
136             @param _pSyncronousResult
137                 a place to take a possible result of the script call.
138 
139             @precond
140                 m_pScriptExecutor is not <NULL/>.
141         */
142         void    impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard& _rGuard, const ScriptEvent& _rEvent, Any* _pSyncronousResult );
143 
144     private:
145         DECL_LINK( OnAsyncScriptEvent, ScriptEvent* );
146     };
147 
148 	//====================================================================
149 	//= FormScriptingEnvironment
150 	//====================================================================
151     class FormScriptingEnvironment : public IFormScriptingEnvironment
152     {
153     private:
154         typedef ::comphelper::ImplementationReference< FormScriptListener, XScriptListener >    ListenerImplementation;
155 
156     private:
157         ::osl::Mutex            m_aMutex;
158         oslInterlockedCount     m_refCount;
159         ListenerImplementation  m_pScriptListener;
160         FmFormModel&            m_rFormModel;
161         bool                    m_bDisposed;
162 
163     public:
164         FormScriptingEnvironment( FmFormModel& _rModel );
165         virtual ~FormScriptingEnvironment();
166 
167         // callback for FormScriptListener
168         void doFireScriptEvent( const ScriptEvent& _rEvent, Any* _pSyncronousResult );
169 
170         // IFormScriptingEnvironment
171         virtual void registerEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager );
172         virtual void revokeEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager );
173         virtual void dispose();
174 
175         // IReference
176 	    virtual oslInterlockedCount SAL_CALL acquire();
177 	    virtual oslInterlockedCount SAL_CALL release();
178 
179     private:
180         void impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister );
181 
182     private:
183         FormScriptingEnvironment();                                                 // never implemented
184         FormScriptingEnvironment( const FormScriptingEnvironment& );                // never implemented
185         FormScriptingEnvironment& operator=( const FormScriptingEnvironment& );     // never implemented
186     };
187 
188 	//====================================================================
189 	//= FormScriptListener
190 	//====================================================================
191     //--------------------------------------------------------------------
192     FormScriptListener::FormScriptListener( const ::rtl::Reference< FormScriptingEnvironment >& _pScriptExecutor )
193         :m_pScriptExecutor( _pScriptExecutor )
194     {
195     }
196 
197     //--------------------------------------------------------------------
198     FormScriptListener::~FormScriptListener()
199     {
200     }
201 
202     //--------------------------------------------------------------------
203     bool FormScriptListener::impl_allowAsynchronousCall_nothrow( const ::rtl::OUString& _rListenerType, const ::rtl::OUString& _rMethodName ) const
204     {
205         bool bAllowAsynchronousCall = false;
206         try
207         {
208             ::comphelper::ComponentContext aContext( ::comphelper::getProcessServiceFactory() );
209             Reference< XHierarchicalNameAccess > xTypeDescriptions( aContext.getSingleton( "com.sun.star.reflection.theTypeDescriptionManager" ), UNO_QUERY_THROW );
210 
211             ::rtl::OUString sMethodDescription( _rListenerType );
212             sMethodDescription += ::rtl::OUString::createFromAscii( "::" );
213             sMethodDescription += _rMethodName;
214 
215             Reference< XInterfaceMethodTypeDescription > xMethod( xTypeDescriptions->getByHierarchicalName( sMethodDescription ), UNO_QUERY_THROW );
216             bAllowAsynchronousCall = xMethod->isOneway();
217         }
218         catch( const Exception& )
219         {
220     	    DBG_UNHANDLED_EXCEPTION();
221         }
222         return bAllowAsynchronousCall;
223     }
224 
225     //--------------------------------------------------------------------
226     void FormScriptListener::impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard& _rGuard, const ScriptEvent& _rEvent, Any* _pSyncronousResult )
227     {
228         OSL_PRECOND( m_pScriptExecutor.is(), "FormScriptListener::impl_doFireScriptEvent_nothrow: this will crash!" );
229 
230         ::rtl::Reference< FormScriptingEnvironment > pExecutor( m_pScriptExecutor );
231         _rGuard.clear();
232         pExecutor->doFireScriptEvent( _rEvent, _pSyncronousResult );
233     }
234 
235     //--------------------------------------------------------------------
236     void SAL_CALL FormScriptListener::firing( const ScriptEvent& _rEvent ) throw (RuntimeException)
237     {
238         ::osl::ClearableMutexGuard aGuard( m_aMutex );
239        static const ::rtl::OUString vbaInterOp =
240            ::rtl::OUString::createFromAscii("VBAInterop");
241        if ( _rEvent.ScriptType.equals(vbaInterOp) )
242            return; // not handled here
243 
244         if ( impl_isDisposed_nothrow() )
245             return;
246 
247         if ( !impl_allowAsynchronousCall_nothrow( _rEvent.ListenerType.getTypeName(), _rEvent.MethodName ) )
248         {
249             impl_doFireScriptEvent_nothrow( aGuard, _rEvent, NULL );
250             return;
251         }
252 
253         acquire();
254         Application::PostUserEvent( LINK( this, FormScriptListener, OnAsyncScriptEvent ), new ScriptEvent( _rEvent ) );
255     }
256 
257     //--------------------------------------------------------------------
258     Any SAL_CALL FormScriptListener::approveFiring( const ScriptEvent& _rEvent ) throw (InvocationTargetException, RuntimeException)
259     {
260         Any aResult;
261 
262         ::osl::ClearableMutexGuard aGuard( m_aMutex );
263         if ( !impl_isDisposed_nothrow() )
264             impl_doFireScriptEvent_nothrow( aGuard, _rEvent, &aResult );
265 
266 	    return aResult;
267     }
268 
269     //--------------------------------------------------------------------
270     void SAL_CALL FormScriptListener::disposing( const EventObject& /*Source*/ ) throw (RuntimeException)
271     {
272         // not interested in
273     }
274 
275     //--------------------------------------------------------------------
276     void SAL_CALL FormScriptListener::dispose()
277     {
278         ::osl::MutexGuard aGuard( m_aMutex );
279         m_pScriptExecutor = NULL;
280     }
281 
282     //--------------------------------------------------------------------
283     IMPL_LINK( FormScriptListener, OnAsyncScriptEvent, ScriptEvent*, _pEvent )
284     {
285         OSL_PRECOND( _pEvent != NULL, "FormScriptListener::OnAsyncScriptEvent: invalid event!" );
286         if ( !_pEvent )
287             return 1L;
288 
289         {
290             ::osl::ClearableMutexGuard aGuard( m_aMutex );
291 
292             if ( !impl_isDisposed_nothrow() )
293                 impl_doFireScriptEvent_nothrow( aGuard, *_pEvent, NULL );
294         }
295 
296         delete _pEvent;
297         // we acquired ourself immediately before posting the event
298         release();
299         return 0L;
300     }
301 
302 	//====================================================================
303 	//= FormScriptingEnvironment
304 	//====================================================================
305     //--------------------------------------------------------------------
306     FormScriptingEnvironment::FormScriptingEnvironment( FmFormModel& _rModel )
307         :m_refCount( 0 )
308         ,m_pScriptListener( NULL )
309         ,m_rFormModel( _rModel )
310         ,m_bDisposed( false )
311     {
312         m_pScriptListener = ListenerImplementation( new FormScriptListener( this ) );
313         // note that this is a cyclic reference between the FormScriptListener and the FormScriptingEnvironment
314         // This cycle is broken up when our instance is disposed.
315     }
316 
317 	//--------------------------------------------------------------------
318     FormScriptingEnvironment::~FormScriptingEnvironment()
319     {
320     }
321 
322     //--------------------------------------------------------------------
323     void FormScriptingEnvironment::impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister )
324     {
325         ::osl::MutexGuard aGuard( m_aMutex );
326 
327         if ( !_rxManager.is() )
328             throw IllegalArgumentException();
329         if ( m_bDisposed )
330             throw DisposedException();
331 
332         try
333         {
334             if ( _bRegister )
335                 _rxManager->addScriptListener( m_pScriptListener.getRef() );
336             else
337                 _rxManager->removeScriptListener( m_pScriptListener.getRef() );
338         }
339         catch( const RuntimeException& ) { throw; }
340         catch( const Exception& )
341         {
342         	DBG_UNHANDLED_EXCEPTION();
343         }
344     }
345 
346     //--------------------------------------------------------------------
347     void FormScriptingEnvironment::registerEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager )
348     {
349         impl_registerOrRevoke_throw( _rxManager, true );
350     }
351 
352     //--------------------------------------------------------------------
353     void FormScriptingEnvironment::revokeEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager )
354     {
355         impl_registerOrRevoke_throw( _rxManager, false );
356     }
357 
358     //--------------------------------------------------------------------
359     oslInterlockedCount SAL_CALL FormScriptingEnvironment::acquire()
360     {
361         return osl_incrementInterlockedCount( &m_refCount );
362     }
363 
364     //--------------------------------------------------------------------
365     oslInterlockedCount SAL_CALL FormScriptingEnvironment::release()
366     {
367         if ( 0 == osl_decrementInterlockedCount( &m_refCount ) )
368         {
369            delete this;
370            return 0;
371         }
372         return m_refCount;
373     }
374 
375     //--------------------------------------------------------------------
376     IFormScriptingEnvironment::~IFormScriptingEnvironment()
377     {
378     }
379 
380     //--------------------------------------------------------------------
381     namespace
382     {
383         //................................................................
384         //. NewStyleUNOScript
385         //................................................................
386         class SAL_NO_VTABLE IScript
387         {
388         public:
389             virtual void invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult ) = 0;
390 
391             virtual ~IScript() { }
392         };
393         typedef ::boost::shared_ptr< IScript >  PScript;
394 
395         //................................................................
396         //. NewStyleUNOScript
397         //................................................................
398         class NewStyleUNOScript : public IScript
399         {
400             SfxObjectShell&         m_rObjectShell;
401             const ::rtl::OUString   m_sScriptCode;
402 
403         public:
404             NewStyleUNOScript( SfxObjectShell& _rObjectShell, const ::rtl::OUString& _rScriptCode )
405                 :m_rObjectShell( _rObjectShell )
406                 ,m_sScriptCode( _rScriptCode )
407             {
408             }
409 
410             // IScript
411             virtual void invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult );
412         };
413 
414         //................................................................
415         void NewStyleUNOScript::invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult )
416         {
417             Sequence< sal_Int16 > aOutArgsIndex;
418             Sequence< Any > aOutArgs;
419 
420             m_rObjectShell.CallXScript( m_sScriptCode, _rArguments, _rSynchronousResult, aOutArgsIndex, aOutArgs );
421         }
422     }
423 
424     //--------------------------------------------------------------------
425     void FormScriptingEnvironment::doFireScriptEvent( const ScriptEvent& _rEvent, Any* _pSyncronousResult )
426     {
427 	    ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
428         ::osl::ClearableMutexGuard aGuard( m_aMutex );
429 
430         if ( m_bDisposed )
431             return;
432 
433         // SfxObjectShellRef is good here since the model controls the lifetime of the object
434         SfxObjectShellRef xObjectShell = m_rFormModel.GetObjectShell();
435 	    if( !xObjectShell.Is() )
436 		    return;
437 
438         // the script to execute
439         PScript pScript;
440 
441         if ( !_rEvent.ScriptType.equalsAscii( "StarBasic" ) )
442         {
443             pScript.reset( new NewStyleUNOScript( *xObjectShell, _rEvent.ScriptCode ) );
444         }
445         else
446         {
447 			::rtl::OUString sScriptCode = _rEvent.ScriptCode;
448 			::rtl::OUString sMacroLocation;
449 
450             // is there a location in the script name ("application" or "document")?
451 			sal_Int32 nPrefixLen = sScriptCode.indexOf( ':' );
452 			DBG_ASSERT( 0 <= nPrefixLen, "FormScriptingEnvironment::doFireScriptEvent: Basic script name in old format encountered!" );
453 
454 			if ( 0 <= nPrefixLen )
455 			{
456 				// and it has such a prefix
457 				sMacroLocation = sScriptCode.copy( 0, nPrefixLen );
458 				DBG_ASSERT( 0 == sMacroLocation.compareToAscii( "document" )
459 						||	0 == sMacroLocation.compareToAscii( "application" ),
460 						"FormScriptingEnvironment::doFireScriptEvent: invalid (unknown) prefix!" );
461 
462 				// strip the prefix: the SfxObjectShell::CallScript knows nothing about such prefixes
463 				sScriptCode = sScriptCode.copy( nPrefixLen + 1 );
464 			}
465 
466             if ( !sMacroLocation.getLength() )
467             {
468                 // legacy format: use the app-wide Basic, if it has a respective method, otherwise fall back to the doc's Basic
469                 if ( SFX_APP()->GetBasicManager()->HasMacro( sScriptCode ) )
470                     sMacroLocation = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "application" ) );
471                 else
472                     sMacroLocation = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "document" ) );
473             }
474 
475             ::rtl::OUStringBuffer aScriptURI;
476             aScriptURI.appendAscii( "vnd.sun.star.script:" );
477             aScriptURI.append( sScriptCode );
478             aScriptURI.appendAscii( "?language=Basic" );
479             aScriptURI.appendAscii( "&location=" );
480             aScriptURI.append( sMacroLocation );
481 
482             const ::rtl::OUString sScriptURI( aScriptURI.makeStringAndClear() );
483             pScript.reset( new NewStyleUNOScript( *xObjectShell, sScriptURI ) );
484         }
485 
486         OSL_ENSURE( pScript.get(), "FormScriptingEnvironment::doFireScriptEvent: no script to execute!" );
487         if ( !pScript.get() )
488             // this is an internal error in the above code
489             throw RuntimeException();
490 
491         aGuard.clear();
492 		aSolarGuard.clear();
493 
494         Any aIgnoreResult;
495         pScript->invoke( _rEvent.Arguments, _pSyncronousResult ? *_pSyncronousResult : aIgnoreResult );
496         pScript.reset();
497 
498 	    {
499 	        // object shells are not thread safe, so guard the destruction
500 		    ::vos::OGuard aSolarGuarsReset( Application::GetSolarMutex() );
501 		    xObjectShell = NULL;
502 	    }
503     }
504 
505     //--------------------------------------------------------------------
506     void FormScriptingEnvironment::dispose()
507     {
508         ::osl::MutexGuard aGuard( m_aMutex );
509         m_bDisposed = true;
510         m_pScriptListener->dispose();
511     }
512 
513 	//--------------------------------------------------------------------
514     PFormScriptingEnvironment createDefaultFormScriptingEnvironment( FmFormModel& _rModel )
515     {
516         return new FormScriptingEnvironment( _rModel );
517     }
518 
519 //........................................................................
520 } // namespace svxform
521 //........................................................................
522 
523