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