xref: /AOO41X/main/desktop/source/app/dispatchwatcher.cxx (revision 2722ceddc26af33ca9ed6a22fc3c4dfb805171c3)
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 {
DispatchHolderdesktop::DispatchHolder71     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 
GetMutex()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.
GetDispatchWatcher()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 
DispatchWatcher()117 DispatchWatcher::DispatchWatcher()
118     : m_nRequestCount(0)
119 {
120 }
121 
122 
~DispatchWatcher()123 DispatchWatcher::~DispatchWatcher()
124 {
125 }
126 
127 
executeDispatchRequests(const DispatchList & aDispatchRequestsList,bool bNoTerminate)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 
disposing(const::com::sun::star::lang::EventObject &)460 void SAL_CALL DispatchWatcher::disposing( const ::com::sun::star::lang::EventObject& )
461 throw(::com::sun::star::uno::RuntimeException)
462 {
463 }
464 
465 
dispatchFinished(const DispatchResultEvent &)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