1
0
Fork 0
libreoffice/extensions/source/update/check/updatecheckjob.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

336 lines
10 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include "updatecheck.hxx"
#include "updatecheckconfig.hxx"
#include "updatehdl.hxx"
#include "updateprotocol.hxx"
#include <memory>
#include <mutex>
#include <utility>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XTerminateListener.hpp>
#include <com/sun/star/task/XJob.hpp>
namespace beans = css::beans ;
namespace frame = css::frame ;
namespace lang = css::lang ;
namespace task = css::task ;
namespace uno = css::uno ;
namespace
{
class InitUpdateCheckJobThread : public osl::Thread
{
public:
InitUpdateCheckJobThread( const uno::Reference< uno::XComponentContext > &xContext,
const uno::Sequence< beans::NamedValue > &xParameters,
bool bShowDialog );
virtual void SAL_CALL run() override;
void setTerminating();
private:
osl::Condition m_aCondition;
uno::Reference<uno::XComponentContext> m_xContext;
uno::Sequence<beans::NamedValue> m_xParameters;
bool m_bShowDialog;
bool m_bTerminating;
std::mutex m_mutex;
rtl::Reference<UpdateCheck> m_controller;
};
class UpdateCheckJob :
public ::cppu::WeakImplHelper< task::XJob, lang::XServiceInfo, frame::XTerminateListener >
{
virtual ~UpdateCheckJob() override;
public:
UpdateCheckJob(
css::uno::Reference<css::uno::XComponentContext> const & context,
css::uno::Reference<css::frame::XDesktop2> const & desktop):
m_xContext(context), m_xDesktop(desktop)
{}
// XJob
virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) override;
// XServiceInfo
virtual OUString SAL_CALL getImplementationName() override;
virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
// XEventListener
virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
// XTerminateListener
virtual void SAL_CALL queryTermination( lang::EventObject const & evt ) override;
virtual void SAL_CALL notifyTermination( lang::EventObject const & evt ) override;
private:
uno::Reference<uno::XComponentContext> m_xContext;
std::mutex m_mutex;
uno::Reference< frame::XDesktop2 > m_xDesktop;
std::unique_ptr< InitUpdateCheckJobThread > m_pInitThread;
void handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp );
void terminateAndJoinThread();
};
InitUpdateCheckJobThread::InitUpdateCheckJobThread(
const uno::Reference< uno::XComponentContext > &xContext,
const uno::Sequence< beans::NamedValue > &xParameters,
bool bShowDialog ) :
m_xContext( xContext ),
m_xParameters( xParameters ),
m_bShowDialog( bShowDialog ),
m_bTerminating( false )
{
create();
}
void SAL_CALL InitUpdateCheckJobThread::run()
{
osl_setThreadName("InitUpdateCheckJobThread");
if (!m_bShowDialog) {
TimeValue tv = { 25, 0 };
m_aCondition.wait( &tv );
if ( m_bTerminating )
return;
}
try {
rtl::Reference< UpdateCheck > aController( UpdateCheck::get() );
// At least for the automatic ("onFirstVisibleTask", i.e., !m_bShowDialog) check, wait for
// m_controller during setTerminating, to prevent m_controller from still having threads
// running during exit (ideally, we would make sure that all threads are joined before exit,
// but the UpdateCheck logic is rather convoluted, so play it safe for now and only address
// the automatic update check that is known to cause issues during `make check`, not the
// manually triggered update check scenario):
if (!m_bShowDialog) {
std::scoped_lock l(m_mutex);
m_controller = aController;
}
aController->initialize( m_xParameters, m_xContext );
if ( m_bShowDialog )
aController->showDialog( true );
} catch (const uno::Exception &) {
// fdo#64962 - don't bring the app down on some unexpected exception.
TOOLS_WARN_EXCEPTION("extensions.update", "Caught init update exception, thread terminated" );
{
std::scoped_lock l(m_mutex);
m_controller.clear();
}
}
}
void InitUpdateCheckJobThread::setTerminating() {
m_bTerminating = true;
m_aCondition.set();
rtl::Reference<UpdateCheck> controller;
{
std::scoped_lock l(m_mutex);
std::swap(controller, m_controller);
}
if (controller.is()) {
controller->waitForUpdateCheckFinished();
}
}
UpdateCheckJob::~UpdateCheckJob()
{
}
uno::Any
UpdateCheckJob::execute(const uno::Sequence<beans::NamedValue>& namedValues)
{
for ( sal_Int32 n=namedValues.getLength(); n-- > 0; )
{
if ( namedValues[ n ].Name == "DynamicData" )
{
uno::Sequence<beans::NamedValue> aListProp;
if ( namedValues[n].Value >>= aListProp )
{
for ( sal_Int32 i=aListProp.getLength(); i-- > 0; )
{
if ( aListProp[ i ].Name == "updateList" )
{
handleExtensionUpdates( aListProp );
return uno::Any();
}
}
}
}
}
uno::Sequence<beans::NamedValue> aConfig =
getValue< uno::Sequence<beans::NamedValue> > (namedValues, "JobConfig");
/* Determine the way we got invoked here -
* see Developers Guide Chapter "4.7.2 Jobs" to understand the magic
*/
uno::Sequence<beans::NamedValue> aEnvironment =
getValue< uno::Sequence<beans::NamedValue> > (namedValues, "Environment");
OUString aEventName = getValue< OUString > (aEnvironment, "EventName");
auto thread = std::make_unique<InitUpdateCheckJobThread >(
m_xContext, aConfig,
aEventName != "onFirstVisibleTask");
{
std::scoped_lock l(m_mutex);
m_pInitThread = std::move(thread);
}
return uno::Any();
}
void UpdateCheckJob::handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp )
{
try {
uno::Sequence< uno::Sequence< OUString > > aList =
getValue< uno::Sequence< uno::Sequence< OUString > > > ( rListProp, "updateList" );
bool bPrepareOnly = getValue< bool > ( rListProp, "prepareOnly" );
// we will first store any new found updates and then check, if there are any
// pending updates.
storeExtensionUpdateInfos( m_xContext, aList );
if ( bPrepareOnly )
return;
bool bHasUpdates = checkForPendingUpdates( m_xContext );
rtl::Reference<UpdateCheck> aController( UpdateCheck::get() );
if ( ! aController.is() )
return;
aController->setHasExtensionUpdates( bHasUpdates );
if ( ! aController->hasOfficeUpdate() )
{
if ( bHasUpdates )
aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL, true );
else
aController->setUIState( UPDATESTATE_NO_UPDATE_AVAIL, true );
}
}
catch( const uno::Exception& )
{
TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated");
}
}
OUString SAL_CALL
UpdateCheckJob::getImplementationName()
{
return u"vnd.sun.UpdateCheck"_ustr;
}
uno::Sequence< OUString > SAL_CALL
UpdateCheckJob::getSupportedServiceNames()
{
return { u"com.sun.star.setup.UpdateCheck"_ustr };
}
sal_Bool SAL_CALL
UpdateCheckJob::supportsService( OUString const & serviceName )
{
return cppu::supportsService(this, serviceName);
}
// XEventListener
void SAL_CALL UpdateCheckJob::disposing( lang::EventObject const & rEvt )
{
css::uno::Reference<css::frame::XDesktop2> desktop;
{
std::scoped_lock l(m_mutex);
if ( rEvt.Source == m_xDesktop ) {
std::swap(m_xDesktop, desktop);
}
}
if ( desktop.is() )
{
terminateAndJoinThread();
desktop->removeTerminateListener( this );
}
}
// XTerminateListener
void SAL_CALL UpdateCheckJob::queryTermination( lang::EventObject const & )
{
}
void UpdateCheckJob::terminateAndJoinThread()
{
std::unique_ptr<InitUpdateCheckJobThread> thread;
{
std::scoped_lock l(m_mutex);
std::swap(m_pInitThread, thread);
}
if (thread != nullptr)
{
thread->setTerminating();
thread->join();
}
}
void SAL_CALL UpdateCheckJob::notifyTermination( lang::EventObject const & )
{
terminateAndJoinThread();
}
} // anonymous namespace
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
extensions_update_UpdateCheckJob_get_implementation(
css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
{
css::uno::Reference<css::frame::XDesktop2> desktop(
css::frame::Desktop::create(context));
rtl::Reference<UpdateCheckJob> job(new UpdateCheckJob(context, desktop));
desktop->addTerminateListener(job);
return cppu::acquire(job.get());
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */