Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / desktop / source / deployment / gui / dp_gui_extensioncmdqueue.cxx
blobfd70b79822ca49e7c56df5158c3602d86cb0a741
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <com/sun/star/beans/NamedValue.hpp>
24 #include <com/sun/star/deployment/DependencyException.hpp>
25 #include <com/sun/star/deployment/LicenseException.hpp>
26 #include <com/sun/star/deployment/VersionException.hpp>
27 #include <com/sun/star/deployment/InstallException.hpp>
28 #include <com/sun/star/deployment/PlatformException.hpp>
30 #include <com/sun/star/deployment/ui/LicenseDialog.hpp>
31 #include <com/sun/star/deployment/DeploymentException.hpp>
32 #include <com/sun/star/deployment/XPackage.hpp>
34 #include <com/sun/star/task/InteractionHandler.hpp>
35 #include <com/sun/star/task/XAbortChannel.hpp>
36 #include <com/sun/star/task/XInteractionAbort.hpp>
37 #include <com/sun/star/task/XInteractionApprove.hpp>
39 #include <com/sun/star/ucb/CommandAbortedException.hpp>
40 #include <com/sun/star/ucb/CommandFailedException.hpp>
41 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
43 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
45 #include <com/sun/star/uno/Reference.hxx>
46 #include <com/sun/star/uno/Sequence.hxx>
47 #include <com/sun/star/uno/TypeClass.hpp>
48 #include <o3tl/any.hxx>
49 #include <osl/diagnose.h>
50 #include <rtl/ref.hxx>
51 #include <rtl/ustring.hxx>
52 #include <sal/types.h>
53 #include <salhelper/thread.hxx>
54 #include <ucbhelper/content.hxx>
55 #include <cppuhelper/exc_hlp.hxx>
56 #include <cppuhelper/implbase.hxx>
57 #include <comphelper/anytostring.hxx>
58 #include <utility>
59 #include <vcl/svapp.hxx>
60 #include <vcl/weld.hxx>
62 #include "dp_gui_extensioncmdqueue.hxx"
63 #include "dp_gui_dependencydialog.hxx"
64 #include "dp_gui_dialog2.hxx"
65 #include <dp_shared.hxx>
66 #include <strings.hrc>
67 #include "dp_gui_theextmgr.hxx"
68 #include "dp_gui_updatedialog.hxx"
69 #include "dp_gui_updateinstalldialog.hxx"
70 #include <dp_dependencies.hxx>
71 #include <dp_misc.h>
72 #include <dp_identifier.hxx>
73 #include <dp_version.hxx>
75 #include <condition_variable>
76 #include <queue>
77 #include <memory>
78 #include <mutex>
80 #ifdef _WIN32
81 #include <o3tl/safeCoInitUninit.hxx>
82 #endif
85 using namespace ::com::sun::star;
87 namespace {
89 OUString getVersion( OUString const & sVersion )
91 return ( sVersion.isEmpty() ) ? OUString( "0" ) : sVersion;
94 OUString getVersion( const uno::Reference< deployment::XPackage > &rPackage )
96 return getVersion( rPackage->getVersion());
101 namespace dp_gui {
103 namespace {
105 class ProgressCmdEnv
106 : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment,
107 task::XInteractionHandler,
108 ucb::XProgressHandler >
110 uno::Reference< task::XInteractionHandler2> m_xHandler;
111 uno::Reference< uno::XComponentContext > m_xContext;
113 DialogHelper* m_pDialogHelper;
114 OUString m_sTitle;
115 bool m_bWarnUser;
116 sal_Int32 m_nCurrentProgress;
118 void updateProgress();
120 /// @throws uno::RuntimeException
121 void update_( uno::Any const & Status );
123 public:
124 /** When param bAskWhenInstalling = true, then the user is asked if he
125 agrees to install this extension. In case this extension is already installed
126 then the user is also notified and asked if he wants to replace that existing
127 extension. In first case an interaction request with an InstallException
128 will be handled and in the second case a VersionException will be handled.
131 ProgressCmdEnv( uno::Reference< uno::XComponentContext > xContext,
132 DialogHelper* pDialogHelper,
133 OUString aTitle )
134 : m_xContext(std::move( xContext ))
135 , m_pDialogHelper( pDialogHelper )
136 , m_sTitle(std::move( aTitle ))
137 , m_bWarnUser( false )
138 , m_nCurrentProgress(0)
141 weld::Window* activeDialog() { return m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr; }
143 void startProgress();
144 void stopProgress();
145 void progressSection( const OUString &rText,
146 const uno::Reference< task::XAbortChannel > &xAbortChannel );
147 void setWarnUser( bool bNewVal ) { m_bWarnUser = bNewVal; }
149 // XCommandEnvironment
150 virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override;
151 virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() override;
153 // XInteractionHandler
154 virtual void SAL_CALL handle( uno::Reference< task::XInteractionRequest > const & xRequest ) override;
156 // XProgressHandler
157 virtual void SAL_CALL push( uno::Any const & Status ) override;
158 virtual void SAL_CALL update( uno::Any const & Status ) override;
159 virtual void SAL_CALL pop() override;
163 struct ExtensionCmd
165 enum E_CMD_TYPE { ADD, ENABLE, DISABLE, REMOVE, CHECK_FOR_UPDATES, ACCEPT_LICENSE };
167 E_CMD_TYPE m_eCmdType;
168 bool m_bWarnUser;
169 OUString m_sExtensionURL;
170 OUString m_sRepository;
171 uno::Reference< deployment::XPackage > m_xPackage;
172 std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
174 ExtensionCmd( const E_CMD_TYPE eCommand,
175 OUString aExtensionURL,
176 OUString aRepository,
177 const bool bWarnUser )
178 : m_eCmdType( eCommand ),
179 m_bWarnUser( bWarnUser ),
180 m_sExtensionURL(std::move( aExtensionURL )),
181 m_sRepository(std::move( aRepository )) {};
182 ExtensionCmd( const E_CMD_TYPE eCommand,
183 uno::Reference< deployment::XPackage > xPackage )
184 : m_eCmdType( eCommand ),
185 m_bWarnUser( false ),
186 m_xPackage(std::move( xPackage )) {};
187 ExtensionCmd( const E_CMD_TYPE eCommand,
188 std::vector<uno::Reference<deployment::XPackage > >&&vExtensionList )
189 : m_eCmdType( eCommand ),
190 m_bWarnUser( false ),
191 m_vExtensionList( std::move(vExtensionList) ) {};
196 typedef std::shared_ptr< ExtensionCmd > TExtensionCmd;
199 class ExtensionCmdQueue::Thread: public salhelper::Thread
201 public:
202 Thread( DialogHelper *pDialogHelper,
203 TheExtensionManager *pManager,
204 uno::Reference< uno::XComponentContext > xContext );
206 void addExtension( const OUString &rExtensionURL,
207 const OUString &rRepository,
208 const bool bWarnUser );
209 void removeExtension( const uno::Reference< deployment::XPackage > &rPackage );
210 void enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
211 const bool bEnable );
212 void checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > && vExtensionList );
213 void acceptLicense( const uno::Reference< deployment::XPackage > &rPackage );
214 void stop();
215 bool isBusy();
217 private:
218 virtual ~Thread() override;
220 virtual void execute() override;
222 void _insert(const TExtensionCmd& rExtCmd);
224 void _addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
225 const OUString &rPackageURL,
226 const OUString &rRepository,
227 const bool bWarnUser );
228 void _removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
229 const uno::Reference< deployment::XPackage > &xPackage );
230 void _enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
231 const uno::Reference< deployment::XPackage > &xPackage );
232 void _disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
233 const uno::Reference< deployment::XPackage > &xPackage );
234 void _checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > &&vExtensionList );
235 void _acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
236 const uno::Reference< deployment::XPackage > &xPackage );
238 enum Input { NONE, START, STOP };
240 uno::Reference< uno::XComponentContext > m_xContext;
241 std::queue< TExtensionCmd > m_queue;
243 DialogHelper *m_pDialogHelper;
244 TheExtensionManager *m_pManager;
246 const OUString m_sEnablingPackages;
247 const OUString m_sDisablingPackages;
248 const OUString m_sAddingPackages;
249 const OUString m_sRemovingPackages;
250 const OUString m_sDefaultCmd;
251 const OUString m_sAcceptLicense;
252 std::condition_variable m_wakeup;
253 std::mutex m_mutex;
254 Input m_eInput;
255 bool m_bStopped;
256 bool m_bWorking;
260 void ProgressCmdEnv::startProgress()
262 m_nCurrentProgress = 0;
264 if ( m_pDialogHelper )
265 m_pDialogHelper->showProgress( true );
269 void ProgressCmdEnv::stopProgress()
271 if ( m_pDialogHelper )
272 m_pDialogHelper->showProgress( false );
276 void ProgressCmdEnv::progressSection( const OUString &rText,
277 const uno::Reference< task::XAbortChannel > &xAbortChannel )
279 m_nCurrentProgress = 0;
280 if ( m_pDialogHelper )
282 m_pDialogHelper->updateProgress( rText, xAbortChannel );
283 m_pDialogHelper->updateProgress( 5 );
288 void ProgressCmdEnv::updateProgress()
290 tools::Long nProgress = ((m_nCurrentProgress*5) % 100) + 5;
291 if ( m_pDialogHelper )
292 m_pDialogHelper->updateProgress( nProgress );
295 // XCommandEnvironment
297 uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler()
299 return this;
303 uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler()
305 return this;
309 // XInteractionHandler
311 void ProgressCmdEnv::handle( uno::Reference< task::XInteractionRequest > const & xRequest )
313 uno::Any request( xRequest->getRequest() );
314 OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
315 dp_misc::TRACE( "[dp_gui_cmdenv.cxx] incoming request:\n"
316 + ::comphelper::anyToString(request) + "\n");
318 lang::WrappedTargetException wtExc;
319 deployment::DependencyException depExc;
320 deployment::LicenseException licExc;
321 deployment::VersionException verExc;
322 deployment::InstallException instExc;
323 deployment::PlatformException platExc;
325 // selections:
326 bool approve = false;
327 bool abort = false;
329 if (request >>= wtExc) {
330 // handable deployment error signalled, e.g.
331 // bundle item registration failed, notify cause only:
332 uno::Any cause;
333 deployment::DeploymentException dpExc;
334 if (wtExc.TargetException >>= dpExc)
335 cause = dpExc.Cause;
336 else {
337 ucb::CommandFailedException cfExc;
338 if (wtExc.TargetException >>= cfExc)
339 cause = cfExc.Reason;
340 else
341 cause = wtExc.TargetException;
343 update_( cause );
345 // ignore intermediate errors of legacy packages, i.e.
346 // former pkgchk behaviour:
347 const uno::Reference< deployment::XPackage > xPackage( wtExc.Context, uno::UNO_QUERY );
348 OSL_ASSERT( xPackage.is() );
349 if ( xPackage.is() )
351 const uno::Reference< deployment::XPackageTypeInfo > xPackageType( xPackage->getPackageType() );
352 OSL_ASSERT( xPackageType.is() );
353 if (xPackageType.is())
355 approve = ( xPackage->isBundle() &&
356 xPackageType->getMediaType().match(
357 "application/vnd.sun.star.legacy-package-bundle" ));
360 abort = !approve;
362 else if (request >>= depExc)
364 std::vector< OUString > deps;
365 deps.reserve(depExc.UnsatisfiedDependencies.getLength());
366 for (auto const & i : std::as_const(depExc.UnsatisfiedDependencies))
368 deps.push_back( dp_misc::Dependencies::getErrorText(i) );
371 SolarMutexGuard guard;
372 if (m_pDialogHelper)
373 m_pDialogHelper->incBusy();
374 DependencyDialog aDlg(activeDialog(), deps);
375 short n = aDlg.run();
376 if (m_pDialogHelper)
377 m_pDialogHelper->decBusy();
378 // Distinguish between closing the dialog and programmatically
379 // canceling the dialog (headless VCL):
380 approve = n == RET_OK
381 || (n == RET_CANCEL && !Application::IsDialogCancelEnabled());
384 else if (request >>= licExc)
386 SolarMutexGuard guard;
388 weld::Window *pTopLevel = activeDialog();
389 if (m_pDialogHelper)
390 m_pDialogHelper->incBusy();
391 uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
392 deployment::ui::LicenseDialog::create(
393 m_xContext, pTopLevel ? pTopLevel->GetXWindow() : nullptr,
394 licExc.ExtensionName, licExc.Text ) );
395 sal_Int16 res = xDialog->execute();
396 if (m_pDialogHelper)
397 m_pDialogHelper->decBusy();
398 if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
399 abort = true;
400 else if ( res == ui::dialogs::ExecutableDialogResults::OK )
401 approve = true;
402 else
404 OSL_ASSERT(false);
407 else if (request >>= verExc)
409 TranslateId id;
410 switch (dp_misc::compareVersions(
411 verExc.NewVersion, verExc.Deployed->getVersion() ))
413 case dp_misc::LESS:
414 id = RID_STR_WARNING_VERSION_LESS;
415 break;
416 case dp_misc::EQUAL:
417 id = RID_STR_WARNING_VERSION_EQUAL;
418 break;
419 default: // dp_misc::GREATER
420 id = RID_STR_WARNING_VERSION_GREATER;
421 break;
423 OSL_ASSERT( verExc.Deployed.is() );
424 bool bEqualNames = verExc.NewDisplayName ==
425 verExc.Deployed->getDisplayName();
427 SolarMutexGuard guard;
429 if (m_pDialogHelper)
430 m_pDialogHelper->incBusy();
432 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
433 VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(id)));
434 OUString s;
435 if (bEqualNames)
437 s = xBox->get_primary_text();
439 else if (id != RID_STR_WARNING_VERSION_EQUAL)
441 //hypothetical: requires two instances of an extension with the same
442 //version to have different display names. Probably the developer forgot
443 //to change the version.
444 s = DpResId(RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES);
446 else if (id != RID_STR_WARNING_VERSION_LESS)
448 s = DpResId(RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES);
450 else if (id != RID_STR_WARNING_VERSION_GREATER)
452 s = DpResId(RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES);
454 s = s.replaceAll("$NAME", verExc.NewDisplayName);
455 s = s.replaceAll("$OLDNAME", verExc.Deployed->getDisplayName());
456 s = s.replaceAll("$NEW", getVersion(verExc.NewVersion));
457 s = s.replaceAll("$DEPLOYED", getVersion(verExc.Deployed));
458 xBox->set_primary_text(s);
459 approve = xBox->run() == RET_OK;
460 if (m_pDialogHelper)
461 m_pDialogHelper->decBusy();
462 abort = !approve;
465 else if (request >>= instExc)
467 if ( ! m_bWarnUser )
469 approve = true;
471 else
473 if ( m_pDialogHelper )
475 SolarMutexGuard guard;
477 approve = m_pDialogHelper->installExtensionWarn( instExc.displayName );
479 else
480 approve = false;
481 abort = !approve;
484 else if (request >>= platExc)
486 SolarMutexGuard guard;
487 OUString sMsg(DpResId(RID_STR_UNSUPPORTED_PLATFORM));
488 sMsg = sMsg.replaceAll("%Name", platExc.package->getDisplayName());
489 if (m_pDialogHelper)
490 m_pDialogHelper->incBusy();
491 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
492 VclMessageType::Warning, VclButtonsType::Ok, sMsg));
493 xBox->run();
494 if (m_pDialogHelper)
495 m_pDialogHelper->decBusy();
496 approve = true;
499 if (!approve && !abort)
501 // forward to UUI handler:
502 if (! m_xHandler.is()) {
503 // late init:
504 m_xHandler = task::InteractionHandler::createWithParentAndContext(m_xContext, nullptr, m_sTitle);
506 m_xHandler->handle( xRequest );
508 else
510 // select:
511 uno::Sequence< uno::Reference< task::XInteractionContinuation > > conts(
512 xRequest->getContinuations() );
513 uno::Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
514 sal_Int32 len = conts.getLength();
515 for ( sal_Int32 pos = 0; pos < len; ++pos )
517 if (approve) {
518 uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
519 if (xInteractionApprove.is()) {
520 xInteractionApprove->select();
521 // don't query again for ongoing continuations:
522 approve = false;
525 else if (abort) {
526 uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
527 if (xInteractionAbort.is()) {
528 xInteractionAbort->select();
529 // don't query again for ongoing continuations:
530 abort = false;
538 // XProgressHandler
540 void ProgressCmdEnv::push( uno::Any const & rStatus )
542 update_( rStatus );
546 void ProgressCmdEnv::update_( uno::Any const & rStatus )
548 OUString text;
549 if ( rStatus.hasValue() && !( rStatus >>= text) )
551 if ( auto e = o3tl::tryAccess<uno::Exception>(rStatus) )
552 text = e->Message;
553 if ( text.isEmpty() )
554 text = ::comphelper::anyToString( rStatus ); // fallback
556 const SolarMutexGuard aGuard;
557 if (m_pDialogHelper)
558 m_pDialogHelper->incBusy();
559 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
560 VclMessageType::Warning, VclButtonsType::Ok, text));
561 xBox->run();
562 if (m_pDialogHelper)
563 m_pDialogHelper->decBusy();
565 ++m_nCurrentProgress;
566 updateProgress();
570 void ProgressCmdEnv::update( uno::Any const & rStatus )
572 update_( rStatus );
576 void ProgressCmdEnv::pop()
578 update_( uno::Any() ); // no message
582 ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper,
583 TheExtensionManager *pManager,
584 uno::Reference< uno::XComponentContext > xContext ) :
585 salhelper::Thread( "dp_gui_extensioncmdqueue" ),
586 m_xContext(std::move( xContext )),
587 m_pDialogHelper( pDialogHelper ),
588 m_pManager( pManager ),
589 m_sEnablingPackages( DpResId( RID_STR_ENABLING_PACKAGES ) ),
590 m_sDisablingPackages( DpResId( RID_STR_DISABLING_PACKAGES ) ),
591 m_sAddingPackages( DpResId( RID_STR_ADDING_PACKAGES ) ),
592 m_sRemovingPackages( DpResId( RID_STR_REMOVING_PACKAGES ) ),
593 m_sDefaultCmd( DpResId( RID_STR_ADD_PACKAGES ) ),
594 m_sAcceptLicense( DpResId( RID_STR_ACCEPT_LICENSE ) ),
595 m_eInput( NONE ),
596 m_bStopped( false ),
597 m_bWorking( false )
599 OSL_ASSERT( pDialogHelper );
603 void ExtensionCmdQueue::Thread::addExtension( const OUString &rExtensionURL,
604 const OUString &rRepository,
605 const bool bWarnUser )
607 if ( !rExtensionURL.isEmpty() )
609 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser );
610 _insert( pEntry );
615 void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
617 if ( rPackage.is() )
619 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::REMOVE, rPackage );
620 _insert( pEntry );
625 void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
627 if ( rPackage.is() )
629 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ACCEPT_LICENSE, rPackage );
630 _insert( pEntry );
635 void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
636 const bool bEnable )
638 if ( rPackage.is() )
640 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( bEnable ? ExtensionCmd::ENABLE :
641 ExtensionCmd::DISABLE,
642 rPackage );
643 _insert( pEntry );
648 void ExtensionCmdQueue::Thread::checkForUpdates(
649 std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
651 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::CHECK_FOR_UPDATES, std::move(vExtensionList) );
652 _insert( pEntry );
656 //Stopping this thread will not abort the installation of extensions.
657 void ExtensionCmdQueue::Thread::stop()
659 std::scoped_lock aGuard( m_mutex );
660 m_bStopped = true;
661 m_eInput = STOP;
662 m_wakeup.notify_all();
666 bool ExtensionCmdQueue::Thread::isBusy()
668 std::scoped_lock aGuard( m_mutex );
669 return m_bWorking;
673 ExtensionCmdQueue::Thread::~Thread() {}
676 void ExtensionCmdQueue::Thread::execute()
678 #ifdef _WIN32
679 //Needed for use of the service "com.sun.star.system.SystemShellExecute" in
680 //DialogHelper::openWebBrowser
681 int nNbCallCoInitializeExForReinit = 0;
682 o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, nNbCallCoInitializeExForReinit);
683 #endif
684 for (;;)
686 int nSize;
687 Input eInput;
689 std::unique_lock aGuard( m_mutex );
690 while (m_eInput == NONE) {
691 m_wakeup.wait(aGuard);
693 eInput = m_eInput;
694 m_eInput = NONE;
695 nSize = m_queue.size();
696 // coverity[missing_lock: FALSE] - maybe due to (by-design) unique_lock vs. scoped_lock?
697 m_bWorking = false;
700 if ( eInput == STOP )
701 break;
703 // We only install the extension which are currently in the queue.
704 // The progressbar will be set to show the progress of the current number
705 // of extensions. If we allowed to add extensions now then the progressbar may
706 // have reached the end while we still install newly added extensions.
707 if ( nSize == 0 )
708 continue;
710 ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) );
712 // Do not lock the following part with addExtension. addExtension may be called in the main thread.
713 // If the message box "Do you want to install the extension (or similar)" is shown and then
714 // addExtension is called, which then blocks the main thread, then we deadlock.
715 bool bStartProgress = true;
717 while ( --nSize >= 0 )
720 std::scoped_lock aGuard( m_mutex );
721 m_bWorking = true;
726 TExtensionCmd pEntry;
728 std::scoped_lock queueGuard( m_mutex );
729 pEntry = m_queue.front();
730 m_queue.pop();
733 if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) )
735 currentCmdEnv->startProgress();
736 bStartProgress = false;
739 switch ( pEntry->m_eCmdType ) {
740 case ExtensionCmd::ADD :
741 _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser );
742 break;
743 case ExtensionCmd::REMOVE :
744 _removeExtension( currentCmdEnv, pEntry->m_xPackage );
745 break;
746 case ExtensionCmd::ENABLE :
747 _enableExtension( currentCmdEnv, pEntry->m_xPackage );
748 break;
749 case ExtensionCmd::DISABLE :
750 _disableExtension( currentCmdEnv, pEntry->m_xPackage );
751 break;
752 case ExtensionCmd::CHECK_FOR_UPDATES :
753 _checkForUpdates( std::vector(pEntry->m_vExtensionList) );
754 break;
755 case ExtensionCmd::ACCEPT_LICENSE :
756 _acceptLicense( currentCmdEnv, pEntry->m_xPackage );
757 break;
760 catch ( const ucb::CommandAbortedException & )
762 //This exception is thrown when the user clicks cancel on the progressbar.
763 //Then we cancel the installation of all extensions and remove them from
764 //the queue.
766 std::scoped_lock queueGuard2(m_mutex);
767 while ( --nSize >= 0 )
768 m_queue.pop();
770 break;
772 catch ( const ucb::CommandFailedException & )
774 //This exception is thrown when a user clicked cancel in the messagebox which was
775 //started by the interaction handler. For example the user will be asked if he/she
776 //really wants to install the extension.
777 //These interactions run for exactly one extension at a time. Therefore we continue
778 //with installing the remaining extensions.
779 continue;
781 catch ( const uno::Exception & )
783 //Todo display the user an error
784 //see also DialogImpl::SyncPushButton::Click()
785 uno::Any exc( ::cppu::getCaughtException() );
786 OUString msg;
787 deployment::DeploymentException dpExc;
788 if (exc >>= dpExc)
790 if (auto e = o3tl::tryAccess<uno::Exception>(dpExc.Cause))
792 // notify error cause only:
793 msg = e->Message;
796 if (msg.isEmpty()) // fallback for debugging purposes
797 msg = ::comphelper::anyToString(exc);
799 const SolarMutexGuard guard;
800 if (m_pDialogHelper)
801 m_pDialogHelper->incBusy();
803 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(currentCmdEnv->activeDialog(),
804 VclMessageType::Warning, VclButtonsType::Ok, msg));
805 if (m_pDialogHelper)
806 xBox->set_title(m_pDialogHelper->getFrameWeld()->get_title());
807 xBox->run();
808 if (m_pDialogHelper)
809 m_pDialogHelper->decBusy();
810 //Continue with installation of the remaining extensions
813 std::scoped_lock aGuard( m_mutex );
814 m_bWorking = false;
819 // when leaving the while loop with break, we should set working to false, too
820 std::scoped_lock aGuard( m_mutex );
821 m_bWorking = false;
824 if ( !bStartProgress )
825 currentCmdEnv->stopProgress();
827 //end for
828 #ifdef _WIN32
829 o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, nNbCallCoInitializeExForReinit);
830 #endif
834 void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
835 const OUString &rPackageURL,
836 const OUString &rRepository,
837 const bool bWarnUser )
839 //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void
840 //and anyTitle.get<OUString> throws as RuntimeException.
841 uno::Any anyTitle;
844 anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv, m_xContext ).getPropertyValue( "Title" );
846 catch ( const uno::Exception & )
848 return;
851 OUString sName;
852 if ( ! (anyTitle >>= sName) )
854 OSL_FAIL("Could not get file name for extension.");
855 return;
858 rCmdEnv->setWarnUser( bWarnUser );
859 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
860 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
861 OUString sTitle(
862 m_sAddingPackages.replaceAll("%EXTENSION_NAME", sName));
863 rCmdEnv->progressSection( sTitle, xAbortChannel );
867 xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(),
868 rRepository, xAbortChannel, rCmdEnv );
870 catch ( const ucb::CommandFailedException & )
872 // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press
873 // cancel this exception is thrown.
875 catch ( const ucb::CommandAbortedException & )
877 // User clicked the cancel button
878 // TODO: handle cancel
880 rCmdEnv->setWarnUser( false );
884 void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
885 const uno::Reference< deployment::XPackage > &xPackage )
887 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
888 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
889 OUString sTitle(
890 m_sRemovingPackages.replaceAll("%EXTENSION_NAME",
891 xPackage->getDisplayName()));
892 rCmdEnv->progressSection( sTitle, xAbortChannel );
894 OUString id( dp_misc::getIdentifier( xPackage ) );
897 xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv );
899 catch ( const deployment::DeploymentException & )
901 catch ( const ucb::CommandFailedException & )
903 catch ( const ucb::CommandAbortedException & )
906 // Check, if there are still updates to be notified via menu bar icon
907 uno::Sequence< uno::Sequence< OUString > > aItemList;
908 UpdateDialog::createNotifyJob( false, aItemList );
912 void ExtensionCmdQueue::Thread::_checkForUpdates(
913 std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
915 const SolarMutexGuard guard;
917 if (m_pDialogHelper)
918 m_pDialogHelper->incBusy();
920 std::vector< UpdateData > vData;
921 UpdateDialog aUpdateDialog(m_xContext, m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, std::move(vExtensionList), &vData);
923 aUpdateDialog.notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon
925 bool bOk = aUpdateDialog.run() == RET_OK;
926 if (m_pDialogHelper)
927 m_pDialogHelper->decBusy();
929 if (bOk && !vData.empty())
931 // If there is at least one directly downloadable extension then we
932 // open the install dialog.
933 std::vector< UpdateData > dataDownload;
935 for (auto const& data : vData)
937 if ( data.sWebsiteURL.isEmpty() )
938 dataDownload.push_back(data);
941 short nDialogResult = RET_OK;
942 if ( !dataDownload.empty() )
944 if (m_pDialogHelper)
945 m_pDialogHelper->incBusy();
946 UpdateInstallDialog aDlg(m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, dataDownload, m_xContext);
947 nDialogResult = aDlg.run();
948 if (m_pDialogHelper)
949 m_pDialogHelper->decBusy();
950 aUpdateDialog.notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon
952 else
953 aUpdateDialog.notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon
955 //Now start the webbrowser and navigate to the websites where we get the updates
956 if ( RET_OK == nDialogResult )
958 for (auto const& data : vData)
960 if ( m_pDialogHelper && ( !data.sWebsiteURL.isEmpty() ) )
961 m_pDialogHelper->openWebBrowser( data.sWebsiteURL, m_pDialogHelper->getFrameWeld()->get_title() );
965 else
966 aUpdateDialog.notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon
970 void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
971 const uno::Reference< deployment::XPackage > &xPackage )
973 if ( !xPackage.is() )
974 return;
976 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
977 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
978 OUString sTitle(
979 m_sEnablingPackages.replaceAll("%EXTENSION_NAME",
980 xPackage->getDisplayName()));
981 rCmdEnv->progressSection( sTitle, xAbortChannel );
985 xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv );
986 if ( m_pDialogHelper )
987 m_pDialogHelper->updatePackageInfo( xPackage );
989 catch ( const ::ucb::CommandAbortedException & )
994 void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
995 const uno::Reference< deployment::XPackage > &xPackage )
997 if ( !xPackage.is() )
998 return;
1000 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1001 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1002 OUString sTitle(
1003 m_sDisablingPackages.replaceAll("%EXTENSION_NAME",
1004 xPackage->getDisplayName()));
1005 rCmdEnv->progressSection( sTitle, xAbortChannel );
1009 xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv );
1010 if ( m_pDialogHelper )
1011 m_pDialogHelper->updatePackageInfo( xPackage );
1013 catch ( const ::ucb::CommandAbortedException & )
1018 void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
1019 const uno::Reference< deployment::XPackage > &xPackage )
1021 if ( !xPackage.is() )
1022 return;
1024 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1025 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1026 OUString sTitle(
1027 m_sAcceptLicense.replaceAll("%EXTENSION_NAME",
1028 xPackage->getDisplayName()));
1029 rCmdEnv->progressSection( sTitle, xAbortChannel );
1033 xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv );
1034 if ( m_pDialogHelper )
1035 m_pDialogHelper->updatePackageInfo( xPackage );
1037 catch ( const ::ucb::CommandAbortedException & )
1041 void ExtensionCmdQueue::Thread::_insert(const TExtensionCmd& rExtCmd)
1043 std::scoped_lock aGuard( m_mutex );
1045 // If someone called stop then we do not process the command -> game over!
1046 if ( m_bStopped )
1047 return;
1049 m_queue.push( rExtCmd );
1050 m_eInput = START;
1051 m_wakeup.notify_all();
1055 ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper,
1056 TheExtensionManager *pManager,
1057 const uno::Reference< uno::XComponentContext > &rContext )
1058 : m_thread( new Thread( pDialogHelper, pManager, rContext ) )
1060 m_thread->launch();
1063 ExtensionCmdQueue::~ExtensionCmdQueue() {
1064 m_thread->stop();
1065 m_thread->join();
1068 void ExtensionCmdQueue::addExtension( const OUString & extensionURL,
1069 const OUString & repository,
1070 const bool bWarnUser )
1072 m_thread->addExtension( extensionURL, repository, bWarnUser );
1075 void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
1077 m_thread->removeExtension( rPackage );
1080 void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
1081 const bool bEnable )
1083 m_thread->enableExtension( rPackage, bEnable );
1086 void ExtensionCmdQueue::checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
1088 m_thread->checkForUpdates( std::move(vExtensionList) );
1091 void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
1093 m_thread->acceptLicense( rPackage );
1096 void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
1098 dp_misc::syncRepositories( false, new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
1101 bool ExtensionCmdQueue::isBusy()
1103 return m_thread->isBusy();
1106 void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext,
1107 const uno::Reference< task::XInteractionRequest > & xRequest )
1109 ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
1110 xCmdEnv->handle( xRequest );
1113 } //namespace dp_gui
1115 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */