xref: /core/desktop/source/app/app.cxx (revision 229123ccc6f90ebf66b3e659bebbd53f8a9bdd3a)
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 <memory>
21 #include <config_features.h>
22 #include <config_feature_desktop.h>
23 #include <config_feature_opencl.h>
24 #include <config_java.h>
25 #include <config_folders.h>
26 #include <config_extensions.h>
27 
28 #include <sal/config.h>
29 
30 #include <iostream>
31 #include <string_view>
32 
33 #include <app.hxx>
34 #include <dp_shared.hxx>
35 #include <strings.hrc>
36 #include "cmdlineargs.hxx"
37 #include <lockfile.hxx>
38 #include "userinstall.hxx"
39 #include "desktopcontext.hxx"
40 #include <migration.hxx>
41 #include "officeipcthread.hxx"
42 #if HAVE_FEATURE_UPDATE_MAR
43 #include "updater.hxx"
44 #endif
45 
46 #include <framework/desktop.hxx>
47 #include <i18nlangtag/languagetag.hxx>
48 #include <o3tl/char16_t2wchar_t.hxx>
49 #include <svl/languageoptions.hxx>
50 #include <svl/cjkoptions.hxx>
51 #include <svl/ctloptions.hxx>
52 #include <svtools/javacontext.hxx>
53 #include <com/sun/star/beans/XPropertySet.hpp>
54 #include <com/sun/star/frame/theAutoRecovery.hpp>
55 #include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
56 #include <com/sun/star/frame/SessionListener.hpp>
57 #include <com/sun/star/frame/XSynchronousDispatch.hpp>
58 #include <com/sun/star/configuration/theDefaultProvider.hpp>
59 #include <com/sun/star/util/XFlushable.hpp>
60 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
61 #include <com/sun/star/frame/Desktop.hpp>
62 #include <com/sun/star/frame/StartModule.hpp>
63 #include <com/sun/star/view/XPrintable.hpp>
64 #include <com/sun/star/awt/XTopWindow.hpp>
65 #include <com/sun/star/util/URLTransformer.hpp>
66 #include <com/sun/star/util/XURLTransformer.hpp>
67 #include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
68 #include <com/sun/star/configuration/MissingBootstrapFileException.hpp>
69 #include <com/sun/star/configuration/InvalidBootstrapFileException.hpp>
70 #include <com/sun/star/configuration/InstallationIncompleteException.hpp>
71 #include <com/sun/star/configuration/backend/BackendSetupException.hpp>
72 #include <com/sun/star/configuration/backend/BackendAccessException.hpp>
73 #include <com/sun/star/task/theJobExecutor.hpp>
74 #include <com/sun/star/task/OfficeRestartManager.hpp>
75 #include <com/sun/star/task/XRestartManager.hpp>
76 #include <com/sun/star/document/XDocumentEventListener.hpp>
77 #include <com/sun/star/office/Quickstart.hpp>
78 #include <com/sun/star/system/XSystemShellExecute.hpp>
79 #include <com/sun/star/system/SystemShellExecute.hpp>
80 #include <com/sun/star/loader/XImplementationLoader.hpp>
81 
82 #include <desktop/exithelper.h>
83 #include <sal/log.hxx>
84 #include <toolkit/helper/vclunohelper.hxx>
85 #include <comphelper/configuration.hxx>
86 #include <comphelper/fileurl.hxx>
87 #include <comphelper/threadpool.hxx>
88 #include <comphelper/processfactory.hxx>
89 #include <comphelper/backupfilehelper.hxx>
90 #include <uno/current_context.hxx>
91 #include <unotools/bootstrap.hxx>
92 #include <unotools/configmgr.hxx>
93 #include <unotools/moduleoptions.hxx>
94 #include <unotools/localfilehelper.hxx>
95 #include <unotools/ucbhelper.hxx>
96 #include <officecfg/Office/Common.hxx>
97 #include <officecfg/Office/Recovery.hxx>
98 #include <officecfg/Office/Update.hxx>
99 #include <officecfg/Setup.hxx>
100 #include <osl/file.hxx>
101 #include <osl/process.h>
102 #include <rtl/byteseq.hxx>
103 #include <unotools/pathoptions.hxx>
104 #include <unotools/VersionConfig.hxx>
105 #include <rtl/bootstrap.hxx>
106 #include <vcl/test/GraphicsRenderTests.hxx>
107 #include <vcl/glxtestprocess.hxx>
108 #include <vcl/help.hxx>
109 #include <vcl/weld.hxx>
110 #include <vcl/settings.hxx>
111 #include <sfx2/flatpak.hxx>
112 #include <sfx2/sfxsids.hrc>
113 #include <sfx2/app.hxx>
114 #include <sfx2/safemode.hxx>
115 #include <svl/itemset.hxx>
116 #include <svl/eitem.hxx>
117 #include <basic/sbstar.hxx>
118 #include <desktop/crashreport.hxx>
119 #include <tools/urlobj.hxx>
120 #include <tools/diagnose_ex.h>
121 #include <svtools/fontsubstconfig.hxx>
122 #include <svtools/accessibilityoptions.hxx>
123 #include <svtools/apearcfg.hxx>
124 #include <vcl/graphicfilter.hxx>
125 #include <vcl/window.hxx>
126 #include "langselect.hxx"
127 #include <salhelper/thread.hxx>
128 
129 #if defined MACOSX
130 #include <errno.h>
131 #include <sys/wait.h>
132 #endif
133 
134 #ifdef _WIN32
135 #define WIN32_LEAN_AND_MEAN
136 #include <windows.h>
137 #include <vcl/fileregistration.hxx>
138 #endif
139 
140 #if defined(_WIN32)
141 #include <process.h>
142 #define GETPID _getpid
143 #else
144 #include <unistd.h>
145 #define GETPID getpid
146 #endif
147 
148 #include <strings.hxx>
149 
150 using namespace ::com::sun::star::awt;
151 using namespace ::com::sun::star::uno;
152 using namespace ::com::sun::star::util;
153 using namespace ::com::sun::star::lang;
154 using namespace ::com::sun::star::beans;
155 using namespace ::com::sun::star::frame;
156 using namespace ::com::sun::star::document;
157 using namespace ::com::sun::star::view;
158 using namespace ::com::sun::star::task;
159 using namespace ::com::sun::star::system;
160 using namespace ::com::sun::star::ui;
161 using namespace ::com::sun::star::ui::dialogs;
162 using namespace ::com::sun::star::container;
163 
164 namespace desktop
165 {
166 
167 static oslSignalHandler pSignalHandler = nullptr;
168 
169 namespace {
170 
171 #if HAVE_FEATURE_EXTENSIONS
172 
173 // Remove any existing UserInstallation's extensions cache data remaining from
174 // old installations.  This addresses at least two problems:
175 //
176 // For one, apparently due to the old share/prereg/bundled mechanism (disabled
177 // since 5c47e5f63a79a9e72ec4a100786b1bbf65137ed4 "fdo#51252 Disable copying
178 // share/prereg/bundled to avoid startup crashes"), the user/extensions/bundled
179 // cache could contain corrupted information (like a UNO component registered
180 // twice, which got changed from active to passive registration in one LO
181 // version, but the version of the corresponding bundled extension only
182 // incremented in a later LO version).
183 //
184 // For another, UserInstallations have been seen in the wild where no extensions
185 // were installed per-user (any longer), but user/uno_packages/cache/registry/
186 // com.sun.star.comp.deployment.component.PackageRegistryBackend/*.rdb files
187 // contained data nevertheless.
188 //
189 // When a LO upgrade is detected (i.e., no user/extensions/buildid or one
190 // containing an old build ID), then user/extensions and
191 // user/uno_packages/cache/registry/
192 // com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc are
193 // removed.  That should prevent any problems starting the service manager due
194 // to old junk.  Later on in Desktop::SynchronizeExtensionRepositories, the
195 // removed cache data is recreated.
196 //
197 // Multiple instances of soffice.bin can execute this code in parallel for a
198 // single UserInstallation, as it is called before RequestHandler is set up.
199 // Therefore, any errors here only lead to SAL_WARNs.
200 //
201 // At least in theory, this function could be removed again once no
202 // UserInstallation can be poisoned by old junk any more.
203 bool cleanExtensionCache() {
204     OUString buildId(
205         "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
206     rtl::Bootstrap::expandMacros(buildId); //TODO: detect failure
207     OUString extDir(
208         "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
209         ":UserInstallation}/user/extensions");
210     rtl::Bootstrap::expandMacros(extDir); //TODO: detect failure
211     OUString buildIdFile(extDir + "/buildid");
212     osl::File fr(buildIdFile);
213     osl::FileBase::RC rc = fr.open(osl_File_OpenFlag_Read);
214     switch (rc) {
215     case osl::FileBase::E_None:
216         {
217             rtl::ByteSequence s1;
218             rc = fr.readLine(s1);
219             osl::FileBase::RC rc2 = fr.close();
220             SAL_WARN_IF(
221                 rc2 != osl::FileBase::E_None, "desktop.app",
222                 "cannot close " << fr.getURL() << " after reading: " << +rc2);
223             // readLine returns E_AGAIN for a zero-size file:
224             if (rc != osl::FileBase::E_None && rc != osl::FileBase::E_AGAIN) {
225                 SAL_WARN( "desktop.app", "cannot read from " << fr.getURL() << ": " << +rc);
226                 break;
227             }
228             OUString s2(
229                 reinterpret_cast< char const * >(s1.getConstArray()),
230                 s1.getLength(), RTL_TEXTENCODING_ISO_8859_1);
231                 // using ISO 8859-1 avoids any and all conversion errors; the
232                 // content should only be a subset of ASCII, anyway
233             if (s2 == buildId) {
234                 return false;
235             }
236             break;
237         }
238     case osl::FileBase::E_NOENT:
239         break;
240     default:
241         SAL_WARN( "desktop.app", "cannot open " << fr.getURL() << " for reading: " << +rc);
242         break;
243     }
244     utl::removeTree(extDir);
245     OUString userRcFile(
246         "$UNO_USER_PACKAGES_CACHE/registry/"
247         "com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc");
248     rtl::Bootstrap::expandMacros(userRcFile); //TODO: detect failure
249     rc = osl::File::remove(userRcFile);
250     SAL_WARN_IF(
251         rc != osl::FileBase::E_None && rc != osl::FileBase::E_NOENT, "desktop.app",
252         "cannot remove file " << userRcFile << ": " << +rc);
253     rc = osl::Directory::createPath(extDir);
254     SAL_WARN_IF(
255         rc != osl::FileBase::E_None && rc != osl::FileBase::E_EXIST, "desktop.app",
256         "cannot create path " << extDir << ": " << +rc);
257     osl::File fw(buildIdFile);
258     rc = fw.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
259     if (rc != osl::FileBase::E_None) {
260         SAL_WARN( "desktop.app", "cannot open " << fw.getURL() << " for writing: " << +rc);
261         return true;
262     }
263     OString buf(OUStringToOString(buildId, RTL_TEXTENCODING_UTF8));
264         // using UTF-8 avoids almost all conversion errors (and buildid
265         // containing single surrogate halves should never happen, anyway); the
266         // content should only be a subset of ASCII, anyway
267     sal_uInt64 n = 0;
268     rc = fw.write(buf.getStr(), buf.getLength(), n);
269     SAL_WARN_IF(
270         (rc != osl::FileBase::E_None
271          || n != static_cast< sal_uInt32 >(buf.getLength())),
272         "desktop.app",
273         "cannot write to " << fw.getURL() << ": " << +rc << ", " << n);
274     rc = fw.close();
275     SAL_WARN_IF(
276         rc != osl::FileBase::E_None, "desktop.app",
277         "cannot close " << fw.getURL() << " after writing: " << +rc);
278     return true;
279 }
280 
281 #endif
282 
283 bool shouldLaunchQuickstart()
284 {
285     bool bQuickstart = Desktop::GetCommandLineArgs().IsQuickstart();
286     if (!bQuickstart)
287     {
288         const SfxPoolItem* pItem=nullptr;
289         SfxItemSetFixed<SID_ATTR_QUICKLAUNCHER, SID_ATTR_QUICKLAUNCHER> aQLSet(SfxGetpApp()->GetPool());
290         SfxGetpApp()->GetOptions(aQLSet);
291         SfxItemState eState = aQLSet.GetItemState(SID_ATTR_QUICKLAUNCHER, false, &pItem);
292         if (SfxItemState::SET == eState)
293             bQuickstart = static_cast<const SfxBoolItem*>(pItem)->GetValue();
294     }
295     return bQuickstart;
296 }
297 
298 void SetRestartState() {
299     try {
300         std::shared_ptr< comphelper::ConfigurationChanges > batch(
301             comphelper::ConfigurationChanges::create());
302         officecfg::Setup::Office::OfficeRestartInProgress::set(true, batch);
303         batch->commit();
304     } catch (css::uno::Exception) {
305         TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
306     }
307 }
308 
309 void DoRestartActionsIfNecessary(bool quickstart) {
310     if (!quickstart)
311         return;
312 
313     try {
314         if (officecfg::Setup::Office::OfficeRestartInProgress::get()) {
315             std::shared_ptr< comphelper::ConfigurationChanges > batch(
316                 comphelper::ConfigurationChanges::create());
317             officecfg::Setup::Office::OfficeRestartInProgress::set(
318                 false, batch);
319             batch->commit();
320             css::office::Quickstart::createStart(
321                 comphelper::getProcessComponentContext(),
322                 shouldLaunchQuickstart());
323         }
324     } catch (css::uno::Exception &) {
325         TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
326     }
327 }
328 
329 void RemoveIconCacheDirectory()
330 {
331     // See getIconCacheUrl in vcl/source/image/ImplImageTree.cxx
332     OUString sUrl = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
333         "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache";
334     rtl::Bootstrap::expandMacros(sUrl);
335     utl::UCBContentHelper::Kill(sUrl);
336 }
337 
338 }
339 
340 namespace {
341 
342 void runGraphicsRenderTests()
343 {
344     if (!utl::isProductVersionUpgraded(false))
345     {
346         return;
347     }
348     GraphicsRenderTests TestObject;
349     TestObject.run();
350 }
351 
352 
353 OUString MakeStartupErrorMessage(std::u16string_view aErrorMessage)
354 {
355     return DpResId(STR_BOOTSTRAP_ERR_CANNOT_START) + "\n" + aErrorMessage;
356 }
357 
358 
359 // shows a simple error box with the given message ... but exits from these process !
360 // Fatal errors can't be solved by the process ... nor any recovery can help.
361 // Mostly the installation was damaged and must be repaired manually .. or by calling
362 // setup again.
363 // On the other side we must make sure that no further actions will be possible within
364 // the current office process ! No pipe requests, no menu/toolbar/shortcut actions
365 // are allowed. Otherwise we will force a "crash inside a crash".
366 // That's why we have to use a special native message box here which does not use yield :-)
367 
368 void FatalError(const OUString& sMessage)
369 {
370     OUString sProductKey = ::utl::Bootstrap::getProductKey();
371     if ( sProductKey.isEmpty())
372     {
373         osl_getExecutableFile( &sProductKey.pData );
374 
375         ::sal_uInt32 nLastIndex = sProductKey.lastIndexOf('/');
376         if ( nLastIndex > 0 )
377             sProductKey = sProductKey.copy( nLastIndex+1 );
378     }
379 
380     OUString sTitle = sProductKey + " - Fatal Error";
381     Application::ShowNativeErrorBox (sTitle, sMessage);
382     std::cerr << sTitle << ": " << sMessage << std::endl;
383     _exit(EXITHELPER_FATAL_ERROR);
384 }
385 
386 }
387 
388 CommandLineArgs& Desktop::GetCommandLineArgs()
389 {
390     static CommandLineArgs theCommandLineArgs;
391     return theCommandLineArgs;
392 }
393 
394 OUString ReplaceStringHookProc( const OUString& rStr )
395 {
396     const static OUString sBuildId(utl::Bootstrap::getBuildIdData("development")),
397         sBrandName(utl::ConfigManager::getProductName()),
398         sVersion(utl::ConfigManager::getProductVersion()),
399         sAboutBoxVersion(utl::ConfigManager::getAboutBoxProductVersion()),
400         sAboutBoxVersionSuffix(utl::ConfigManager::getAboutBoxProductVersionSuffix()),
401         sExtension(utl::ConfigManager::getProductExtension());
402 
403     OUString sRet(rStr);
404     if (sRet.indexOf("%PRODUCT") != -1 || sRet.indexOf("%ABOUTBOX") != -1)
405     {
406         sRet = sRet.replaceAll( "%PRODUCTNAME", sBrandName );
407         sRet = sRet.replaceAll( "%PRODUCTVERSION", sVersion );
408         sRet = sRet.replaceAll( "%BUILDID", sBuildId );
409         sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSIONSUFFIX", sAboutBoxVersionSuffix );
410         sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSION", sAboutBoxVersion );
411         sRet = sRet.replaceAll( "%PRODUCTEXTENSION", sExtension );
412     }
413 
414     if ( sRet.indexOf( "%OOOVENDOR" ) != -1 )
415     {
416         const static OUString sOOOVendor = utl::ConfigManager::getVendor();
417         sRet = sRet.replaceAll( "%OOOVENDOR", sOOOVendor );
418     }
419 
420     return sRet;
421 }
422 
423 Desktop::Desktop()
424     : m_bCleanedExtensionCache(false)
425     , m_bServicesRegistered(false)
426     , m_aBootstrapError(BE_OK)
427     , m_aBootstrapStatus(BS_OK)
428     , m_firstRunTimer( "desktop::Desktop m_firstRunTimer" )
429 {
430     m_firstRunTimer.SetTimeout(3000); // 3 sec.
431     m_firstRunTimer.SetInvokeHandler(LINK(this, Desktop, AsyncInitFirstRun));
432 }
433 
434 Desktop::~Desktop()
435 {
436 }
437 
438 void Desktop::Init()
439 {
440     SetBootstrapStatus(BS_OK);
441 
442 #if HAVE_FEATURE_EXTENSIONS
443     m_bCleanedExtensionCache = cleanExtensionCache();
444 #endif
445 
446     // We need to have service factory before going further, but see fdo#37195.
447     // Doing this will mmap common.rdb, making it not overwritable on windows,
448     // so this can't happen before the synchronization above. Lets rework this
449     // so that the above is called *from* CreateApplicationServiceManager or
450     // something to enforce this gotcha
451     try
452     {
453         InitApplicationServiceManager();
454     }
455     catch (css::uno::Exception & e)
456     {
457         SetBootstrapError( BE_UNO_SERVICEMANAGER, e.Message );
458     }
459 
460     // Check whether safe mode is enabled
461     const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
462     // Check if we are restarting from safe mode - in that case we don't want to enter it again
463     if (sfx2::SafeMode::hasRestartFlag())
464         sfx2::SafeMode::removeRestartFlag();
465     else if (rCmdLineArgs.IsSafeMode() || sfx2::SafeMode::hasFlag())
466         Application::EnableSafeMode();
467 
468     // When we are in SafeMode we need to do changes before the configuration
469     // gets read (langselect::prepareLocale() by UNO API -> Components::Components)
470     // This may prepare SafeMode or restore from it by moving data in
471     // the UserConfiguration directory
472     comphelper::BackupFileHelper::reactOnSafeMode(Application::IsSafeModeEnabled());
473 
474     if ( m_aBootstrapError == BE_OK )
475     {
476         try
477         {
478             if (!langselect::prepareLocale())
479             {
480                 SetBootstrapError( BE_LANGUAGE_MISSING, OUString() );
481             }
482         }
483         catch (css::uno::Exception & e)
484         {
485             SetBootstrapError( BE_OFFICECONFIG_BROKEN, e.Message );
486         }
487 
488         // test code for ProfileSafeMode to allow testing the fail
489         // of loading the office configuration initially. To use,
490         // either set to true and compile, or set a breakpoint
491         // in debugger and change the local bool
492         static bool bTryHardOfficeconfigBroken(false); // loplugin:constvars:ignore
493 
494         if (bTryHardOfficeconfigBroken)
495         {
496             SetBootstrapError(BE_OFFICECONFIG_BROKEN, OUString());
497         }
498     }
499 
500     // start ipc thread only for non-remote offices
501     RequestHandler::Status aStatus = RequestHandler::Enable(true);
502     if ( aStatus == RequestHandler::IPC_STATUS_PIPE_ERROR )
503     {
504 #if defined ANDROID
505         // Ignore crack pipe errors on Android
506 #else
507         // Keep using this oddly named BE_PATHINFO_MISSING value
508         // for pipe-related errors on other platforms. Of course
509         // this crack with two (if not more) levels of our own
510         // error codes hiding the actual system error code is
511         // broken, but that is done all over the code, let's leave
512         // reengineering that to another year.
513         SetBootstrapError( BE_PATHINFO_MISSING, OUString() );
514 #endif
515     }
516     else if ( aStatus == RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR )
517     {
518         SetBootstrapError( BE_PATHINFO_MISSING, OUString() );
519     }
520     else if ( aStatus == RequestHandler::IPC_STATUS_2ND_OFFICE )
521     {
522         // 2nd office startup should terminate after sending cmdlineargs through pipe
523         SetBootstrapStatus(BS_TERMINATE);
524     }
525     else if ( !rCmdLineArgs.GetUnknown().isEmpty()
526               || rCmdLineArgs.IsHelp() || rCmdLineArgs.IsVersion() )
527     {
528         // disable IPC thread in an instance that is just showing a help message
529         RequestHandler::Disable();
530     }
531     pSignalHandler = osl_addSignalHandler(SalMainPipeExchangeSignal_impl, nullptr);
532 }
533 
534 void Desktop::InitFinished()
535 {
536     CloseSplashScreen();
537 }
538 
539 void Desktop::DeInit()
540 {
541     try {
542         // instead of removing of the configManager just let it commit all the changes
543         utl::ConfigManager::storeConfigItems();
544         FlushConfiguration();
545 
546         // close splashscreen if it's still open
547         CloseSplashScreen();
548         Reference< XComponent >(
549             comphelper::getProcessComponentContext(), UNO_QUERY_THROW )->
550             dispose();
551         // nobody should get a destroyed service factory...
552         ::comphelper::setProcessServiceFactory( nullptr );
553 
554         // clear lockfile
555         m_xLockfile.reset();
556 
557         RequestHandler::Disable();
558         if( pSignalHandler )
559             osl_removeSignalHandler( pSignalHandler );
560     } catch (const RuntimeException&) {
561         // someone threw an exception during shutdown
562         // this will leave some garbage behind...
563         TOOLS_WARN_EXCEPTION("desktop.app", "exception throwing during shutdown, will leave some garbage behind");
564     }
565 }
566 
567 bool Desktop::QueryExit()
568 {
569     try
570     {
571         utl::ConfigManager::storeConfigItems();
572     }
573     catch ( const RuntimeException& )
574     {
575     }
576 
577     static constexpr OUStringLiteral SUSPEND_QUICKSTARTVETO = u"SuspendQuickstartVeto";
578 
579     Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
580     Reference< XPropertySet > xPropertySet(xDesktop, UNO_QUERY_THROW);
581     xPropertySet->setPropertyValue( SUSPEND_QUICKSTARTVETO, Any(true) );
582 
583     bool bExit = xDesktop->terminate();
584 
585     if ( !bExit )
586     {
587         xPropertySet->setPropertyValue( SUSPEND_QUICKSTARTVETO, Any(false) );
588     }
589     else if (!Application::IsEventTestingModeEnabled())
590     {
591         FlushConfiguration();
592         try
593         {
594             // it is no problem to call RequestHandler::Disable() more than once
595             // it also looks to be threadsafe
596             RequestHandler::Disable();
597         }
598         catch ( const RuntimeException& )
599         {
600         }
601 
602         m_xLockfile.reset();
603 
604     }
605 
606     return bExit;
607 }
608 
609 void Desktop::Shutdown()
610 {
611     framework::getDesktop(::comphelper::getProcessComponentContext())->shutdown();
612 }
613 
614 void Desktop::HandleBootstrapPathErrors( ::utl::Bootstrap::Status aBootstrapStatus, std::u16string_view aDiagnosticMessage )
615 {
616     if ( aBootstrapStatus == ::utl::Bootstrap::DATA_OK )
617         return;
618 
619     OUString        aProductKey;
620     OUString        aTemp;
621 
622     osl_getExecutableFile( &aProductKey.pData );
623     sal_uInt32     lastIndex = aProductKey.lastIndexOf('/');
624     if ( lastIndex > 0 )
625         aProductKey = aProductKey.copy( lastIndex+1 );
626 
627     aTemp = ::utl::Bootstrap::getProductKey( aProductKey );
628     if ( !aTemp.isEmpty() )
629         aProductKey = aTemp;
630 
631     OUString const aMessage(OUString::Concat(aDiagnosticMessage) + "\n");
632 
633     std::unique_ptr<weld::MessageDialog> xBootstrapFailedBox(Application::CreateMessageDialog(nullptr,
634                                                              VclMessageType::Warning, VclButtonsType::Ok, aMessage));
635     xBootstrapFailedBox->set_title(aProductKey);
636     xBootstrapFailedBox->run();
637 }
638 
639 // Create an error message depending on bootstrap failure code and an optional file url
640 OUString    Desktop::CreateErrorMsgString(
641     utl::Bootstrap::FailureCode nFailureCode,
642     const OUString& aFileURL )
643 {
644     OUString        aMsg;
645     bool            bFileInfo = true;
646 
647     switch ( nFailureCode )
648     {
649         /// the shared installation directory could not be located
650         case ::utl::Bootstrap::MISSING_INSTALL_DIRECTORY:
651         {
652             aMsg = DpResId(STR_BOOTSTRAP_ERR_PATH_INVALID);
653             bFileInfo = false;
654         }
655         break;
656 
657         /// the bootstrap INI file could not be found or read
658         case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE:
659         {
660             aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_MISSING);
661         }
662         break;
663 
664         /// the bootstrap INI is missing a required entry
665         /// the bootstrap INI contains invalid data
666          case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY:
667          case ::utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY:
668         {
669             aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_CORRUPT);
670         }
671         break;
672 
673         /// the version locator INI file could not be found or read
674         case ::utl::Bootstrap::MISSING_VERSION_FILE:
675         {
676             aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_MISSING);
677         }
678         break;
679 
680         /// the version locator INI has no entry for this version
681         case ::utl::Bootstrap::MISSING_VERSION_FILE_ENTRY:
682         {
683             aMsg = DpResId(STR_BOOTSTRAP_ERR_NO_SUPPORT);
684         }
685         break;
686 
687         /// the user installation directory does not exist
688         case ::utl::Bootstrap::MISSING_USER_DIRECTORY:
689         {
690             aMsg = DpResId(STR_BOOTSTRAP_ERR_DIR_MISSING);
691         }
692         break;
693 
694         /// some bootstrap data was invalid in unexpected ways
695         case ::utl::Bootstrap::INVALID_BOOTSTRAP_DATA:
696         {
697             aMsg = DpResId(STR_BOOTSTRAP_ERR_INTERNAL);
698             bFileInfo = false;
699         }
700         break;
701 
702         case ::utl::Bootstrap::INVALID_VERSION_FILE_ENTRY:
703         {
704             // This needs to be improved, see #i67575#:
705             aMsg = "Invalid version file entry";
706             bFileInfo = false;
707         }
708         break;
709 
710         case ::utl::Bootstrap::NO_FAILURE:
711         {
712             OSL_ASSERT(false);
713         }
714         break;
715     }
716 
717     if ( bFileInfo )
718     {
719         OUString aMsgString( aMsg );
720         OUString        aFilePath;
721 
722         osl::File::getSystemPathFromFileURL( aFileURL, aFilePath );
723 
724         aMsgString = aMsgString.replaceFirst( "$1", aFilePath );
725         aMsg = aMsgString;
726     }
727 
728     return MakeStartupErrorMessage( aMsg );
729 }
730 
731 void Desktop::HandleBootstrapErrors(
732     BootstrapError aBootstrapError, OUString const & aErrorMessage )
733 {
734     if ( aBootstrapError == BE_PATHINFO_MISSING )
735     {
736         OUString                    aErrorMsg;
737         OUString                    aBuffer;
738         utl::Bootstrap::Status        aBootstrapStatus;
739         utl::Bootstrap::FailureCode    nFailureCode;
740 
741         aBootstrapStatus = ::utl::Bootstrap::checkBootstrapStatus( aBuffer, nFailureCode );
742         if ( aBootstrapStatus != ::utl::Bootstrap::DATA_OK )
743         {
744             switch ( nFailureCode )
745             {
746                 case ::utl::Bootstrap::MISSING_INSTALL_DIRECTORY:
747                 case ::utl::Bootstrap::INVALID_BOOTSTRAP_DATA:
748                 {
749                     aErrorMsg = CreateErrorMsgString( nFailureCode, OUString() );
750                 }
751                 break;
752 
753                 /// the bootstrap INI file could not be found or read
754                 /// the bootstrap INI is missing a required entry
755                 /// the bootstrap INI contains invalid data
756                 case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY:
757                 case ::utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY:
758                 case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE:
759                 {
760                     OUString aBootstrapFileURL;
761 
762                     utl::Bootstrap::locateBootstrapFile( aBootstrapFileURL );
763                     aErrorMsg = CreateErrorMsgString( nFailureCode, aBootstrapFileURL );
764                 }
765                 break;
766 
767                 /// the version locator INI file could not be found or read
768                 /// the version locator INI has no entry for this version
769                 /// the version locator INI entry is not a valid directory URL
770                  case ::utl::Bootstrap::INVALID_VERSION_FILE_ENTRY:
771                  case ::utl::Bootstrap::MISSING_VERSION_FILE_ENTRY:
772                  case ::utl::Bootstrap::MISSING_VERSION_FILE:
773                 {
774                     OUString aVersionFileURL;
775 
776                     utl::Bootstrap::locateVersionFile( aVersionFileURL );
777                     aErrorMsg = CreateErrorMsgString( nFailureCode, aVersionFileURL );
778                 }
779                 break;
780 
781                 /// the user installation directory does not exist
782                  case ::utl::Bootstrap::MISSING_USER_DIRECTORY:
783                 {
784                     OUString aUserInstallationURL;
785 
786                     utl::Bootstrap::locateUserInstallation( aUserInstallationURL );
787                     aErrorMsg = CreateErrorMsgString( nFailureCode, aUserInstallationURL );
788                 }
789                 break;
790 
791                 case ::utl::Bootstrap::NO_FAILURE:
792                 {
793                     OSL_ASSERT(false);
794                 }
795                 break;
796             }
797 
798             HandleBootstrapPathErrors( aBootstrapStatus, aErrorMsg );
799         }
800     }
801     else if ( aBootstrapError == BE_UNO_SERVICEMANAGER || aBootstrapError == BE_UNO_SERVICE_CONFIG_MISSING )
802     {
803         // UNO service manager is not available. VCL needs a UNO service manager to display a message box!!!
804         // Currently we are not able to display a message box with a service manager due to this limitations inside VCL.
805 
806         // When UNO is not properly initialized, all kinds of things can fail
807         // and cause the process to crash. To give the user a hint even if
808         // generating and displaying a message box below crashes, print a
809         // hard-coded message on stderr first:
810         std::cerr
811             << "The application cannot be started.\n"
812                 // STR_BOOTSTRAP_ERR_CANNOT_START
813             << (aBootstrapError == BE_UNO_SERVICEMANAGER
814                 ? "The component manager is not available.\n"
815                     // STR_BOOTSTRAP_ERR_NO_SERVICE
816                 : "The configuration service is not available.\n");
817                     // STR_BOOTSTRAP_ERR_NO_CFG_SERVICE
818         if ( !aErrorMessage.isEmpty() )
819         {
820             std::cerr << "(\"" << aErrorMessage << "\")\n";
821         }
822 
823         // First sentence. We cannot bootstrap office further!
824         OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NO_CFG_SERVICE) + "\n";
825         if ( !aErrorMessage.isEmpty() )
826         {
827             aDiagnosticMessage += "(\"" + aErrorMessage + "\")\n";
828         }
829 
830         // Due to the fact the we haven't a backup applicat.rdb file anymore it is not possible to
831         // repair the installation with the setup executable besides the office executable. Now
832         // we have to ask the user to start the setup on CD/installation directory manually!!
833         aDiagnosticMessage += DpResId(STR_ASK_START_SETUP_MANUALLY);
834 
835         FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
836     }
837     else if ( aBootstrapError == BE_OFFICECONFIG_BROKEN )
838     {
839         // set flag at BackupFileHelper to be able to know if _exit was called and
840         // actions are executed after this. This method we are in will not return,
841         // but end up in a _exit() call
842         comphelper::BackupFileHelper::setExitWasCalled();
843 
844         // enter safe mode, too
845         sfx2::SafeMode::putFlag();
846 
847         OUString msg(DpResId(STR_CONFIG_ERR_ACCESS_GENERAL));
848         if (!aErrorMessage.isEmpty()) {
849             msg += "\n(\"" + aErrorMessage + "\")";
850         }
851         FatalError(MakeStartupErrorMessage(msg));
852     }
853     else if ( aBootstrapError == BE_USERINSTALL_FAILED )
854     {
855         OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_USERINSTALL_FAILED);
856         FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
857     }
858     else if ( aBootstrapError == BE_LANGUAGE_MISSING )
859     {
860         OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_LANGUAGE_MISSING);
861         FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
862     }
863     else if (( aBootstrapError == BE_USERINSTALL_NOTENOUGHDISKSPACE ) ||
864              ( aBootstrapError == BE_USERINSTALL_NOWRITEACCESS      ))
865     {
866         OUString aUserInstallationURL;
867         OUString aUserInstallationPath;
868         utl::Bootstrap::locateUserInstallation( aUserInstallationURL );
869         osl::File::getSystemPathFromFileURL( aUserInstallationURL, aUserInstallationPath );
870 
871         OUString aDiagnosticMessage;
872         if ( aBootstrapError == BE_USERINSTALL_NOTENOUGHDISKSPACE )
873             aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NOTENOUGHDISKSPACE);
874         else
875             aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NOACCESSRIGHTS);
876         aDiagnosticMessage += aUserInstallationPath;
877 
878         FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
879     }
880 }
881 
882 
883 namespace {
884 
885 
886 #if HAVE_FEATURE_BREAKPAD
887 void handleCrashReport()
888 {
889     static constexpr OUStringLiteral SERVICENAME_CRASHREPORT = u"com.sun.star.comp.svx.CrashReportUI";
890 
891     css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
892 
893     Reference< css::frame::XSynchronousDispatch > xRecoveryUI(
894         xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_CRASHREPORT, xContext),
895         css::uno::UNO_QUERY_THROW);
896 
897     Reference< css::util::XURLTransformer > xURLParser =
898         css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
899 
900     css::util::URL aURL;
901     css::uno::Any aRet = xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
902     bool bRet = false;
903     aRet >>= bRet;
904 }
905 #endif
906 
907 #if !defined ANDROID
908 void handleSafeMode()
909 {
910     css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
911 
912     Reference< css::frame::XSynchronousDispatch > xSafeModeUI(
913         xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.svx.SafeModeUI", xContext),
914         css::uno::UNO_QUERY_THROW);
915 
916     css::util::URL aURL;
917     css::uno::Any aRet = xSafeModeUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
918     bool bRet = false;
919     aRet >>= bRet;
920 }
921 #endif
922 
923 /** @short  check if recovery must be started or not.
924 
925     @param  bCrashed [boolean ... out!]
926             the office crashed last times.
927             But may be there are no recovery data.
928             Useful to trigger the error report tool without
929             showing the recovery UI.
930 
931     @param  bRecoveryDataExists [boolean ... out!]
932             there exists some recovery data.
933 
934     @param  bSessionDataExists [boolean ... out!]
935             there exists some session data.
936             Because the user may be logged out last time from its
937             unix session...
938 */
939 void impl_checkRecoveryState(bool& bCrashed           ,
940                              bool& bRecoveryDataExists,
941                              bool& bSessionDataExists )
942 {
943     bCrashed = officecfg::Office::Recovery::RecoveryInfo::Crashed::get()
944 #if HAVE_FEATURE_BREAKPAD
945         || CrashReporter::crashReportInfoExists();
946 #else
947         ;
948 #endif
949     bool elements = officecfg::Office::Recovery::RecoveryList::get()->
950         hasElements();
951     bool session
952         = officecfg::Office::Recovery::RecoveryInfo::SessionData::get();
953     bRecoveryDataExists = elements && !session;
954     bSessionDataExists = elements && session;
955 }
956 
957 Reference< css::frame::XSynchronousDispatch > g_xRecoveryUI;
958 
959 template <class Ref>
960 struct RefClearGuard
961 {
962     Ref& m_Ref;
963     RefClearGuard(Ref& ref) : m_Ref(ref) {}
964     ~RefClearGuard() { m_Ref.clear(); }
965 };
966 
967 /*  @short  start the recovery wizard.
968 
969     @param  bEmergencySave
970             differs between EMERGENCY_SAVE and RECOVERY
971 */
972 bool impl_callRecoveryUI(bool bEmergencySave     ,
973                          bool bExistsRecoveryData)
974 {
975     constexpr OUStringLiteral COMMAND_EMERGENCYSAVE = u"vnd.sun.star.autorecovery:/doEmergencySave";
976     constexpr OUStringLiteral COMMAND_RECOVERY = u"vnd.sun.star.autorecovery:/doAutoRecovery";
977 
978     css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
979 
980     g_xRecoveryUI.set(
981         xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.svx.RecoveryUI", xContext),
982         css::uno::UNO_QUERY_THROW);
983     RefClearGuard<Reference< css::frame::XSynchronousDispatch >> refClearGuard(g_xRecoveryUI);
984 
985     Reference< css::util::XURLTransformer > xURLParser =
986         css::util::URLTransformer::create(xContext);
987 
988     css::util::URL aURL;
989     if (bEmergencySave)
990         aURL.Complete = COMMAND_EMERGENCYSAVE;
991     else if (bExistsRecoveryData)
992         aURL.Complete = COMMAND_RECOVERY;
993     else
994         return false;
995 
996     xURLParser->parseStrict(aURL);
997 
998     css::uno::Any aRet = g_xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
999     bool bRet = false;
1000     aRet >>= bRet;
1001     return bRet;
1002 }
1003 
1004 bool impl_bringToFrontRecoveryUI()
1005 {
1006     Reference< css::frame::XSynchronousDispatch > xRecoveryUI(g_xRecoveryUI);
1007     if (!xRecoveryUI.is())
1008         return false;
1009 
1010     css::util::URL aURL;
1011     aURL.Complete = "vnd.sun.star.autorecovery:/doBringToFront";
1012     Reference< css::util::XURLTransformer > xURLParser =
1013         css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
1014     xURLParser->parseStrict(aURL);
1015 
1016     css::uno::Any aRet = xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
1017     bool bRet = false;
1018     aRet >>= bRet;
1019     return bRet;
1020 }
1021 
1022 }
1023 
1024 namespace {
1025 
1026 void restartOnMac(bool passArguments) {
1027 #if defined MACOSX
1028     RequestHandler::Disable();
1029 #if HAVE_FEATURE_MACOSX_SANDBOX
1030     (void) passArguments; // avoid warnings
1031     OUString aMessage = DpResId(STR_LO_MUST_BE_RESTARTED);
1032 
1033     std::unique_ptr<weld::MessageDialog> xRestartBox(Application::CreateMessageDialog(nullptr,
1034                                                      VclMessageType::Warning, VclButtonsType::Ok, aMessage));
1035     xRestartBox->run();
1036 #else
1037     OUString execUrl;
1038     OSL_VERIFY(osl_getExecutableFile(&execUrl.pData) == osl_Process_E_None);
1039     OUString execPath;
1040     OString execPath8;
1041     if ((osl::FileBase::getSystemPathFromFileURL(execUrl, execPath)
1042          != osl::FileBase::E_None) ||
1043         !execPath.convertToString(
1044             &execPath8, osl_getThreadTextEncoding(),
1045             (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
1046              RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
1047     {
1048         std::abort();
1049     }
1050     std::vector< OString > args { execPath8 };
1051     bool wait = false;
1052     if (passArguments) {
1053         sal_uInt32 n = osl_getCommandArgCount();
1054         for (sal_uInt32 i = 0; i < n; ++i) {
1055             OUString arg;
1056             osl_getCommandArg(i, &arg.pData);
1057             if (arg.match("--accept=")) {
1058                 wait = true;
1059             }
1060             OString arg8;
1061             if (!arg.convertToString(
1062                     &arg8, osl_getThreadTextEncoding(),
1063                     (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
1064                      RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
1065             {
1066                 std::abort();
1067             }
1068             args.push_back(arg8);
1069         }
1070     }
1071     std::vector< char const * > argPtrs;
1072     for (auto const& elem : args)
1073     {
1074         argPtrs.push_back(elem.getStr());
1075     }
1076     argPtrs.push_back(nullptr);
1077     execv(execPath8.getStr(), const_cast< char ** >(argPtrs.data()));
1078     if (errno == ENOTSUP) { // happens when multithreaded on macOS < 10.6
1079         pid_t pid = fork();
1080         if (pid == 0) {
1081             execv(execPath8.getStr(), const_cast< char ** >(argPtrs.data()));
1082         } else if (pid > 0) {
1083             // Two simultaneously running soffice processes lead to two dock
1084             // icons, so avoid waiting here unless it must be assumed that the
1085             // process invoking soffice itself wants to wait for soffice to
1086             // finish:
1087             if (!wait) {
1088                 return;
1089             }
1090             int stat;
1091             if (waitpid(pid, &stat, 0) == pid && WIFEXITED(stat)) {
1092                 _exit(WEXITSTATUS(stat));
1093             }
1094         }
1095     }
1096     std::abort();
1097 #endif
1098 #else
1099     (void) passArguments; // avoid warnings
1100 #endif
1101 }
1102 
1103 #if HAVE_FEATURE_UPDATE_MAR
1104 bool isTimeForUpdateCheck()
1105 {
1106     sal_uInt64 nLastUpdate = officecfg::Office::Update::Update::LastUpdateTime::get();
1107     sal_uInt64 nNow = tools::Time::GetSystemTicks();
1108 
1109     sal_uInt64 n7DayInMS = 1000 * 60 * 60 * 12 * 1; // 12 hours in ms
1110     if (nNow - n7DayInMS >= nLastUpdate)
1111         return true;
1112 
1113     return false;
1114 }
1115 #endif
1116 
1117 }
1118 
1119 void Desktop::Exception(ExceptionCategory nCategory)
1120 {
1121     // protect against recursive calls
1122     static bool bInException = false;
1123 
1124 #if HAVE_FEATURE_BREAKPAD
1125     CrashReporter::removeExceptionHandler(); // disallow re-entry
1126 #endif
1127 
1128     SystemWindowFlags nOldMode = Application::GetSystemWindowMode();
1129     Application::SetSystemWindowMode( nOldMode & ~SystemWindowFlags::NOAUTOMODE );
1130     if ( bInException )
1131     {
1132         Application::Abort( OUString() );
1133     }
1134 
1135     bInException = true;
1136     const CommandLineArgs& rArgs = GetCommandLineArgs();
1137 
1138     // save all modified documents ... if it's allowed doing so.
1139     bool bRestart                           = false;
1140     bool bAllowRecoveryAndSessionManagement = (
1141                                                     ( !rArgs.IsNoRestore()                    ) && // some use cases of office must work without recovery
1142                                                     ( !rArgs.IsHeadless()                     ) &&
1143                                                     ( nCategory != ExceptionCategory::UserInterface ) && // recovery can't work without UI ... but UI layer seems to be the reason for this crash
1144                                                     ( Application::IsInExecute()               )    // crashes during startup and shutdown should be ignored (they indicate a corrupted installation...)
1145                                                   );
1146     if ( bAllowRecoveryAndSessionManagement )
1147     {
1148         // Save all open documents so they will be reopened
1149         // the next time the application is started
1150         // returns true if at least one document could be saved...
1151         bRestart = impl_callRecoveryUI(
1152                         true , // force emergency save
1153                         false);
1154     }
1155 
1156     FlushConfiguration();
1157 
1158     m_xLockfile.reset();
1159 
1160     if( bRestart )
1161     {
1162         RequestHandler::Disable();
1163         if( pSignalHandler )
1164             osl_removeSignalHandler( pSignalHandler );
1165 
1166         restartOnMac(false);
1167         if ( m_rSplashScreen.is() )
1168             m_rSplashScreen->reset();
1169 
1170         _exit( EXITHELPER_CRASH_WITH_RESTART );
1171     }
1172     else
1173     {
1174         Application::Abort( OUString() );
1175     }
1176 
1177     OSL_ASSERT(false); // unreachable
1178 }
1179 
1180 void Desktop::AppEvent( const ApplicationEvent& rAppEvent )
1181 {
1182     HandleAppEvent( rAppEvent );
1183 }
1184 
1185 namespace {
1186 
1187 class JVMloadThread : public salhelper::Thread {
1188 public:
1189     JVMloadThread() : salhelper::Thread("Preload JVM thread")
1190     {
1191     }
1192 
1193 private:
1194     virtual void execute() override final
1195     {
1196         Reference< XMultiServiceFactory > xSMgr = comphelper::getProcessServiceFactory();
1197 
1198         Reference< css::loader::XImplementationLoader > xJavaComponentLoader(
1199             xSMgr->createInstance("com.sun.star.comp.stoc.JavaComponentLoader"),
1200             css::uno::UNO_QUERY_THROW);
1201 
1202         if (xJavaComponentLoader.is())
1203         {
1204             const css::uno::Reference< ::com::sun::star::registry::XRegistryKey > xRegistryKey;
1205             try
1206             {
1207                 xJavaComponentLoader->activate("", "", "", xRegistryKey);
1208             }
1209             catch (...)
1210             {
1211                 SAL_WARN("desktop.app", "Cannot activate factory during JVM preloading");
1212             }
1213         }
1214     }
1215 };
1216 
1217 struct ExecuteGlobals
1218 {
1219     Reference < css::document::XDocumentEventListener > xGlobalBroadcaster;
1220     bool bRestartRequested;
1221     bool bUseSystemFileDialog;
1222     std::unique_ptr<SvtCTLOptions> pCTLLanguageOptions;
1223     std::unique_ptr<SvtPathOptions> pPathOptions;
1224     rtl::Reference< JVMloadThread > xJVMloadThread;
1225 
1226     ExecuteGlobals()
1227     : bRestartRequested( false )
1228     , bUseSystemFileDialog( true )
1229     {}
1230 };
1231 
1232 }
1233 
1234 static ExecuteGlobals* pExecGlobals = nullptr;
1235 
1236 int Desktop::Main()
1237 {
1238     pExecGlobals = new ExecuteGlobals();
1239 
1240     // Remember current context object
1241     css::uno::ContextLayer layer( css::uno::getCurrentContext() );
1242 
1243     if ( m_aBootstrapError != BE_OK )
1244     {
1245         HandleBootstrapErrors( m_aBootstrapError, m_aBootstrapErrorMessage );
1246         return EXIT_FAILURE;
1247     }
1248 
1249     BootstrapStatus eStatus = GetBootstrapStatus();
1250     if (eStatus == BS_TERMINATE) {
1251         return EXIT_SUCCESS;
1252     }
1253 
1254     // Detect desktop environment - need to do this as early as possible
1255     css::uno::setCurrentContext( new DesktopContext( css::uno::getCurrentContext() ) );
1256 
1257     if (officecfg::Office::Common::Misc::PreloadJVM::get() && pExecGlobals)
1258     {
1259         SAL_INFO("desktop.app", "Preload JVM");
1260 
1261         // pre-load JVM
1262         pExecGlobals->xJVMloadThread = new JVMloadThread();
1263         pExecGlobals->xJVMloadThread->launch();
1264     }
1265 
1266     CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
1267 
1268     Translate::SetReadStringHook(ReplaceStringHookProc);
1269 
1270     // Startup screen
1271     OpenSplashScreen();
1272 
1273     SetSplashScreenProgress(10);
1274 
1275     userinstall::Status inst_fin = userinstall::finalize();
1276     if (inst_fin != userinstall::EXISTED && inst_fin != userinstall::CREATED)
1277     {
1278         SAL_WARN( "desktop.app", "userinstall failed");
1279         if ( inst_fin == userinstall::ERROR_NO_SPACE )
1280             HandleBootstrapErrors(
1281                 BE_USERINSTALL_NOTENOUGHDISKSPACE, OUString() );
1282         else if ( inst_fin == userinstall::ERROR_CANT_WRITE )
1283             HandleBootstrapErrors( BE_USERINSTALL_NOWRITEACCESS, OUString() );
1284         else
1285             HandleBootstrapErrors( BE_USERINSTALL_FAILED, OUString() );
1286         return EXIT_FAILURE;
1287     }
1288     // refresh path information
1289     utl::Bootstrap::reloadData();
1290     SetSplashScreenProgress(20);
1291 
1292     Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1293 
1294     Reference< XRestartManager > xRestartManager( OfficeRestartManager::get(xContext) );
1295 
1296     Reference< XDesktop2 > xDesktop;
1297 
1298     RegisterServices(xContext);
1299 
1300     SetSplashScreenProgress(25);
1301 
1302 #if HAVE_FEATURE_DESKTOP
1303     // check user installation directory for lockfile so we can be sure
1304     // there is no other instance using our data files from a remote host
1305 
1306     bool bMustLockProfile = ( getenv( "SAL_NOLOCK_PROFILE" ) == nullptr );
1307     if ( bMustLockProfile )
1308     {
1309         m_xLockfile.reset(new Lockfile);
1310 
1311         if ( !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsInvisible() &&
1312              !rCmdLineArgs.IsNoLockcheck() && !m_xLockfile->check( Lockfile_execWarning ))
1313         {
1314             // Lockfile exists, and user clicked 'no'
1315             return EXIT_FAILURE;
1316         }
1317     }
1318 
1319     // check if accessibility is enabled but not working and allow to quit
1320     if( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() )
1321     {
1322         if( !InitAccessBridge() )
1323             return EXIT_FAILURE;
1324     }
1325 #endif
1326 
1327     // terminate if requested...
1328     if( rCmdLineArgs.IsTerminateAfterInit() )
1329         return EXIT_SUCCESS;
1330 
1331     //  Read the common configuration items for optimization purpose
1332     if ( !InitializeConfiguration() )
1333         return EXIT_FAILURE;
1334 
1335 #if HAVE_FEATURE_UPDATE_MAR
1336     const char* pUpdaterTestEnable = std::getenv("LIBO_UPDATER_TEST_ENABLE");
1337     if (pUpdaterTestEnable || officecfg::Office::Update::Update::Enabled::get())
1338     {
1339         // check if we just updated
1340         const char* pUpdaterRunning = std::getenv("LIBO_UPDATER_TEST_RUNNING");
1341         bool bUpdateRunning = officecfg::Office::Update::Update::UpdateRunning::get() || pUpdaterRunning;
1342         if (bUpdateRunning)
1343         {
1344             OUString aSeeAlso = officecfg::Office::Update::Update::SeeAlso::get();
1345             OUString aOldBuildID = officecfg::Office::Update::Update::OldBuildID::get();
1346 
1347             OUString aBuildID = Updater::getBuildID();
1348             if (aOldBuildID == aBuildID)
1349             {
1350                 Updater::log("Old and new Build ID are the same. No Updating took place.");
1351             }
1352             else
1353             {
1354                 if (!aSeeAlso.isEmpty())
1355                 {
1356                     SAL_INFO("desktop.updater", "See also: " << aSeeAlso);
1357                             Reference< css::system::XSystemShellExecute > xSystemShell(
1358                     SystemShellExecute::create(::comphelper::getProcessComponentContext()) );
1359 
1360                     xSystemShell->execute( aSeeAlso, OUString(), SystemShellExecuteFlags::URIS_ONLY );
1361                 }
1362             }
1363 
1364             // reset all the configuration values,
1365             // all values need to be read before this code
1366             std::shared_ptr< comphelper::ConfigurationChanges > batch(
1367                     comphelper::ConfigurationChanges::create());
1368             officecfg::Office::Update::Update::UpdateRunning::set(false, batch);
1369             officecfg::Office::Update::Update::SeeAlso::set(OUString(), batch);
1370             officecfg::Office::Update::Update::OldBuildID::set(OUString(), batch);
1371             batch->commit();
1372 
1373             Updater::removeUpdateFiles();
1374         }
1375 
1376         osl::DirectoryItem aUpdateFile;
1377         osl::DirectoryItem::get(Updater::getUpdateFileURL(), aUpdateFile);
1378 
1379         const char* pUpdaterTestUpdate = std::getenv("LIBO_UPDATER_TEST_UPDATE");
1380         const char* pForcedUpdateCheck = std::getenv("LIBO_UPDATER_TEST_UPDATE_CHECK");
1381         if (pUpdaterTestUpdate || aUpdateFile.is())
1382         {
1383             OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
1384             rtl::Bootstrap::expandMacros(aBuildID);
1385             std::shared_ptr< comphelper::ConfigurationChanges > batch(
1386                     comphelper::ConfigurationChanges::create());
1387             officecfg::Office::Update::Update::OldBuildID::set(aBuildID, batch);
1388             officecfg::Office::Update::Update::UpdateRunning::set(true, batch);
1389             batch->commit();
1390 
1391             // make sure the change is written to the configuration before we start the update
1392             css::uno::Reference<css::util::XFlushable> xFlushable(css::configuration::theDefaultProvider::get(xContext), UNO_QUERY);
1393             xFlushable->flush();
1394             // avoid the old oosplash staying around
1395             CloseSplashScreen();
1396             bool bSuccess = update();
1397             if (bSuccess)
1398                 return EXIT_SUCCESS;
1399         }
1400         else if (isTimeForUpdateCheck() || pForcedUpdateCheck)
1401         {
1402             sal_uInt64 nNow = tools::Time::GetSystemTicks();
1403             Updater::log("Update Check Time: " + OUString::number(nNow));
1404             std::shared_ptr< comphelper::ConfigurationChanges > batch(
1405                     comphelper::ConfigurationChanges::create());
1406             officecfg::Office::Update::Update::LastUpdateTime::set(nNow, batch);
1407             batch->commit();
1408             m_aUpdateThread = std::thread(update_checker);
1409         }
1410     }
1411 #endif
1412 
1413     SetSplashScreenProgress(30);
1414 
1415     // create title string
1416     OUString aTitle(ReplaceStringHookProc(RID_APPTITLE));
1417 #ifdef DBG_UTIL
1418     //include buildid in non product builds
1419     aTitle += " [" + utl::Bootstrap::getBuildIdData("development") + "]";
1420 #endif
1421 
1422     SetDisplayName( aTitle );
1423     SetSplashScreenProgress(35);
1424     pExecGlobals->pPathOptions.reset( new SvtPathOptions);
1425     SetSplashScreenProgress(40);
1426 
1427     xDesktop = css::frame::Desktop::create( xContext );
1428 
1429     // create service for loading SFX (still needed in startup)
1430     pExecGlobals->xGlobalBroadcaster = Reference < css::document::XDocumentEventListener >
1431         ( css::frame::theGlobalEventBroadcaster::get(xContext), UNO_SET_THROW );
1432 
1433     /* ensure existence of a default window that messages can be dispatched to
1434        This is for the benefit of testtool which uses PostUserEvent extensively
1435        and else can deadlock while creating this window from another thread while
1436        the main thread is not yet in the event loop.
1437     */
1438     Application::GetDefaultDevice();
1439 
1440 #if HAVE_FEATURE_EXTENSIONS
1441     // Check if bundled or shared extensions were added /removed
1442     // and process those extensions (has to be done before checking
1443     // the extension dependencies!
1444     SynchronizeExtensionRepositories(m_bCleanedExtensionCache, this);
1445     bool bAbort = CheckExtensionDependencies();
1446     if ( bAbort )
1447         return EXIT_FAILURE;
1448 
1449     if (inst_fin == userinstall::CREATED)
1450     {
1451         Migration::migrateSettingsIfNecessary();
1452     }
1453 #endif
1454 
1455     // keep a language options instance...
1456     pExecGlobals->pCTLLanguageOptions.reset( new SvtCTLOptions(true));
1457 
1458     css::document::DocumentEvent aEvent;
1459     aEvent.EventName = "OnStartApp";
1460     pExecGlobals->xGlobalBroadcaster->documentEventOccured(aEvent);
1461 
1462     SetSplashScreenProgress(50);
1463 
1464     // Backing Component
1465     bool bCrashed            = false;
1466     bool bExistsRecoveryData = false;
1467     bool bExistsSessionData  = false;
1468 
1469     impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
1470 
1471     OUString pidfileName = rCmdLineArgs.GetPidfileName();
1472     if ( !pidfileName.isEmpty() )
1473     {
1474         OUString pidfileURL;
1475 
1476         if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
1477         {
1478             osl::File pidfile( pidfileURL );
1479             osl::FileBase::RC rc;
1480 
1481             osl::File::remove( pidfileURL );
1482             if ( (rc = pidfile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) ) == osl::File::E_None )
1483             {
1484                 OString pid( OString::number( GETPID() ) );
1485                 sal_uInt64 written = 0;
1486                 if ( pidfile.write(pid.getStr(), pid.getLength(), written) != osl::File::E_None )
1487                 {
1488                     SAL_WARN("desktop.app", "cannot write pidfile " << pidfile.getURL());
1489                 }
1490                 pidfile.close();
1491             }
1492             else
1493             {
1494                 SAL_WARN("desktop.app", "cannot open pidfile " << pidfile.getURL() << rc);
1495             }
1496         }
1497         else
1498         {
1499             SAL_WARN("desktop.app", "cannot get pidfile URL from path" << pidfileName);
1500         }
1501     }
1502 
1503     if ( rCmdLineArgs.IsHeadless() || rCmdLineArgs.IsEventTesting() )
1504     {
1505         // Ensure that we use not the system file dialogs as
1506         // headless mode relies on Application::EnableHeadlessMode()
1507         // which does only work for VCL dialogs!!
1508         pExecGlobals->bUseSystemFileDialog = officecfg::Office::Common::Misc::UseSystemFileDialog::get();
1509         std::shared_ptr< comphelper::ConfigurationChanges > xChanges(
1510                 comphelper::ConfigurationChanges::create());
1511         officecfg::Office::Common::Misc::UseSystemFileDialog::set( false, xChanges );
1512         xChanges->commit();
1513     }
1514 
1515     pExecGlobals->bRestartRequested = xRestartManager->isRestartRequested(true);
1516     if ( !pExecGlobals->bRestartRequested )
1517     {
1518         if ((!rCmdLineArgs.WantsToLoadDocument() && !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsQuickstart()) &&
1519             (SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE)) &&
1520             (!bExistsRecoveryData                                                  ) &&
1521             (!bExistsSessionData                                                   ) &&
1522             (!Application::AnyInput( VclInputFlags::APPEVENT )                          ))
1523         {
1524              ShowBackingComponent(this);
1525         }
1526     }
1527 
1528     SetSplashScreenProgress(55);
1529 
1530     svtools::ApplyFontSubstitutionsToVcl();
1531 
1532     SvtTabAppearanceCfg aAppearanceCfg;
1533     SvtTabAppearanceCfg::SetInitialized();
1534     aAppearanceCfg.SetApplicationDefaults( this );
1535     SvtAccessibilityOptions aOptions;
1536     aOptions.SetVCLSettings();
1537     SetSplashScreenProgress(60);
1538 
1539     if ( !pExecGlobals->bRestartRequested )
1540     {
1541         Application::SetFilterHdl( LINK( this, Desktop, ImplInitFilterHdl ) );
1542 
1543         // Preload function depends on an initialized sfx application!
1544         SetSplashScreenProgress(75);
1545 
1546         // use system window dialogs
1547         Application::SetSystemWindowMode( SystemWindowFlags::DIALOG );
1548 
1549         SetSplashScreenProgress(80);
1550 
1551         if ( !rCmdLineArgs.IsInvisible() &&
1552              !rCmdLineArgs.IsNoQuickstart() )
1553             InitializeQuickstartMode( xContext );
1554 
1555         if ( xDesktop.is() )
1556             xDesktop->addTerminateListener( new RequestHandlerController );
1557         SetSplashScreenProgress(100);
1558 
1559         // FIXME: move this somewhere sensible.
1560 #if HAVE_FEATURE_OPENCL
1561         CheckOpenCLCompute(xDesktop);
1562 #endif
1563 
1564         //Running the VCL graphics rendering tests
1565         runGraphicsRenderTests();
1566 
1567         // Reap the process started by fire_glxtest_process().
1568         reap_glxtest_process();
1569 
1570         // Release solar mutex just before we wait for our client to connect
1571         {
1572             SolarMutexReleaser aReleaser;
1573 
1574             // Post user event to startup first application component window
1575             // We have to send this OpenClients message short before execute() to
1576             // minimize the risk that this message overtakes type detection construction!!
1577             Application::PostUserEvent( LINK( this, Desktop, OpenClients_Impl ) );
1578 
1579             // Post event to enable acceptors
1580             Application::PostUserEvent( LINK( this, Desktop, EnableAcceptors_Impl) );
1581 
1582             // Acquire solar mutex just before we enter our message loop
1583         }
1584 
1585         // call Application::Execute to process messages in vcl message loop
1586 #if HAVE_FEATURE_JAVA
1587         // The JavaContext contains an interaction handler which is used when
1588         // the creation of a Java Virtual Machine fails
1589         css::uno::ContextLayer layer2(
1590             new svt::JavaContext( css::uno::getCurrentContext() ) );
1591 #endif
1592         // check whether the shutdown is caused by restart just before entering the Execute
1593         pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
1594                 xRestartManager->isRestartRequested(true);
1595 
1596         if ( !pExecGlobals->bRestartRequested )
1597         {
1598             // if this run of the office is triggered by restart, some additional actions should be done
1599             DoRestartActionsIfNecessary( !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsNoQuickstart() );
1600 
1601             Execute();
1602         }
1603     }
1604     else
1605     {
1606         if (xDesktop.is())
1607             xDesktop->terminate();
1608     }
1609     // CAUTION: you do not necessarily get here e.g. on the Mac.
1610     // please put all deinitialization code into doShutdown
1611     return doShutdown();
1612 }
1613 
1614 int Desktop::doShutdown()
1615 {
1616     if( ! pExecGlobals )
1617         return EXIT_SUCCESS;
1618 
1619     if (m_aUpdateThread.joinable())
1620         m_aUpdateThread.join();
1621 
1622     if (pExecGlobals->xJVMloadThread.is())
1623     {
1624         pExecGlobals->xJVMloadThread->join();
1625         pExecGlobals->xJVMloadThread.clear();
1626     }
1627 
1628     pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
1629         OfficeRestartManager::get(comphelper::getProcessComponentContext())->
1630         isRestartRequested(true);
1631     if ( pExecGlobals->bRestartRequested )
1632         SetRestartState();
1633 
1634     // Restore old value
1635     const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
1636     if ( rCmdLineArgs.IsHeadless() || rCmdLineArgs.IsEventTesting() )
1637     {
1638         std::shared_ptr< comphelper::ConfigurationChanges > xChanges(
1639                 comphelper::ConfigurationChanges::create());
1640         officecfg::Office::Common::Misc::UseSystemFileDialog::set( pExecGlobals->bUseSystemFileDialog, xChanges );
1641         xChanges->commit();
1642     }
1643 
1644     OUString pidfileName = rCmdLineArgs.GetPidfileName();
1645     if ( !pidfileName.isEmpty() )
1646     {
1647         OUString pidfileURL;
1648 
1649         if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
1650         {
1651             if ( osl::File::remove( pidfileURL ) != osl::FileBase::E_None )
1652             {
1653                 SAL_WARN("desktop.app", "shutdown: cannot remove pidfile " << pidfileURL);
1654             }
1655         }
1656         else
1657         {
1658             SAL_WARN("desktop.app", "shutdown: cannot get pidfile URL from path" << pidfileName);
1659         }
1660     }
1661 
1662     // remove temp directory
1663     RemoveTemporaryDirectory();
1664     flatpak::removeTemporaryHtmlDirectory();
1665 
1666     // flush evtl. configuration changes so that all config files in user
1667     // dir are written
1668     FlushConfiguration();
1669 
1670     if (pExecGlobals->bRestartRequested)
1671     {
1672         // tdf#128523
1673         RemoveIconCacheDirectory();
1674 
1675         // a restart is already requested, usually due to a configuration change
1676         // that needs a restart to get active. If this is the case, do not try
1677         // to use SecureUserConfig to safe this still untested new configuration
1678     }
1679     else
1680     {
1681         // Test if SecureUserConfig is active. If yes and we are at this point, regular shutdown
1682         // is in progress and the currently used configuration was working. Try to secure this
1683         // working configuration for later eventually necessary restores
1684         comphelper::BackupFileHelper aBackupFileHelper;
1685 
1686         aBackupFileHelper.tryPush();
1687         aBackupFileHelper.tryPushExtensionInfo();
1688     }
1689 
1690     // The acceptors in the AcceptorMap must be released (in DeregisterServices)
1691     // with the solar mutex unlocked, to avoid deadlock:
1692     {
1693         SolarMutexReleaser aReleaser;
1694         DeregisterServices();
1695 #if HAVE_FEATURE_SCRIPTING
1696         StarBASIC::DetachAllDocBasicItems();
1697 #endif
1698     }
1699 
1700     // be sure that path/language options gets destroyed before
1701     // UCB is deinitialized
1702     pExecGlobals->pCTLLanguageOptions.reset();
1703     pExecGlobals->pPathOptions.reset();
1704 
1705     comphelper::ThreadPool::getSharedOptimalPool().shutdown();
1706 
1707     bool bRR = pExecGlobals->bRestartRequested;
1708     delete pExecGlobals;
1709     pExecGlobals = nullptr;
1710 
1711     if ( bRR )
1712     {
1713         restartOnMac(true);
1714         if ( m_rSplashScreen.is() )
1715             m_rSplashScreen->reset();
1716 
1717         return EXITHELPER_NORMAL_RESTART;
1718     }
1719     return EXIT_SUCCESS;
1720 }
1721 
1722 IMPL_STATIC_LINK( Desktop, ImplInitFilterHdl, ::ConvertData&, rData, bool )
1723 {
1724     return GraphicFilter::GetGraphicFilter().GetFilterCallback().Call( rData );
1725 }
1726 
1727 bool Desktop::InitializeConfiguration()
1728 {
1729     try
1730     {
1731         css::configuration::theDefaultProvider::get(
1732             comphelper::getProcessComponentContext() );
1733         return true;
1734     }
1735     catch( css::lang::ServiceNotRegisteredException & e )
1736     {
1737         HandleBootstrapErrors(
1738             Desktop::BE_UNO_SERVICE_CONFIG_MISSING, e.Message );
1739     }
1740     catch( const css::configuration::MissingBootstrapFileException& e )
1741     {
1742         OUString aMsg( CreateErrorMsgString( utl::Bootstrap::MISSING_BOOTSTRAP_FILE,
1743                                                 e.BootstrapFileURL ));
1744         HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_USER_INSTALL, aMsg );
1745     }
1746     catch( const css::configuration::InvalidBootstrapFileException& e )
1747     {
1748         OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY,
1749                                                 e.BootstrapFileURL ));
1750         HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
1751     }
1752     catch( const css::configuration::InstallationIncompleteException& )
1753     {
1754         OUString aVersionFileURL;
1755         OUString aMsg;
1756         utl::Bootstrap::PathStatus aPathStatus = utl::Bootstrap::locateVersionFile( aVersionFileURL );
1757         if ( aPathStatus == utl::Bootstrap::PATH_EXISTS )
1758             aMsg = CreateErrorMsgString( utl::Bootstrap::MISSING_VERSION_FILE_ENTRY, aVersionFileURL );
1759         else
1760             aMsg = CreateErrorMsgString( utl::Bootstrap::MISSING_VERSION_FILE, aVersionFileURL );
1761 
1762         HandleBootstrapPathErrors( ::utl::Bootstrap::MISSING_USER_INSTALL, aMsg );
1763     }
1764     catch ( const css::configuration::backend::BackendAccessException& exception)
1765     {
1766         // [cm122549] It is assumed in this case that the message
1767         // coming from InitConfiguration (in fact CreateApplicationConf...)
1768         // is suitable for display directly.
1769         FatalError( MakeStartupErrorMessage( exception.Message ) );
1770     }
1771     catch ( const css::configuration::backend::BackendSetupException& exception)
1772     {
1773         // [cm122549] It is assumed in this case that the message
1774         // coming from InitConfiguration (in fact CreateApplicationConf...)
1775         // is suitable for display directly.
1776         FatalError( MakeStartupErrorMessage( exception.Message ) );
1777     }
1778     catch ( const css::configuration::CannotLoadConfigurationException& )
1779     {
1780         OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_DATA,
1781                                                 OUString() ));
1782         HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
1783     }
1784     catch( const css::uno::Exception& )
1785     {
1786         OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_DATA,
1787                                                 OUString() ));
1788         HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
1789     }
1790     return false;
1791 }
1792 
1793 void Desktop::FlushConfiguration()
1794 {
1795     css::uno::Reference< css::util::XFlushable >(
1796         css::configuration::theDefaultProvider::get(
1797             comphelper::getProcessComponentContext()),
1798         css::uno::UNO_QUERY_THROW)->flush();
1799 }
1800 
1801 bool Desktop::InitializeQuickstartMode( const Reference< XComponentContext >& rxContext )
1802 {
1803     try
1804     {
1805         // the shutdown icon sits in the systray and allows the user to keep
1806         // the office instance running for quicker restart
1807         // this will only be activated if --quickstart was specified on cmdline
1808 
1809         bool bQuickstart = shouldLaunchQuickstart();
1810 
1811         // Try to instantiate quickstart service. This service is not mandatory, so
1812         // do nothing if service is not available
1813 
1814         // #i105753# the following if was invented for performance
1815         // unfortunately this broke the Mac behavior which is to always run
1816         // in quickstart mode since Mac applications do not usually quit
1817         // when the last document closes.
1818         // Note that this claim that on macOS we "always run in quickstart mode"
1819         // has nothing to do with (quick) *starting* (i.e. starting automatically
1820         // when the user logs in), though, but with not quitting when no documents
1821         // are open.
1822         #ifndef MACOSX
1823         if ( bQuickstart )
1824         #endif
1825         {
1826             css::office::Quickstart::createStart(rxContext, bQuickstart);
1827         }
1828         return true;
1829     }
1830     catch( const css::uno::Exception& )
1831     {
1832         return false;
1833     }
1834 }
1835 
1836 void Desktop::OverrideSystemSettings( AllSettings& rSettings )
1837 {
1838     if ( !SvtTabAppearanceCfg::IsInitialized () )
1839         return;
1840 
1841     StyleSettings hStyleSettings   = rSettings.GetStyleSettings();
1842     MouseSettings hMouseSettings = rSettings.GetMouseSettings();
1843 
1844     DragFullOptions nDragFullOptions = hStyleSettings.GetDragFullOptions();
1845 
1846     SvtTabAppearanceCfg aAppearanceCfg;
1847     DragMode nDragMode = aAppearanceCfg.GetDragMode();
1848     switch ( nDragMode )
1849     {
1850     case DragMode::FullWindow:
1851         nDragFullOptions |= DragFullOptions::All;
1852         break;
1853     case DragMode::Frame:
1854         nDragFullOptions &= ~DragFullOptions::All;
1855         break;
1856     case DragMode::SystemDep:
1857     default:
1858         break;
1859     }
1860 
1861     MouseFollowFlags nFollow = hMouseSettings.GetFollow();
1862     hMouseSettings.SetFollow( aAppearanceCfg.IsMenuMouseFollow() ? (nFollow|MouseFollowFlags::Menu) : (nFollow&~MouseFollowFlags::Menu));
1863     rSettings.SetMouseSettings(hMouseSettings);
1864 
1865     bool bMenuIcons = officecfg::Office::Common::View::Menu::ShowIconsInMenues::get();
1866     bool bSystemMenuIcons = officecfg::Office::Common::View::Menu::IsSystemIconsInMenus::get();
1867     TriState eMenuIcons = bSystemMenuIcons ? TRISTATE_INDET : static_cast<TriState>(bMenuIcons);
1868     hStyleSettings.SetUseImagesInMenus(eMenuIcons);
1869     hStyleSettings.SetContextMenuShortcuts(static_cast<TriState>(officecfg::Office::Common::View::Menu::ShortcutsInContextMenus::get()));
1870     hStyleSettings.SetDragFullOptions( nDragFullOptions );
1871     rSettings.SetStyleSettings ( hStyleSettings );
1872 }
1873 
1874 namespace {
1875 
1876 class ExitTimer : public Timer
1877 {
1878   public:
1879     ExitTimer() : Timer("desktop ExitTimer")
1880     {
1881         SetTimeout(500);
1882         Start();
1883     }
1884     virtual void Invoke() override
1885     {
1886         _exit(42);
1887     }
1888 };
1889 
1890 }
1891 
1892 IMPL_LINK_NOARG(Desktop, OpenClients_Impl, void*, void)
1893 {
1894     // #i114963#
1895     // Enable IPC thread before OpenClients
1896     //
1897     // This is because it is possible for another client to connect during the OpenClients() call.
1898     // This can happen on Windows when document is printed (not opened) and another client wants to print (when printing multiple documents).
1899     // If the IPC thread is enabled after OpenClients, then the client will not be processed because the application will exit after printing. i.e RequestHandler::AreRequestsPending() will always return false
1900     //
1901     // ALSO:
1902     //
1903     // Multiple clients may request simultaneous connections.
1904     // When this server closes down it attempts to recreate the pipe (in RequestHandler::Disable()).
1905     // It's possible that the client has a pending connection request.
1906     // When the IPC thread is not running, this connection locks (because maPipe.accept()) is never called
1907     RequestHandler::SetReady(true);
1908     OpenClients();
1909 
1910     CloseSplashScreen();
1911     CheckFirstRun( );
1912 #ifdef _WIN32
1913     bool bDontShowDialogs
1914         = Application::IsHeadlessModeEnabled(); // uitest.uicheck fails when the dialog is open
1915     for (sal_uInt16 i = 0; !bDontShowDialogs && i < Application::GetCommandLineParamCount(); i++)
1916     {
1917         if (Application::GetCommandLineParam(i) == "--nologo")
1918             bDontShowDialogs = true;
1919     }
1920     if (!bDontShowDialogs)
1921         vcl::fileregistration::CheckFileExtRegistration(SfxGetpApp()->GetTopWindow());
1922     // Registers a COM class factory of the service manager with the windows operating system.
1923     Reference< XMultiServiceFactory > xSMgr=  comphelper::getProcessServiceFactory();
1924     xSMgr->createInstance("com.sun.star.bridge.OleApplicationRegistration");
1925     xSMgr->createInstance("com.sun.star.comp.ole.EmbedServer");
1926 #endif
1927     const char *pExitPostStartup = getenv ("OOO_EXIT_POST_STARTUP");
1928     if (pExitPostStartup && *pExitPostStartup)
1929         new ExitTimer();
1930 }
1931 
1932 void Desktop::OpenClients()
1933 {
1934 
1935     const CommandLineArgs& rArgs = GetCommandLineArgs();
1936 
1937     if (!rArgs.IsQuickstart())
1938     {
1939         OUString aHelpModule;
1940         if (rArgs.IsHelpWriter()) {
1941             aHelpModule = "swriter";
1942         } else if (rArgs.IsHelpCalc()) {
1943             aHelpModule = "scalc";
1944         } else if (rArgs.IsHelpDraw()) {
1945             aHelpModule = "sdraw";
1946         } else if (rArgs.IsHelpImpress()) {
1947             aHelpModule = "simpress";
1948         } else if (rArgs.IsHelpBase()) {
1949             aHelpModule = "sdatabase";
1950         } else if (rArgs.IsHelpBasic()) {
1951             aHelpModule = "sbasic";
1952         } else if (rArgs.IsHelpMath()) {
1953             aHelpModule = "smath";
1954         }
1955         if (!aHelpModule.isEmpty()) {
1956             OUString aHelpURL = "vnd.sun.star.help://"
1957                               + aHelpModule
1958                               + "/start?Language="
1959                               + utl::ConfigManager::getUILocale();
1960 #if defined UNX
1961             aHelpURL += "&System=UNX";
1962 #elif defined _WIN32
1963             aHelpURL += "&System=WIN";
1964 #endif
1965             Application::GetHelp()->Start(aHelpURL);
1966             return;
1967         }
1968     }
1969 
1970     // Disable AutoSave feature in case "--norestore" or a similar command line switch is set on the command line.
1971     // The reason behind: AutoSave/EmergencySave/AutoRecovery share the same data.
1972     // But the require that all documents, which are saved as backup should exists inside
1973     // memory. May be this mechanism will be inconsistent if the configuration exists...
1974     // but no document inside memory corresponds to this data.
1975     // Further it's not acceptable to recover such documents without any UI. It can
1976     // need some time, where the user won't see any results and wait for finishing the office startup...
1977     bool bAllowRecoveryAndSessionManagement = ( !rArgs.IsNoRestore() ) && ( !rArgs.IsHeadless()  );
1978 
1979 #if !defined ANDROID
1980     // Enter safe mode if requested
1981     if (Application::IsSafeModeEnabled()) {
1982         handleSafeMode();
1983     }
1984 #endif
1985 
1986 #if HAVE_FEATURE_BREAKPAD
1987     if (officecfg::Office::Common::Misc::CrashReport::get() && CrashReporter::crashReportInfoExists())
1988         handleCrashReport();
1989 #endif
1990 
1991     if ( ! bAllowRecoveryAndSessionManagement )
1992     {
1993         try
1994         {
1995             Reference< XDispatch > xRecovery = css::frame::theAutoRecovery::get( ::comphelper::getProcessComponentContext() );
1996             Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
1997 
1998             css::util::URL aCmd;
1999             aCmd.Complete = "vnd.sun.star.autorecovery:/disableRecovery";
2000             xParser->parseStrict(aCmd);
2001 
2002             xRecovery->dispatch(aCmd, css::uno::Sequence< css::beans::PropertyValue >());
2003         }
2004         catch(const css::uno::Exception&)
2005         {
2006             TOOLS_WARN_EXCEPTION( "desktop.app", "Could not disable AutoRecovery.");
2007         }
2008     }
2009     else
2010     {
2011         bool bCrashed            = false;
2012         bool bExistsRecoveryData = false;
2013         bool bExistsSessionData  = false;
2014         bool const bDisableRecovery
2015             = getenv("OOO_DISABLE_RECOVERY") != nullptr
2016               || !officecfg::Office::Recovery::RecoveryInfo::Enabled::get();
2017 
2018         impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
2019 
2020         if ( !bDisableRecovery &&
2021             (
2022                 bExistsRecoveryData || // => crash with files    => recovery
2023                 bCrashed               // => crash without files => error report
2024             )
2025            )
2026         {
2027             try
2028             {
2029                 impl_callRecoveryUI(
2030                     false          , // false => force recovery instead of emergency save
2031                     bExistsRecoveryData);
2032             }
2033             catch(const css::uno::Exception&)
2034             {
2035                 TOOLS_WARN_EXCEPTION( "desktop.app", "Error during recovery");
2036             }
2037         }
2038 
2039         Reference< XSessionManagerListener2 > xSessionListener;
2040         try
2041         {
2042             // specifies whether the UI-interaction on Session shutdown is allowed
2043             bool bUIOnSessionShutdownAllowed = officecfg::Office::Recovery::SessionShutdown::DocumentStoreUIEnabled::get();
2044             xSessionListener = SessionListener::createWithOnQuitFlag(
2045                     ::comphelper::getProcessComponentContext(), bUIOnSessionShutdownAllowed);
2046         }
2047         catch(const css::uno::Exception&)
2048         {
2049             TOOLS_WARN_EXCEPTION( "desktop.app", "Registration of session listener failed");
2050         }
2051 
2052         if ( !bExistsRecoveryData && xSessionListener.is() )
2053         {
2054             // session management
2055             try
2056             {
2057                 xSessionListener->doRestore();
2058             }
2059             catch(const css::uno::Exception&)
2060             {
2061                 TOOLS_WARN_EXCEPTION( "desktop.app", "Error in session management");
2062             }
2063         }
2064     }
2065 
2066     // write this information here to avoid depending on vcl in the crash reporter lib
2067     CrashReporter::addKeyValue("Language", Application::GetSettings().GetLanguageTag().getBcp47(), CrashReporter::Create);
2068 
2069     RequestHandler::EnableRequests();
2070 
2071     ProcessDocumentsRequest aRequest(rArgs.getCwdUrl());
2072     aRequest.aOpenList = rArgs.GetOpenList();
2073     aRequest.aViewList = rArgs.GetViewList();
2074     aRequest.aStartList = rArgs.GetStartList();
2075     aRequest.aPrintList = rArgs.GetPrintList();
2076     aRequest.aPrintToList = rArgs.GetPrintToList();
2077     aRequest.aPrinterName = rArgs.GetPrinterName();
2078     aRequest.aForceOpenList = rArgs.GetForceOpenList();
2079     aRequest.aForceNewList = rArgs.GetForceNewList();
2080     aRequest.aConversionList = rArgs.GetConversionList();
2081     aRequest.aConversionParams = rArgs.GetConversionParams();
2082     aRequest.aConversionOut = rArgs.GetConversionOut();
2083     aRequest.aImageConversionType = rArgs.GetImageConversionType();
2084     aRequest.aInFilter = rArgs.GetInFilter();
2085     aRequest.bTextCat = rArgs.IsTextCat();
2086     aRequest.bScriptCat = rArgs.IsScriptCat();
2087 
2088     if ( !aRequest.aOpenList.empty() ||
2089          !aRequest.aViewList.empty() ||
2090          !aRequest.aStartList.empty() ||
2091          !aRequest.aPrintList.empty() ||
2092          !aRequest.aForceOpenList.empty() ||
2093          !aRequest.aForceNewList.empty() ||
2094          ( !aRequest.aPrintToList.empty() && !aRequest.aPrinterName.isEmpty() ) ||
2095          !aRequest.aConversionList.empty() )
2096     {
2097         if ( rArgs.HasModuleParam() )
2098         {
2099             SvtModuleOptions    aOpt;
2100 
2101             // Support command line parameters to start a module (as preselection)
2102             if ( rArgs.IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2103                 aRequest.aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
2104             else if ( rArgs.IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
2105                 aRequest.aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
2106             else if ( rArgs.IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
2107                 aRequest.aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
2108             else if ( rArgs.IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
2109                 aRequest.aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
2110         }
2111 
2112         // check for printing disabled
2113         if( ( !(aRequest.aPrintList.empty() && aRequest.aPrintToList.empty()) )
2114             && Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
2115         {
2116             aRequest.aPrintList.clear();
2117             aRequest.aPrintToList.clear();
2118             std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
2119                                                       VclMessageType::Warning, VclButtonsType::Ok,
2120                                                       DpResId(STR_ERR_PRINTDISABLED)));
2121             xBox->run();
2122         }
2123 
2124         // Process request
2125         if ( RequestHandler::ExecuteCmdLineRequests(aRequest, false) )
2126         {
2127             // Don't do anything if we have successfully called terminate at desktop:
2128             return;
2129         }
2130     }
2131 
2132     // no default document if a document was loaded by recovery or by command line or if soffice is used as server
2133     Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
2134     Reference< XElementAccess > xList( xDesktop->getFrames(), UNO_QUERY_THROW );
2135     if ( xList->hasElements() )
2136         return;
2137 
2138     if ( rArgs.IsQuickstart() || rArgs.IsInvisible() || Application::AnyInput( VclInputFlags::APPEVENT ) )
2139         // soffice was started as tray icon ...
2140         return;
2141 
2142     OpenDefault();
2143 }
2144 
2145 void Desktop::OpenDefault()
2146 {
2147     OUString        aName;
2148     SvtModuleOptions    aOpt;
2149 
2150     const CommandLineArgs& rArgs = GetCommandLineArgs();
2151     if ( rArgs.IsNoDefault() ) return;
2152     if ( rArgs.HasModuleParam() )
2153     {
2154         // Support new command line parameters to start a module
2155         if ( rArgs.IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2156             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER );
2157         else if ( rArgs.IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
2158             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC );
2159         else if ( rArgs.IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
2160             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS );
2161         else if ( rArgs.IsBase() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
2162             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DATABASE );
2163         else if ( rArgs.IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
2164             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW );
2165         else if ( rArgs.IsMath() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::MATH ) )
2166             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::MATH );
2167         else if ( rArgs.IsGlobal() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2168             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITERGLOBAL );
2169         else if ( rArgs.IsWeb() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2170             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITERWEB );
2171     }
2172 
2173     if ( aName.isEmpty() )
2174     {
2175         if (aOpt.IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE))
2176         {
2177             ShowBackingComponent(nullptr);
2178             return;
2179         }
2180 
2181         // Old way to create a default document
2182         if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2183             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER );
2184         else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
2185             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC );
2186         else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
2187             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS );
2188         else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
2189             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DATABASE );
2190         else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
2191             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW );
2192         else
2193             return;
2194     }
2195 
2196     ProcessDocumentsRequest aRequest(rArgs.getCwdUrl());
2197     aRequest.aOpenList.push_back(aName);
2198     RequestHandler::ExecuteCmdLineRequests(aRequest, false);
2199 }
2200 
2201 
2202 OUString GetURL_Impl(
2203     const OUString& rName, std::optional< OUString > const & cwdUrl )
2204 {
2205     // if rName is a vnd.sun.star.script URL do not attempt to parse it
2206     // as INetURLObj does not handle URLs there
2207     if (rName.startsWith("vnd.sun.star.script"))
2208     {
2209         return rName;
2210     }
2211 
2212     // don't touch file urls, those should already be in internal form
2213     // they won't get better here (#112849#)
2214     if (comphelper::isFileUrl(rName))
2215     {
2216         return rName;
2217     }
2218 
2219     if ( rName.startsWith("service:"))
2220     {
2221         return rName;
2222     }
2223 
2224     // Add path separator to these directory and make given URL (rName) absolute by using of current working directory
2225     // Attention: "setFinalSlash()" is necessary for calling "smartRel2Abs()"!!!
2226     // Otherwise last part will be ignored and wrong result will be returned!!!
2227     // "smartRel2Abs()" interpret given URL as file not as path. So he truncate last element to get the base path ...
2228     // But if we add a separator - he doesn't do it anymore.
2229     INetURLObject aObj;
2230     if (cwdUrl) {
2231         aObj.SetURL(*cwdUrl);
2232         aObj.setFinalSlash();
2233     }
2234 
2235     // Use the provided parameters for smartRel2Abs to support the usage of '%' in system paths.
2236     // Otherwise this char won't get encoded and we are not able to load such files later,
2237     bool bWasAbsolute;
2238     INetURLObject aURL     = aObj.smartRel2Abs( rName, bWasAbsolute, false, INetURLObject::EncodeMechanism::WasEncoded,
2239                                                 RTL_TEXTENCODING_UTF8, true );
2240     OUString      aFileURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2241 
2242     ::osl::FileStatus aStatus( osl_FileStatus_Mask_FileURL );
2243     ::osl::DirectoryItem aItem;
2244     if( ::osl::FileBase::E_None == ::osl::DirectoryItem::get( aFileURL, aItem ) &&
2245         ::osl::FileBase::E_None == aItem.getFileStatus( aStatus ) )
2246             aFileURL = aStatus.getFileURL();
2247 
2248     return aFileURL;
2249 }
2250 
2251 void Desktop::HandleAppEvent( const ApplicationEvent& rAppEvent )
2252 {
2253     switch ( rAppEvent.GetEvent() )
2254     {
2255     case ApplicationEvent::Type::Accept:
2256         // every time an accept parameter is used we create an acceptor
2257         // with the corresponding accept-string
2258         createAcceptor(rAppEvent.GetStringData());
2259         break;
2260     case ApplicationEvent::Type::Appear:
2261         if ( !GetCommandLineArgs().IsInvisible() && !impl_bringToFrontRecoveryUI() )
2262         {
2263             Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2264 
2265             // find active task - the active task is always a visible task
2266             Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
2267             Reference< css::frame::XFrame > xTask = xDesktop->getActiveFrame();
2268             if ( !xTask.is() )
2269             {
2270                 // get any task if there is no active one
2271                 Reference< css::container::XIndexAccess > xList = xDesktop->getFrames();
2272                 if ( xList->getCount() > 0 )
2273                     xList->getByIndex(0) >>= xTask;
2274             }
2275 
2276             if ( xTask.is() )
2277             {
2278                 Reference< css::awt::XTopWindow > xTop( xTask->getContainerWindow(), UNO_QUERY );
2279                 xTop->toFront();
2280             }
2281             else
2282             {
2283                 // no visible task that could be activated found
2284                 Reference< css::awt::XWindow > xContainerWindow;
2285                 Reference< XFrame > xBackingFrame = xDesktop->findFrame( "_blank", 0);
2286                 if (xBackingFrame.is())
2287                     xContainerWindow = xBackingFrame->getContainerWindow();
2288                 if (xContainerWindow.is())
2289                 {
2290                     Reference< XController > xStartModule = StartModule::createWithParentWindow(xContext, xContainerWindow);
2291                     Reference< css::awt::XWindow > xBackingWin(xStartModule, UNO_QUERY);
2292                     // Attention: You MUST(!) call setComponent() before you call attachFrame().
2293                     // Because the backing component set the property "IsBackingMode" of the frame
2294                     // to true inside attachFrame(). But setComponent() reset this state every time ...
2295                     xBackingFrame->setComponent(xBackingWin, xStartModule);
2296                     xStartModule->attachFrame(xBackingFrame);
2297                     xContainerWindow->setVisible(true);
2298 
2299                     VclPtr<vcl::Window> pCompWindow = VCLUnoHelper::GetWindow(xBackingFrame->getComponentWindow());
2300                     if (pCompWindow)
2301                         pCompWindow->PaintImmediately();
2302                 }
2303             }
2304         }
2305         break;
2306     case ApplicationEvent::Type::Open:
2307         {
2308             const CommandLineArgs& rCmdLine = GetCommandLineArgs();
2309             if ( !rCmdLine.IsInvisible() && !rCmdLine.IsTerminateAfterInit() )
2310             {
2311                 ProcessDocumentsRequest docsRequest(rCmdLine.getCwdUrl());
2312                 std::vector<OUString> const & data(rAppEvent.GetStringsData());
2313                 docsRequest.aOpenList.insert(
2314                     docsRequest.aOpenList.end(), data.begin(), data.end());
2315                 RequestHandler::ExecuteCmdLineRequests(docsRequest, false);
2316             }
2317         }
2318         break;
2319     case ApplicationEvent::Type::OpenHelpUrl:
2320         // start help for a specific URL
2321         Application::GetHelp()->Start(rAppEvent.GetStringData());
2322         break;
2323     case ApplicationEvent::Type::Print:
2324         {
2325             const CommandLineArgs& rCmdLine = GetCommandLineArgs();
2326             if ( !rCmdLine.IsInvisible() && !rCmdLine.IsTerminateAfterInit() )
2327             {
2328                 ProcessDocumentsRequest docsRequest(rCmdLine.getCwdUrl());
2329                 std::vector<OUString> const & data(rAppEvent.GetStringsData());
2330                 docsRequest.aPrintList.insert(
2331                     docsRequest.aPrintList.end(), data.begin(), data.end());
2332                 RequestHandler::ExecuteCmdLineRequests(docsRequest, false);
2333             }
2334         }
2335         break;
2336     case ApplicationEvent::Type::PrivateDoShutdown:
2337         {
2338             Desktop* pD = dynamic_cast<Desktop*>(GetpApp());
2339             OSL_ENSURE( pD, "no desktop ?!?" );
2340             if( pD )
2341                 pD->doShutdown();
2342         }
2343         break;
2344     case ApplicationEvent::Type::QuickStart:
2345         if ( !GetCommandLineArgs().IsInvisible()  )
2346         {
2347             // If the office has been started the second time its command line arguments are sent through a pipe
2348             // connection to the first office. We want to reuse the quickstart option for the first office.
2349             // NOTICE: The quickstart service must be initialized inside the "main thread", so we use the
2350             // application events to do this (they are executed inside main thread)!!!
2351             // Don't start quickstart service if the user specified "--invisible" on the command line!
2352             Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2353             css::office::Quickstart::createStart(xContext, true/*Quickstart*/);
2354         }
2355         break;
2356     case ApplicationEvent::Type::ShowDialog:
2357         // This is only used on macOS, and only for About or Preferences.
2358         // Ignore all errors here. It's clicking a menu entry only ...
2359         // The user will try it again, in case nothing happens .-)
2360         try
2361         {
2362             Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2363 
2364             Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
2365 
2366             Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create(xContext);
2367             css::util::URL aCommand;
2368             if( rAppEvent.GetStringData() == "PREFERENCES" )
2369                 aCommand.Complete = ".uno:OptionsTreeDialog";
2370             else if( rAppEvent.GetStringData() == "ABOUT" )
2371                 aCommand.Complete = ".uno:About";
2372             if( !aCommand.Complete.isEmpty() )
2373             {
2374                 xParser->parseStrict(aCommand);
2375 
2376                 css::uno::Reference< css::frame::XDispatch > xDispatch = xDesktop->queryDispatch(aCommand, OUString(), 0);
2377                 if (xDispatch.is())
2378                     xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >());
2379             }
2380         }
2381         catch(const css::uno::Exception&)
2382         {
2383             TOOLS_WARN_EXCEPTION("desktop.app", "exception thrown by dialog");
2384         }
2385         break;
2386     case ApplicationEvent::Type::Unaccept:
2387         // try to remove corresponding acceptor
2388         destroyAcceptor(rAppEvent.GetStringData());
2389         break;
2390     default:
2391         SAL_WARN( "desktop.app", "this cannot happen");
2392         break;
2393     }
2394 }
2395 
2396 void Desktop::OpenSplashScreen()
2397 {
2398     const CommandLineArgs &rCmdLine = GetCommandLineArgs();
2399     // Show intro only if this is normal start (e.g. no server, no quickstart, no printing )
2400     if ( !(!rCmdLine.IsInvisible() &&
2401          !rCmdLine.IsHeadless() &&
2402          !rCmdLine.IsQuickstart() &&
2403          !rCmdLine.IsMinimized() &&
2404          !rCmdLine.IsNoLogo() &&
2405          !rCmdLine.IsTerminateAfterInit() &&
2406          rCmdLine.GetPrintList().empty() &&
2407          rCmdLine.GetPrintToList().empty() &&
2408          rCmdLine.GetConversionList().empty()) )
2409         return;
2410 
2411     // Determine application name from command line parameters
2412     OUString aAppName;
2413     if ( rCmdLine.IsWriter() )
2414         aAppName = "writer";
2415     else if ( rCmdLine.IsCalc() )
2416         aAppName = "calc";
2417     else if ( rCmdLine.IsDraw() )
2418         aAppName = "draw";
2419     else if ( rCmdLine.IsImpress() )
2420         aAppName = "impress";
2421     else if ( rCmdLine.IsBase() )
2422         aAppName = "base";
2423     else if ( rCmdLine.IsGlobal() )
2424         aAppName = "global";
2425     else if ( rCmdLine.IsMath() )
2426         aAppName = "math";
2427     else if ( rCmdLine.IsWeb() )
2428         aAppName = "web";
2429 
2430     // Which splash to use
2431     OUString aSplashService( "com.sun.star.office.SplashScreen" );
2432     if ( rCmdLine.HasSplashPipe() )
2433         aSplashService = "com.sun.star.office.PipeSplashScreen";
2434 
2435     Sequence< Any > aSeq{ Any(true) /* bVisible */, Any(aAppName) };
2436     css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2437     m_rSplashScreen.set(
2438         xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aSplashService, aSeq, xContext),
2439         UNO_QUERY);
2440 
2441     if(m_rSplashScreen.is())
2442             m_rSplashScreen->start("SplashScreen", 100);
2443 
2444 }
2445 
2446 void Desktop::SetSplashScreenProgress(sal_Int32 iProgress)
2447 {
2448     if(m_rSplashScreen.is())
2449     {
2450         m_rSplashScreen->setValue(iProgress);
2451     }
2452 }
2453 
2454 void Desktop::SetSplashScreenText( const OUString& rText )
2455 {
2456     if( m_rSplashScreen.is() )
2457     {
2458         m_rSplashScreen->setText( rText );
2459     }
2460 }
2461 
2462 void Desktop::CloseSplashScreen()
2463 {
2464     if(m_rSplashScreen.is())
2465     {
2466         SolarMutexGuard ensureSolarMutex;
2467         m_rSplashScreen->end();
2468         m_rSplashScreen = nullptr;
2469     }
2470 }
2471 
2472 
2473 IMPL_STATIC_LINK_NOARG(Desktop, AsyncInitFirstRun, Timer *, void)
2474 {
2475     // does initializations which are necessary for the first run of the office
2476     try
2477     {
2478         Reference< XJobExecutor > xExecutor = theJobExecutor::get( ::comphelper::getProcessComponentContext() );
2479         xExecutor->trigger( "onFirstRunInitialization" );
2480     }
2481     catch(const css::uno::Exception&)
2482     {
2483         TOOLS_WARN_EXCEPTION( "desktop.app", "Desktop::DoFirstRunInitializations: caught an exception while trigger job executor" );
2484     }
2485 }
2486 
2487 void Desktop::ShowBackingComponent(Desktop * progress)
2488 {
2489     if (GetCommandLineArgs().IsNoDefault())
2490     {
2491         return;
2492     }
2493     Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
2494     Reference< XDesktop2 > xDesktop = css::frame::Desktop::create(xContext);
2495     if (progress != nullptr)
2496     {
2497         progress->SetSplashScreenProgress(60);
2498     }
2499     Reference< XFrame > xBackingFrame = xDesktop->findFrame( "_blank", 0);
2500     Reference< css::awt::XWindow > xContainerWindow;
2501 
2502     if (xBackingFrame.is())
2503         xContainerWindow = xBackingFrame->getContainerWindow();
2504     if (!xContainerWindow.is())
2505         return;
2506 
2507     // set the WindowExtendedStyle::Document style. Normally, this is done by the TaskCreator service when a "_blank"
2508     // frame/window is created. Since we do not use the TaskCreator here, we need to mimic its behavior,
2509     // otherwise documents loaded into this frame will later on miss functionality depending on the style.
2510     VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow );
2511     SAL_WARN_IF( !pContainerWindow, "desktop.app", "Desktop::Main: no implementation access to the frame's container window!" );
2512     pContainerWindow->SetExtendedStyle( pContainerWindow->GetExtendedStyle() | WindowExtendedStyle::Document );
2513     if (progress != nullptr)
2514     {
2515         progress->SetSplashScreenProgress(75);
2516     }
2517 
2518     Reference< XController > xStartModule = StartModule::createWithParentWindow( xContext, xContainerWindow);
2519     // Attention: You MUST(!) call setComponent() before you call attachFrame().
2520     // Because the backing component set the property "IsBackingMode" of the frame
2521     // to true inside attachFrame(). But setComponent() reset this state everytimes ...
2522     xBackingFrame->setComponent(Reference< XWindow >(xStartModule, UNO_QUERY), xStartModule);
2523     if (progress != nullptr)
2524     {
2525         progress->SetSplashScreenProgress(100);
2526     }
2527     xStartModule->attachFrame(xBackingFrame);
2528     if (progress != nullptr)
2529     {
2530         progress->CloseSplashScreen();
2531     }
2532     xContainerWindow->setVisible(true);
2533 }
2534 
2535 
2536 void Desktop::CheckFirstRun( )
2537 {
2538     if (!officecfg::Office::Common::Misc::FirstRun::get())
2539         return;
2540 
2541     // use VCL timer, which won't trigger during shutdown if the
2542     // application exits before timeout
2543     m_firstRunTimer.Start();
2544 
2545 #ifdef _WIN32
2546     // Check if Quickstarter should be started (on Windows only)
2547     OUString sRootKey = ReplaceStringHookProc("Software\\%OOOVENDOR\\%PRODUCTNAME\\%PRODUCTVERSION");
2548     WCHAR szValue[8192];
2549     DWORD nValueSize = sizeof(szValue);
2550     HKEY hKey;
2551     if (ERROR_SUCCESS == RegOpenKeyW(HKEY_LOCAL_MACHINE, o3tl::toW(sRootKey.getStr()), &hKey))
2552     {
2553         if ( ERROR_SUCCESS == RegQueryValueExW( hKey, L"RunQuickstartAtFirstStart", nullptr, nullptr, reinterpret_cast<LPBYTE>(szValue), &nValueSize ) )
2554         {
2555             css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2556             css::office::Quickstart::createAutoStart(xContext, true/*Quickstart*/, true/*bAutostart*/);
2557             RegCloseKey( hKey );
2558         }
2559     }
2560 #endif
2561 
2562     std::shared_ptr< comphelper::ConfigurationChanges > batch(
2563         comphelper::ConfigurationChanges::create());
2564     officecfg::Office::Common::Misc::FirstRun::set(false, batch);
2565     batch->commit();
2566 }
2567 
2568 }
2569 
2570 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2571