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