summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/download_manager_delegate_qt.cpp2
-rw-r--r--src/core/profile_adapter.cpp6
-rw-r--r--src/core/profile_adapter.h4
-rw-r--r--src/webengine/api/qquickwebengineprofile.cpp41
-rw-r--r--src/webengine/api/qquickwebengineprofile.h5
-rw-r--r--src/webengine/plugin/plugin.pro2
-rw-r--r--src/webengine/plugin/plugins.qmltypes16
-rw-r--r--src/webenginewidgets/api/qwebengineprofile.cpp28
-rw-r--r--src/webenginewidgets/api/qwebengineprofile.h3
-rw-r--r--tests/auto/quick/qmltests/data/tst_download.qml20
-rw-r--r--tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp268
11 files changed, 387 insertions, 8 deletions
diff --git a/src/core/download_manager_delegate_qt.cpp b/src/core/download_manager_delegate_qt.cpp
index 948a62047..398bde710 100644
--- a/src/core/download_manager_delegate_qt.cpp
+++ b/src/core/download_manager_delegate_qt.cpp
@@ -164,7 +164,7 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem*
suggestedFilename += QStringLiteral(".") + mimeType.preferredSuffix();
}
- QDir defaultDownloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+ QDir defaultDownloadDirectory(m_profileAdapter->downloadPath());
QFileInfo suggestedFile(defaultDownloadDirectory.absoluteFilePath(suggestedFilename));
QString suggestedFilePath = suggestedFile.absoluteFilePath();
diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp
index cee783118..dfe701cc9 100644
--- a/src/core/profile_adapter.cpp
+++ b/src/core/profile_adapter.cpp
@@ -84,6 +84,7 @@ ProfileAdapter::ProfileAdapter(const QString &storageName):
, m_visitedLinksPolicy(TrackVisitedLinksOnDisk)
, m_httpCacheMaxSize(0)
, m_pageRequestInterceptors(0)
+ , m_downloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))
{
WebEngineContext::current()->addProfileAdapter(this);
// creation of profile requires webengine context
@@ -253,6 +254,11 @@ void ProfileAdapter::setDataPath(const QString &path)
}
}
+void ProfileAdapter::setDownloadPath(const QString &path)
+{
+ m_downloadPath = path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) : path;
+}
+
QString ProfileAdapter::cachePath() const
{
if (m_offTheRecord)
diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h
index 9849d2788..3b1b9bc91 100644
--- a/src/core/profile_adapter.h
+++ b/src/core/profile_adapter.h
@@ -113,6 +113,9 @@ public:
QString dataPath() const;
void setDataPath(const QString &path);
+ QString downloadPath() const { return m_downloadPath; }
+ void setDownloadPath(const QString &path);
+
QString cachePath() const;
void setCachePath(const QString &path);
@@ -211,6 +214,7 @@ private:
QPointer<QWebEngineUrlRequestInterceptor> m_requestInterceptor;
QString m_dataPath;
+ QString m_downloadPath;
QString m_cachePath;
QString m_httpUserAgent;
HttpCacheType m_httpCacheType;
diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp
index 1d93c5070..4448d44d1 100644
--- a/src/webengine/api/qquickwebengineprofile.cpp
+++ b/src/webengine/api/qquickwebengineprofile.cpp
@@ -850,6 +850,47 @@ bool QQuickWebEngineProfile::isUsedForGlobalCertificateVerification() const
}
/*!
+ \qmlproperty string WebEngineProfile::downloadPath
+ \since QtWebEngine 1.9
+
+ The path to the location where the downloaded files are stored.
+
+ Overrides the default path used for download location.
+
+ If set to the null string, the default path is restored.
+
+ \note By default, the download path is QStandardPaths::DownloadLocation.
+*/
+
+/*!
+ \property QQuickWebEngineProfile::downloadPath
+ \since QtWebEngine 1.9
+
+ The path to the location where the downloaded files are stored.
+
+ Overrides the default path used for download location, setting it to \a path.
+
+ If set to the null string, the default path is restored.
+
+ \note By default, the download path is QStandardPaths::DownloadLocation.
+*/
+
+void QQuickWebEngineProfile::setDownloadPath(const QString &path)
+{
+ Q_D(QQuickWebEngineProfile);
+ if (downloadPath() == path)
+ return;
+ d->profileAdapter()->setDownloadPath(path);
+ emit downloadPathChanged();
+}
+
+QString QQuickWebEngineProfile::downloadPath() const
+{
+ const Q_D(QQuickWebEngineProfile);
+ return d->profileAdapter()->downloadPath();
+}
+
+/*!
Returns the cookie store for this profile.
*/
diff --git a/src/webengine/api/qquickwebengineprofile.h b/src/webengine/api/qquickwebengineprofile.h
index 1a3abe044..1e2e3e030 100644
--- a/src/webengine/api/qquickwebengineprofile.h
+++ b/src/webengine/api/qquickwebengineprofile.h
@@ -77,6 +77,7 @@ class Q_WEBENGINE_EXPORT QQuickWebEngineProfile : public QObject {
WRITE setUseForGlobalCertificateVerification
NOTIFY useForGlobalCertificateVerificationChanged
FINAL REVISION 5)
+ Q_PROPERTY(QString downloadPath READ downloadPath WRITE setDownloadPath NOTIFY downloadPathChanged FINAL REVISION 5)
public:
QQuickWebEngineProfile(QObject *parent = Q_NULLPTR);
@@ -145,6 +146,9 @@ public:
void setUseForGlobalCertificateVerification(bool b);
bool isUsedForGlobalCertificateVerification() const;
+ QString downloadPath() const;
+ void setDownloadPath(const QString &path);
+
static QQuickWebEngineProfile *defaultProfile();
Q_SIGNALS:
@@ -160,6 +164,7 @@ Q_SIGNALS:
Q_REVISION(3) void spellCheckLanguagesChanged();
Q_REVISION(3) void spellCheckEnabledChanged();
Q_REVISION(5) void useForGlobalCertificateVerificationChanged();
+ Q_REVISION(5) void downloadPathChanged();
void downloadRequested(QQuickWebEngineDownloadItem *download);
void downloadFinished(QQuickWebEngineDownloadItem *download);
diff --git a/src/webengine/plugin/plugin.pro b/src/webengine/plugin/plugin.pro
index c3702e81d..102f9a9fe 100644
--- a/src/webengine/plugin/plugin.pro
+++ b/src/webengine/plugin/plugin.pro
@@ -1,7 +1,7 @@
CXX_MODULE = qml
TARGET = qtwebengineplugin
TARGETPATH = QtWebEngine
-IMPORT_VERSION = 1.8
+IMPORT_VERSION = 1.9
QT += qml quick
QT_PRIVATE += core-private webenginecore-private webengine-private
diff --git a/src/webengine/plugin/plugins.qmltypes b/src/webengine/plugin/plugins.qmltypes
index 1f295ac57..435124e30 100644
--- a/src/webengine/plugin/plugins.qmltypes
+++ b/src/webengine/plugin/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -defaultplatform -dependencies dependencies.json -nonrelocatable QtWebEngine 1.8'
+// 'qmlplugindump -defaultplatform -dependencies dependencies.json -nonrelocatable QtWebEngine 1.9'
Module {
dependencies: ["QtQuick 2.8"]
@@ -437,9 +437,10 @@ Module {
"QtWebEngine/WebEngineProfile 1.2",
"QtWebEngine/WebEngineProfile 1.3",
"QtWebEngine/WebEngineProfile 1.4",
- "QtWebEngine/WebEngineProfile 1.5"
+ "QtWebEngine/WebEngineProfile 1.5",
+ "QtWebEngine/WebEngineProfile 1.9"
]
- exportMetaObjectRevisions: [0, 1, 2, 3, 4]
+ exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5]
Enum {
name: "HttpCacheType"
values: {
@@ -474,9 +475,13 @@ Module {
isList: true
isReadonly: true
}
+ Property { name: "useForGlobalCertificateVerification"; revision: 5; type: "bool" }
+ Property { name: "downloadPath"; revision: 5; type: "string" }
Signal { name: "httpAcceptLanguageChanged"; revision: 1 }
Signal { name: "spellCheckLanguagesChanged"; revision: 3 }
Signal { name: "spellCheckEnabledChanged"; revision: 3 }
+ Signal { name: "useForGlobalCertificateVerificationChanged"; revision: 5 }
+ Signal { name: "downloadPathChanged"; revision: 5 }
Signal {
name: "downloadRequested"
Parameter { name: "download"; type: "QQuickWebEngineDownloadItem"; isPointer: true }
@@ -664,9 +669,10 @@ Module {
"QtWebEngine/WebEngineView 1.5",
"QtWebEngine/WebEngineView 1.6",
"QtWebEngine/WebEngineView 1.7",
- "QtWebEngine/WebEngineView 1.8"
+ "QtWebEngine/WebEngineView 1.8",
+ "QtWebEngine/WebEngineView 1.9"
]
- exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5, 6, 7, 8]
+ exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Enum {
name: "NavigationRequestAction"
values: {
diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp
index 7b2f398ba..7e80f9720 100644
--- a/src/webenginewidgets/api/qwebengineprofile.cpp
+++ b/src/webenginewidgets/api/qwebengineprofile.cpp
@@ -359,6 +359,34 @@ void QWebEngineProfile::setPersistentStoragePath(const QString &path)
}
/*!
+ \since 5.13
+
+ The path to the location where the downloaded files are stored.
+
+ \note By default, the download path is QStandardPaths::DownloadLocation.
+
+ \sa setDownloadPath(), QStandardPaths::writableLocation()
+*/
+QString QWebEngineProfile::downloadPath() const
+{
+ const Q_D(QWebEngineProfile);
+ return d->profileAdapter()->downloadPath();
+}
+
+/*!
+ Overrides the default path used for download location, setting it to \a path.
+
+ If set to the null string, the default path is restored.
+
+ \sa downloadPath()
+*/
+void QWebEngineProfile::setDownloadPath(const QString &path)
+{
+ Q_D(QWebEngineProfile);
+ d->profileAdapter()->setDownloadPath(path);
+}
+
+/*!
Returns the path used for caches.
By default, this is below StandardPaths::CacheLocation in a QtWebengine/StorageName specific
diff --git a/src/webenginewidgets/api/qwebengineprofile.h b/src/webenginewidgets/api/qwebengineprofile.h
index 7ec28ac0a..9fc509851 100644
--- a/src/webenginewidgets/api/qwebengineprofile.h
+++ b/src/webenginewidgets/api/qwebengineprofile.h
@@ -131,6 +131,9 @@ public:
void setUseForGlobalCertificateVerification();
bool isUsedForGlobalCertificateVerification() const;
+ QString downloadPath() const;
+ void setDownloadPath(const QString &path);
+
static QWebEngineProfile *defaultProfile();
Q_SIGNALS:
diff --git a/tests/auto/quick/qmltests/data/tst_download.qml b/tests/auto/quick/qmltests/data/tst_download.qml
index 019ebd9dc..5eb704cce 100644
--- a/tests/auto/quick/qmltests/data/tst_download.qml
+++ b/tests/auto/quick/qmltests/data/tst_download.qml
@@ -28,7 +28,8 @@
import QtQuick 2.0
import QtTest 1.0
-import QtWebEngine 1.5
+import QtWebEngine 1.9
+import Qt.labs.platform 1.0
TestWebEngineView {
id: webEngineView
@@ -42,6 +43,12 @@ TestWebEngineView {
property var downloadState: []
property var downloadInterruptReason: null
+ function urlToPath(url) {
+ var path = url.toString()
+ path = path.replace(/^(file:\/{2})/,"")
+ return path
+ }
+
SignalSpy {
id: downLoadRequestedSpy
target: testDownloadProfile
@@ -135,5 +142,16 @@ TestWebEngineView {
tryCompare(downloadState, "1", WebEngineDownloadItem.DownloadCancelled)
tryCompare(webEngineView, "downloadInterruptReason", WebEngineDownloadItem.UserCanceled)
}
+
+ function test_downloadLocation() {
+ var tmpPath = urlToPath(StandardPaths.writableLocation(StandardPaths.TempLocation));
+ var downloadPath = urlToPath(StandardPaths.writableLocation(StandardPaths.DownloadLocation));
+
+ testDownloadProfile.downloadPath = tmpPath;
+ compare(testDownloadProfile.downloadPath, tmpPath);
+
+ testDownloadProfile.downloadPath = downloadPath;
+ compare(testDownloadProfile.downloadPath, downloadPath);
+ }
}
}
diff --git a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp
index 5227f2d0a..b50036aa8 100644
--- a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp
+++ b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp
@@ -31,6 +31,7 @@
#include <QStandardPaths>
#include <QTemporaryDir>
#include <QTest>
+#include <QRegularExpression>
#include <QWebEngineDownloadItem>
#include <QWebEnginePage>
#include <QWebEngineProfile>
@@ -69,6 +70,12 @@ private Q_SLOTS:
void downloadFileNot1();
void downloadFileNot2();
void downloadDeleted();
+ void downloadUniqueFilename_data();
+ void downloadUniqueFilename();
+ void downloadUniqueFileNameWithTimeStamp();
+ void downloadToDefaultLocation();
+ void downloadToNonExistentDir();
+ void downloadToReadOnlyDir();
private:
void saveLink(QPoint linkPos);
@@ -134,6 +141,8 @@ void tst_QWebEngineDownloadItem::cleanup()
QTRY_COMPARE(m_requestedDownloads.count(), 0);
QCOMPARE(m_finishedDownloads.count(), 0);
QVERIFY(m_server->stop());
+ // Set download path to default.
+ m_profile->setDownloadPath("");
}
void tst_QWebEngineDownloadItem::cleanupTestCase()
@@ -813,5 +822,264 @@ void tst_QWebEngineDownloadItem::downloadDeleted()
QTRY_COMPARE(finishedCount, 1);
}
+void tst_QWebEngineDownloadItem::downloadUniqueFilename_data()
+{
+ QTest::addColumn<QString>("baseName");
+ QTest::addColumn<QString>("extension");
+
+ QTest::newRow("txt") << QString("test(1.test)") << QString("txt");
+ QTest::newRow("tar.gz") << QString("test(1.test)") << QString("tar.gz");
+}
+
+void tst_QWebEngineDownloadItem::downloadUniqueFilename()
+{
+ QFETCH(QString, baseName);
+ QFETCH(QString, extension);
+ QString fileName = QString("%1.%2").arg(baseName).arg(extension);
+ QString downloadedFilePath;
+ bool downloadFinished = false;
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ m_profile->setDownloadPath(tmpDir.path());
+
+ // Set up HTTP server
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) {
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream"));
+ rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
+ rr->setResponseBody(QByteArrayLiteral("a"));
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
+
+ // Set up profile and download handler
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
+ item->accept();
+ connect(item, &QWebEngineDownloadItem::finished, [&, item]() {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
+ QCOMPARE(item->isFinished(), true);
+ QCOMPARE(item->totalBytes(), item->receivedBytes());
+ QVERIFY(item->receivedBytes() > 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), QWebEngineDownloadItem::Attachment);
+ QCOMPARE(item->isSavePageDownload(), false);
+ downloadedFilePath = item->path();
+ downloadFinished = true;
+ });
+ });
+
+ m_page->setUrl(m_server->url("/" + fileName));
+ QTRY_VERIFY(downloadFinished);
+ QVERIFY(QFile(downloadedFilePath).exists());
+ QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + "." + extension);
+
+ for (int i = 1; i <= 2; ++i) {
+ downloadFinished = false;
+ m_page->setUrl(m_server->url("/" + fileName));
+ QTRY_VERIFY(downloadFinished);
+ QVERIFY(QFile(downloadedFilePath).exists());
+ QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + " (" + QString::number(i) + ")." + extension);
+ }
+}
+
+void tst_QWebEngineDownloadItem::downloadUniqueFileNameWithTimeStamp()
+{
+ // Set up HTTP server
+ QString baseName("test(1.test)");
+ QString extension("txt");
+ QString fileName = QString("%1.%2").arg(baseName).arg(extension);
+ QString downloadedFilePath;
+ bool downloadFinished = false;
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ m_profile->setDownloadPath(tmpDir.path());
+
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) {
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream"));
+ rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
+ rr->setResponseBody(QByteArrayLiteral("a"));
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
+
+ // Set up profile and download handler
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
+ item->accept();
+ connect(item, &QWebEngineDownloadItem::finished, [&, item]() {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
+ QCOMPARE(item->isFinished(), true);
+ QCOMPARE(item->totalBytes(), item->receivedBytes());
+ QVERIFY(item->receivedBytes() > 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->page(), m_page);
+ downloadFinished = true;
+ downloadedFilePath = item->path();
+ });
+ });
+
+ // Create the first empty file without uniquifier.
+ {
+ QFile file(m_profile->downloadPath() + "/" + fileName);
+ file.open(QIODevice::ReadWrite);
+ }
+
+ // Create 99 empty files with uniquifier.
+ for (int i = 1; i < 100; i++) {
+ QFile file(m_profile->downloadPath() + "/" + baseName + " (" + QString::number(i) + ")." + extension);
+ file.open(QIODevice::ReadWrite);
+ }
+
+ // Create 100th (kMaxUniqueFiles) empty file with uniquifier.
+ m_page->setUrl(m_server->url("/" + fileName));
+ QTRY_VERIFY(downloadFinished);
+ QVERIFY(QFile(downloadedFilePath).exists());
+ QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + " (100)." + extension);
+
+ // Check if the downloaded files are suffixed with timestamp after the 100th download.
+ for (int i = 101; i < 103; i++) {
+ downloadFinished = false;
+ m_page->setUrl(m_server->url("/" + fileName));
+ QTRY_VERIFY(downloadFinished);
+ QVERIFY(QFile(downloadedFilePath).exists());
+ QRegularExpression fileNameCheck("^.*" + QRegularExpression::escape(baseName) + " - (.*)[.]" + QRegularExpression::escape(extension) + "$");
+ QRegularExpressionMatch match = fileNameCheck.match(downloadedFilePath);
+ QVERIFY(match.hasMatch());
+ // ISO 8601 Date and time in UTC
+ QRegExp timeStamp("^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])([.][0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$");
+ QVERIFY(timeStamp.exactMatch(match.captured(1)));
+ }
+}
+
+void tst_QWebEngineDownloadItem::downloadToDefaultLocation()
+{
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+
+ QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
+
+ m_profile->setDownloadPath("");
+ QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
+
+ m_profile->setDownloadPath(tmpDir.path());
+ QCOMPARE(m_profile->downloadPath(), tmpDir.path());
+
+ m_profile->setDownloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
+ QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
+}
+
+void tst_QWebEngineDownloadItem::downloadToNonExistentDir()
+{
+ QString baseName("test(1.test)");
+ QString extension("txt");
+ QString fileName = QString("%1.%2").arg(baseName).arg(extension);
+ QString downloadedFilePath;
+ bool downloadFinished = false;
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ m_profile->setDownloadPath(tmpDir.path());
+
+ // Set up HTTP server
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) {
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream"));
+ rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
+ rr->setResponseBody(QByteArrayLiteral("a"));
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
+
+ // Set up profile and download handler
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
+ item->accept();
+ connect(item, &QWebEngineDownloadItem::finished, [&, item]() {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
+ QCOMPARE(item->isFinished(), true);
+ QCOMPARE(item->totalBytes(), item->receivedBytes());
+ QVERIFY(item->receivedBytes() > 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->page(), m_page);
+ downloadFinished = true;
+ downloadedFilePath = item->path();
+ });
+ });
+
+ // Set a non-existent directory for the default download location.
+ QString nonExistentDownloadPath(m_profile->downloadPath() + "/non_existent_dir");
+ m_profile->setDownloadPath(nonExistentDownloadPath);
+ QCOMPARE(m_profile->downloadPath(), nonExistentDownloadPath);
+ m_page->setUrl(m_server->url("/" + fileName));
+ QTRY_VERIFY(downloadFinished);
+ QVERIFY(QFile(downloadedFilePath).exists());
+ QCOMPARE(downloadedFilePath, nonExistentDownloadPath + "/" + fileName);
+}
+
+void tst_QWebEngineDownloadItem::downloadToReadOnlyDir()
+{
+ QString baseName("test(1.test)");
+ QString extension("txt");
+ QString fileName = QString("%1.%2").arg(baseName).arg(extension);
+ QString downloadedFilePath;
+ bool downloadAccepted = false;
+ bool downloadFinished = false;
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ m_profile->setDownloadPath(tmpDir.path());
+
+ // Set up HTTP server
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) {
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream"));
+ rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
+ rr->setResponseBody(QByteArrayLiteral("a"));
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
+
+ QPointer<QWebEngineDownloadItem> downloadItem;
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
+ downloadItem = item;
+ item->accept();
+ connect(item, &QWebEngineDownloadItem::finished, [&, item]() {
+ downloadFinished = true;
+ });
+ downloadAccepted = true;
+ });
+
+ // Change permission for directory.
+ QFile(m_profile->downloadPath()).setPermissions(QFileDevice::ReadOwner);
+ QVERIFY(QFile(m_profile->downloadPath()).exists());
+
+ m_page->setUrl(m_server->url("/" + fileName));
+ QTRY_VERIFY(downloadAccepted);
+
+ QVERIFY(downloadItem);
+ QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadInterrupted);
+ QCOMPARE(downloadItem->isFinished(), false);
+ QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::FileAccessDenied);
+ QVERIFY(!QFile(downloadedFilePath).exists());
+
+ // Clear m_requestedDownloads explicitly because download is accepted but never finished.
+ m_requestedDownloads.clear();
+ QVERIFY(!downloadFinished);
+ QFile(m_profile->downloadPath()).setPermissions(QFileDevice::WriteOwner);
+}
+
QTEST_MAIN(tst_QWebEngineDownloadItem)
#include "tst_qwebenginedownloaditem.moc"