1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <sal/types.h>
21 #include <sal/log.hxx>
22
23 #include <services/desktop.hxx>
24 #include <protocols.h>
25 #include <general.h>
26
27 #include <tools/diagnose_ex.h>
28 #include <vcl/svapp.hxx>
29 #include <unotools/tempfile.hxx>
30 #include <com/sun/star/beans/NamedValue.hpp>
31 #include <com/sun/star/beans/PropertyValue.hpp>
32 #include <com/sun/star/frame/theAutoRecovery.hpp>
33 #include <com/sun/star/frame/Desktop.hpp>
34 #include <com/sun/star/frame/FeatureStateEvent.hpp>
35 #include <com/sun/star/frame/XDispatch.hpp>
36 #include <com/sun/star/frame/XSessionManagerListener2.hpp>
37 #include <com/sun/star/frame/XSessionManagerClient.hpp>
38 #include <com/sun/star/frame/XStatusListener.hpp>
39 #include <com/sun/star/lang/EventObject.hpp>
40 #include <com/sun/star/lang/XInitialization.hpp>
41 #include <com/sun/star/util/URLTransformer.hpp>
42 #include <com/sun/star/util/XURLTransformer.hpp>
43 #include <com/sun/star/util/URL.hpp>
44 #include <cppuhelper/implbase.hxx>
45 #include <cppuhelper/supportsservice.hxx>
46 #include <unotools/pathoptions.hxx>
47
48 #include <com/sun/star/uno/Any.hxx>
49 #include <com/sun/star/uno/Sequence.hxx>
50
51 using namespace css;
52 using namespace com::sun::star::uno;
53 using namespace com::sun::star::util;
54 using namespace com::sun::star::beans;
55 using namespace framework;
56
57 namespace {
58
59 /// @HTML
60 /** @short implements flat/deep detection of file/stream formats and provides
61 further read/write access to the global office type configuration.
62
63 @descr Using of this class makes it possible to get information about the
64 format type of a given URL or stream. The returned internal type name
65 can be used to get more information about this format. Further this
66 class provides full access to the configuration data and following
67 implementations will support some special query modes.
68
69 @docdate 10.03.2003 by as96863
70
71 @todo <ul>
72 <li>implementation of query mode</li>
73 <li>simple restore mechanism of last consistent cache state,
74 if flush failed</li>
75 </ul>
76 */
77 typedef cppu::WeakImplHelper<
78 css::lang::XInitialization,
79 css::frame::XSessionManagerListener2,
80 css::frame::XStatusListener,
81 css::lang::XServiceInfo> SessionListener_BASE;
82
83 class SessionListener : public SessionListener_BASE
84 {
85 private:
86 osl::Mutex m_aMutex;
87
88 /** reference to the uno service manager, which created this service.
89 It can be used to create own needed helper services. */
90 css::uno::Reference< css::uno::XComponentContext > m_xContext;
91
92 css::uno::Reference< css::frame::XSessionManagerClient > m_rSessionManager;
93
94 // restore handling
95 bool m_bRestored;
96
97 bool m_bSessionStoreRequested;
98
99 bool m_bAllowUserInteractionOnQuit;
100 bool m_bTerminated;
101
102 // in case of synchronous call the caller should do saveDone() call himself!
103 void StoreSession( bool bAsync );
104
105 // let session quietly close the documents, remove lock files, store configuration and etc.
106 void QuitSessionQuietly();
107
108 public:
109 explicit SessionListener(const css::uno::Reference< css::uno::XComponentContext >& xContext);
110
111 virtual ~SessionListener() override;
112
getImplementationName()113 virtual OUString SAL_CALL getImplementationName() override
114 {
115 return "com.sun.star.comp.frame.SessionListener";
116 }
117
supportsService(OUString const & ServiceName)118 virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
119 {
120 return cppu::supportsService(this, ServiceName);
121 }
122
getSupportedServiceNames()123 virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
124 {
125 return {"com.sun.star.frame.SessionListener"};
126 }
127
128 virtual void SAL_CALL disposing(const css::lang::EventObject&) override;
129
130 // XInitialization
131 virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any >& args) override;
132
133 // XSessionManagerListener
134 virtual void SAL_CALL doSave( sal_Bool bShutdown, sal_Bool bCancelable ) override;
135 virtual void SAL_CALL approveInteraction( sal_Bool bInteractionGranted ) override;
136 virtual void SAL_CALL shutdownCanceled() override;
137 virtual sal_Bool SAL_CALL doRestore() override;
138
139 // XSessionManagerListener2
140 virtual void SAL_CALL doQuit() override;
141
142 // XStatusListener
143 virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& event) override;
144 };
145
SessionListener(const css::uno::Reference<css::uno::XComponentContext> & rxContext)146 SessionListener::SessionListener(const css::uno::Reference< css::uno::XComponentContext >& rxContext )
147 : m_xContext( rxContext )
148 , m_bRestored( false )
149 , m_bSessionStoreRequested( false )
150 , m_bAllowUserInteractionOnQuit( false )
151 , m_bTerminated( false )
152 {
153 SAL_INFO("fwk.session", "SessionListener::SessionListener");
154 }
155
~SessionListener()156 SessionListener::~SessionListener()
157 {
158 SAL_INFO("fwk.session", "SessionListener::~SessionListener");
159 if (m_rSessionManager.is())
160 {
161 css::uno::Reference< XSessionManagerListener> me(this);
162 m_rSessionManager->removeSessionManagerListener(me);
163 }
164 }
165
StoreSession(bool bAsync)166 void SessionListener::StoreSession( bool bAsync )
167 {
168 SAL_INFO("fwk.session", "SessionListener::StoreSession");
169 osl::MutexGuard g(m_aMutex);
170 try
171 {
172 // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch
173 // xd->dispatch("vnd.sun.star.autorecovery:/doSessionSave, async=bAsync
174 // on stop event m_rSessionManager->saveDone(this); in case of asynchronous call
175 // in case of synchronous call the caller should do saveDone() call himself!
176
177 css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
178 css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext );
179 URL aURL;
180 aURL.Complete = "vnd.sun.star.autorecovery:/doSessionSave";
181 xURLTransformer->parseStrict(aURL);
182
183 // in case of asynchronous call the notification will trigger saveDone()
184 if ( bAsync )
185 xDispatch->addStatusListener(this, aURL);
186
187 Sequence< PropertyValue > args(1);
188 args[0] = PropertyValue("DispatchAsynchron",-1,makeAny(bAsync),PropertyState_DIRECT_VALUE);
189 xDispatch->dispatch(aURL, args);
190 } catch (const css::uno::Exception&) {
191 TOOLS_WARN_EXCEPTION("fwk.session", "");
192 // save failed, but tell manager to go on if we haven't yet dispatched the request
193 // in case of synchronous saving the notification is done by the caller
194 if ( bAsync && m_rSessionManager.is() )
195 m_rSessionManager->saveDone(this);
196 }
197 }
198
QuitSessionQuietly()199 void SessionListener::QuitSessionQuietly()
200 {
201 SAL_INFO("fwk.session", "SessionListener::QuitSessionQuietly");
202 osl::MutexGuard g(m_aMutex);
203 try
204 {
205 // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch
206 // xd->dispatch("vnd.sun.star.autorecovery:/doSessionQuietQuit, async=false
207 // it is done synchronously to avoid conflict with normal quit process
208
209 css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
210 css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext );
211 URL aURL;
212 aURL.Complete = "vnd.sun.star.autorecovery:/doSessionQuietQuit";
213 xURLTransformer->parseStrict(aURL);
214
215 Sequence< PropertyValue > args(1);
216 args[0] = PropertyValue("DispatchAsynchron",-1,makeAny(false),PropertyState_DIRECT_VALUE);
217 xDispatch->dispatch(aURL, args);
218 } catch (const css::uno::Exception&) {
219 TOOLS_WARN_EXCEPTION("fwk.session", "");
220 }
221 }
222
disposing(const css::lang::EventObject & Source)223 void SAL_CALL SessionListener::disposing(const css::lang::EventObject& Source)
224 {
225 SAL_INFO("fwk.session", "SessionListener::disposing");
226 if (Source.Source == m_rSessionManager) {
227 m_rSessionManager.clear();
228 }
229 }
230
initialize(const Sequence<Any> & args)231 void SAL_CALL SessionListener::initialize(const Sequence< Any >& args)
232 {
233 SAL_INFO("fwk.session", "SessionListener::initialize");
234
235 OUString aSMgr("com.sun.star.frame.SessionManagerClient");
236 if ( (args.getLength() == 1) && (args[0] >>= m_bAllowUserInteractionOnQuit) )
237 ;// do nothing
238 else if (args.hasElements())
239 {
240 NamedValue v;
241 for (int i = 0; i < args.getLength(); i++)
242 {
243 if (args[i] >>= v)
244 {
245 if ( v.Name == "SessionManagerName" )
246 v.Value >>= aSMgr;
247 else if ( v.Name == "SessionManager" )
248 v.Value >>= m_rSessionManager;
249 else if ( v.Name == "AllowUserInteractionOnQuit" )
250 v.Value >>= m_bAllowUserInteractionOnQuit;
251 }
252 }
253 }
254
255 SAL_INFO("fwk.session.debug", " m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false"));
256 if (!m_rSessionManager.is())
257 m_rSessionManager = css::uno::Reference< frame::XSessionManagerClient >
258 (m_xContext->getServiceManager()->createInstanceWithContext(aSMgr, m_xContext), UNO_QUERY);
259
260 if (m_rSessionManager.is())
261 {
262 m_rSessionManager->addSessionManagerListener(this);
263 }
264 }
265
statusChanged(const frame::FeatureStateEvent & event)266 void SAL_CALL SessionListener::statusChanged(const frame::FeatureStateEvent& event)
267 {
268 SAL_INFO("fwk.session", "SessionListener::statusChanged");
269
270 SAL_INFO("fwk.session.debug", " ev.Feature = " << event.FeatureURL.Complete <<
271 ", ev.Descript = " << event.FeatureDescriptor);
272 if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doSessionRestore" )
273 {
274 if (event.FeatureDescriptor == "update")
275 m_bRestored = true; // a document was restored
276
277 }
278 else if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doAutoSave" )
279 { // the "doSessionSave" was never set, look to framework/source/services/autorecovery.cxx
280 // it always testing but never setting (enum AutoRecovery::E_SESSION_SAVE)
281 if (event.FeatureDescriptor == "update")
282 {
283 if (m_rSessionManager.is())
284 m_rSessionManager->saveDone(this); // done with save
285 }
286 }
287 }
288
doRestore()289 sal_Bool SAL_CALL SessionListener::doRestore()
290 {
291 SAL_INFO("fwk.session", "SessionListener::doRestore");
292 osl::MutexGuard g(m_aMutex);
293 m_bRestored = false;
294 try {
295 css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
296
297 URL aURL;
298 aURL.Complete = "vnd.sun.star.autorecovery:/doSessionRestore";
299 css::uno::Reference< XURLTransformer > xURLTransformer(URLTransformer::create(m_xContext));
300 xURLTransformer->parseStrict(aURL);
301 Sequence< PropertyValue > args;
302 xDispatch->addStatusListener(this, aURL);
303 xDispatch->dispatch(aURL, args);
304 m_bRestored = true;
305
306 } catch (const css::uno::Exception&) {
307 TOOLS_WARN_EXCEPTION("fwk.session", "");
308 }
309
310 return m_bRestored;
311 }
312
doSave(sal_Bool bShutdown,sal_Bool)313 void SAL_CALL SessionListener::doSave( sal_Bool bShutdown, sal_Bool /*bCancelable*/ )
314 {
315 SAL_INFO("fwk.session", "SessionListener::doSave");
316
317 SAL_INFO("fwk.session.debug", " m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false") <<
318 ", bShutdown = " << (bShutdown ? "true" : "false"));
319 if (bShutdown)
320 {
321 m_bSessionStoreRequested = true; // there is no need to protect it with mutex
322 if ( m_bAllowUserInteractionOnQuit && m_rSessionManager.is() )
323 m_rSessionManager->queryInteraction( static_cast< css::frame::XSessionManagerListener* >( this ) );
324 else
325 StoreSession( true );
326 }
327 // we don't have anything to do so tell the session manager we're done
328 else if( m_rSessionManager.is() )
329 m_rSessionManager->saveDone( this );
330 }
331
approveInteraction(sal_Bool bInteractionGranted)332 void SAL_CALL SessionListener::approveInteraction( sal_Bool bInteractionGranted )
333 {
334 SAL_INFO("fwk.session", "SessionListener::approveInteraction");
335 // do AutoSave as the first step
336 osl::MutexGuard g(m_aMutex);
337
338 if ( bInteractionGranted )
339 {
340 // close the office documents in normal way
341 try
342 {
343 // first of all let the session be stored to be sure that we lose no information
344 StoreSession( false );
345
346 css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
347 // honestly: how many implementations of XDesktop will we ever have?
348 // so casting this directly to the implementation
349 Desktop* pDesktop(dynamic_cast<Desktop*>(xDesktop.get()));
350 if(pDesktop)
351 {
352 SAL_INFO("fwk.session", " XDesktop is a framework::Desktop -- good.");
353 m_bTerminated = pDesktop->terminateQuickstarterToo();
354 }
355 else
356 {
357 SAL_WARN("fwk.session", " XDesktop is not a framework::Desktop -- this should never happen.");
358 m_bTerminated = xDesktop->terminate();
359 }
360
361 if ( m_rSessionManager.is() )
362 {
363 // false means that the application closing has been cancelled
364 if ( !m_bTerminated )
365 m_rSessionManager->cancelShutdown();
366 else
367 m_rSessionManager->interactionDone( this );
368 }
369 }
370 catch( const css::uno::Exception& )
371 {
372 StoreSession( true );
373 m_rSessionManager->interactionDone( this );
374 }
375
376 if ( m_rSessionManager.is() && m_bTerminated )
377 m_rSessionManager->saveDone(this);
378 }
379 else
380 {
381 StoreSession( true );
382 }
383 }
384
shutdownCanceled()385 void SessionListener::shutdownCanceled()
386 {
387 SAL_INFO("fwk.session", "SessionListener::shutdownCanceled");
388 // set the state back
389 m_bSessionStoreRequested = false; // there is no need to protect it with mutex
390
391 if ( m_rSessionManager.is() )
392 m_rSessionManager->saveDone(this);
393 }
394
doQuit()395 void SessionListener::doQuit()
396 {
397 SAL_INFO("fwk.session", "SessionListener::doQuit");
398 if ( m_bSessionStoreRequested && !m_bTerminated )
399 {
400 // let the session be closed quietly in this case
401 QuitSessionQuietly();
402 }
403 }
404
405 }
406
407 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_frame_SessionListener_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)408 com_sun_star_comp_frame_SessionListener_get_implementation(
409 css::uno::XComponentContext *context,
410 css::uno::Sequence<css::uno::Any> const &)
411 {
412 SAL_INFO("fwk.session", "com_sun_star_comp_frame_SessionListener_get_implementation");
413
414 return cppu::acquire(new SessionListener(context));
415 }
416
417 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
418