use insert function instead of for loop
[LibreOffice.git] / desktop / source / deployment / gui / dp_gui_extensioncmdqueue.cxx
blobe9b5326afe10ffcf10489b23e19d5bb182849440
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() ) ? u"0"_ustr : 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 : 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 assert(approve != abort);
511 // select:
512 for (auto& cont : xRequest->getContinuations())
514 if (approve) {
515 uno::Reference<task::XInteractionApprove> xInteractionApprove(cont, uno::UNO_QUERY);
516 if (xInteractionApprove.is()) {
517 xInteractionApprove->select();
518 // don't query again for ongoing continuations:
519 break;
522 else /*if (abort)*/ {
523 uno::Reference<task::XInteractionAbort> xInteractionAbort(cont, uno::UNO_QUERY);
524 if (xInteractionAbort.is()) {
525 xInteractionAbort->select();
526 // don't query again for ongoing continuations:
527 break;
535 // XProgressHandler
537 void ProgressCmdEnv::push( uno::Any const & rStatus )
539 update_( rStatus );
543 void ProgressCmdEnv::update_( uno::Any const & rStatus )
545 OUString text;
546 if ( rStatus.hasValue() && !( rStatus >>= text) )
548 if ( auto e = o3tl::tryAccess<uno::Exception>(rStatus) )
549 text = e->Message;
550 if ( text.isEmpty() )
551 text = ::comphelper::anyToString( rStatus ); // fallback
553 const SolarMutexGuard aGuard;
554 if (m_pDialogHelper)
555 m_pDialogHelper->incBusy();
556 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
557 VclMessageType::Warning, VclButtonsType::Ok, text));
558 xBox->run();
559 if (m_pDialogHelper)
560 m_pDialogHelper->decBusy();
562 ++m_nCurrentProgress;
563 updateProgress();
567 void ProgressCmdEnv::update( uno::Any const & rStatus )
569 update_( rStatus );
573 void ProgressCmdEnv::pop()
575 update_( uno::Any() ); // no message
579 ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper,
580 TheExtensionManager *pManager,
581 uno::Reference< uno::XComponentContext > xContext ) :
582 salhelper::Thread( "dp_gui_extensioncmdqueue" ),
583 m_xContext(std::move( xContext )),
584 m_pDialogHelper( pDialogHelper ),
585 m_pManager( pManager ),
586 m_sEnablingPackages( DpResId( RID_STR_ENABLING_PACKAGES ) ),
587 m_sDisablingPackages( DpResId( RID_STR_DISABLING_PACKAGES ) ),
588 m_sAddingPackages( DpResId( RID_STR_ADDING_PACKAGES ) ),
589 m_sRemovingPackages( DpResId( RID_STR_REMOVING_PACKAGES ) ),
590 m_sDefaultCmd( DpResId( RID_STR_ADD_PACKAGES ) ),
591 m_sAcceptLicense( DpResId( RID_STR_ACCEPT_LICENSE ) ),
592 m_eInput( NONE ),
593 m_bStopped( false ),
594 m_bWorking( false )
596 OSL_ASSERT( pDialogHelper );
600 void ExtensionCmdQueue::Thread::addExtension( const OUString &rExtensionURL,
601 const OUString &rRepository,
602 const bool bWarnUser )
604 if ( !rExtensionURL.isEmpty() )
606 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser );
607 _insert( pEntry );
612 void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
614 if ( rPackage.is() )
616 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::REMOVE, rPackage );
617 _insert( pEntry );
622 void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
624 if ( rPackage.is() )
626 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ACCEPT_LICENSE, rPackage );
627 _insert( pEntry );
632 void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
633 const bool bEnable )
635 if ( rPackage.is() )
637 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( bEnable ? ExtensionCmd::ENABLE :
638 ExtensionCmd::DISABLE,
639 rPackage );
640 _insert( pEntry );
645 void ExtensionCmdQueue::Thread::checkForUpdates(
646 std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
648 TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::CHECK_FOR_UPDATES, std::move(vExtensionList) );
649 _insert( pEntry );
653 //Stopping this thread will not abort the installation of extensions.
654 void ExtensionCmdQueue::Thread::stop()
656 std::scoped_lock aGuard( m_mutex );
657 m_bStopped = true;
658 m_eInput = STOP;
659 m_wakeup.notify_all();
663 bool ExtensionCmdQueue::Thread::isBusy()
665 std::scoped_lock aGuard( m_mutex );
666 return m_bWorking;
670 ExtensionCmdQueue::Thread::~Thread() {}
673 void ExtensionCmdQueue::Thread::execute()
675 #ifdef _WIN32
676 //Needed for use of the service "com.sun.star.system.SystemShellExecute" in
677 //DialogHelper::openWebBrowser
678 int nNbCallCoInitializeExForReinit = 0;
679 o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, nNbCallCoInitializeExForReinit);
680 #endif
681 for (;;)
683 int nSize;
684 Input eInput;
686 std::unique_lock aGuard( m_mutex );
687 while (m_eInput == NONE) {
688 m_wakeup.wait(aGuard);
690 eInput = m_eInput;
691 m_eInput = NONE;
692 nSize = m_queue.size();
693 // coverity[missing_lock: FALSE] - maybe due to (by-design) unique_lock vs. scoped_lock?
694 m_bWorking = false;
697 if ( eInput == STOP )
698 break;
700 // We only install the extension which are currently in the queue.
701 // The progressbar will be set to show the progress of the current number
702 // of extensions. If we allowed to add extensions now then the progressbar may
703 // have reached the end while we still install newly added extensions.
704 if ( nSize == 0 )
705 continue;
707 ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) );
709 // Do not lock the following part with addExtension. addExtension may be called in the main thread.
710 // If the message box "Do you want to install the extension (or similar)" is shown and then
711 // addExtension is called, which then blocks the main thread, then we deadlock.
712 bool bStartProgress = true;
714 while ( --nSize >= 0 )
717 std::scoped_lock aGuard( m_mutex );
718 m_bWorking = true;
723 TExtensionCmd pEntry;
725 std::scoped_lock queueGuard( m_mutex );
726 pEntry = m_queue.front();
727 m_queue.pop();
730 if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) )
732 currentCmdEnv->startProgress();
733 bStartProgress = false;
736 switch ( pEntry->m_eCmdType ) {
737 case ExtensionCmd::ADD :
738 _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser );
739 break;
740 case ExtensionCmd::REMOVE :
741 _removeExtension( currentCmdEnv, pEntry->m_xPackage );
742 break;
743 case ExtensionCmd::ENABLE :
744 _enableExtension( currentCmdEnv, pEntry->m_xPackage );
745 break;
746 case ExtensionCmd::DISABLE :
747 _disableExtension( currentCmdEnv, pEntry->m_xPackage );
748 break;
749 case ExtensionCmd::CHECK_FOR_UPDATES :
750 _checkForUpdates( std::vector(pEntry->m_vExtensionList) );
751 break;
752 case ExtensionCmd::ACCEPT_LICENSE :
753 _acceptLicense( currentCmdEnv, pEntry->m_xPackage );
754 break;
757 catch ( const ucb::CommandAbortedException & )
759 //This exception is thrown when the user clicks cancel on the progressbar.
760 //Then we cancel the installation of all extensions and remove them from
761 //the queue.
763 std::scoped_lock queueGuard2(m_mutex);
764 while ( --nSize >= 0 )
765 m_queue.pop();
767 break;
769 catch ( const ucb::CommandFailedException & )
771 //This exception is thrown when a user clicked cancel in the messagebox which was
772 //started by the interaction handler. For example the user will be asked if he/she
773 //really wants to install the extension.
774 //These interactions run for exactly one extension at a time. Therefore we continue
775 //with installing the remaining extensions.
776 continue;
778 catch ( const uno::Exception & )
780 //Todo display the user an error
781 //see also DialogImpl::SyncPushButton::Click()
782 uno::Any exc( ::cppu::getCaughtException() );
783 OUString msg;
784 deployment::DeploymentException dpExc;
785 if (exc >>= dpExc)
787 if (auto e = o3tl::tryAccess<uno::Exception>(dpExc.Cause))
789 // notify error cause only:
790 msg = e->Message;
793 if (msg.isEmpty()) // fallback for debugging purposes
794 msg = ::comphelper::anyToString(exc);
796 const SolarMutexGuard guard;
797 if (m_pDialogHelper)
798 m_pDialogHelper->incBusy();
800 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(currentCmdEnv->activeDialog(),
801 VclMessageType::Warning, VclButtonsType::Ok, msg));
802 if (m_pDialogHelper)
803 xBox->set_title(m_pDialogHelper->getFrameWeld()->get_title());
804 xBox->run();
805 if (m_pDialogHelper)
806 m_pDialogHelper->decBusy();
807 //Continue with installation of the remaining extensions
810 std::scoped_lock aGuard( m_mutex );
811 m_bWorking = false;
816 // when leaving the while loop with break, we should set working to false, too
817 std::scoped_lock aGuard( m_mutex );
818 m_bWorking = false;
821 if ( !bStartProgress )
822 currentCmdEnv->stopProgress();
824 //end for
825 #ifdef _WIN32
826 o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, nNbCallCoInitializeExForReinit);
827 #endif
831 void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
832 const OUString &rPackageURL,
833 const OUString &rRepository,
834 const bool bWarnUser )
836 //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void
837 //and anyTitle.get<OUString> throws as RuntimeException.
838 uno::Any anyTitle;
841 anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv, m_xContext ).getPropertyValue( u"Title"_ustr );
843 catch ( const uno::Exception & )
845 return;
848 OUString sName;
849 if ( ! (anyTitle >>= sName) )
851 OSL_FAIL("Could not get file name for extension.");
852 return;
855 rCmdEnv->setWarnUser( bWarnUser );
856 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
857 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
858 OUString sTitle(
859 m_sAddingPackages.replaceAll("%EXTENSION_NAME", sName));
860 rCmdEnv->progressSection( sTitle, xAbortChannel );
864 xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(),
865 rRepository, xAbortChannel, rCmdEnv );
867 catch ( const ucb::CommandFailedException & )
869 // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press
870 // cancel this exception is thrown.
872 catch ( const ucb::CommandAbortedException & )
874 // User clicked the cancel button
875 // TODO: handle cancel
877 rCmdEnv->setWarnUser( false );
881 void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
882 const uno::Reference< deployment::XPackage > &xPackage )
884 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
885 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
886 OUString sTitle(
887 m_sRemovingPackages.replaceAll("%EXTENSION_NAME",
888 xPackage->getDisplayName()));
889 rCmdEnv->progressSection( sTitle, xAbortChannel );
891 OUString id( dp_misc::getIdentifier( xPackage ) );
894 xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv );
896 catch ( const deployment::DeploymentException & )
898 catch ( const ucb::CommandFailedException & )
900 catch ( const ucb::CommandAbortedException & )
903 // Check, if there are still updates to be notified via menu bar icon
904 uno::Sequence< uno::Sequence< OUString > > aItemList;
905 UpdateDialog::createNotifyJob( false, aItemList );
909 void ExtensionCmdQueue::Thread::_checkForUpdates(
910 std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
912 const SolarMutexGuard guard;
914 if (m_pDialogHelper)
915 m_pDialogHelper->incBusy();
917 std::vector< UpdateData > vData;
918 UpdateDialog aUpdateDialog(m_xContext, m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, std::move(vExtensionList), &vData);
920 aUpdateDialog.notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon
922 bool bOk = aUpdateDialog.run() == RET_OK;
923 if (m_pDialogHelper)
924 m_pDialogHelper->decBusy();
926 if (bOk && !vData.empty())
928 // If there is at least one directly downloadable extension then we
929 // open the install dialog.
930 std::vector< UpdateData > dataDownload;
932 for (auto const& data : vData)
934 if ( data.sWebsiteURL.isEmpty() )
935 dataDownload.push_back(data);
938 short nDialogResult = RET_OK;
939 if ( !dataDownload.empty() )
941 if (m_pDialogHelper)
942 m_pDialogHelper->incBusy();
943 UpdateInstallDialog aDlg(m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, dataDownload, m_xContext);
944 nDialogResult = aDlg.run();
945 if (m_pDialogHelper)
946 m_pDialogHelper->decBusy();
947 aUpdateDialog.notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon
949 else
950 aUpdateDialog.notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon
952 //Now start the webbrowser and navigate to the websites where we get the updates
953 if ( RET_OK == nDialogResult )
955 for (auto const& data : vData)
957 if ( m_pDialogHelper && ( !data.sWebsiteURL.isEmpty() ) )
958 m_pDialogHelper->openWebBrowser( data.sWebsiteURL, m_pDialogHelper->getFrameWeld()->get_title() );
962 else
963 aUpdateDialog.notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon
967 void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
968 const uno::Reference< deployment::XPackage > &xPackage )
970 if ( !xPackage.is() )
971 return;
973 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
974 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
975 OUString sTitle(
976 m_sEnablingPackages.replaceAll("%EXTENSION_NAME",
977 xPackage->getDisplayName()));
978 rCmdEnv->progressSection( sTitle, xAbortChannel );
982 xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv );
983 if ( m_pDialogHelper )
984 m_pDialogHelper->updatePackageInfo( xPackage );
986 catch ( const ::ucb::CommandAbortedException & )
991 void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
992 const uno::Reference< deployment::XPackage > &xPackage )
994 if ( !xPackage.is() )
995 return;
997 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
998 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
999 OUString sTitle(
1000 m_sDisablingPackages.replaceAll("%EXTENSION_NAME",
1001 xPackage->getDisplayName()));
1002 rCmdEnv->progressSection( sTitle, xAbortChannel );
1006 xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv );
1007 if ( m_pDialogHelper )
1008 m_pDialogHelper->updatePackageInfo( xPackage );
1010 catch ( const ::ucb::CommandAbortedException & )
1015 void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
1016 const uno::Reference< deployment::XPackage > &xPackage )
1018 if ( !xPackage.is() )
1019 return;
1021 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1022 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1023 OUString sTitle(
1024 m_sAcceptLicense.replaceAll("%EXTENSION_NAME",
1025 xPackage->getDisplayName()));
1026 rCmdEnv->progressSection( sTitle, xAbortChannel );
1030 xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv );
1031 if ( m_pDialogHelper )
1032 m_pDialogHelper->updatePackageInfo( xPackage );
1034 catch ( const ::ucb::CommandAbortedException & )
1038 void ExtensionCmdQueue::Thread::_insert(const TExtensionCmd& rExtCmd)
1040 std::scoped_lock aGuard( m_mutex );
1042 // If someone called stop then we do not process the command -> game over!
1043 if ( m_bStopped )
1044 return;
1046 m_queue.push( rExtCmd );
1047 m_eInput = START;
1048 m_wakeup.notify_all();
1052 ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper,
1053 TheExtensionManager *pManager,
1054 const uno::Reference< uno::XComponentContext > &rContext )
1055 : m_thread( new Thread( pDialogHelper, pManager, rContext ) )
1057 m_thread->launch();
1060 ExtensionCmdQueue::~ExtensionCmdQueue() {
1061 m_thread->stop();
1062 m_thread->join();
1065 void ExtensionCmdQueue::addExtension( const OUString & extensionURL,
1066 const OUString & repository,
1067 const bool bWarnUser )
1069 m_thread->addExtension( extensionURL, repository, bWarnUser );
1072 void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
1074 m_thread->removeExtension( rPackage );
1077 void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
1078 const bool bEnable )
1080 m_thread->enableExtension( rPackage, bEnable );
1083 void ExtensionCmdQueue::checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
1085 m_thread->checkForUpdates( std::move(vExtensionList) );
1088 void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
1090 m_thread->acceptLicense( rPackage );
1093 void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
1095 dp_misc::syncRepositories( false, new ProgressCmdEnv( xContext, nullptr, u"Extension Manager"_ustr ) );
1098 bool ExtensionCmdQueue::isBusy()
1100 return m_thread->isBusy();
1103 void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext,
1104 const uno::Reference< task::XInteractionRequest > & xRequest )
1106 ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, nullptr, u"Extension Manager"_ustr ) );
1107 xCmdEnv->handle( xRequest );
1110 } //namespace dp_gui
1112 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */