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