xref: /trunk/main/desktop/source/app/dispatchwatcher.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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