diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp index fd3761a4734..8f237960d65 100644 --- a/src/gui/util/qdesktopservices.cpp +++ b/src/gui/util/qdesktopservices.cpp @@ -74,13 +74,16 @@ public: typedef QHash 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) diff --git a/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp b/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp index 7446f8ed9fa..4df34280b04 100644 --- a/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp +++ b/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp @@ -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)