QDesktopServices: deprecate destroying URL handlers w/o explicit unsetUrlHandler()

[ChangeLog][QtGui][QDesktopServices] URL handlers that have been passed
to setUrlHandler() must now be removed by calling unsetUrlHandler()
before they are destroyed. Relying on the handler's destructor to
implicitly unset it is now deprecated, because it may already be in use
by concurrent openUrl() calls. Support for implicit unsetting will be
removed in 6.6 and, until then, a qWarning() is raised if it is
exercised.

Fixes: QTBUG-100775
Fixes: QTBUG-100779
Pick-to: 6.3 6.2 5.15
Change-Id: I0c4f91b78f847b135fdeb38766babc892bdc1379
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
This commit is contained in:
Marc Mutz 2022-02-11 10:34:21 +01:00
parent 06e45cbd6a
commit 37a25fce94
2 changed files with 37 additions and 0 deletions

View File

@ -74,13 +74,16 @@ public:
typedef QHash<QString, Handler> HandlerHash;
HandlerHash handlers;
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
public Q_SLOTS:
void handlerDestroyed(QObject *handler);
#endif
};
Q_GLOBAL_STATIC(QOpenUrlHandlerRegistry, handlerRegistry)
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler)
{
const auto lock = qt_scoped_lock(mutex);
@ -88,11 +91,16 @@ void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler)
while (it != handlers.end()) {
if (it->receiver == handler) {
it = handlers.erase(it);
qWarning("Please call QDesktopServices::unsetUrlHandler() before destroying a "
"registered URL handler object.\n"
"Support for destroying a registered URL handler object is deprecated, "
"and will be removed in Qt 6.6.");
} else {
++it;
}
}
}
#endif
/*!
\class QDesktopServices
@ -255,6 +263,10 @@ bool QDesktopServices::openUrl(const QUrl &url)
Note that the handler will always be called from within the same thread that
calls QDesktopServices::openUrl().
You must call unsetUrlHandler() before destroying the handler object, so
the destruction of the handler object does not overlap with concurrent
invocations of openUrl() using it.
\section1 iOS
To use this function for receiving data from other apps on iOS you also need to
@ -323,14 +335,20 @@ void QDesktopServices::setUrlHandler(const QString &scheme, QObject *receiver, c
h.receiver = receiver;
h.name = method;
registry->handlers.insert(scheme.toLower(), h);
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
QObject::connect(receiver, SIGNAL(destroyed(QObject*)),
registry, SLOT(handlerDestroyed(QObject*)),
Qt::DirectConnection);
#endif
}
/*!
Removes a previously set URL handler for the specified \a scheme.
Call this function before the handler object that was registered for \a scheme
is destroyed, to prevent concurrent openUrl() calls from continuing to call
the destroyed handler object.
\sa setUrlHandler()
*/
void QDesktopServices::unsetUrlHandler(const QString &scheme)

View File

@ -65,6 +65,10 @@ public slots:
}
};
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
# define CAN_IMPLICITLY_UNSET
#endif
void tst_qdesktopservices::handlers()
{
MyUrlHandler fooHandler;
@ -72,6 +76,12 @@ void tst_qdesktopservices::handlers()
QDesktopServices::setUrlHandler(QString("foo"), &fooHandler, "handle");
QDesktopServices::setUrlHandler(QString("bar"), &barHandler, "handle");
#ifndef CAN_IMPLICITLY_UNSET
const auto unsetHandlers = qScopeGuard([] {
QDesktopServices::unsetUrlHandler(u"bar"_qs);
QDesktopServices::unsetUrlHandler(u"foo"_qs);
});
#endif
QUrl fooUrl("foo://blub/meh");
QUrl barUrl("bar://hmm/hmmmm");
@ -81,6 +91,15 @@ void tst_qdesktopservices::handlers()
QCOMPARE(fooHandler.lastHandledUrl.toString(), fooUrl.toString());
QCOMPARE(barHandler.lastHandledUrl.toString(), barUrl.toString());
#ifdef CAN_IMPLICITLY_UNSET
for (int i = 0; i < 2; ++i)
QTest::ignoreMessage(QtWarningMsg,
"Please call QDesktopServices::unsetUrlHandler() before destroying a "
"registered URL handler object.\n"
"Support for destroying a registered URL handler object is deprecated, "
"and will be removed in Qt 6.6.");
#endif
}
QTEST_MAIN(tst_qdesktopservices)