LCOV - code coverage report
Current view: top level - manager-lib - applicationmanager.cpp (source / functions) Coverage Total Hit
Test: QtApplicationManager Lines: 81.3 % 615 500
Test Date: 2025-05-03 10:54:12 Functions: 79.7 % 69 55
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 45.6 % 1105 504

             Branch data     Line data    Source code
       1                 :             : // Copyright (C) 2021 The Qt Company Ltd.
       2                 :             : // Copyright (C) 2019 Luxoft Sweden AB
       3                 :             : // Copyright (C) 2018 Pelagicore AG
       4                 :             : // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
       5                 :             : 
       6                 :             : #include <QCoreApplication>
       7                 :             : #include <QUrl>
       8                 :             : #include <QRegularExpression>
       9                 :             : #include <QFileInfo>
      10                 :             : #include <QProcess>
      11                 :             : #include <QDir>
      12                 :             : #include <QMetaObject>
      13                 :             : #include <QUuid>
      14                 :             : #include <QThread>
      15                 :             : #include <QMimeDatabase>
      16                 :             : #include <QScopedValueRollback>
      17                 :             : #include <qplatformdefs.h>
      18                 :             : #if defined(QT_GUI_LIB)
      19                 :             : #  include <QDesktopServices>
      20                 :             : #endif
      21                 :             : #if defined(Q_OS_WINDOWS)
      22                 :             : #  define write(a, b, c) _write(a, b, static_cast<unsigned int>(c))
      23                 :             : #endif
      24                 :             : 
      25                 :             : #include "global.h"
      26                 :             : #include "applicationinfo.h"
      27                 :             : #include "installationreport.h"
      28                 :             : #include "logging.h"
      29                 :             : #include "exception.h"
      30                 :             : #include "applicationmanager.h"
      31                 :             : #include "applicationmodel.h"
      32                 :             : #include "applicationmanager_p.h"
      33                 :             : #include "application.h"
      34                 :             : #include "runtimefactory.h"
      35                 :             : #include "containerfactory.h"
      36                 :             : #include "quicklauncher.h"
      37                 :             : #include "abstractruntime.h"
      38                 :             : #include "abstractcontainer.h"
      39                 :             : #include "globalruntimeconfiguration.h"
      40                 :             : #include "qml-utilities.h"
      41                 :             : #include "utilities.h"
      42                 :             : #include "qtyaml.h"
      43                 :             : #include "debugwrapper.h"
      44                 :             : #include "amnamespace.h"
      45                 :             : #include "package.h"
      46                 :             : #include "packagemanager.h"
      47                 :             : 
      48                 :             : #include <memory>
      49                 :             : 
      50                 :             : using namespace Qt::StringLiterals;
      51                 :             : 
      52                 :             : /*!
      53                 :             :     \qmltype ApplicationManager
      54                 :             :     \inqmlmodule QtApplicationManager.SystemUI
      55                 :             :     \ingroup system-ui-singletons
      56                 :             :     \brief The application model and controller.
      57                 :             : 
      58                 :             :     The ApplicationManager singleton type is the core of the application manager.
      59                 :             :     It provides both a DBus and a QML API for all of its functionality.
      60                 :             : 
      61                 :             :     The type is derived from \c QAbstractListModel, so it can be used directly
      62                 :             :     as a model in app-grid views.
      63                 :             : 
      64                 :             :     \target ApplicationManager Roles
      65                 :             : 
      66                 :             :     The following roles are available in this model:
      67                 :             : 
      68                 :             :     \table
      69                 :             :     \header
      70                 :             :         \li Role name
      71                 :             :         \li Type
      72                 :             :         \li Description
      73                 :             :     \row
      74                 :             :         \li \c applicationId
      75                 :             :         \li string
      76                 :             :         \li The unique Id of an application, represented as a string (e.g. \c Browser or
      77                 :             :             \c com.pelagicore.music)
      78                 :             :     \row
      79                 :             :         \li \c name
      80                 :             :         \li string
      81                 :             :         \li The name of the application. If possible, already translated to the current locale.
      82                 :             :             If no name was defined for the application, the name of the corresponding package will
      83                 :             :             be returned.
      84                 :             :     \row
      85                 :             :         \li \c description
      86                 :             :         \li string
      87                 :             :         \li The description of the application. If possible, already translated to the current locale.
      88                 :             :             If no description was defined for the application, the description of the corresponding
      89                 :             :             package will be returned.
      90                 :             :     \row
      91                 :             :         \li \c icon
      92                 :             :         \li string
      93                 :             :         \li The URL of the application's icon.
      94                 :             : 
      95                 :             :     \row
      96                 :             :         \li \c isRunning
      97                 :             :         \li bool
      98                 :             :         \li A boolean value representing the run-state of the application.
      99                 :             :     \row
     100                 :             :         \li \c isStartingUp
     101                 :             :         \li bool
     102                 :             :         \li A boolean value indicating whether the application is starting up and not fully operational yet.
     103                 :             :     \row
     104                 :             :         \li \c isShuttingDown
     105                 :             :         \li bool
     106                 :             :         \li A boolean value indicating whether the application is currently shutting down.
     107                 :             :     \row
     108                 :             :         \li \c isBlocked
     109                 :             :         \li bool
     110                 :             :         \li A boolean value that gets set when the application manager needs to block the application
     111                 :             :         from running: this is normally only the case while an update is applied.
     112                 :             :     \row
     113                 :             :         \li \c isUpdating
     114                 :             :         \li bool
     115                 :             :         \li A boolean value indicating whether the application is currently being installed or updated.
     116                 :             :             If \c true, the \c updateProgress can be used to track the actual progress.
     117                 :             :     \row
     118                 :             :         \li \c isRemovable
     119                 :             :         \li bool
     120                 :             :         \li A boolean value indicating whether this application is user-removable; \c true for all
     121                 :             :             dynamically installed third party applications and \c false for all system applications.
     122                 :             : 
     123                 :             :     \row
     124                 :             :         \li \c updateProgress
     125                 :             :         \li real
     126                 :             :         \li While \c isUpdating is \c true, querying this role returns the actual progress as a floating-point
     127                 :             :             value in the \c 0.0 to \c 1.0 range.
     128                 :             : 
     129                 :             :     \row
     130                 :             :         \li \c codeFilePath
     131                 :             :         \li string
     132                 :             :         \li The filesystem path to the main "executable". The format of this file is dependent on the
     133                 :             :         actual runtime though: for QML applications the "executable" is the \c main.qml file.
     134                 :             : 
     135                 :             :     \row
     136                 :             :         \li \c categories
     137                 :             :         \li list<string>
     138                 :             :         \li The categories this application is registered for via its meta-data file.
     139                 :             : 
     140                 :             :     \row
     141                 :             :         \li \c version
     142                 :             :         \li string
     143                 :             :         \li The currently installed version of this application.
     144                 :             : 
     145                 :             :     \row
     146                 :             :         \li \c application
     147                 :             :         \li ApplicationObject
     148                 :             :         \li The underlying \l ApplicationObject for quick access to the properties outside of a
     149                 :             :             model delegate.
     150                 :             :     \row
     151                 :             :         \li \c applicationObject
     152                 :             :         \li ApplicationObject
     153                 :             :         \li Exactly the same as \c application. This was added to keep the role names between the
     154                 :             :             PackageManager and ApplicationManager models as similar as possible.
     155                 :             :             This role was introduced in Qt version 6.6.
     156                 :             :     \endtable
     157                 :             : 
     158                 :             :     \note The index-based API is currently not available via DBus. However, the same functionality
     159                 :             :     is provided by the id-based API.
     160                 :             : 
     161                 :             :     After importing, you can just use the ApplicationManager singleton as follows:
     162                 :             : 
     163                 :             :     \qml
     164                 :             :     import QtQuick
     165                 :             :     import QtApplicationManager.SystemUI
     166                 :             : 
     167                 :             :     ListView {
     168                 :             :         id: appList
     169                 :             :         model: ApplicationManager
     170                 :             : 
     171                 :             :         delegate: Text {
     172                 :             :             text: name + "(" + applicationId + ")"
     173                 :             : 
     174                 :             :             MouseArea {
     175                 :             :                 anchors.fill: parent
     176                 :             :                 onClick: ApplicationManager.startApplication(applicationId)
     177                 :             :             }
     178                 :             :         }
     179                 :             :     }
     180                 :             :     \endqml
     181                 :             : */
     182                 :             : 
     183                 :             : /*!
     184                 :             :     \qmlproperty int ApplicationManager::count
     185                 :             :     \readonly
     186                 :             : 
     187                 :             :     This property holds the number of applications available.
     188                 :             : */
     189                 :             : 
     190                 :             : /*!
     191                 :             :     \qmlproperty bool ApplicationManager::singleProcess
     192                 :             :     \readonly
     193                 :             : 
     194                 :             :     This property indicates whether the application manager runs in single- or multi-process mode.
     195                 :             : */
     196                 :             : 
     197                 :             : /*!
     198                 :             :     \qmlproperty bool ApplicationManager::shuttingDown
     199                 :             :     \readonly
     200                 :             : 
     201                 :             :     This property is there to inform the System UI, when the application manager has entered its
     202                 :             :     shutdown phase. New application starts are already prevented, but the System UI might want
     203                 :             :     to impose additional restrictions in this state.
     204                 :             : */
     205                 :             : 
     206                 :             : /*!
     207                 :             :     \qmlproperty bool ApplicationManager::securityChecksEnabled
     208                 :             :     \readonly
     209                 :             : 
     210                 :             :     This property holds whether security related checks are enabled.
     211                 :             : 
     212                 :             :     \sa no-security
     213                 :             : */
     214                 :             : 
     215                 :             : /*!
     216                 :             :     \qmlproperty var ApplicationManager::systemProperties
     217                 :             :     \readonly
     218                 :             : 
     219                 :             :     Returns the project specific \l{system properties} that were set via the config file.
     220                 :             : */
     221                 :             : 
     222                 :             : /*!
     223                 :             :     \qmlproperty var ApplicationManager::containerSelectionFunction
     224                 :             : 
     225                 :             :     A JavaScript function callback that will be called whenever the application manager needs to
     226                 :             :     instantiate a container for running an application. See \l {Container Selection Configuration}
     227                 :             :     for more information.
     228                 :             : */
     229                 :             : 
     230                 :             : /*!
     231                 :             :     \qmlsignal ApplicationManager::applicationWasActivated(string id, string aliasId)
     232                 :             : 
     233                 :             :     This signal is emitted when an application identified by \a id is (re-)started via
     234                 :             :     the ApplicationManager API, possibly through an alias, provided in \a aliasId.
     235                 :             : 
     236                 :             :     The window manager should take care of raising the application's window in this case.
     237                 :             : */
     238                 :             : 
     239                 :             : /*!
     240                 :             :     \qmlsignal ApplicationManager::applicationRunStateChanged(string id, enumeration runState)
     241                 :             : 
     242                 :             :     This signal is emitted when the \a runState of the application identified by \a id changed.
     243                 :             :     Possible values for the \l {ApplicationObject::runState} {runState} are defined by the
     244                 :             :     ApplicationObject type.
     245                 :             : 
     246                 :             :     For example this signal can be used to restart an application in multi-process mode when
     247                 :             :     it has crashed:
     248                 :             : 
     249                 :             :     \qml
     250                 :             :     Connections {
     251                 :             :         target: ApplicationManager
     252                 :             :         function onApplicationRunStateChanged() {
     253                 :             :             if (runState === Am.NotRunning
     254                 :             :                 && ApplicationManager.application(id).lastExitStatus === Am.CrashExit) {
     255                 :             :                 ApplicationManager.startApplication(id);
     256                 :             :             }
     257                 :             :         }
     258                 :             :     }
     259                 :             :     \endqml
     260                 :             : 
     261                 :             :     See also Application::runState
     262                 :             : */
     263                 :             : 
     264                 :             : /*!
     265                 :             :     \qmlsignal ApplicationManager::applicationAdded(string id)
     266                 :             : 
     267                 :             :     This signal is emitted after a new application, identified by \a id, has been installed via the
     268                 :             :     PackageManager. The model has already been update before this signal is sent out.
     269                 :             : 
     270                 :             :     \note In addition to the normal "low-level" QAbstractListModel signals, the application manager
     271                 :             :           will also emit these "high-level" signals for System UIs that cannot work directly on the
     272                 :             :           ApplicationManager model: applicationAdded, applicationAboutToBeRemoved and applicationChanged.
     273                 :             : */
     274                 :             : 
     275                 :             : /*!
     276                 :             :     \qmlsignal ApplicationManager::applicationAboutToBeRemoved(string id)
     277                 :             : 
     278                 :             :     This signal is emitted before an existing application, identified by \a id, is removed via the
     279                 :             :     PackageManager.
     280                 :             : 
     281                 :             :     \note In addition to the normal "low-level" QAbstractListModel signals, the application manager
     282                 :             :           will also emit these "high-level" signals for System UIs that cannot work directly on the
     283                 :             :           ApplicationManager model: applicationAdded, applicationAboutToBeRemoved and applicationChanged.
     284                 :             : */
     285                 :             : 
     286                 :             : /*!
     287                 :             :     \qmlsignal ApplicationManager::applicationChanged(string id, list<string> changedRoles)
     288                 :             : 
     289                 :             :     Emitted whenever one or more data roles, denoted by \a changedRoles, changed on the application
     290                 :             :     identified by \a id. An empty list in the \a changedRoles argument means that all roles should
     291                 :             :     be considered modified.
     292                 :             : 
     293                 :             :     \note In addition to the normal "low-level" QAbstractListModel signals, the application manager
     294                 :             :           will also emit these "high-level" signals for System UIs that cannot work directly on the
     295                 :             :           ApplicationManager model: applicationAdded, applicationAboutToBeRemoved and applicationChanged.
     296                 :             : */
     297                 :             : 
     298                 :             : /*!
     299                 :             :     \qmlproperty bool ApplicationManager::windowManagerCompositorReady
     300                 :             :     \readonly
     301                 :             : 
     302                 :             :     This property starts with the value \c false and will change to \c true once the Wayland
     303                 :             :     compositor is ready to accept connections from other processes. In multi-process mode, this
     304                 :             :     happens implicitly after the System UI's main QML has been loaded.
     305                 :             : */
     306                 :             : 
     307                 :             : enum AMRoles
     308                 :             : {
     309                 :             :     Id = Qt::UserRole,
     310                 :             :     Name,
     311                 :             :     Description,
     312                 :             :     Icon,
     313                 :             : 
     314                 :             :     IsRunning,
     315                 :             :     IsStartingUp,
     316                 :             :     IsShuttingDown,
     317                 :             :     IsBlocked,
     318                 :             :     IsUpdating,
     319                 :             :     IsRemovable,
     320                 :             : 
     321                 :             :     UpdateProgress,
     322                 :             : 
     323                 :             :     CodeFilePath,
     324                 :             :     RuntimeName,
     325                 :             :     RuntimeParameters,
     326                 :             :     Capabilities,
     327                 :             :     Categories,
     328                 :             :     Version,
     329                 :             :     ApplicationItem,
     330                 :             :     ApplicationObject, // needed to keep the roles similar to PackageManager
     331                 :             : 
     332                 :             :     LastExitCode,
     333                 :             :     LastExitStatus
     334                 :             : };
     335                 :             : 
     336                 :             : QT_BEGIN_NAMESPACE_AM
     337                 :             : 
     338   [ +  -  +  - ]:          53 : ApplicationManagerPrivate::ApplicationManagerPrivate()
     339                 :             : {
     340   [ +  -  +  - ]:          53 :     currentLocale = QLocale::system().name(); //TODO: language changes
     341                 :             : 
     342   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::Id, "applicationId");
     343   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::Name, "name");
     344   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::Description, "description");
     345   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::Icon, "icon");
     346   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::IsRunning, "isRunning");
     347   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::IsStartingUp, "isStartingUp");
     348   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::IsShuttingDown, "isShuttingDown");
     349   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::IsBlocked, "isBlocked");
     350   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::IsUpdating, "isUpdating");
     351   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::IsRemovable, "isRemovable");
     352   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::UpdateProgress, "updateProgress");
     353   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::CodeFilePath, "codeFilePath");
     354   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::RuntimeName, "runtimeName");
     355   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::RuntimeParameters, "runtimeParameters");
     356   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::Capabilities, "capabilities");
     357   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::Categories, "categories");
     358   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::Version, "version");
     359   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::ApplicationItem, "application");
     360   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::ApplicationObject, "applicationObject");
     361   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::LastExitCode, "lastExitCode");
     362   [ +  -  +  - ]:          53 :     roleNames.insert(AMRoles::LastExitStatus, "lastExitStatus");
     363                 :          53 : }
     364                 :             : 
     365                 :          52 : ApplicationManagerPrivate::~ApplicationManagerPrivate()
     366                 :             : {
     367         [ +  + ]:         192 :     for (const QString &scheme : std::as_const(registeredMimeSchemes))
     368                 :         140 :         QDesktopServices::unsetUrlHandler(scheme);
     369                 :          52 :     qDeleteAll(apps);
     370                 :          52 : }
     371                 :             : 
     372                 :             : ApplicationManager *ApplicationManager::s_instance = nullptr;
     373                 :             : 
     374                 :          53 : ApplicationManager *ApplicationManager::createInstance(bool singleProcess)
     375                 :             : {
     376         [ -  + ]:          53 :     if (Q_UNLIKELY(s_instance))
     377                 :           0 :         qFatal("ApplicationManager::createInstance() was called a second time.");
     378                 :             : 
     379         [ +  - ]:          53 :     std::unique_ptr<ApplicationManager> am(new ApplicationManager(singleProcess));
     380                 :             : 
     381         [ +  - ]:          53 :     qRegisterMetaType<Am::RunState>();
     382         [ +  - ]:          53 :     qRegisterMetaType<Am::ExitStatus>();
     383         [ +  - ]:          53 :     qRegisterMetaType<Am::ProcessError>();
     384                 :             : 
     385   [ +  -  -  + ]:          53 :     if (Q_UNLIKELY(!PackageManager::instance()))
     386                 :           0 :         qFatal("ApplicationManager::createInstance() was called before a PackageManager singleton was instantiated.");
     387                 :             : 
     388         [ +  - ]:          53 :     s_instance = am.release();
     389   [ +  -  +  - ]:          53 :     connect(&PackageManager::instance()->internalSignals, &PackageManagerInternalSignals::registerApplication,
     390                 :         111 :             s_instance, [](ApplicationInfo *applicationInfo, Package *package) {
     391                 :         111 :         instance()->addApplication(applicationInfo, package);
     392   [ +  -  +  -  :         224 :         qCDebug(LogSystem).nospace().noquote() << " ++ application: " << applicationInfo->id() << " [package: " << package->id() << "]";
          +  -  +  -  +  
          -  +  -  +  -  
                   +  + ]
     393                 :         111 :     });
     394   [ +  -  +  - ]:          53 :     connect(&PackageManager::instance()->internalSignals, &PackageManagerInternalSignals::unregisterApplication,
     395                 :          20 :             s_instance, [](ApplicationInfo *applicationInfo, Package *package) {
     396                 :          20 :         instance()->removeApplication(applicationInfo, package);
     397   [ +  -  +  -  :          41 :         qCDebug(LogSystem).nospace().noquote() << " -- application: " << applicationInfo->id() << " [package: " << package->id() << "]";
          +  -  +  -  +  
          -  +  -  +  -  
                   +  + ]
     398                 :          20 :     });
     399                 :             : 
     400                 :          53 :     return s_instance;
     401                 :          53 : }
     402                 :             : 
     403                 :         838 : ApplicationManager *ApplicationManager::instance()
     404                 :             : {
     405         [ -  + ]:         838 :     if (!s_instance)
     406                 :           0 :         qFatal("ApplicationManager::instance() was called before createInstance().");
     407                 :         838 :     return s_instance;
     408                 :             : }
     409                 :             : 
     410                 :          53 : ApplicationManager::ApplicationManager(bool singleProcess, QObject *parent)
     411                 :             :     : QAbstractListModel(parent)
     412   [ +  -  +  - ]:          53 :     , d(new ApplicationManagerPrivate())
     413                 :             : {
     414                 :          53 :     d->singleProcess = singleProcess;
     415         [ +  - ]:          53 :     connect(this, &QAbstractItemModel::rowsInserted, this, &ApplicationManager::countChanged);
     416         [ +  - ]:          53 :     connect(this, &QAbstractItemModel::rowsRemoved, this, &ApplicationManager::countChanged);
     417         [ +  - ]:          53 :     connect(this, &QAbstractItemModel::layoutChanged, this, &ApplicationManager::countChanged);
     418         [ +  - ]:          53 :     connect(this, &QAbstractItemModel::modelReset, this, &ApplicationManager::countChanged);
     419                 :          53 : }
     420                 :             : 
     421                 :         104 : ApplicationManager::~ApplicationManager()
     422                 :             : {
     423         [ +  - ]:          52 :     delete d;
     424                 :          52 :     s_instance = nullptr;
     425                 :         104 : }
     426                 :             : 
     427                 :          96 : bool ApplicationManager::isSingleProcess() const
     428                 :             : {
     429                 :          37 :     return d->singleProcess;
     430                 :             : }
     431                 :             : 
     432                 :           0 : bool ApplicationManager::isShuttingDown() const
     433                 :             : {
     434                 :           0 :     return d->shuttingDown;
     435                 :             : }
     436                 :             : 
     437                 :           2 : bool ApplicationManager::securityChecksEnabled() const
     438                 :             : {
     439                 :           2 :     return d->securityChecksEnabled;
     440                 :             : }
     441                 :             : 
     442                 :           7 : void ApplicationManager::setSecurityChecksEnabled(bool enabled)
     443                 :             : {
     444                 :           7 :     d->securityChecksEnabled = enabled;
     445                 :           7 : }
     446                 :             : 
     447                 :         128 : QVariantMap ApplicationManager::systemProperties() const
     448                 :             : {
     449         [ +  + ]:         128 :     return d->systemProperties;
     450                 :             : }
     451                 :             : 
     452                 :          52 : void ApplicationManager::setSystemProperties(const QVariantMap &map)
     453                 :             : {
     454                 :          52 :     d->systemProperties = map;
     455                 :          52 : }
     456                 :             : 
     457                 :          52 : void ApplicationManager::setContainerSelectionConfiguration(const QList<std::pair<QString, QString>> &containerSelectionConfig)
     458                 :             : {
     459                 :          52 :     d->containerSelectionConfig = containerSelectionConfig;
     460                 :          52 : }
     461                 :             : 
     462                 :           1 : QJSValue ApplicationManager::containerSelectionFunction() const
     463                 :             : {
     464                 :           1 :     return d->containerSelectionFunction;
     465                 :             : }
     466                 :             : 
     467                 :           1 : void ApplicationManager::setContainerSelectionFunction(const QJSValue &callback)
     468                 :             : {
     469   [ +  -  +  - ]:           1 :     if (callback.isCallable() && !callback.equals(d->containerSelectionFunction)) {
     470                 :           1 :         d->containerSelectionFunction = callback;
     471                 :           1 :         emit containerSelectionFunctionChanged();
     472                 :             :     }
     473                 :           1 : }
     474                 :             : 
     475                 :          90 : bool ApplicationManager::isWindowManagerCompositorReady() const
     476                 :             : {
     477                 :           1 :     return d->windowManagerCompositorReady;
     478                 :             : }
     479                 :             : 
     480                 :          25 : void ApplicationManager::setWindowManagerCompositorReady(bool ready)
     481                 :             : {
     482         [ +  - ]:          25 :     if (d->windowManagerCompositorReady != ready) {
     483                 :          25 :         d->windowManagerCompositorReady = ready;
     484                 :          25 :         emit windowManagerCompositorReadyChanged(ready);
     485                 :             :     }
     486                 :          25 : }
     487                 :             : 
     488                 :           0 : QStringList ApplicationManager::availableRuntimeIds() const
     489                 :             : {
     490                 :           0 :     return RuntimeFactory::instance()->runtimeIds();
     491                 :             : }
     492                 :             : 
     493                 :           2 : QStringList ApplicationManager::availableContainerIds() const
     494                 :             : {
     495                 :           2 :     return ContainerFactory::instance()->containerIds();
     496                 :             : }
     497                 :             : 
     498                 :           0 : QVector<Application *> ApplicationManager::applications() const
     499                 :             : {
     500                 :           0 :     return d->apps;
     501                 :             : }
     502                 :             : 
     503                 :         339 : Application *ApplicationManager::fromId(const QString &id) const
     504                 :             : {
     505         [ +  + ]:         617 :     for (Application *app : std::as_const(d->apps)) {
     506         [ +  + ]:         599 :         if (app->id() == id)
     507                 :         321 :             return app;
     508                 :             :     }
     509                 :             :     return nullptr;
     510                 :             : }
     511                 :             : 
     512                 :          82 : QVector<Application *> ApplicationManager::fromProcessId(qint64 pid) const
     513                 :             : {
     514                 :          82 :     QVector<Application *> apps;
     515                 :             : 
     516                 :             :     // pid could be an indirect child (e.g. when started via gdbserver)
     517         [ +  - ]:          82 :     qint64 appmanPid = QCoreApplication::applicationPid();
     518                 :             : 
     519                 :             :     int level = 0;
     520   [ +  +  +  - ]:         166 :     while ((pid > 1) && (pid != appmanPid) && (level < 5)) {
     521         [ +  + ]:         395 :         for (Application *app : std::as_const(d->apps)) {
     522         [ -  + ]:         311 :             if (apps.contains(app))
     523                 :           0 :                 continue;
     524   [ +  +  +  -  :         311 :             if (app->currentRuntime() && (app->currentRuntime()->applicationProcessId() == pid))
                   +  + ]
     525         [ +  - ]:          82 :                 apps.append(app);
     526                 :             :         }
     527         [ +  - ]:          84 :         pid = getParentPid(pid);
     528                 :          84 :         ++level;
     529                 :             :     }
     530                 :          82 :     return apps;
     531                 :           0 : }
     532                 :             : 
     533                 :           0 : Application *ApplicationManager::fromSecurityToken(const QByteArray &securityToken) const
     534                 :             : {
     535         [ #  # ]:           0 :     if (securityToken.size() != AbstractRuntime::SecurityTokenSize)
     536                 :             :         return nullptr;
     537                 :             : 
     538         [ #  # ]:           0 :     for (Application *app : std::as_const(d->apps)) {
     539   [ #  #  #  #  :           0 :         if (app->currentRuntime() && app->currentRuntime()->securityToken() == securityToken)
             #  #  #  # ]
     540                 :           0 :             return app;
     541                 :             :     }
     542                 :             :     return nullptr;
     543                 :             : }
     544                 :             : 
     545                 :           4 : QVector<Application *> ApplicationManager::schemeHandlers(const QString &scheme) const
     546                 :             : {
     547                 :           4 :     QVector<Application *> handlers;
     548                 :             : 
     549         [ +  + ]:          16 :     for (Application *app : std::as_const(d->apps)) {
     550         [ +  - ]:          12 :         const auto mimeTypes = app->supportedMimeTypes();
     551         [ +  + ]:          20 :         for (const QString &mime : mimeTypes) {
     552         [ +  - ]:           8 :             auto pos = mime.indexOf(u'/');
     553                 :             : 
     554                 :          16 :             if ((pos > 0)
     555   [ +  -  +  +  :          16 :                     && (mime.left(pos) == u"x-scheme-handler")
                   +  + ]
     556   [ +  -  +  -  :          20 :                     && (mime.mid(pos + 1) == scheme)) {
          -  +  +  +  +  
                -  -  - ]
     557         [ +  - ]:          12 :                 handlers << app;
     558                 :             :             }
     559                 :             :         }
     560                 :          12 :     }
     561                 :           4 :     return handlers;
     562                 :           0 : }
     563                 :             : 
     564                 :           4 : QVector<Application *> ApplicationManager::mimeTypeHandlers(const QString &mimeType) const
     565                 :             : {
     566                 :           4 :     QVector<Application *> handlers;
     567                 :             : 
     568         [ +  + ]:          16 :     for (Application *app : std::as_const(d->apps)) {
     569   [ +  -  +  + ]:          24 :         if (app->supportedMimeTypes().contains(mimeType))
     570         [ +  - ]:          16 :             handlers << app;
     571                 :             :     }
     572                 :           4 :     return handlers;
     573                 :           0 : }
     574                 :             : 
     575                 :          27 : QVariantMap ApplicationManager::get(Application *app) const
     576                 :             : {
     577                 :          27 :     QVariantMap map;
     578         [ +  + ]:          27 :     if (app) {
     579         [ +  - ]:          25 :         const QHash<int, QByteArray> roles = roleNames();
     580         [ +  + ]:         550 :         for (auto it = roles.begin(); it != roles.end(); ++it)
     581   [ +  -  +  -  :        1050 :             map.insert(QString::fromLatin1(it.value()), dataForRole(app, it.key()));
                   +  - ]
     582                 :          25 :     }
     583                 :          27 :     return map;
     584                 :           0 : }
     585                 :             : 
     586                 :         131 : void ApplicationManager::registerMimeTypes()
     587                 :             : {
     588                 :             : #if defined(QT_GUI_LIB)
     589                 :         131 :     QSet<QString> schemes;
     590   [ +  -  +  -  :         131 :     schemes << u"file"_s << u"http"_s << u"https"_s;
                   +  - ]
     591                 :             : 
     592         [ +  + ]:         343 :     for (Application *app : std::as_const(d->apps)) {
     593         [ +  - ]:         212 :         const auto mimeTypes = app->supportedMimeTypes();
     594         [ +  + ]:         224 :         for (const QString &mime : mimeTypes) {
     595         [ +  - ]:          12 :             auto pos = mime.indexOf(u'/');
     596                 :             : 
     597   [ +  -  +  -  :          24 :             if ((pos > 0) && (mime.left(pos) == u"x-scheme-handler"))
          +  +  +  -  +  
                      + ]
     598         [ +  - ]:          12 :                 schemes << mime.mid(pos + 1);
     599                 :             :         }
     600                 :         212 :     }
     601         [ +  - ]:         131 :     QSet<QString> registerSchemes = schemes;
     602         [ +  - ]:         131 :     registerSchemes.subtract(d->registeredMimeSchemes);
     603         [ +  + ]:         131 :     QSet<QString> unregisterSchemes = d->registeredMimeSchemes;
     604         [ +  - ]:         131 :     unregisterSchemes.subtract(schemes);
     605                 :             : 
     606   [ +  -  -  - ]:         131 :     for (const QString &scheme : std::as_const(unregisterSchemes))
     607         [ #  # ]:           0 :         QDesktopServices::unsetUrlHandler(scheme);
     608   [ +  +  +  - ]:         274 :     for (const QString &scheme : std::as_const(registerSchemes))
     609         [ +  - ]:         143 :         QDesktopServices::setUrlHandler(scheme, this, "openUrlRelay");
     610                 :             : 
     611                 :         131 :     d->registeredMimeSchemes = schemes;
     612                 :             : #endif
     613                 :         131 : }
     614                 :             : 
     615                 :         211 : bool ApplicationManager::startApplicationInternal(const QString &appId, const QString &documentUrl,
     616                 :             :                                                   const QString &documentMimeType,
     617                 :             :                                                   const QString &debugWrapperSpecification,
     618                 :             :                                                   QVector<int> &&stdioRedirections)  noexcept(false)
     619                 :             : {
     620                 :         422 :     auto redirectionGuard = qScopeGuard([&stdioRedirections]() {
     621                 :         211 :         closeAndClearFileDescriptors(stdioRedirections);
     622         [ -  + ]:         211 :     });
     623                 :             : 
     624         [ -  + ]:         211 :     if (d->shuttingDown)
     625                 :           0 :         throw Exception("Cannot start applications during shutdown");
     626   [ +  -  +  + ]:         211 :     QPointer<Application> app = fromId(appId);
     627         [ +  + ]:         211 :     if (!app)
     628                 :           2 :         throw Exception("Cannot start application: id '%1' is not known").arg(appId);
     629   [ +  -  -  + ]:         209 :     if (app->isBlocked())
     630         [ #  # ]:           0 :         throw Exception("Application %1 is blocked - cannot start").arg( app->id());
     631                 :             : 
     632         [ +  + ]:         209 :     AbstractRuntime *runtime = app->currentRuntime();
     633   [ +  +  +  -  :         215 :     auto runtimeManager = runtime ? runtime->manager() : RuntimeFactory::instance()->manager(app->runtimeName());
          +  -  +  -  +  
                      - ]
     634         [ +  + ]:         209 :     if (!runtimeManager)
     635         [ +  - ]:           1 :         throw Exception("No RuntimeManager found for runtime: %1").arg(app->runtimeName());
     636         [ +  - ]:         208 :     bool inProcess = runtimeManager->inProcess();
     637                 :             : 
     638                 :             :     // validate stdio redirections
     639         [ -  + ]:         208 :     if (stdioRedirections.size() > 3) {
     640                 :           0 :         throw Exception("Tried to start application %1 using an invalid standard IO redirection specification")
     641         [ #  # ]:           0 :                 .arg(app->id());
     642                 :             :     }
     643         [ +  + ]:         208 :     bool hasStdioRedirections = !stdioRedirections.isEmpty();
     644         [ +  + ]:         208 :     if (hasStdioRedirections) {
     645                 :             :         // we have an array - check if it just consists of -1 fds
     646                 :           2 :         hasStdioRedirections = false;
     647                 :           2 :         std::for_each(stdioRedirections.cbegin(), stdioRedirections.cend(), [&hasStdioRedirections](int fd) {
     648         [ -  + ]:           6 :             if (fd >= 0)
     649                 :           0 :                 hasStdioRedirections = true;
     650                 :             :         });
     651                 :             :     }
     652                 :             : 
     653                 :             :     // validate the debug-wrapper
     654                 :         208 :     QStringList debugWrapperCommand;
     655                 :         208 :     QMap<QString, QString> debugEnvironmentVariables;
     656         [ +  + ]:         208 :     if (!debugWrapperSpecification.isEmpty()) {
     657         [ +  + ]:           5 :         if (isSingleProcess())
     658                 :           1 :             throw Exception("Using debug-wrappers is not supported when the application manager is running in single-process mode.");
     659         [ -  + ]:           4 :         if (inProcess) {
     660                 :           0 :             throw Exception("Using debug-wrappers is not supported when starting an app using an in-process runtime (%1).")
     661         [ #  # ]:           0 :                 .arg(runtimeManager->identifier());
     662                 :             :         }
     663                 :             : 
     664   [ +  -  +  + ]:           4 :         if (!DebugWrapper::parseSpecification(debugWrapperSpecification, debugWrapperCommand,
     665                 :             :                                               debugEnvironmentVariables)) {
     666                 :           1 :             throw Exception("Tried to start application %1 using an invalid debug-wrapper specification: %2")
     667         [ +  - ]:           2 :                     .arg(app->id(), debugWrapperSpecification);
     668                 :             :         }
     669                 :             :     }
     670                 :             : 
     671         [ +  + ]:         206 :     if (runtime) {
     672   [ +  -  +  -  :          54 :         switch (runtime->state()) {
                   -  - ]
     673                 :          54 :         case Am::StartingUp:
     674                 :          54 :         case Am::Running:
     675         [ +  + ]:          54 :             if (!debugWrapperCommand.isEmpty()) {
     676                 :           1 :                 throw Exception("Application %1 is already running - cannot start with debug-wrapper: %2")
     677         [ +  - ]:           2 :                         .arg(app->id(), debugWrapperSpecification);
     678                 :             :             }
     679         [ -  + ]:          53 :             if (hasStdioRedirections) {
     680                 :           0 :                 throw Exception("Application %1 is already running - cannot set standard IO redirections")
     681         [ #  # ]:           0 :                         .arg(app->id());
     682                 :             :             }
     683         [ +  - ]:          53 :             if (!documentUrl.isNull())
     684         [ +  - ]:          53 :                 runtime->openDocument(documentUrl, documentMimeType);
     685   [ #  #  #  # ]:           0 :             else if (!app->documentUrl().isNull())
     686   [ -  -  -  - ]:           3 :                 runtime->openDocument(app->documentUrl(), documentMimeType);
     687                 :             : 
     688         [ +  - ]:          53 :             emitActivated(app);
     689                 :             :             return true;
     690                 :             : 
     691                 :             :         case Am::ShuttingDown:
     692                 :             :             return false;
     693                 :             : 
     694                 :           0 :         case Am::NotRunning:
     695                 :           0 :             throw Exception("Application %1 is not running, but still has a Runtime object attached")
     696         [ #  # ]:           0 :                     .arg(app->id());
     697                 :             :         }
     698                 :             :     }
     699                 :             : 
     700                 :         152 :     AbstractContainer *container = nullptr;
     701                 :         205 :     QString containerId;
     702                 :             : 
     703         [ +  + ]:         152 :     if (!inProcess) {
     704         [ +  + ]:          89 :         if (d->containerSelectionConfig.isEmpty()) {
     705                 :          88 :             containerId = u"process"_s;
     706                 :             :         } else {
     707                 :             :             // check config file
     708         [ +  - ]:           1 :             for (const auto &it : std::as_const(d->containerSelectionConfig)) {
     709                 :           1 :                 const QString &key = it.first;
     710                 :           1 :                 const QString &value = it.second;
     711         [ -  + ]:           1 :                 bool hasAsterisk = key.contains(u'*');
     712                 :             : 
     713         [ #  # ]:           0 :                 if ((hasAsterisk && key.length() == 1)
     714   [ +  -  +  -  :           3 :                         || (!hasAsterisk && key == app->id())
             -  +  +  - ]
     715   [ -  +  -  -  :           2 :                         || QRegularExpression(QRegularExpression::wildcardToRegularExpression(key)).match(app->id()).hasMatch()) {
          -  -  -  -  -  
          -  -  -  -  -  
          -  +  -  +  -  
          +  -  +  +  -  
          -  -  -  -  -  
             -  -  -  -  
                      - ]
     716                 :           1 :                     containerId = value;
     717                 :           1 :                     break;
     718                 :             :                 }
     719                 :             :             }
     720                 :             :         }
     721                 :             : 
     722   [ +  -  +  + ]:          89 :         if (d->containerSelectionFunction.isCallable()) {
     723   [ +  -  +  -  :          56 :             QJSValueList args = { QJSValue(app->id()), QJSValue(containerId) };
             +  -  -  + ]
     724   [ +  -  +  - ]:          14 :             containerId = d->containerSelectionFunction.call(args).toString();
     725                 :          14 :         }
     726                 :             : 
     727   [ +  -  +  -  :          89 :         if (!ContainerFactory::instance()->manager(containerId))
                   -  + ]
     728                 :           0 :             throw Exception("No ContainerManager found for container: %1").arg(containerId);
     729                 :             :     }
     730                 :         152 :     bool attachRuntime = false;
     731                 :             : 
     732         [ +  - ]:         152 :     if (!runtime) {
     733         [ +  + ]:         152 :         if (!inProcess) {
     734   [ +  -  +  + ]:          89 :             if (QuickLauncher::instance()) {
     735                 :             :                 // we cannot use the quicklaunch pool, if
     736                 :             :                 //  (a) a debug-wrapper is being used,
     737                 :             :                 //  (b) stdio is redirected or
     738                 :             :                 //  (c) the app requests special environment variables or
     739                 :             :                 //  (d) the app requests a different OpenGL config from the AM
     740                 :          11 :                 const char *cannotUseQuickLaunch = nullptr;
     741                 :             : 
     742         [ +  - ]:          11 :                 if (!debugWrapperCommand.isEmpty())
     743                 :             :                     cannotUseQuickLaunch = "the app is started using a debug-wrapper";
     744         [ +  - ]:          11 :                 else if (hasStdioRedirections)
     745                 :             :                     cannotUseQuickLaunch = "standard I/O is redirected";
     746   [ +  -  +  -  :          22 :                 else if (!app->runtimeParameters().value(u"environmentVariables"_s).toMap().isEmpty())
             +  -  +  - ]
     747                 :             :                     cannotUseQuickLaunch = "the app requests custom environment variables";
     748   [ +  -  +  -  :          11 :                 else if (app->info()->openGLConfiguration() != GlobalRuntimeConfiguration::instance().openGLConfiguration)
          +  -  +  -  +  
                      - ]
     749                 :             :                     cannotUseQuickLaunch = "the app requests a custom OpenGL configuration";
     750                 :             : 
     751                 :          11 :                 if (cannotUseQuickLaunch) {
     752   [ #  #  #  #  :           0 :                     qCDebug(LogSystem) << "Cannot use quick-launch for application" << app->id()
          #  #  #  #  #  
                #  #  # ]
     753   [ #  #  #  # ]:           0 :                                        << "because" << cannotUseQuickLaunch;
     754                 :             :                 } else {
     755                 :             :                     // check quicklaunch pool
     756                 :          11 :                     QPair<AbstractContainer *, AbstractRuntime *> quickLaunch =
     757   [ +  -  +  -  :          11 :                             QuickLauncher::instance()->take(containerId, app->info()->runtimeName());
             +  -  +  - ]
     758                 :          11 :                     container = quickLaunch.first;
     759                 :          11 :                     runtime = quickLaunch.second;
     760                 :             : 
     761         [ +  - ]:          11 :                     if (container || runtime) {
     762   [ +  -  -  -  :          22 :                         qCDebug(LogSystem) << "Found a quick-launch entry for container" << containerId
          -  -  -  -  -  
                      + ]
     763   [ #  #  #  #  :           0 :                                            << "and runtime" << app->info()->runtimeName() << "->"
          #  #  #  #  #  
                #  #  # ]
     764   [ #  #  #  # ]:           0 :                                            << container << runtime;
     765                 :             : 
     766         [ -  + ]:          11 :                         if (!container && runtime) {
     767         [ #  # ]:           0 :                             runtime->deleteLater();
     768   [ #  #  #  #  :           0 :                             qCCritical(LogSystem) << "ERROR: QuickLauncher provided a runtime without a container.";
             #  #  #  # ]
     769                 :           0 :                             return false;
     770                 :             :                         }
     771                 :             :                     }
     772                 :             :                 }
     773                 :             :             }
     774                 :             : 
     775         [ -  + ]:          11 :             if (!container) {
     776   [ +  -  +  - ]:          78 :                 container = ContainerFactory::instance()->create(containerId, app, std::move(stdioRedirections),
     777                 :             :                                                                  debugEnvironmentVariables, debugWrapperCommand);
     778                 :             :             } else {
     779         [ +  - ]:          11 :                 container->setApplication(app);
     780                 :             :             }
     781         [ -  + ]:          89 :             if (!container) {
     782   [ #  #  #  #  :           0 :                 qCCritical(LogSystem) << "ERROR: Couldn't create Container for Application (" << app->id() <<")!";
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     783                 :             :                 return false;
     784                 :             :             }
     785         [ +  + ]:          89 :             if (runtime)
     786                 :           9 :                 attachRuntime = true;
     787                 :             : 
     788                 :             :         } else { // inProcess
     789         [ -  + ]:          63 :             if (hasStdioRedirections) {
     790                 :           0 :                 static const char *noRedirectMsg = "NOTE: redirecting standard IO is not possible for in-process runtimes";
     791                 :             : 
     792         [ #  # ]:           0 :                 int fd = stdioRedirections.value(2, -1);
     793         [ #  # ]:           0 :                 if (fd < 0)
     794         [ #  # ]:           0 :                     fd = stdioRedirections.value(1, -1);
     795         [ #  # ]:           0 :                 if (fd >= 0) {
     796   [ #  #  #  # ]:           0 :                     auto dummy = write(fd, noRedirectMsg, qstrlen(noRedirectMsg));
     797         [ #  # ]:           0 :                     dummy += write(fd, "\n", 1);
     798                 :             :                     Q_UNUSED(dummy)
     799                 :             :                 }
     800   [ #  #  #  #  :           0 :                 qCWarning(LogSystem) << noRedirectMsg;
             #  #  #  # ]
     801                 :             :             }
     802                 :             :         }
     803                 :             : 
     804         [ +  + ]:         152 :         if (!runtime)
     805   [ +  -  +  - ]:         143 :             runtime = RuntimeFactory::instance()->create(container, app);
     806                 :             : 
     807         [ +  - ]:         152 :         if (runtime)
     808         [ +  - ]:         152 :             emit internalSignals.newRuntimeCreated(runtime);
     809                 :             :     }
     810                 :             : 
     811         [ -  + ]:         152 :     if (!runtime) {
     812   [ #  #  #  #  :           0 :         qCCritical(LogSystem) << "ERROR: Couldn't create Runtime for Application (" << app->id() <<")!";
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     813                 :             :         return false;
     814                 :             :     }
     815                 :             : 
     816                 :             :     // if an app is stopped because of a removal and the container is slow to stop, we might
     817                 :             :     // end up with a dead app pointer in this callback at some point
     818   [ +  -  +  - ]:         456 :     connect(runtime, &AbstractRuntime::stateChanged, this, [this, app, appId](Am::RunState newRuntimeState) {
     819         [ +  - ]:         583 :         if (app)
     820                 :         583 :             app->setRunState(newRuntimeState);
     821                 :         583 :         emit applicationRunStateChanged(appId, newRuntimeState);
     822         [ +  - ]:         583 :         if (app)
     823         [ +  - ]:        1166 :             emitDataChanged(app, QVector<int> { AMRoles::IsRunning, AMRoles::IsStartingUp, AMRoles::IsShuttingDown });
     824                 :         583 :     });
     825                 :             : 
     826         [ +  + ]:         152 :     if (!documentUrl.isNull())
     827         [ +  - ]:          39 :         runtime->openDocument(documentUrl, documentMimeType);
     828   [ +  -  -  + ]:         113 :     else if (!app->documentUrl().isNull())
     829   [ #  #  #  # ]:           0 :         runtime->openDocument(app->documentUrl(), documentMimeType);
     830                 :             : 
     831                 :         152 :     Q_ASSERT(inProcess == containerId.isEmpty());
     832                 :             : 
     833         [ +  - ]:          63 :     const QString containerInfo = (containerId.isEmpty() && inProcess)
     834         [ +  + ]:         152 :                                       ? u"in-process"_s
     835   [ +  +  +  -  :         545 :                                       : u"in container \""_s + containerId + u'"';
          +  +  +  +  +  
                      + ]
     836                 :             : 
     837   [ +  -  -  -  :         304 :     qCDebug(LogSystem).noquote().nospace()
             -  -  -  + ]
     838   [ #  #  #  #  :           0 :         << "Starting application \"" << app->id() << "\" " << containerInfo
          #  #  #  #  #  
                      # ]
     839   [ #  #  #  #  :           0 :         << " using runtime \"" << runtimeManager->identifier() << '"';
             #  #  #  # ]
     840         [ +  + ]:         152 :     if (!documentUrl.isEmpty())
     841   [ +  -  -  -  :          74 :         qCDebug(LogSystem) << "  documentUrl:" << documentUrl;
          -  -  -  -  -  
                      + ]
     842                 :             : 
     843                 :             :     // We can only start the app when both the container and the windowmanager are ready.
     844                 :             :     // Using a state-machine would be one option, but then we would need that state-machine
     845                 :             :     // object plus the per-app state. Relying on 2 lambdas is the easier choice for now.
     846                 :             : 
     847                 :         304 :     auto doStartInContainer = [this, app, attachRuntime, runtime, containerInfo]() -> bool {
     848                 :         152 :         bool successfullyStarted = false;
     849         [ +  - ]:         152 :         if (app) {
     850         [ +  + ]:         152 :             successfullyStarted = attachRuntime ? runtime->attachApplicationToQuickLauncher(app)
     851                 :         143 :                                                 : runtime->start();
     852                 :             :         }
     853         [ +  + ]:         152 :         if (successfullyStarted) {
     854                 :         150 :             emitActivated(app);
     855                 :             :         } else {
     856   [ +  -  +  + ]:           6 :             qCWarning(LogSystem).noquote().nospace()
     857   [ +  -  +  -  :           4 :                 << "Failed to start application \"" << app->id() << "\" " << containerInfo
          +  -  +  -  +  
                      - ]
     858   [ +  -  +  -  :           4 :                 << " using runtime \"" << runtime->manager()->identifier() << '"';
          +  -  +  -  +  
                      - ]
     859         [ +  - ]:           2 :             delete runtime; // ~Runtime() will clean up app->m_runtime
     860                 :             :         }
     861                 :         152 :         return successfullyStarted;
     862         [ +  - ]:         456 :     };
     863                 :             : 
     864                 :         152 :     auto tryStartInContainer = [this, container, inProcess, doStartInContainer]() -> bool {
     865   [ +  +  +  - ]:         152 :         if (inProcess || container->isReady()) {
     866                 :             :             // Since the container is already ready, start the app immediately
     867                 :         152 :             return doStartInContainer();
     868                 :             :         } else {
     869                 :             :             // We postpone the starting of the application to a later point in time,
     870                 :             :             // since the container is not ready yet
     871                 :             :             // since the container is not ready yet
     872                 :             : #if defined(Q_CC_MSVC)
     873                 :             :             qApp->connect(container, &AbstractContainer::ready, this, doStartInContainer); // MSVC cannot distinguish between static and non-static overloads in lambdas
     874                 :             : #else
     875                 :           0 :             connect(container, &AbstractContainer::ready, this, doStartInContainer);
     876                 :             : #endif
     877                 :           0 :             return true;
     878                 :             :         }
     879                 :         304 :     };
     880                 :             : 
     881   [ +  +  -  + ]:         152 :     if (inProcess || isWindowManagerCompositorReady()) {
     882         [ +  - ]:         152 :         return tryStartInContainer();
     883                 :             :     } else {
     884         [ #  # ]:           0 :         connect(this, &ApplicationManager::windowManagerCompositorReadyChanged, tryStartInContainer);
     885                 :           0 :         return true;
     886                 :             :     }
     887                 :         217 : }
     888                 :             : 
     889                 :         104 : void ApplicationManager::stopApplicationInternal(Application *app, bool forceKill)
     890                 :             : {
     891         [ +  - ]:         104 :     if (!app)
     892                 :             :         return;
     893         [ +  + ]:         104 :     AbstractRuntime *rt = app->currentRuntime();
     894         [ +  + ]:         104 :     if (rt)
     895                 :          96 :         rt->stop(forceKill);
     896                 :             : }
     897                 :             : 
     898                 :             : /*!
     899                 :             :     \qmlmethod bool ApplicationManager::startApplication(string id, string document)
     900                 :             : 
     901                 :             :     Instructs the application manager to start the application identified by its unique \a id. The
     902                 :             :     optional argument \a document will be supplied to the application as is - most commonly this
     903                 :             :     is used to refer to a document to display.
     904                 :             :     Returns \c true if the application \a id is valid and the application manager was able to start
     905                 :             :     the runtime plugin. Returns \c false otherwise. Note that even though this call may
     906                 :             :     indicate success, the application may still later fail to start correctly as the actual
     907                 :             :     startup process within the runtime plugin may be asynchronous.
     908                 :             : 
     909                 :             :     \sa ApplicationObject::start
     910                 :             : */
     911                 :         197 : bool ApplicationManager::startApplication(const QString &id, const QString &documentUrl)
     912                 :             : {
     913                 :         197 :     try {
     914         [ +  + ]:         200 :         return startApplicationInternal(id, documentUrl);
     915         [ -  + ]:           3 :     } catch (const Exception &e) {
     916   [ +  -  +  -  :           9 :         qCWarning(LogSystem) << e.what();
             +  -  +  + ]
     917                 :           3 :         return false;
     918                 :           3 :     }
     919                 :             : }
     920                 :             : 
     921                 :             : /*!
     922                 :             :     \qmlmethod bool ApplicationManager::debugApplication(string id, string debugWrapper, string document)
     923                 :             : 
     924                 :             :     Instructs the application manager to start the application identified by its unique \a id, just
     925                 :             :     like startApplication. The application is started via the given \a debugWrapper though. The
     926                 :             :     optional argument \a document will be supplied to the application as is - most commonly this is
     927                 :             :     used to refer to a document to display.
     928                 :             : 
     929                 :             :     Returns a \c bool value indicating success. See the full documentation at
     930                 :             :     ApplicationManager::startApplication for more information.
     931                 :             : 
     932                 :             :     Please see the \l{Debugging} page for more information on how to setup and use these
     933                 :             :     debug-wrappers.
     934                 :             : 
     935                 :             :     \sa ApplicationObject::debug
     936                 :             : */
     937                 :             : 
     938                 :           4 : bool ApplicationManager::debugApplication(const QString &id, const QString &debugWrapper, const QString &documentUrl)
     939                 :             : {
     940                 :           4 :     try {
     941         [ +  + ]:           7 :         return startApplicationInternal(id, documentUrl, QString(), debugWrapper);
     942         [ -  + ]:           3 :     } catch (const Exception &e) {
     943   [ +  -  +  -  :           9 :         qCWarning(LogSystem) << e.what();
             +  -  +  + ]
     944                 :           3 :         return false;
     945                 :           3 :     }
     946                 :             : }
     947                 :             : 
     948                 :             : /*!
     949                 :             :     \qmlmethod ApplicationManager::stopApplication(string id, bool forceKill)
     950                 :             : 
     951                 :             :     Tells the application manager to stop an application identified by its unique \a id. The
     952                 :             :     meaning of the \a forceKill parameter is runtime dependent, but in general you should always try
     953                 :             :     to stop an application with \a forceKill set to \c false first in order to allow a clean
     954                 :             :     shutdown. Use \a forceKill set to \c true only as a last resort to kill hanging applications.
     955                 :             : 
     956                 :             :     QML applications and native applications that \l {manifest supportsApplicationInterface}
     957                 :             :     {support the ApplicationInterface} will be notified via ApplicationInterface::quit().
     958                 :             :     All other applications will be sent the Unix \c TERM signal.
     959                 :             : 
     960                 :             :     \sa ApplicationObject::stop
     961                 :             : */
     962                 :         104 : void ApplicationManager::stopApplication(const QString &id, bool forceKill)
     963                 :             : {
     964                 :         104 :     stopApplicationInternal(fromId(id), forceKill);
     965                 :         104 : }
     966                 :             : 
     967                 :             : /*!
     968                 :             :     \qmlmethod ApplicationManager::stopAllApplications(bool forceKill)
     969                 :             : 
     970                 :             :     Tells the application manager to stop all running applications. The meaning of the \a forceKill
     971                 :             :     parameter is runtime dependent, but in general you should always try to stop an application
     972                 :             :     with \a forceKill set to \c false first in order to allow a clean shutdown.
     973                 :             :     Use \a forceKill set to \c true only as a last resort to kill hanging applications.
     974                 :             : 
     975                 :             :     \sa stopApplication
     976                 :             : */
     977                 :          40 : void ApplicationManager::stopAllApplications(bool forceKill)
     978                 :             : {
     979         [ +  + ]:         275 :     for (Application *app : std::as_const(d->apps)) {
     980         [ +  + ]:         235 :         AbstractRuntime *rt = app->currentRuntime();
     981         [ +  + ]:         235 :         if (rt)
     982                 :          27 :             rt->stop(forceKill);
     983                 :             :     }
     984                 :          40 : }
     985                 :             : 
     986                 :             : /*!
     987                 :             :     \qmlmethod bool ApplicationManager::openUrl(string url)
     988                 :             : 
     989                 :             :     Tries start an application that is capable of handling \a url. The application manager will
     990                 :             :     first look at the URL's scheme:
     991                 :             :     \list
     992                 :             :     \li If it is \c{file:}, the operating system's MIME database will be consulted, which will
     993                 :             :         try to find a MIME type match, based on file endings or file content. In case this is
     994                 :             :         successful, the application manager will use this MIME type to find all of its applications
     995                 :             :         that claim support for it (see the \l{mimeTypes field} in the application's manifest).
     996                 :             :         A music player application that can handle \c mp3 and \c wav files, could add this to its
     997                 :             :         manifest:
     998                 :             :         \badcode
     999                 :             :         mimeTypes: [ 'audio/mpeg', 'audio/wav' ]
    1000                 :             :         \endcode
    1001                 :             :     \li If it is something other than \c{file:}, the application manager will consult its
    1002                 :             :         internal database of applications that claim support for a matching \c{x-scheme-handler/...}
    1003                 :             :         MIME type. In order to have your web-browser application handle \c{http:} and \c{https:}
    1004                 :             :         URLs, you would have to have this in your application's manifest:
    1005                 :             :         \badcode
    1006                 :             :         mimeTypes: [ 'x-scheme-handler/http', 'x-scheme-handler/https' ]
    1007                 :             :         \endcode
    1008                 :             :     \endlist
    1009                 :             : 
    1010                 :             :     If there is at least one possible match, it depends on the signal openUrlRequested() being
    1011                 :             :     connected within the System UI:
    1012                 :             :     In case the signal is not connected, an arbitrary application from the matching set will be
    1013                 :             :     started. Otherwise the application manager will emit the openUrlRequested signal and
    1014                 :             :     return \c true. It is up to the receiver of this signal to choose from one of possible
    1015                 :             :     applications via acknowledgeOpenUrlRequest or deny the request entirely via rejectOpenUrlRequest.
    1016                 :             :     Not calling one of these two functions will result in memory leaks.
    1017                 :             : 
    1018                 :             :     If an application is started by one of the two mechanisms, the \a url is supplied to
    1019                 :             :     the application as a document to open via its ApplicationInterface.
    1020                 :             : 
    1021                 :             :     Returns \c true, if a match was found in the database, or \c false otherwise.
    1022                 :             : 
    1023                 :             :     \sa openUrlRequested, acknowledgeOpenUrlRequest, rejectOpenUrlRequest
    1024                 :             : */
    1025                 :           8 : bool ApplicationManager::openUrl(const QString &urlStr)
    1026                 :             : {
    1027                 :             :     //TODO: relay to a well-known Intent call
    1028                 :             : 
    1029                 :             :     // QDesktopServices::openUrl has a special behavior when called recursively, which makes sense
    1030                 :             :     // on the desktop, but is completely counter-productive for the AM.
    1031                 :           8 :     static bool recursionGuard = false;
    1032         [ -  + ]:           8 :     if (recursionGuard) {
    1033         [ #  # ]:           0 :         QMetaObject::invokeMethod(this, [urlStr, this]() { openUrl(urlStr); }, Qt::QueuedConnection);
    1034                 :           0 :         return true; // this is not correct, but the best we can do in this situation
    1035                 :             :     }
    1036                 :           8 :     recursionGuard = true;
    1037                 :             : 
    1038                 :           8 :     QUrl url(urlStr);
    1039                 :           8 :     QString mimeTypeName;
    1040                 :           8 :     QVector<Application *> apps;
    1041                 :             : 
    1042   [ +  -  +  - ]:           8 :     if (url.isValid()) {
    1043         [ +  - ]:           8 :         QString scheme = url.scheme();
    1044         [ +  + ]:           8 :         if (scheme != u"file")
    1045         [ +  - ]:           8 :             apps = schemeHandlers(scheme);
    1046                 :             : 
    1047         [ +  + ]:           8 :         if (apps.isEmpty()) {
    1048         [ +  - ]:           4 :             QMimeDatabase mdb;
    1049         [ +  - ]:           4 :             QMimeType mt = mdb.mimeTypeForUrl(url);
    1050         [ +  - ]:           4 :             mimeTypeName = mt.name();
    1051                 :             : 
    1052         [ +  - ]:           8 :             apps = mimeTypeHandlers(mimeTypeName);
    1053                 :           4 :         }
    1054                 :           8 :     }
    1055                 :             : 
    1056         [ +  - ]:           8 :     if (!apps.isEmpty()) {
    1057   [ +  -  +  -  :           8 :         if (!isSignalConnected(QMetaMethod::fromSignal(&ApplicationManager::openUrlRequested))) {
                   +  - ]
    1058                 :             :             // If the System UI does not react to the signal, then just use the first match.
    1059                 :           8 :             try {
    1060   [ +  -  +  - ]:          16 :                 startApplicationInternal(apps.constFirst()->id(), urlStr, mimeTypeName);
    1061         [ -  - ]:           0 :             } catch (const Exception &e) {
    1062   [ -  -  -  -  :           0 :                 qCWarning(LogSystem) << "openUrl for" << urlStr << "requested app" << apps.constFirst()->id()
          -  -  -  -  -  
          -  -  -  -  -  
                   -  - ]
    1063   [ -  -  -  - ]:           0 :                                      << "which could not be started:" << e.errorString();
    1064                 :           0 :             }
    1065                 :             :         } else {
    1066                 :           0 :             ApplicationManagerPrivate::OpenUrlRequest req {
    1067         [ #  # ]:           0 :                 QUuid::createUuid().toString(),
    1068                 :             :                         urlStr,
    1069                 :             :                         mimeTypeName,
    1070                 :             :                         QStringList()
    1071         [ #  # ]:           0 :             };
    1072         [ #  # ]:           0 :             for (const auto &app : std::as_const(apps))
    1073         [ #  # ]:           0 :                 req.possibleAppIds << app->id();
    1074         [ #  # ]:           0 :             d->openUrlRequests << req;
    1075                 :             : 
    1076         [ #  # ]:           0 :             emit openUrlRequested(req.requestId, req.urlStr, req.mimeTypeName, req.possibleAppIds);
    1077                 :           0 :         }
    1078                 :             :     }
    1079                 :             : 
    1080                 :           8 :     recursionGuard = false;
    1081                 :           8 :     return !apps.isEmpty();
    1082                 :           8 : }
    1083                 :             : 
    1084                 :             : /*!
    1085                 :             :     \qmlsignal ApplicationManager::openUrlRequested(string requestId, string url, string mimeType, list<string> possibleAppIds)
    1086                 :             : 
    1087                 :             :     This signal is emitted when the application manager is requested to open an URL. This can happen
    1088                 :             :     by calling
    1089                 :             :     \list
    1090                 :             :     \li Qt.openUrlExternally in an application,
    1091                 :             :     \li Qt.openUrlExternally in the System UI,
    1092                 :             :     \li ApplicationManager::openUrl in the System UI or
    1093                 :             :     \li \c io.qt.ApplicationManager.openUrl via D-Bus
    1094                 :             :     \endlist
    1095                 :             :     \note This signal is only emitted, if there is a receiver connected at all - see openUrl for the
    1096                 :             :           fallback behavior.
    1097                 :             : 
    1098                 :             :     The receiver of this signal can inspect the requested \a url and its \a mimeType. It can then
    1099                 :             :     either call acknowledgeOpenUrlRequest to choose from one of the supplied \a possibleAppIds or
    1100                 :             :     rejectOpenUrlRequest to ignore the request. In both cases the unique \a requestId needs to be
    1101                 :             :     sent to identify the request.
    1102                 :             :     Not calling one of these two functions will result in memory leaks.
    1103                 :             : 
    1104                 :             :     \sa openUrl, acknowledgeOpenUrlRequest, rejectOpenUrlRequest
    1105                 :             : */
    1106                 :             : 
    1107                 :             : /*!
    1108                 :             :     \qmlmethod ApplicationManager::acknowledgeOpenUrlRequest(string requestId, string appId)
    1109                 :             : 
    1110                 :             :     Tells the application manager to go ahead with the request to open an URL, identified by \a
    1111                 :             :     requestId. The chosen \a appId needs to be one of the \c possibleAppIds supplied to the
    1112                 :             :     receiver of the openUrlRequested signal.
    1113                 :             : 
    1114                 :             :     \sa openUrl, openUrlRequested
    1115                 :             : */
    1116                 :           0 : void ApplicationManager::acknowledgeOpenUrlRequest(const QString &requestId, const QString &appId)
    1117                 :             : {
    1118         [ #  # ]:           0 :     for (auto it = d->openUrlRequests.cbegin(); it != d->openUrlRequests.cend(); ++it) {
    1119         [ #  # ]:           0 :         if (it->requestId == requestId) {
    1120         [ #  # ]:           0 :             if (it->possibleAppIds.contains(appId)) {
    1121                 :           0 :                 try {
    1122         [ #  # ]:           0 :                     startApplicationInternal(appId, it->urlStr, it->mimeTypeName);
    1123         [ -  - ]:           0 :                 } catch (const Exception &e) {
    1124   [ -  -  -  -  :           0 :                     qCWarning(LogSystem) << "acknowledgeOpenUrlRequest for" << it->urlStr << "requested app"
          -  -  -  -  -  
                -  -  - ]
    1125   [ -  -  -  -  :           0 :                                          << appId << "which could not be started:" << e.errorString();
                   -  - ]
    1126                 :           0 :                 }
    1127                 :             :             } else {
    1128   [ #  #  #  #  :           0 :                 qCWarning(LogSystem) << "acknowledgeOpenUrlRequest for" << it->urlStr << "requested app"
             #  #  #  # ]
    1129   [ #  #  #  #  :           0 :                                      << appId << "which is not one of the registered possibilities:"
                   #  # ]
    1130         [ #  # ]:           0 :                                      << it->possibleAppIds;
    1131                 :             :             }
    1132                 :           0 :             d->openUrlRequests.erase(it);
    1133                 :             :             break;
    1134                 :             :         }
    1135                 :             :     }
    1136                 :           0 : }
    1137                 :             : 
    1138                 :             : /*!
    1139                 :             :     \qmlmethod ApplicationManager::rejectOpenUrlRequest(string requestId)
    1140                 :             : 
    1141                 :             :     Tells the application manager to ignore the request to open an URL, identified by \a requestId.
    1142                 :             : 
    1143                 :             :     \sa openUrl, openUrlRequested
    1144                 :             : */
    1145                 :           0 : void ApplicationManager::rejectOpenUrlRequest(const QString &requestId)
    1146                 :             : {
    1147         [ #  # ]:           0 :     for (auto it = d->openUrlRequests.cbegin(); it != d->openUrlRequests.cend(); ++it) {
    1148         [ #  # ]:           0 :         if (it->requestId == requestId) {
    1149                 :           0 :             d->openUrlRequests.erase(it);
    1150                 :             :             break;
    1151                 :             :         }
    1152                 :             :     }
    1153                 :           0 : }
    1154                 :             : 
    1155                 :             : /*!
    1156                 :             :     \qmlmethod list<string> ApplicationManager::capabilities(string id)
    1157                 :             : 
    1158                 :             :     Returns a list of all capabilities granted by the user to the application identified by \a id.
    1159                 :             :     Returns an empty list if the application \a id is not valid.
    1160                 :             : */
    1161                 :           4 : QStringList ApplicationManager::capabilities(const QString &id) const
    1162                 :             : {
    1163                 :           4 :     Application *app = fromId(id);
    1164         [ +  - ]:           4 :     return app ? app->capabilities() : QStringList();
    1165                 :             : }
    1166                 :             : 
    1167                 :             : /*!
    1168                 :             :     \qmlmethod string ApplicationManager::identifyApplication(int pid)
    1169                 :             : 
    1170                 :             :     Validates the process running with process-identifier \a pid as a process started by the
    1171                 :             :     application manager.
    1172                 :             : 
    1173                 :             :     \note If multiple applications are running within the same container process, this function
    1174                 :             :           will return only the first matching application. See identifyAllApplications() for
    1175                 :             :           a way to retrieve all application ids.
    1176                 :             : 
    1177                 :             :     Returns the application's \c id on success, or an empty string on failure.
    1178                 :             : */
    1179                 :           0 : QString ApplicationManager::identifyApplication(qint64 pid) const
    1180                 :             : {
    1181                 :           0 :     const auto apps = fromProcessId(pid);
    1182   [ #  #  #  # ]:           0 :     return !apps.isEmpty() ? apps.constFirst()->id() : QString();
    1183                 :           0 : }
    1184                 :             : 
    1185                 :             : /*!
    1186                 :             :     \qmlmethod list<string> ApplicationManager::identifyAllApplications(int pid)
    1187                 :             : 
    1188                 :             :     Validates the process running with process-identifier \a pid as a process started by the
    1189                 :             :     application manager.
    1190                 :             : 
    1191                 :             :     If multiple applications are running within the same container process, this function will
    1192                 :             :     return all those application ids.
    1193                 :             : 
    1194                 :             :     Returns a list with the applications' \c ids on success, or an empty list on failure.
    1195                 :             : */
    1196                 :           0 : QStringList ApplicationManager::identifyAllApplications(qint64 pid) const
    1197                 :             : {
    1198                 :           0 :     const auto apps = fromProcessId(pid);
    1199                 :           0 :     QStringList result;
    1200                 :           0 :     result.reserve(apps.size());
    1201         [ #  # ]:           0 :     for (const auto &app : apps)
    1202         [ #  # ]:           0 :         result << app->id();
    1203                 :           0 :     return result;
    1204                 :           0 : }
    1205                 :             : 
    1206                 :          52 : void ApplicationManager::shutDown()
    1207                 :             : {
    1208                 :          52 :     d->shuttingDown = true;
    1209                 :          52 :     emit shuttingDownChanged();
    1210                 :             : 
    1211                 :         118 :     auto shutdownHelper = [this]() {
    1212                 :          66 :         bool activeRuntime = false;
    1213         [ +  + ]:         172 :         for (Application *app : std::as_const(d->apps)) {
    1214         [ +  + ]:         115 :             AbstractRuntime *rt = app->currentRuntime();
    1215         [ +  + ]:         115 :             if (rt) {
    1216                 :             :                 activeRuntime = true;
    1217                 :             :                 break;
    1218                 :             :             }
    1219                 :             :         }
    1220         [ +  + ]:          66 :         if (!activeRuntime)
    1221                 :          57 :             emit internalSignals.shutDownFinished();
    1222                 :         118 :     };
    1223                 :             : 
    1224         [ +  + ]:         142 :     for (Application *app : std::as_const(d->apps)) {
    1225         [ +  + ]:          90 :         AbstractRuntime *rt = app->currentRuntime();
    1226         [ +  + ]:          90 :         if (rt) {
    1227                 :          14 :             connect(rt, &AbstractRuntime::destroyed,
    1228                 :             :                     this, shutdownHelper);
    1229                 :          14 :             rt->stop();
    1230                 :             :         }
    1231                 :             :     }
    1232                 :          52 :     shutdownHelper();
    1233                 :          52 : }
    1234                 :             : 
    1235                 :           4 : void ApplicationManager::openUrlRelay(const QUrl &url)
    1236                 :             : {
    1237         [ -  + ]:           4 :     if (QThread::currentThread() != thread()) {
    1238                 :           0 :         staticMetaObject.invokeMethod(this, "openUrlRelay", Qt::QueuedConnection, Q_ARG(QUrl, url));
    1239                 :           0 :         return;
    1240                 :             :     }
    1241         [ +  - ]:           8 :     openUrl(url.toString());
    1242                 :             : }
    1243                 :             : 
    1244                 :         651 : void ApplicationManager::emitDataChanged(Application *app, const QVector<int> &roles)
    1245                 :             : {
    1246         [ +  - ]:         651 :     auto row = d->apps.indexOf(app);
    1247         [ +  - ]:         651 :     if (row >= 0) {
    1248                 :         651 :         emit dataChanged(index(int(row)), index(int(row)), roles);
    1249                 :             : 
    1250   [ +  +  +  - ]:         688 :         static const auto appChanged = QMetaMethod::fromSignal(&ApplicationManager::applicationChanged);
    1251         [ +  + ]:         651 :         if (isSignalConnected(appChanged)) {
    1252                 :         646 :             QStringList stringRoles;
    1253                 :         646 :             stringRoles.reserve(roles.size());
    1254         [ +  + ]:        2440 :             for (auto role : roles)
    1255   [ +  -  +  -  :        3588 :                 stringRoles << QString::fromLatin1(d->roleNames[role]);
                   +  - ]
    1256   [ +  -  +  - ]:        1292 :             emit applicationChanged(app->id(), stringRoles);
    1257                 :         646 :         }
    1258                 :             :     }
    1259                 :         651 : }
    1260                 :             : 
    1261                 :         203 : void ApplicationManager::emitActivated(Application *app)
    1262                 :             : {
    1263   [ +  -  +  - ]:         406 :     emit applicationWasActivated(app->id(), app->id());
    1264                 :         203 :     emit app->activated();
    1265                 :         203 : }
    1266                 :             : 
    1267                 :             : // item model part
    1268                 :             : 
    1269                 :        3093 : int ApplicationManager::rowCount(const QModelIndex &parent) const
    1270                 :             : {
    1271         [ -  + ]:        3093 :     if (parent.isValid())
    1272                 :             :         return 0;
    1273                 :        3093 :     return int(d->apps.count());
    1274                 :             : }
    1275                 :             : 
    1276                 :         456 : QVariant ApplicationManager::data(const QModelIndex &index, int role) const
    1277                 :             : {
    1278         [ +  - ]:         912 :     if (index.parent().isValid() || !index.isValid())
    1279                 :           0 :         return QVariant();
    1280                 :             : 
    1281                 :         456 :     Application *app = d->apps.at(index.row());
    1282                 :         456 :     return dataForRole(app, role);
    1283                 :             : }
    1284                 :             : 
    1285                 :         981 : QVariant ApplicationManager::dataForRole(Application *app, int role) const
    1286                 :             : {
    1287   [ +  +  +  +  :         981 :     switch (role) {
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                   +  + ]
    1288                 :          27 :     case AMRoles::Id:
    1289                 :          27 :         return app->id();
    1290                 :          29 :     case AMRoles::Name:
    1291                 :          29 :         return app->name();
    1292                 :          25 :     case AMRoles::Description:
    1293                 :          25 :         return app->description();
    1294                 :          27 :     case AMRoles::Icon:
    1295                 :          27 :         return app->icon();
    1296                 :          71 :     case AMRoles::IsRunning:
    1297   [ +  +  +  + ]:          93 :         return app->currentRuntime() ? (app->currentRuntime()->state() == Am::Running) : false;
    1298                 :          71 :     case AMRoles::IsStartingUp:
    1299   [ +  +  +  + ]:          93 :         return app->currentRuntime() ? (app->currentRuntime()->state() == Am::StartingUp) : false;
    1300                 :          71 :     case AMRoles::IsShuttingDown:
    1301   [ +  +  +  + ]:          97 :         return app->currentRuntime() ? (app->currentRuntime()->state() == Am::ShuttingDown) : false;
    1302                 :          27 :     case AMRoles::IsBlocked:
    1303                 :          27 :         return app->isBlocked();
    1304                 :          27 :     case AMRoles::IsUpdating:
    1305                 :          27 :         return app->state() != Application::Installed;
    1306                 :          27 :     case AMRoles::UpdateProgress:
    1307                 :          27 :         return app->progress();
    1308                 :          27 :     case AMRoles::IsRemovable:
    1309                 :          27 :         return !app->isBuiltIn();
    1310                 :          27 :     case AMRoles::CodeFilePath:
    1311                 :          27 :         return app->info()->absoluteCodeFilePath();
    1312                 :          27 :     case AMRoles::RuntimeName:
    1313                 :          27 :         return app->runtimeName();
    1314                 :          25 :     case AMRoles::RuntimeParameters:
    1315                 :          25 :         return app->runtimeParameters();
    1316                 :          29 :     case AMRoles::Capabilities:
    1317                 :          29 :         return app->capabilities();
    1318                 :          25 :     case AMRoles::Categories:
    1319                 :          25 :         return app->categories();
    1320                 :          27 :     case AMRoles::Version:
    1321                 :          27 :         return app->version();
    1322                 :          70 :     case AMRoles::ApplicationItem:
    1323                 :          70 :     case AMRoles::ApplicationObject:
    1324                 :          70 :         return QVariant::fromValue(app);
    1325                 :          25 :     case AMRoles::LastExitCode:
    1326                 :          25 :         return app->lastExitCode();
    1327                 :          25 :     case AMRoles::LastExitStatus:
    1328                 :          25 :         return app->lastExitStatus();
    1329                 :             :     }
    1330                 :         272 :     return QVariant();
    1331                 :             : }
    1332                 :             : 
    1333                 :          34 : QHash<int, QByteArray> ApplicationManager::roleNames() const
    1334                 :             : {
    1335         [ +  - ]:          34 :     return d->roleNames;
    1336                 :             : }
    1337                 :             : 
    1338                 :        1143 : int ApplicationManager::count() const
    1339                 :             : {
    1340                 :        1143 :     return rowCount();
    1341                 :             : }
    1342                 :             : 
    1343                 :             : /*!
    1344                 :             :     \qmlmethod object ApplicationManager::get(int index)
    1345                 :             : 
    1346                 :             :     Retrieves the model data at \a index as a JavaScript object. See the
    1347                 :             :     \l {ApplicationManager Roles}{role names} for the expected object fields.
    1348                 :             : 
    1349                 :             :     Returns an empty object if the specified \a index is invalid.
    1350                 :             : 
    1351                 :             :     \note This is very inefficient if you only want to access a single property from QML; use
    1352                 :             :           application() instead to access the Application object's properties directly.
    1353                 :             : */
    1354                 :           4 : QVariantMap ApplicationManager::get(int index) const
    1355                 :             : {
    1356   [ +  +  -  + ]:           4 :     if (index < 0 || index >= count()) {
    1357   [ +  -  +  -  :           6 :         qCWarning(LogSystem) << "ApplicationManager::get(index): invalid index:" << index;
                   +  + ]
    1358                 :           4 :         return QVariantMap();
    1359                 :             :     }
    1360                 :           2 :     return get(d->apps.at(index));
    1361                 :             : }
    1362                 :             : 
    1363                 :             : /*!
    1364                 :             :     \qmlmethod ApplicationObject ApplicationManager::application(int index)
    1365                 :             : 
    1366                 :             :     Returns the \l{ApplicationObject}{application} corresponding to the given \a index in the
    1367                 :             :     model, or \c null if the index is invalid.
    1368                 :             : 
    1369                 :             :     \note The object ownership of the returned Application object stays with the application manager.
    1370                 :             :           If you want to store this pointer, you can use the ApplicationManager's QAbstractListModel
    1371                 :             :           signals or the applicationAboutToBeRemoved signal to get notified if the object is about
    1372                 :             :           to be deleted on the C++ side.
    1373                 :             : */
    1374                 :         668 : Application *ApplicationManager::application(int index) const
    1375                 :             : {
    1376   [ +  +  -  + ]:         668 :     if (index < 0 || index >= count()) {
    1377   [ +  -  +  -  :           6 :         qCWarning(LogSystem) << "ApplicationManager::application(index): invalid index:" << index;
                   +  + ]
    1378                 :             :         return nullptr;
    1379                 :             :     }
    1380                 :         666 :     return d->apps.at(index);
    1381                 :             : }
    1382                 :             : 
    1383                 :             : /*!
    1384                 :             :     \qmlmethod ApplicationObject ApplicationManager::application(string id)
    1385                 :             : 
    1386                 :             :     Returns the \l{ApplicationObject}{application} corresponding to the given application \a id,
    1387                 :             :     or \c null if the id does not exist.
    1388                 :             : 
    1389                 :             :     \note The object ownership of the returned Application object stays with the application manager.
    1390                 :             :           If you want to store this pointer, you can use the ApplicationManager's QAbstractListModel
    1391                 :             :           signals or the applicationAboutToBeRemoved signal to get notified if the object is about
    1392                 :             :           to be deleted on the C++ side.
    1393                 :             : */
    1394                 :         222 : Application *ApplicationManager::application(const QString &id) const
    1395                 :             : {
    1396                 :         222 :     auto index = indexOfApplication(id);
    1397         [ +  + ]:         222 :     return (index < 0) ? nullptr : application(index);
    1398                 :             : }
    1399                 :             : 
    1400                 :             : /*!
    1401                 :             :     \qmlmethod int ApplicationManager::indexOfApplication(string id)
    1402                 :             : 
    1403                 :             :     Maps the application corresponding to the given \a id to its position within the model. Returns
    1404                 :             :     \c -1 if the specified \a id is invalid.
    1405                 :             : */
    1406                 :         315 : int ApplicationManager::indexOfApplication(const QString &id) const
    1407                 :             : {
    1408         [ +  + ]:         607 :     for (int i = 0; i < d->apps.size(); ++i) {
    1409         [ +  + ]:         599 :         if (d->apps.at(i)->id() == id)
    1410                 :         307 :             return i;
    1411                 :             :     }
    1412                 :             :     return -1;
    1413                 :             : }
    1414                 :             : 
    1415                 :             : /*!
    1416                 :             :     \qmlmethod int ApplicationManager::indexOfApplication(ApplicationObject application)
    1417                 :             : 
    1418                 :             :     Maps the \a application to its position within this model. Returns \c -1 if the specified
    1419                 :             :     application is invalid.
    1420                 :             : */
    1421                 :           0 : int ApplicationManager::indexOfApplication(Application *application) const
    1422                 :             : {
    1423         [ #  # ]:           0 :     return int(d->apps.indexOf(application));
    1424                 :             : }
    1425                 :             : 
    1426                 :             : /*!
    1427                 :             :     \qmlmethod list<string> ApplicationManager::applicationIds()
    1428                 :             : 
    1429                 :             :     Returns a list of all available application ids. This can be used to further query for specific
    1430                 :             :     information via get().
    1431                 :             : */
    1432                 :           7 : QStringList ApplicationManager::applicationIds() const
    1433                 :             : {
    1434                 :           7 :     QStringList ids;
    1435                 :           7 :     ids.reserve(d->apps.size());
    1436         [ +  + ]:          27 :     for (int i = 0; i < d->apps.size(); ++i)
    1437         [ +  - ]:          40 :         ids << d->apps.at(i)->id();
    1438                 :           7 :     return ids;
    1439                 :           0 : }
    1440                 :             : 
    1441                 :             : /*!
    1442                 :             :     \qmlmethod object ApplicationManager::get(string id)
    1443                 :             : 
    1444                 :             :     Retrieves the model data for the application identified by \a id as a JavaScript object.
    1445                 :             :     See the \l {ApplicationManager Roles}{role names} for the expected object fields.
    1446                 :             : 
    1447                 :             :     Returns an empty object if the specified \a id is invalid.
    1448                 :             : */
    1449                 :           5 : QVariantMap ApplicationManager::get(const QString &id) const
    1450                 :             : {
    1451                 :           5 :     return get(application(id));
    1452                 :             : }
    1453                 :             : 
    1454                 :          61 : Am::RunState ApplicationManager::applicationRunState(const QString &id) const
    1455                 :             : {
    1456                 :          61 :     int index = indexOfApplication(id);
    1457         [ +  + ]:          61 :     return (index < 0) ? Am::NotRunning : d->apps.at(index)->runState();
    1458                 :             : }
    1459                 :             : 
    1460                 :         111 : void ApplicationManager::addApplication(ApplicationInfo *appInfo, Package *package)
    1461                 :             : {
    1462                 :             :     // check for id clashes outside of the package (the scanner made sure the package itself is
    1463                 :             :     // consistent and doesn't have duplicates already)
    1464         [ +  + ]:         202 :     for (Application *checkApp : std::as_const(d->apps)) {
    1465   [ +  -  +  -  :         182 :         if ((checkApp->id() == appInfo->id()) && (checkApp->package() != package)) {
          -  +  -  -  -  
          -  -  +  -  -  
                   -  - ]
    1466                 :           0 :             throw Exception("found an application with the same id in package %1")
    1467   [ #  #  #  # ]:           0 :                 .arg(checkApp->packageInfo()->id());
    1468                 :             :         }
    1469                 :             :     }
    1470                 :             : 
    1471         [ +  - ]:         111 :     auto app = new Application(appInfo, package);
    1472                 :         111 :     QQmlEngine::setObjectOwnership(app, QQmlEngine::CppOwnership);
    1473                 :             : 
    1474                 :         242 :     app->requests.startRequested = [this, app](const QString &documentUrl) {
    1475         [ +  - ]:         131 :         return startApplication(app->id(), documentUrl);
    1476                 :         111 :     };
    1477                 :             : 
    1478                 :         111 :     app->requests.debugRequested =  [this, app](const QString &debugWrapper, const QString &documentUrl) {
    1479         [ #  # ]:           0 :         return debugApplication(app->id(), debugWrapper, documentUrl);
    1480                 :         111 :     };
    1481                 :             : 
    1482                 :         184 :     app->requests.stopRequested = [this, app](bool forceKill) {
    1483         [ +  - ]:          73 :         stopApplication(app->id(), forceKill);
    1484                 :         184 :     };
    1485                 :             : 
    1486                 :         111 :     connect(app, &Application::blockedChanged,
    1487                 :          96 :             this, [this, app]() {
    1488         [ +  - ]:          48 :         emitDataChanged(app, QVector<int> { AMRoles::IsBlocked });
    1489                 :          48 :     });
    1490                 :         111 :     connect(app, &Application::bulkChange,
    1491                 :          20 :             this, [this, app]() {
    1492         [ +  - ]:          20 :         emitDataChanged(app);
    1493                 :          20 :     });
    1494                 :             : 
    1495                 :         111 :     beginInsertRows(QModelIndex(), int(d->apps.count()), int(d->apps.count()));
    1496                 :         111 :     d->apps << app;
    1497                 :             : 
    1498                 :         111 :     endInsertRows();
    1499                 :             : 
    1500                 :         111 :     registerMimeTypes();
    1501                 :             : 
    1502                 :         111 :     package->addApplication(app);
    1503         [ +  - ]:         111 :     emit applicationAdded(appInfo->id());
    1504                 :         111 : }
    1505                 :             : 
    1506                 :          20 : void ApplicationManager::removeApplication(ApplicationInfo *appInfo, Package *package)
    1507                 :             : {
    1508                 :          20 :     int index = -1;
    1509                 :             : 
    1510         [ +  - ]:          28 :     for (int i = 0; i < d->apps.size(); ++i) {
    1511         [ +  + ]:          28 :         if (d->apps.at(i)->info() == appInfo) {
    1512                 :             :             index = i;
    1513                 :             :             break;
    1514                 :             :         }
    1515                 :             :     }
    1516         [ +  - ]:          20 :     if (index < 0)
    1517                 :             :         return;
    1518                 :             : 
    1519                 :          20 :     Q_ASSERT(d->apps.at(index)->package() == package);
    1520                 :             : 
    1521         [ -  + ]:          20 :     if (d->aboutToBeRemoved) {
    1522   [ #  #  #  # ]:           0 :         qCFatal(LogSystem) << "ApplicationManager::removeApplication was called recursively";
    1523                 :             :         return;
    1524                 :             :     }
    1525         [ +  - ]:          20 :     QScopedValueRollback<bool> rollback(d->aboutToBeRemoved, true);
    1526                 :             : 
    1527   [ +  -  +  - ]:          20 :     emit applicationAboutToBeRemoved(appInfo->id());
    1528                 :             : 
    1529         [ +  - ]:          20 :     package->removeApplication(d->apps.at(index));
    1530                 :             : 
    1531         [ +  - ]:          20 :     beginRemoveRows(QModelIndex(), index, index);
    1532         [ +  - ]:          20 :     auto app = d->apps.takeAt(index);
    1533                 :             : 
    1534         [ +  - ]:          20 :     endRemoveRows();
    1535                 :             : 
    1536         [ +  - ]:          20 :     registerMimeTypes();
    1537                 :             : 
    1538         [ +  - ]:          20 :     delete app;
    1539                 :          20 : }
    1540                 :             : 
    1541                 :             : QT_END_NAMESPACE_AM
    1542                 :             : 
    1543                 :             : #include "moc_applicationmanager.cpp"
    1544                 :             : #include "moc_amnamespace.cpp" // amnamespace is header only, so we include it here
        

Generated by: LCOV version 2.0-1