File indexing completed on 2024-10-13 09:35:32
0001 /* 0002 SPDX-FileCopyrightText: 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> 0003 SPDX-FileCopyrightText: 2010 Shaun Reich <shaun.reich@kdemail.net> 0004 SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de> 0005 0006 SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "kdynamicjobtracker_p.h" 0010 #include "kio_widgets_debug.h" 0011 #include "kuiserver_interface.h" 0012 0013 #include <KJobTrackerInterface> 0014 #include <KUiServerJobTracker> 0015 #include <KUiServerV2JobTracker> 0016 #include <KWidgetJobTracker> 0017 #include <kio/jobtracker.h> 0018 0019 #include <QApplication> 0020 #include <QDBusConnection> 0021 #include <QDBusConnectionInterface> 0022 #include <QDBusServiceWatcher> 0023 #include <QMap> 0024 #include <QXmlStreamReader> 0025 0026 struct AllTrackers { 0027 KUiServerJobTracker *kuiserverTracker; 0028 KUiServerV2JobTracker *kuiserverV2Tracker; 0029 KWidgetJobTracker *widgetTracker; 0030 }; 0031 0032 class KDynamicJobTrackerPrivate 0033 { 0034 public: 0035 KDynamicJobTrackerPrivate() 0036 { 0037 } 0038 0039 ~KDynamicJobTrackerPrivate() 0040 { 0041 delete kuiserverTracker; 0042 delete kuiserverV2Tracker; 0043 delete widgetTracker; 0044 } 0045 0046 static bool hasDBusInterface(const QString &introspectionData, const QString &interface) 0047 { 0048 QXmlStreamReader xml(introspectionData); 0049 while (!xml.atEnd() && !xml.hasError()) { 0050 xml.readNext(); 0051 0052 if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == QLatin1String("interface")) { 0053 if (xml.attributes().value(QLatin1String("name")) == interface) { 0054 return true; 0055 } 0056 } 0057 } 0058 return false; 0059 } 0060 0061 KUiServerJobTracker *kuiserverTracker = nullptr; 0062 KUiServerV2JobTracker *kuiserverV2Tracker = nullptr; 0063 KWidgetJobTracker *widgetTracker = nullptr; 0064 QMap<KJob *, AllTrackers> trackers; 0065 0066 enum JobViewServerSupport { 0067 NeedsChecking, 0068 Error, 0069 V2Supported, 0070 V2NotSupported, 0071 }; 0072 JobViewServerSupport jobViewServerSupport = NeedsChecking; 0073 QDBusServiceWatcher *jobViewServerWatcher = nullptr; 0074 }; 0075 0076 KDynamicJobTracker::KDynamicJobTracker(QObject *parent) 0077 : KJobTrackerInterface(parent) 0078 , d(new KDynamicJobTrackerPrivate) 0079 { 0080 } 0081 0082 KDynamicJobTracker::~KDynamicJobTracker() = default; 0083 0084 void KDynamicJobTracker::registerJob(KJob *job) 0085 { 0086 if (d->trackers.contains(job)) { 0087 return; 0088 } 0089 0090 // only interested in finished() signal, 0091 // so catching ourselves instead of using KJobTrackerInterface::registerJob() 0092 connect(job, &KJob::finished, this, &KDynamicJobTracker::unregisterJob); 0093 0094 const bool canHaveWidgets = (qobject_cast<QApplication *>(qApp) != nullptr); 0095 0096 // always add an entry, even with no trackers used at all, 0097 // so unregisterJob() will work as normal 0098 AllTrackers &trackers = d->trackers[job]; 0099 trackers.kuiserverTracker = nullptr; 0100 trackers.kuiserverV2Tracker = nullptr; 0101 trackers.widgetTracker = nullptr; 0102 0103 // do not try to query kuiserver if dbus is not available 0104 if (!QDBusConnection::sessionBus().interface()) { 0105 if (canHaveWidgets) { 0106 // fallback to widget tracker only! 0107 if (!d->widgetTracker) { 0108 d->widgetTracker = new KWidgetJobTracker(); 0109 } 0110 0111 trackers.widgetTracker = d->widgetTracker; 0112 trackers.widgetTracker->registerJob(job); 0113 } 0114 return; 0115 } 0116 0117 const QString kuiserverService = QStringLiteral("org.kde.kuiserver"); 0118 0119 if (!d->jobViewServerWatcher) { 0120 d->jobViewServerWatcher = new QDBusServiceWatcher(kuiserverService, 0121 QDBusConnection::sessionBus(), 0122 QDBusServiceWatcher::WatchForOwnerChange | QDBusServiceWatcher::WatchForUnregistration, 0123 this); 0124 connect(d->jobViewServerWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, [this] { 0125 d->jobViewServerSupport = KDynamicJobTrackerPrivate::NeedsChecking; 0126 }); 0127 } 0128 0129 if (d->jobViewServerSupport == KDynamicJobTrackerPrivate::NeedsChecking) { 0130 // Unfortunately no DBus ObjectManager support in Qt DBus. 0131 QDBusMessage msg = QDBusMessage::createMethodCall(kuiserverService, 0132 QStringLiteral("/JobViewServer"), 0133 QStringLiteral("org.freedesktop.DBus.Introspectable"), 0134 QStringLiteral("Introspect")); 0135 auto reply = QDBusConnection::sessionBus().call(msg); 0136 if (reply.type() == QDBusMessage::ErrorMessage || reply.arguments().count() != 1) { 0137 qCWarning(KIO_WIDGETS) << "Failed to check which JobView API is supported" << reply.errorMessage(); 0138 d->jobViewServerSupport = KDynamicJobTrackerPrivate::Error; 0139 } else { 0140 const QString introspectionData = reply.arguments().first().toString(); 0141 0142 if (KDynamicJobTrackerPrivate::hasDBusInterface(introspectionData, QStringLiteral("org.kde.JobViewServerV2"))) { 0143 d->jobViewServerSupport = KDynamicJobTrackerPrivate::V2Supported; 0144 } else { 0145 d->jobViewServerSupport = KDynamicJobTrackerPrivate::V2NotSupported; 0146 } 0147 } 0148 } 0149 0150 if (d->jobViewServerSupport == KDynamicJobTrackerPrivate::V2Supported) { 0151 if (!d->kuiserverV2Tracker) { 0152 d->kuiserverV2Tracker = new KUiServerV2JobTracker(); 0153 } 0154 0155 trackers.kuiserverV2Tracker = d->kuiserverV2Tracker; 0156 trackers.kuiserverV2Tracker->registerJob(job); 0157 return; 0158 } 0159 0160 // No point in trying to set up V1 if calling the service above failed. 0161 if (d->jobViewServerSupport != KDynamicJobTrackerPrivate::Error) { 0162 if (!d->kuiserverTracker) { 0163 d->kuiserverTracker = new KUiServerJobTracker(); 0164 } 0165 0166 trackers.kuiserverTracker = d->kuiserverTracker; 0167 trackers.kuiserverTracker->registerJob(job); 0168 } 0169 0170 if (canHaveWidgets) { 0171 bool needsWidgetTracker = d->jobViewServerSupport == KDynamicJobTrackerPrivate::Error; 0172 0173 if (!needsWidgetTracker) { 0174 org::kde::kuiserver interface(kuiserverService, QStringLiteral("/JobViewServer"), QDBusConnection::sessionBus(), this); 0175 QDBusReply<bool> reply = interface.requiresJobTracker(); 0176 needsWidgetTracker = !reply.isValid() || reply.value(); 0177 } 0178 0179 // If kuiserver isn't available or it tells us a job tracker is required 0180 // create a widget tracker. 0181 if (needsWidgetTracker) { 0182 if (!d->widgetTracker) { 0183 d->widgetTracker = new KWidgetJobTracker(); 0184 } 0185 trackers.widgetTracker = d->widgetTracker; 0186 trackers.widgetTracker->registerJob(job); 0187 } 0188 } 0189 } 0190 0191 void KDynamicJobTracker::unregisterJob(KJob *job) 0192 { 0193 job->disconnect(this); 0194 0195 QMap<KJob *, AllTrackers>::Iterator it = d->trackers.find(job); 0196 0197 if (it == d->trackers.end()) { 0198 qCWarning(KIO_WIDGETS) << "Tried to unregister a kio job that hasn't been registered."; 0199 return; 0200 } 0201 0202 const AllTrackers &trackers = it.value(); 0203 KUiServerJobTracker *kuiserverTracker = trackers.kuiserverTracker; 0204 KUiServerV2JobTracker *kuiserverV2Tracker = trackers.kuiserverV2Tracker; 0205 KWidgetJobTracker *widgetTracker = trackers.widgetTracker; 0206 0207 if (kuiserverTracker) { 0208 kuiserverTracker->unregisterJob(job); 0209 } 0210 0211 if (kuiserverV2Tracker) { 0212 kuiserverV2Tracker->unregisterJob(job); 0213 } 0214 0215 if (widgetTracker) { 0216 widgetTracker->unregisterJob(job); 0217 } 0218 0219 d->trackers.erase(it); 0220 } 0221 0222 Q_GLOBAL_STATIC(KDynamicJobTracker, globalJobTracker) 0223 0224 // Simply linking to this library, creates a GUI job tracker for all KIO jobs 0225 static int registerDynamicJobTracker() 0226 { 0227 KIO::setJobTracker(globalJobTracker()); 0228 0229 return 0; // something 0230 } 0231 0232 Q_CONSTRUCTOR_FUNCTION(registerDynamicJobTracker) 0233 0234 #include "moc_kdynamicjobtracker_p.cpp"