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_desktop.hxx" 30 31 #include "dispatchwatcher.hxx" 32 #include <rtl/ustring.hxx> 33 #include <tools/string.hxx> 34 #include <comphelper/processfactory.hxx> 35 #include <comphelper/synchronousdispatch.hxx> 36 #include <com/sun/star/util/XCloseable.hpp> 37 #include <com/sun/star/util/CloseVetoException.hpp> 38 #include <com/sun/star/task/XInteractionHandler.hpp> 39 #include <com/sun/star/util/URL.hpp> 40 #include <com/sun/star/frame/XDesktop.hpp> 41 #include <com/sun/star/container/XEnumeration.hpp> 42 #include <com/sun/star/frame/XFramesSupplier.hpp> 43 #include <com/sun/star/frame/XDispatch.hpp> 44 #include <com/sun/star/frame/XComponentLoader.hpp> 45 #include <com/sun/star/beans/PropertyValue.hpp> 46 #include <com/sun/star/view/XPrintable.hpp> 47 #include <com/sun/star/frame/XDispatchProvider.hpp> 48 #include <com/sun/star/util/XURLTransformer.hpp> 49 #include <com/sun/star/document/MacroExecMode.hpp> 50 #include <com/sun/star/document/UpdateDocMode.hpp> 51 52 #include <tools/urlobj.hxx> 53 #include <comphelper/mediadescriptor.hxx> 54 55 #include <vector> 56 57 using namespace ::rtl; 58 using namespace ::osl; 59 using namespace ::com::sun::star::uno; 60 using namespace ::com::sun::star::util; 61 using namespace ::com::sun::star::lang; 62 using namespace ::com::sun::star::frame; 63 using namespace ::com::sun::star::container; 64 using namespace ::com::sun::star::beans; 65 using namespace ::com::sun::star::view; 66 67 namespace desktop 68 { 69 70 String GetURL_Impl( 71 const String& rName, boost::optional< rtl::OUString > const & cwdUrl ); 72 73 struct DispatchHolder 74 { 75 DispatchHolder( const URL& rURL, Reference< XDispatch >& rDispatch ) : 76 aURL( rURL ), xDispatch( rDispatch ) {} 77 78 URL aURL; 79 rtl::OUString cwdUrl; 80 Reference< XDispatch > xDispatch; 81 }; 82 83 Mutex* DispatchWatcher::pWatcherMutex = NULL; 84 85 Mutex& DispatchWatcher::GetMutex() 86 { 87 if ( !pWatcherMutex ) 88 { 89 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); 90 if ( !pWatcherMutex ) 91 pWatcherMutex = new osl::Mutex(); 92 } 93 94 return *pWatcherMutex; 95 } 96 97 // Create or get the dispatch watcher implementation. This implementation must be 98 // a singleton to prevent access to the framework after it wants to terminate. 99 DispatchWatcher* DispatchWatcher::GetDispatchWatcher() 100 { 101 static Reference< XInterface > xDispatchWatcher; 102 static DispatchWatcher* pDispatchWatcher = NULL; 103 104 if ( !xDispatchWatcher.is() ) 105 { 106 ::osl::MutexGuard aGuard( GetMutex() ); 107 108 if ( !xDispatchWatcher.is() ) 109 { 110 pDispatchWatcher = new DispatchWatcher(); 111 112 // We have to hold a reference to ourself forever to prevent our own destruction. 113 xDispatchWatcher = static_cast< cppu::OWeakObject *>( pDispatchWatcher ); 114 } 115 } 116 117 return pDispatchWatcher; 118 } 119 120 121 DispatchWatcher::DispatchWatcher() 122 : m_nRequestCount(0) 123 { 124 } 125 126 127 DispatchWatcher::~DispatchWatcher() 128 { 129 } 130 131 132 sal_Bool DispatchWatcher::executeDispatchRequests( const DispatchList& aDispatchRequestsList, bool bNoTerminate ) 133 { 134 Reference< XComponentLoader > xDesktop( ::comphelper::getProcessServiceFactory()->createInstance( 135 OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ), 136 UNO_QUERY ); 137 138 DispatchList::const_iterator p; 139 std::vector< DispatchHolder > aDispatches; 140 ::rtl::OUString aAsTemplateArg( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate")); 141 142 for ( p = aDispatchRequestsList.begin(); p != aDispatchRequestsList.end(); p++ ) 143 { 144 String aPrinterName; 145 const DispatchRequest& aDispatchRequest = *p; 146 147 // create parameter array 148 sal_Int32 nCount = 4; 149 if ( aDispatchRequest.aPreselectedFactory.getLength() ) 150 nCount++; 151 152 // we need more properties for a print/print to request 153 if ( aDispatchRequest.aRequestType == REQUEST_PRINT || 154 aDispatchRequest.aRequestType == REQUEST_PRINTTO ) 155 nCount++; 156 157 Sequence < PropertyValue > aArgs( nCount ); 158 159 // mark request as user interaction from outside 160 aArgs[0].Name = ::rtl::OUString::createFromAscii("Referer"); 161 aArgs[0].Value <<= ::rtl::OUString::createFromAscii("private:OpenEvent"); 162 163 if ( aDispatchRequest.aRequestType == REQUEST_PRINT || 164 aDispatchRequest.aRequestType == REQUEST_PRINTTO ) 165 { 166 aArgs[1].Name = ::rtl::OUString::createFromAscii("ReadOnly"); 167 aArgs[2].Name = ::rtl::OUString::createFromAscii("OpenNewView"); 168 aArgs[3].Name = ::rtl::OUString::createFromAscii("Hidden"); 169 aArgs[4].Name = ::rtl::OUString::createFromAscii("Silent"); 170 } 171 else 172 { 173 Reference < com::sun::star::task::XInteractionHandler > xInteraction( 174 ::comphelper::getProcessServiceFactory()->createInstance( OUString::createFromAscii("com.sun.star.task.InteractionHandler") ), 175 com::sun::star::uno::UNO_QUERY ); 176 177 aArgs[1].Name = OUString::createFromAscii( "InteractionHandler" ); 178 aArgs[1].Value <<= xInteraction; 179 180 sal_Int16 nMacroExecMode = ::com::sun::star::document::MacroExecMode::USE_CONFIG; 181 aArgs[2].Name = OUString::createFromAscii( "MacroExecutionMode" ); 182 aArgs[2].Value <<= nMacroExecMode; 183 184 sal_Int16 nUpdateDoc = ::com::sun::star::document::UpdateDocMode::ACCORDING_TO_CONFIG; 185 aArgs[3].Name = OUString::createFromAscii( "UpdateDocMode" ); 186 aArgs[3].Value <<= nUpdateDoc; 187 } 188 189 if ( aDispatchRequest.aPreselectedFactory.getLength() ) 190 { 191 aArgs[nCount-1].Name = ::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE(); 192 aArgs[nCount-1].Value <<= aDispatchRequest.aPreselectedFactory; 193 } 194 195 String aName( GetURL_Impl( aDispatchRequest.aURL, aDispatchRequest.aCwdUrl ) ); 196 ::rtl::OUString aTarget( RTL_CONSTASCII_USTRINGPARAM("_default") ); 197 198 if ( aDispatchRequest.aRequestType == REQUEST_PRINT || 199 aDispatchRequest.aRequestType == REQUEST_PRINTTO ) 200 { 201 // documents opened for printing are opened readonly because they must be opened as a new document and this 202 // document could be open already 203 aArgs[1].Value <<= sal_True; 204 205 // always open a new document for printing, because it must be disposed afterwards 206 aArgs[2].Value <<= sal_True; 207 208 // printing is done in a hidden view 209 aArgs[3].Value <<= sal_True; 210 211 // load document for printing without user interaction 212 aArgs[4].Value <<= sal_True; 213 214 // hidden documents should never be put into open tasks 215 aTarget = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("_blank") ); 216 } 217 218 // load the document ... if they are loadable! 219 // Otherwise try to dispatch it ... 220 Reference < XPrintable > xDoc; 221 if( 222 ( aName.CompareToAscii( ".uno" , 4 ) == COMPARE_EQUAL ) || 223 ( aName.CompareToAscii( "slot:" , 5 ) == COMPARE_EQUAL ) || 224 ( aName.CompareToAscii( "macro:", 6 ) == COMPARE_EQUAL ) || 225 ( aName.CompareToAscii("vnd.sun.star.script", 19) == COMPARE_EQUAL) 226 ) 227 { 228 // Attention: URL must be parsed full. Otherwise some detections on it will fail! 229 // It doesnt matter, if parser isn't available. Because; We try loading of URL then ... 230 URL aURL ; 231 aURL.Complete = aName; 232 233 Reference < XDispatch > xDispatcher ; 234 Reference < XDispatchProvider > xProvider ( xDesktop, UNO_QUERY ); 235 Reference < XURLTransformer > xParser ( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer")) ), ::com::sun::star::uno::UNO_QUERY ); 236 237 if( xParser.is() == sal_True ) 238 xParser->parseStrict( aURL ); 239 240 if( xProvider.is() == sal_True ) 241 xDispatcher = xProvider->queryDispatch( aURL, ::rtl::OUString(), 0 ); 242 243 if( xDispatcher.is() == sal_True ) 244 { 245 { 246 ::osl::ClearableMutexGuard aGuard( GetMutex() ); 247 // Remember request so we can find it in statusChanged! 248 m_aRequestContainer.insert( DispatchWatcherHashMap::value_type( aURL.Complete, (sal_Int32)1 ) ); 249 m_nRequestCount++; 250 } 251 252 // Use local vector to store dispatcher because we have to fill our request container before 253 // we can dispatch. Otherwise it would be possible that statusChanged is called before we dispatched all requests!! 254 aDispatches.push_back( DispatchHolder( aURL, xDispatcher )); 255 } 256 } 257 else if ( ( aName.CompareToAscii( "service:" , 8 ) == COMPARE_EQUAL ) ) 258 { 259 // TODO: the dispatch has to be done for loadComponentFromURL as well. Please ask AS for more details. 260 URL aURL ; 261 aURL.Complete = aName; 262 263 Reference < XDispatch > xDispatcher ; 264 Reference < XDispatchProvider > xProvider ( xDesktop, UNO_QUERY ); 265 Reference < XURLTransformer > xParser ( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer")) ), ::com::sun::star::uno::UNO_QUERY ); 266 267 if( xParser.is() == sal_True ) 268 xParser->parseStrict( aURL ); 269 270 if( xProvider.is() == sal_True ) 271 xDispatcher = xProvider->queryDispatch( aURL, ::rtl::OUString(), 0 ); 272 273 if( xDispatcher.is() == sal_True ) 274 { 275 try 276 { 277 // We have to be listener to catch errors during dispatching URLs. 278 // Otherwise it would be possible to have an office running without an open 279 // window!! 280 Sequence < PropertyValue > aArgs2(1); 281 aArgs2[0].Name = ::rtl::OUString::createFromAscii("SynchronMode"); 282 aArgs2[0].Value <<= sal_True; 283 Reference < XNotifyingDispatch > xDisp( xDispatcher, UNO_QUERY ); 284 if ( xDisp.is() ) 285 xDisp->dispatchWithNotification( aURL, aArgs2, DispatchWatcher::GetDispatchWatcher() ); 286 else 287 xDispatcher->dispatch( aURL, aArgs2 ); 288 } 289 catch ( ::com::sun::star::uno::Exception& ) 290 { 291 OUString aMsg = OUString::createFromAscii( 292 "Desktop::OpenDefault() IllegalArgumentException while calling XNotifyingDispatch: "); 293 OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr()); 294 } 295 } 296 } 297 else 298 { 299 INetURLObject aObj( aName ); 300 if ( aObj.GetProtocol() == INET_PROT_PRIVATE ) 301 aTarget = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("_default") ); 302 303 // Set "AsTemplate" argument according to request type 304 if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW || 305 aDispatchRequest.aRequestType == REQUEST_FORCEOPEN ) 306 { 307 sal_Int32 nIndex = aArgs.getLength(); 308 aArgs.realloc( nIndex+1 ); 309 aArgs[nIndex].Name = aAsTemplateArg; 310 if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW ) 311 aArgs[nIndex].Value <<= sal_True; 312 else 313 aArgs[nIndex].Value <<= sal_False; 314 } 315 316 // if we are called in viewmode, open document read-only 317 // #95425# 318 if(aDispatchRequest.aRequestType == REQUEST_VIEW) { 319 sal_Int32 nIndex = aArgs.getLength(); 320 aArgs.realloc(nIndex+1); 321 aArgs[nIndex].Name = OUString::createFromAscii("ReadOnly"); 322 aArgs[nIndex].Value <<= sal_True; 323 } 324 325 // if we are called with -start set Start in mediadescriptor 326 if(aDispatchRequest.aRequestType == REQUEST_START) { 327 sal_Int32 nIndex = aArgs.getLength(); 328 aArgs.realloc(nIndex+1); 329 aArgs[nIndex].Name = OUString::createFromAscii("StartPresentation"); 330 aArgs[nIndex].Value <<= sal_True; 331 } 332 333 // This is a synchron loading of a component so we don't have to deal with our statusChanged listener mechanism. 334 335 try 336 { 337 xDoc = Reference < XPrintable >( ::comphelper::SynchronousDispatch::dispatch( xDesktop, aName, aTarget, 0, aArgs ), UNO_QUERY ); 338 //xDoc = Reference < XPrintable >( xDesktop->loadComponentFromURL( aName, aTarget, 0, aArgs ), UNO_QUERY ); 339 } 340 catch ( ::com::sun::star::lang::IllegalArgumentException& iae) 341 { 342 OUString aMsg = OUString::createFromAscii( 343 "Dispatchwatcher IllegalArgumentException while calling loadComponentFromURL: ") 344 + iae.Message; 345 OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr()); 346 } 347 catch (com::sun::star::io::IOException& ioe) 348 { 349 OUString aMsg = OUString::createFromAscii( 350 "Dispatchwatcher IOException while calling loadComponentFromURL: ") 351 + ioe.Message; 352 OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr()); 353 } 354 if ( aDispatchRequest.aRequestType == REQUEST_OPEN || 355 aDispatchRequest.aRequestType == REQUEST_VIEW || 356 aDispatchRequest.aRequestType == REQUEST_START || 357 aDispatchRequest.aRequestType == REQUEST_FORCEOPEN || 358 aDispatchRequest.aRequestType == REQUEST_FORCENEW ) 359 { 360 // request is completed 361 OfficeIPCThread::RequestsCompleted( 1 ); 362 } 363 else if ( aDispatchRequest.aRequestType == REQUEST_PRINT || 364 aDispatchRequest.aRequestType == REQUEST_PRINTTO ) 365 { 366 if ( xDoc.is() ) 367 { 368 if ( aDispatchRequest.aRequestType == REQUEST_PRINTTO ) 369 { 370 // create the printer 371 Sequence < PropertyValue > aPrinterArgs( 1 ); 372 aPrinterArgs[0].Name = ::rtl::OUString::createFromAscii("Name"); 373 aPrinterArgs[0].Value <<= ::rtl::OUString( aDispatchRequest.aPrinterName ); 374 xDoc->setPrinter( aPrinterArgs ); 375 } 376 377 // print ( also without user interaction ) 378 Sequence < PropertyValue > aPrinterArgs( 1 ); 379 aPrinterArgs[0].Name = ::rtl::OUString::createFromAscii("Wait"); 380 aPrinterArgs[0].Value <<= ( sal_Bool ) sal_True; 381 xDoc->print( aPrinterArgs ); 382 } 383 else 384 { 385 // place error message here ... 386 } 387 388 // remove the document 389 try 390 { 391 Reference < XCloseable > xClose( xDoc, UNO_QUERY ); 392 if ( xClose.is() ) 393 xClose->close( sal_True ); 394 else 395 { 396 Reference < XComponent > xComp( xDoc, UNO_QUERY ); 397 if ( xComp.is() ) 398 xComp->dispose(); 399 } 400 } 401 catch ( com::sun::star::util::CloseVetoException& ) 402 { 403 } 404 405 // request is completed 406 OfficeIPCThread::RequestsCompleted( 1 ); 407 } 408 } 409 } 410 411 if ( aDispatches.size() > 0 ) 412 { 413 // Execute all asynchronous dispatches now after we placed them into our request container! 414 Sequence < PropertyValue > aArgs( 2 ); 415 aArgs[0].Name = ::rtl::OUString::createFromAscii("Referer"); 416 aArgs[0].Value <<= ::rtl::OUString::createFromAscii("private:OpenEvent"); 417 aArgs[1].Name = ::rtl::OUString::createFromAscii("SynchronMode"); 418 aArgs[1].Value <<= sal_True; 419 420 for ( sal_uInt32 n = 0; n < aDispatches.size(); n++ ) 421 { 422 Reference< XDispatch > xDispatch = aDispatches[n].xDispatch; 423 Reference < XNotifyingDispatch > xDisp( xDispatch, UNO_QUERY ); 424 if ( xDisp.is() ) 425 xDisp->dispatchWithNotification( aDispatches[n].aURL, aArgs, this ); 426 else 427 { 428 ::osl::ClearableMutexGuard aGuard( GetMutex() ); 429 m_nRequestCount--; 430 aGuard.clear(); 431 xDispatch->dispatch( aDispatches[n].aURL, aArgs ); 432 } 433 } 434 } 435 436 ::osl::ClearableMutexGuard aGuard( GetMutex() ); 437 bool bEmpty = (m_nRequestCount == 0); 438 aGuard.clear(); 439 440 // No more asynchronous requests? 441 // The requests are removed from the request container after they called back to this 442 // implementation via statusChanged!! 443 if ( bEmpty && !bNoTerminate /*m_aRequestContainer.empty()*/ ) 444 { 445 // We have to check if we have an open task otherwise we have to shutdown the office. 446 Reference< XFramesSupplier > xTasksSupplier( xDesktop, UNO_QUERY ); 447 aGuard.clear(); 448 449 Reference< XElementAccess > xList( xTasksSupplier->getFrames(), UNO_QUERY ); 450 451 if ( !xList->hasElements() ) 452 { 453 // We don't have any task open so we have to shutdown ourself!! 454 Reference< XDesktop > xDesktop2( xTasksSupplier, UNO_QUERY ); 455 if ( xDesktop2.is() ) 456 return xDesktop2->terminate(); 457 } 458 } 459 460 return sal_False; 461 } 462 463 464 void SAL_CALL DispatchWatcher::disposing( const ::com::sun::star::lang::EventObject& ) 465 throw(::com::sun::star::uno::RuntimeException) 466 { 467 } 468 469 470 void SAL_CALL DispatchWatcher::dispatchFinished( const DispatchResultEvent& ) throw( RuntimeException ) 471 { 472 osl::ClearableMutexGuard aGuard( GetMutex() ); 473 sal_Int16 nCount = --m_nRequestCount; 474 aGuard.clear(); 475 OfficeIPCThread::RequestsCompleted( 1 ); 476 /* 477 // Find request in our hash map and remove it as a pending request 478 DispatchWatcherHashMap::iterator pDispatchEntry = m_aRequestContainer.find( rEvent.FeatureURL.Complete ) ; 479 if ( pDispatchEntry != m_aRequestContainer.end() ) 480 { 481 m_aRequestContainer.erase( pDispatchEntry ); 482 aGuard.clear(); 483 OfficeIPCThread::RequestsCompleted( 1 ); 484 } 485 else 486 aGuard.clear(); 487 */ 488 if ( !nCount && !OfficeIPCThread::AreRequestsPending() ) 489 { 490 // We have to check if we have an open task otherwise we have to shutdown the office. 491 Reference< XFramesSupplier > xTasksSupplier( ::comphelper::getProcessServiceFactory()->createInstance( 492 OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ), 493 UNO_QUERY ); 494 Reference< XElementAccess > xList( xTasksSupplier->getFrames(), UNO_QUERY ); 495 496 if ( !xList->hasElements() ) 497 { 498 // We don't have any task open so we have to shutdown ourself!! 499 Reference< XDesktop > xDesktop( xTasksSupplier, UNO_QUERY ); 500 if ( xDesktop.is() ) 501 xDesktop->terminate(); 502 } 503 } 504 } 505 506 } 507 508 509 510 511 512 513 514 515