/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_desktop.hxx" #include "dispatchwatcher.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::rtl; using namespace ::osl; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::util; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::container; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::view; namespace desktop { String GetURL_Impl( const String& rName, boost::optional< rtl::OUString > const & cwdUrl ); struct DispatchHolder { DispatchHolder( const URL& rURL, Reference< XDispatch >& rDispatch ) : aURL( rURL ), xDispatch( rDispatch ) {} URL aURL; rtl::OUString cwdUrl; Reference< XDispatch > xDispatch; }; Mutex* DispatchWatcher::pWatcherMutex = NULL; Mutex& DispatchWatcher::GetMutex() { if ( !pWatcherMutex ) { ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); if ( !pWatcherMutex ) pWatcherMutex = new osl::Mutex(); } return *pWatcherMutex; } // Create or get the dispatch watcher implementation. This implementation must be // a singleton to prevent access to the framework after it wants to terminate. DispatchWatcher* DispatchWatcher::GetDispatchWatcher() { static Reference< XInterface > xDispatchWatcher; static DispatchWatcher* pDispatchWatcher = NULL; if ( !xDispatchWatcher.is() ) { ::osl::MutexGuard aGuard( GetMutex() ); if ( !xDispatchWatcher.is() ) { pDispatchWatcher = new DispatchWatcher(); // We have to hold a reference to ourself forever to prevent our own destruction. xDispatchWatcher = static_cast< cppu::OWeakObject *>( pDispatchWatcher ); } } return pDispatchWatcher; } DispatchWatcher::DispatchWatcher() : m_nRequestCount(0) { } DispatchWatcher::~DispatchWatcher() { } sal_Bool DispatchWatcher::executeDispatchRequests( const DispatchList& aDispatchRequestsList, bool bNoTerminate ) { Reference< XComponentLoader > xDesktop( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ), UNO_QUERY ); DispatchList::const_iterator p; std::vector< DispatchHolder > aDispatches; ::rtl::OUString aAsTemplateArg( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate")); for ( p = aDispatchRequestsList.begin(); p != aDispatchRequestsList.end(); p++ ) { String aPrinterName; const DispatchRequest& aDispatchRequest = *p; // create parameter array sal_Int32 nCount = 4; if ( aDispatchRequest.aPreselectedFactory.getLength() ) nCount++; // we need more properties for a print/print to request if ( aDispatchRequest.aRequestType == REQUEST_PRINT || aDispatchRequest.aRequestType == REQUEST_PRINTTO ) nCount++; Sequence < PropertyValue > aArgs( nCount ); // mark request as user interaction from outside aArgs[0].Name = ::rtl::OUString::createFromAscii("Referer"); aArgs[0].Value <<= ::rtl::OUString::createFromAscii("private:OpenEvent"); if ( aDispatchRequest.aRequestType == REQUEST_PRINT || aDispatchRequest.aRequestType == REQUEST_PRINTTO ) { aArgs[1].Name = ::rtl::OUString::createFromAscii("ReadOnly"); aArgs[2].Name = ::rtl::OUString::createFromAscii("OpenNewView"); aArgs[3].Name = ::rtl::OUString::createFromAscii("Hidden"); aArgs[4].Name = ::rtl::OUString::createFromAscii("Silent"); } else { Reference < com::sun::star::task::XInteractionHandler > xInteraction( ::comphelper::getProcessServiceFactory()->createInstance( OUString::createFromAscii("com.sun.star.task.InteractionHandler") ), com::sun::star::uno::UNO_QUERY ); aArgs[1].Name = OUString::createFromAscii( "InteractionHandler" ); aArgs[1].Value <<= xInteraction; sal_Int16 nMacroExecMode = ::com::sun::star::document::MacroExecMode::USE_CONFIG; aArgs[2].Name = OUString::createFromAscii( "MacroExecutionMode" ); aArgs[2].Value <<= nMacroExecMode; sal_Int16 nUpdateDoc = ::com::sun::star::document::UpdateDocMode::ACCORDING_TO_CONFIG; aArgs[3].Name = OUString::createFromAscii( "UpdateDocMode" ); aArgs[3].Value <<= nUpdateDoc; } if ( aDispatchRequest.aPreselectedFactory.getLength() ) { aArgs[nCount-1].Name = ::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE(); aArgs[nCount-1].Value <<= aDispatchRequest.aPreselectedFactory; } String aName( GetURL_Impl( aDispatchRequest.aURL, aDispatchRequest.aCwdUrl ) ); ::rtl::OUString aTarget( RTL_CONSTASCII_USTRINGPARAM("_default") ); if ( aDispatchRequest.aRequestType == REQUEST_PRINT || aDispatchRequest.aRequestType == REQUEST_PRINTTO ) { // documents opened for printing are opened readonly because they must be opened as a new document and this // document could be open already aArgs[1].Value <<= sal_True; // always open a new document for printing, because it must be disposed afterwards aArgs[2].Value <<= sal_True; // printing is done in a hidden view aArgs[3].Value <<= sal_True; // load document for printing without user interaction aArgs[4].Value <<= sal_True; // hidden documents should never be put into open tasks aTarget = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("_blank") ); } // load the document ... if they are loadable! // Otherwise try to dispatch it ... Reference < XPrintable > xDoc; if( ( aName.CompareToAscii( ".uno" , 4 ) == COMPARE_EQUAL ) || ( aName.CompareToAscii( "slot:" , 5 ) == COMPARE_EQUAL ) || ( aName.CompareToAscii( "macro:", 6 ) == COMPARE_EQUAL ) || ( aName.CompareToAscii("vnd.sun.star.script", 19) == COMPARE_EQUAL) ) { // Attention: URL must be parsed full. Otherwise some detections on it will fail! // It doesnt matter, if parser isn't available. Because; We try loading of URL then ... URL aURL ; aURL.Complete = aName; Reference < XDispatch > xDispatcher ; Reference < XDispatchProvider > xProvider ( xDesktop, UNO_QUERY ); Reference < XURLTransformer > xParser ( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer")) ), ::com::sun::star::uno::UNO_QUERY ); if( xParser.is() == sal_True ) xParser->parseStrict( aURL ); if( xProvider.is() == sal_True ) xDispatcher = xProvider->queryDispatch( aURL, ::rtl::OUString(), 0 ); if( xDispatcher.is() == sal_True ) { { ::osl::ClearableMutexGuard aGuard( GetMutex() ); // Remember request so we can find it in statusChanged! m_aRequestContainer.insert( DispatchWatcherHashMap::value_type( aURL.Complete, (sal_Int32)1 ) ); m_nRequestCount++; } // Use local vector to store dispatcher because we have to fill our request container before // we can dispatch. Otherwise it would be possible that statusChanged is called before we dispatched all requests!! aDispatches.push_back( DispatchHolder( aURL, xDispatcher )); } } else if ( ( aName.CompareToAscii( "service:" , 8 ) == COMPARE_EQUAL ) ) { // TODO: the dispatch has to be done for loadComponentFromURL as well. Please ask AS for more details. URL aURL ; aURL.Complete = aName; Reference < XDispatch > xDispatcher ; Reference < XDispatchProvider > xProvider ( xDesktop, UNO_QUERY ); Reference < XURLTransformer > xParser ( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer")) ), ::com::sun::star::uno::UNO_QUERY ); if( xParser.is() == sal_True ) xParser->parseStrict( aURL ); if( xProvider.is() == sal_True ) xDispatcher = xProvider->queryDispatch( aURL, ::rtl::OUString(), 0 ); if( xDispatcher.is() == sal_True ) { try { // We have to be listener to catch errors during dispatching URLs. // Otherwise it would be possible to have an office running without an open // window!! Sequence < PropertyValue > aArgs2(1); aArgs2[0].Name = ::rtl::OUString::createFromAscii("SynchronMode"); aArgs2[0].Value <<= sal_True; Reference < XNotifyingDispatch > xDisp( xDispatcher, UNO_QUERY ); if ( xDisp.is() ) xDisp->dispatchWithNotification( aURL, aArgs2, DispatchWatcher::GetDispatchWatcher() ); else xDispatcher->dispatch( aURL, aArgs2 ); } catch ( ::com::sun::star::uno::Exception& ) { OUString aMsg = OUString::createFromAscii( "Desktop::OpenDefault() IllegalArgumentException while calling XNotifyingDispatch: "); OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr()); } } } else { INetURLObject aObj( aName ); if ( aObj.GetProtocol() == INET_PROT_PRIVATE ) aTarget = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("_default") ); // Set "AsTemplate" argument according to request type if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW || aDispatchRequest.aRequestType == REQUEST_FORCEOPEN ) { sal_Int32 nIndex = aArgs.getLength(); aArgs.realloc( nIndex+1 ); aArgs[nIndex].Name = aAsTemplateArg; if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW ) aArgs[nIndex].Value <<= sal_True; else aArgs[nIndex].Value <<= sal_False; } // if we are called in viewmode, open document read-only // #95425# if(aDispatchRequest.aRequestType == REQUEST_VIEW) { sal_Int32 nIndex = aArgs.getLength(); aArgs.realloc(nIndex+1); aArgs[nIndex].Name = OUString::createFromAscii("ReadOnly"); aArgs[nIndex].Value <<= sal_True; } // if we are called with -start set Start in mediadescriptor if(aDispatchRequest.aRequestType == REQUEST_START) { sal_Int32 nIndex = aArgs.getLength(); aArgs.realloc(nIndex+1); aArgs[nIndex].Name = OUString::createFromAscii("StartPresentation"); aArgs[nIndex].Value <<= sal_True; } // This is a synchron loading of a component so we don't have to deal with our statusChanged listener mechanism. try { xDoc = Reference < XPrintable >( ::comphelper::SynchronousDispatch::dispatch( xDesktop, aName, aTarget, 0, aArgs ), UNO_QUERY ); //xDoc = Reference < XPrintable >( xDesktop->loadComponentFromURL( aName, aTarget, 0, aArgs ), UNO_QUERY ); } catch ( ::com::sun::star::lang::IllegalArgumentException& iae) { OUString aMsg = OUString::createFromAscii( "Dispatchwatcher IllegalArgumentException while calling loadComponentFromURL: ") + iae.Message; OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr()); } catch (com::sun::star::io::IOException& ioe) { OUString aMsg = OUString::createFromAscii( "Dispatchwatcher IOException while calling loadComponentFromURL: ") + ioe.Message; OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr()); } if ( aDispatchRequest.aRequestType == REQUEST_OPEN || aDispatchRequest.aRequestType == REQUEST_VIEW || aDispatchRequest.aRequestType == REQUEST_START || aDispatchRequest.aRequestType == REQUEST_FORCEOPEN || aDispatchRequest.aRequestType == REQUEST_FORCENEW ) { // request is completed OfficeIPCThread::RequestsCompleted( 1 ); } else if ( aDispatchRequest.aRequestType == REQUEST_PRINT || aDispatchRequest.aRequestType == REQUEST_PRINTTO ) { if ( xDoc.is() ) { if ( aDispatchRequest.aRequestType == REQUEST_PRINTTO ) { // create the printer Sequence < PropertyValue > aPrinterArgs( 1 ); aPrinterArgs[0].Name = ::rtl::OUString::createFromAscii("Name"); aPrinterArgs[0].Value <<= ::rtl::OUString( aDispatchRequest.aPrinterName ); xDoc->setPrinter( aPrinterArgs ); } // print ( also without user interaction ) Sequence < PropertyValue > aPrinterArgs( 1 ); aPrinterArgs[0].Name = ::rtl::OUString::createFromAscii("Wait"); aPrinterArgs[0].Value <<= ( sal_Bool ) sal_True; xDoc->print( aPrinterArgs ); } else { // place error message here ... } // remove the document try { Reference < XCloseable > xClose( xDoc, UNO_QUERY ); if ( xClose.is() ) xClose->close( sal_True ); else { Reference < XComponent > xComp( xDoc, UNO_QUERY ); if ( xComp.is() ) xComp->dispose(); } } catch ( com::sun::star::util::CloseVetoException& ) { } // request is completed OfficeIPCThread::RequestsCompleted( 1 ); } } } if ( aDispatches.size() > 0 ) { // Execute all asynchronous dispatches now after we placed them into our request container! Sequence < PropertyValue > aArgs( 2 ); aArgs[0].Name = ::rtl::OUString::createFromAscii("Referer"); aArgs[0].Value <<= ::rtl::OUString::createFromAscii("private:OpenEvent"); aArgs[1].Name = ::rtl::OUString::createFromAscii("SynchronMode"); aArgs[1].Value <<= sal_True; for ( sal_uInt32 n = 0; n < aDispatches.size(); n++ ) { Reference< XDispatch > xDispatch = aDispatches[n].xDispatch; Reference < XNotifyingDispatch > xDisp( xDispatch, UNO_QUERY ); if ( xDisp.is() ) xDisp->dispatchWithNotification( aDispatches[n].aURL, aArgs, this ); else { ::osl::ClearableMutexGuard aGuard( GetMutex() ); m_nRequestCount--; aGuard.clear(); xDispatch->dispatch( aDispatches[n].aURL, aArgs ); } } } ::osl::ClearableMutexGuard aGuard( GetMutex() ); bool bEmpty = (m_nRequestCount == 0); aGuard.clear(); // No more asynchronous requests? // The requests are removed from the request container after they called back to this // implementation via statusChanged!! if ( bEmpty && !bNoTerminate /*m_aRequestContainer.empty()*/ ) { // We have to check if we have an open task otherwise we have to shutdown the office. Reference< XFramesSupplier > xTasksSupplier( xDesktop, UNO_QUERY ); aGuard.clear(); Reference< XElementAccess > xList( xTasksSupplier->getFrames(), UNO_QUERY ); if ( !xList->hasElements() ) { // We don't have any task open so we have to shutdown ourself!! Reference< XDesktop > xDesktop2( xTasksSupplier, UNO_QUERY ); if ( xDesktop2.is() ) return xDesktop2->terminate(); } } return sal_False; } void SAL_CALL DispatchWatcher::disposing( const ::com::sun::star::lang::EventObject& ) throw(::com::sun::star::uno::RuntimeException) { } void SAL_CALL DispatchWatcher::dispatchFinished( const DispatchResultEvent& ) throw( RuntimeException ) { osl::ClearableMutexGuard aGuard( GetMutex() ); sal_Int16 nCount = --m_nRequestCount; aGuard.clear(); OfficeIPCThread::RequestsCompleted( 1 ); /* // Find request in our hash map and remove it as a pending request DispatchWatcherHashMap::iterator pDispatchEntry = m_aRequestContainer.find( rEvent.FeatureURL.Complete ) ; if ( pDispatchEntry != m_aRequestContainer.end() ) { m_aRequestContainer.erase( pDispatchEntry ); aGuard.clear(); OfficeIPCThread::RequestsCompleted( 1 ); } else aGuard.clear(); */ if ( !nCount && !OfficeIPCThread::AreRequestsPending() ) { // We have to check if we have an open task otherwise we have to shutdown the office. Reference< XFramesSupplier > xTasksSupplier( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ), UNO_QUERY ); Reference< XElementAccess > xList( xTasksSupplier->getFrames(), UNO_QUERY ); if ( !xList->hasElements() ) { // We don't have any task open so we have to shutdown ourself!! Reference< XDesktop > xDesktop( xTasksSupplier, UNO_QUERY ); if ( xDesktop.is() ) xDesktop->terminate(); } } } }