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