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