diff --git a/autotests/openurljobtest.h b/autotests/openurljobtest.h --- a/autotests/openurljobtest.h +++ b/autotests/openurljobtest.h @@ -55,6 +55,8 @@ void ftpUrlWithKIO(); void takeOverAfterMimeTypeFound(); + void onlyOpenWithDialog_data(); + void onlyOpenWithDialog(); private: void writeApplicationDesktopFile(const QString &filePath); diff --git a/autotests/openurljobtest.cpp b/autotests/openurljobtest.cpp --- a/autotests/openurljobtest.cpp +++ b/autotests/openurljobtest.cpp @@ -39,14 +39,16 @@ #include #include #include +#include #include QTEST_GUILESS_MAIN(OpenUrlJobTest) extern KSERVICE_EXPORT int ksycoca_ms_between_checks; namespace KIO { KIOGUI_EXPORT void setDefaultUntrustedProgramHandler(KIO::UntrustedProgramHandlerInterface *iface); +KIOGUI_EXPORT void setDefaultOpenUrlJobHandler(KIO::OpenUrlJobHandlerInterface *iface); } class TestUntrustedProgramHandler : public KIO::UntrustedProgramHandlerInterface { @@ -62,9 +64,28 @@ QStringList m_calls; bool m_retVal = false; }; - static TestUntrustedProgramHandler s_handler; +class TestOpenUrlJobHandler : public KIO::OpenUrlJobHandlerInterface +{ +public: + void promptUserForApplication(KIO::OpenUrlJob *job, const QUrl &url, const QString &mimeType) override + { + Q_UNUSED(job); + m_urls << url; + m_mimeTypes << mimeType; + if (m_chosenService) { + Q_EMIT serviceSelected(m_chosenService); + } else { + Q_EMIT canceled(); + } + } + QList m_urls; + QStringList m_mimeTypes; + KService::Ptr m_chosenService; +}; +static TestOpenUrlJobHandler s_openUrlJobHandler; + static const char s_tempServiceName[] = "openurljobtest_service.desktop"; void OpenUrlJobTest::initTestCase() @@ -442,6 +463,63 @@ QCOMPARE(foundMime, "image/jpeg"); } +void OpenUrlJobTest::onlyOpenWithDialog_data() +{ + QTest::addColumn("knownMimeType"); + QTest::addColumn("handlerRetVal"); + + QTest::newRow("false_knownMimeType") << true << false; + QTest::newRow("true_knownMimeType") << true << true; + + QTest::newRow("false_unknownMimeType") << false << false; + QTest::newRow("true_unknownMimeType") << false << true; +} + +void OpenUrlJobTest::onlyOpenWithDialog() +{ +#ifdef Q_OS_UNIX + QFETCH(bool, knownMimeType); + QFETCH(bool, handlerRetVal); + + // Given a local text file + QTemporaryDir tempDir; + const QString srcDir = tempDir.path(); + const QString srcFile = srcDir + QLatin1String("/file.txt"); + createSrcFile(srcFile); + + const QString mimeType = knownMimeType ? "text/plain" : ""; + KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(srcFile), mimeType, this); + job->setShowOpenWithDialog(true); + + KService::Ptr service = KService::serviceByDesktopName(QString(s_tempServiceName).remove(".desktop")); + QVERIFY(service); + s_openUrlJobHandler.m_urls.clear(); + s_openUrlJobHandler.m_mimeTypes.clear(); + s_openUrlJobHandler.m_chosenService = handlerRetVal ? service : KService::Ptr{}; + KIO::setDefaultOpenUrlJobHandler(&s_openUrlJobHandler); + + const bool success = job->exec(); + + // Then --- it depends on what the user says via the handler + + QCOMPARE(s_openUrlJobHandler.m_urls.count(), 1); + QCOMPARE(s_openUrlJobHandler.m_mimeTypes.count(), 1); + QCOMPARE(s_openUrlJobHandler.m_mimeTypes.at(0), "text/plain"); + if (handlerRetVal) { + QVERIFY2(success, qPrintable(job->errorString())); + // If the user chose a service, it should be executed (it writes to "dest") + const QString dest = m_tempDir.path() + "/dest"; + QTRY_VERIFY2(QFile::exists(dest), qPrintable(dest)); + QCOMPARE(readFile(dest), srcFile); + } else { + QVERIFY(!success); + QCOMPARE(job->error(), KIO::ERR_USER_CANCELED); + } +#else + QSKIP("Test skipped on Windows because the code ends up in QDesktopServices::openUrl") +#endif +} + void OpenUrlJobTest::writeApplicationDesktopFile(const QString &filePath) { KDesktopFile file(filePath); diff --git a/src/gui/openurljob.h b/src/gui/openurljob.h --- a/src/gui/openurljob.h +++ b/src/gui/openurljob.h @@ -68,6 +68,7 @@ /** * Specifies that the URL passed to the application will be deleted when it exits (if the URL is a local file) + * The default is false. */ void setDeleteTemporaryFile(bool b); @@ -89,7 +90,7 @@ /** * Set this to true if this class should allow the user to run executables. - * Unlike KF5's KRun, this setting is OFF by default here for security reasons. + * Unlike KF5's KRun, this setting is false by default here for security reasons. * File managers can enable this, but e.g. web browsers, mail clients etc. shouldn't. */ void setRunExecutables(bool allow); @@ -109,6 +110,20 @@ */ void setFollowRedirections(bool b); + /** + * Sets whether the job should right away show the "open with" dialog, + * without checking for executables, or for associated applications. + * + * Compared to using KOpenWithDialog directly, this takes care of determining + * the mimetype first (if not passed to the constructor), and it allows for + * a different implementation on Windows. + * + * The default is false. + * + * @param b whether to only show the "open with" dialog + */ + void setShowOpenWithDialog(bool b); + /** * Starts the job. * You must call this, after having called all the needed setters. diff --git a/src/gui/openurljob.cpp b/src/gui/openurljob.cpp --- a/src/gui/openurljob.cpp +++ b/src/gui/openurljob.cpp @@ -80,6 +80,7 @@ bool m_runExecutables = false; bool m_externalBrowserEnabled = true; bool m_followRedirections = true; + bool m_showOpenWithDialog = false; private: void executeCommand(); @@ -134,6 +135,11 @@ d->m_followRedirections = b; } +void KIO::OpenUrlJob::setShowOpenWithDialog(bool b) +{ + d->m_showOpenWithDialog = b; +} + static bool checkNeedPortalSupport() { return !(QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty() @@ -549,6 +555,11 @@ return; } + if (m_showOpenWithDialog) { + showOpenWithDialog(); + return; + } + // Support for preferred service setting, see setPreferredService if (m_preferredService && m_preferredService->hasMimeType(m_mimeTypeName)) { startService(m_preferredService);