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