diff options
20 files changed, 476 insertions, 57 deletions
diff --git a/examples/webengine/quicknanobrowser/DownloadView.qml b/examples/webengine/quicknanobrowser/DownloadView.qml index f6ebeab4a..e26c770bc 100644 --- a/examples/webengine/quicknanobrowser/DownloadView.qml +++ b/examples/webengine/quicknanobrowser/DownloadView.qml @@ -101,7 +101,7 @@ Rectangle { } Label { id: label - text: path + text: downloadDirectory + "/" + downloadFileName anchors { verticalCenter: cancelButton.verticalCenter left: parent.left diff --git a/examples/webenginewidgets/simplebrowser/downloadmanagerwidget.cpp b/examples/webenginewidgets/simplebrowser/downloadmanagerwidget.cpp index e0a511475..b6f9e9c13 100644 --- a/examples/webenginewidgets/simplebrowser/downloadmanagerwidget.cpp +++ b/examples/webenginewidgets/simplebrowser/downloadmanagerwidget.cpp @@ -55,6 +55,7 @@ #include "downloadwidget.h" #include <QFileDialog> +#include <QDir> #include <QWebEngineDownloadItem> DownloadManagerWidget::DownloadManagerWidget(QWidget *parent) @@ -68,11 +69,12 @@ void DownloadManagerWidget::downloadRequested(QWebEngineDownloadItem *download) { Q_ASSERT(download && download->state() == QWebEngineDownloadItem::DownloadRequested); - QString path = QFileDialog::getSaveFileName(this, tr("Save as"), download->path()); + QString path = QFileDialog::getSaveFileName(this, tr("Save as"), QDir(download->downloadDirectory()).filePath(download->downloadFileName())); if (path.isEmpty()) return; - download->setPath(path); + download->setDownloadDirectory(QFileInfo(path).path()); + download->setDownloadFileName(QFileInfo(path).fileName()); download->accept(); add(new DownloadWidget(download)); diff --git a/examples/webenginewidgets/simplebrowser/downloadwidget.cpp b/examples/webenginewidgets/simplebrowser/downloadwidget.cpp index 1d599a19d..835a901c6 100644 --- a/examples/webenginewidgets/simplebrowser/downloadwidget.cpp +++ b/examples/webenginewidgets/simplebrowser/downloadwidget.cpp @@ -60,7 +60,7 @@ DownloadWidget::DownloadWidget(QWebEngineDownloadItem *download, QWidget *parent , m_timeAdded(QTime::currentTime()) { setupUi(this); - m_dstName->setText(QFileInfo(m_download->path()).fileName()); + m_dstName->setText(m_download->downloadFileName()); m_srcUrl->setText(m_download->url().toDisplayString()); connect(m_cancelButton, &QPushButton::clicked, diff --git a/src/core/download_manager_delegate_qt.cpp b/src/core/download_manager_delegate_qt.cpp index ba506601f..34e290317 100644 --- a/src/core/download_manager_delegate_qt.cpp +++ b/src/core/download_manager_delegate_qt.cpp @@ -168,22 +168,7 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem* QDir defaultDownloadDirectory(m_profileAdapter->downloadPath()); - QFileInfo suggestedFile(defaultDownloadDirectory.absoluteFilePath(suggestedFilename)); - QString suggestedFilePath = suggestedFile.absoluteFilePath(); - base::FilePath tmpFilePath(toFilePath(suggestedFilePath).NormalizePathSeparatorsTo('/')); - - int uniquifier = base::GetUniquePathNumber(tmpFilePath, base::FilePath::StringType()); - if (uniquifier > 0) - suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier)).AsUTF8Unsafe()); - else if (uniquifier == -1) { - base::Time::Exploded exploded; - item->GetStartTime().LocalExplode(&exploded); - std::string suffix = base::StringPrintf( - " - %04d-%02d-%02dT%02d%02d%02d.%03d", exploded.year, exploded.month, - exploded.day_of_month, exploded.hour, exploded.minute, - exploded.second, exploded.millisecond); - suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(suffix).AsUTF8Unsafe()); - } + QString suggestedFilePath = m_profileAdapter->determineDownloadPath(defaultDownloadDirectory.absolutePath(), suggestedFilename, item->GetStartTime().ToTimeT()); item->AddObserver(this); QList<ProfileAdapterClient*> clients = m_profileAdapter->clients(); @@ -209,7 +194,8 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem* downloadType, item->GetLastReason(), adapterClient, - suggestedFilename + suggestedFilename, + item->GetStartTime().ToTimeT() }; for (ProfileAdapterClient *client : qAsConst(clients)) { @@ -218,7 +204,7 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem* break; } - suggestedFile.setFile(info.path); + QFileInfo suggestedFile(info.path); if (info.accepted && !suggestedFile.absoluteDir().mkpath(suggestedFile.absolutePath())) { qWarning("Creating download path failed, download cancelled: %s", suggestedFile.absolutePath().toUtf8().data()); @@ -279,7 +265,7 @@ void DownloadManagerDelegateQt::ChooseSavePath(content::WebContents *web_content acceptedByDefault = true; } if (QFileInfo(suggestedFilePath).isRelative()) { - const QDir downloadDir(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + const QDir downloadDir(m_profileAdapter->downloadPath()); suggestedFilePath = downloadDir.absoluteFilePath(suggestedFilePath); } @@ -311,8 +297,8 @@ void DownloadManagerDelegateQt::ChooseSavePath(content::WebContents *web_content ProfileAdapterClient::SavePage, ProfileAdapterClient::NoReason, adapterClient, - QFileInfo(suggestedFilePath).fileName() - + QFileInfo(suggestedFilePath).fileName(), + time_t(QDateTime::currentMSecsSinceEpoch()) }; for (ProfileAdapterClient *client : qAsConst(clients)) { @@ -379,7 +365,8 @@ void DownloadManagerDelegateQt::OnDownloadUpdated(download::DownloadItem *downlo 0 /* downloadType (unused) */, download->GetLastReason(), adapterClient, - toQt(download->GetSuggestedFilename()) + toQt(download->GetSuggestedFilename()), + download->GetStartTime().ToTimeT() }; for (ProfileAdapterClient *client : qAsConst(clients)) { diff --git a/src/core/download_manager_delegate_qt.h b/src/core/download_manager_delegate_qt.h index 382c57524..6acfa42ce 100644 --- a/src/core/download_manager_delegate_qt.h +++ b/src/core/download_manager_delegate_qt.h @@ -109,6 +109,7 @@ private: bool m_nextDownloadIsUserRequested; friend class DownloadManagerDelegateInstance; + friend class ProfileAdapter; DISALLOW_COPY_AND_ASSIGN(DownloadManagerDelegateQt); }; diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index 8d32aad84..65d5c2da2 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -57,6 +57,8 @@ #include "web_engine_context.h" #include "web_contents_adapter_client.h" +#include "base/files/file_util.h" +#include "base/time/time_to_iso8601.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #if BUILDFLAG(ENABLE_EXTENSIONS) @@ -691,6 +693,34 @@ bool ProfileAdapter::isUsedForGlobalCertificateVerification() const return m_usedForGlobalCertificateVerification; } +QString ProfileAdapter::determineDownloadPath(const QString &downloadDirectory, const QString &suggestedFilename, const time_t &startTime) +{ + QFileInfo suggestedFile(QDir(downloadDirectory).absoluteFilePath(suggestedFilename)); + QString suggestedFilePath = suggestedFile.absoluteFilePath(); + base::FilePath tmpFilePath(toFilePath(suggestedFilePath).NormalizePathSeparatorsTo('/')); + + int uniquifier = base::GetUniquePathNumber(tmpFilePath, base::FilePath::StringType()); + if (uniquifier > 0) + suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier)).AsUTF8Unsafe()); + else if (uniquifier == -1) { + base::Time::Exploded exploded; + base::Time::FromTimeT(startTime).LocalExplode(&exploded); + std::string suffix = base::StringPrintf( + " - %04d-%02d-%02dT%02d%02d%02d.%03d", exploded.year, exploded.month, + exploded.day_of_month, exploded.hour, exploded.minute, + exploded.second, exploded.millisecond); + suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(suffix).AsUTF8Unsafe()); + } + return suggestedFilePath; +} + +QString ProfileAdapter::updateDownloadPath(int downloadId, const QString &directory, const QString &fileName) +{ + download::DownloadItem *download = m_downloadManagerDelegate->findDownloadById(downloadId); + Q_ASSERT(download); + return determineDownloadPath(directory, fileName, download->GetStartTime().ToTimeT()); +} + #if QT_CONFIG(ssl) QWebEngineClientCertificateStore *ProfileAdapter::clientCertificateStore() { diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h index 1f94f59a9..8df85a68f 100644 --- a/src/core/profile_adapter.h +++ b/src/core/profile_adapter.h @@ -214,6 +214,9 @@ public: QHash<QByteArray, QSharedPointer<UserNotificationController>> &persistentNotifications() { return m_persistentNotifications; } + QString determineDownloadPath(const QString &downloadDirectory, const QString &suggestedFilename, const time_t &startTime); + QString updateDownloadPath(int downloadId, const QString &directory, const QString &filename); + private: void updateCustomUrlSchemeHandlers(); void resetVisitedLinksManager(); diff --git a/src/core/profile_adapter_client.h b/src/core/profile_adapter_client.h index 3589f6cd7..dc0f508a1 100644 --- a/src/core/profile_adapter_client.h +++ b/src/core/profile_adapter_client.h @@ -55,6 +55,7 @@ #include <QSharedPointer> #include <QString> #include <QUrl> +#include <time.h> namespace QtWebEngineCore { @@ -139,6 +140,7 @@ public: int downloadInterruptReason; WebContentsAdapterClient *page; QString suggestedFileName; + time_t startTime; }; virtual ~ProfileAdapterClient() { } diff --git a/src/webengine/api/qquickwebenginedownloaditem.cpp b/src/webengine/api/qquickwebenginedownloaditem.cpp index 2b3e50a72..f9b305e88 100644 --- a/src/webengine/api/qquickwebenginedownloaditem.cpp +++ b/src/webengine/api/qquickwebenginedownloaditem.cpp @@ -43,6 +43,7 @@ #include "profile_adapter.h" #include "qquickwebengineprofile_p.h" +#include <QDir> #include "QFileInfo" using QtWebEngineCore::ProfileAdapterClient; @@ -417,6 +418,7 @@ QString QQuickWebEngineDownloadItem::mimeType() const /*! \qmlproperty string WebEngineDownloadItem::path + \obsolete Holds the full target path where data is being downloaded to. @@ -433,7 +435,7 @@ QString QQuickWebEngineDownloadItem::mimeType() const QString QQuickWebEngineDownloadItem::path() const { Q_D(const QQuickWebEngineDownloadItem); - return d->downloadPath; + return QDir::cleanPath(QDir(d->downloadDirectory).filePath(d->downloadFileName)); } void QQuickWebEngineDownloadItem::setPath(QString path) @@ -443,7 +445,7 @@ void QQuickWebEngineDownloadItem::setPath(QString path) qWarning("Setting the download path is not allowed after the download has been accepted."); return; } - if (d->downloadPath != path) { + if (QDir(d->downloadDirectory).filePath(d->downloadFileName) != path) { if (QFileInfo(path).fileName().isEmpty()) { qWarning("The download path does not include file name."); return; @@ -454,10 +456,113 @@ void QQuickWebEngineDownloadItem::setPath(QString path) return; } - d->downloadPath = path; + QString newDirectory; + QString newFileName; + + if (QFileInfo(path).fileName() == path) { + newDirectory = QStringLiteral(""); + newFileName = path; + } else { + newDirectory = QFileInfo(path).filePath(); + newFileName = QFileInfo(path).fileName(); + } + + if (d->downloadDirectory != newDirectory) { + d->downloadDirectory = newDirectory; + Q_EMIT pathChanged(); + Q_EMIT downloadDirectoryChanged(); + } + + if (d->downloadFileName != newFileName) { + d->downloadFileName = newFileName; + Q_EMIT pathChanged(); + Q_EMIT downloadFileNameChanged(); + } + } +} + +/*! + \qmlproperty string WebEngineDownloadItem::downloadDirectory + \since QtWebEngine 1.10 + + Holds the full target path without file name where data is being downloaded to. + + The download directory can only be set in the + \l{WebEngineProfile::downloadRequested}{downloadRequested} handler before + the download is accepted. + + \sa WebEngineProfile::downloadRequested(), accept() +*/ + +QString QQuickWebEngineDownloadItem::downloadDirectory() const +{ + Q_D(const QQuickWebEngineDownloadItem); + return d->downloadDirectory; +} + +void QQuickWebEngineDownloadItem::setDownloadDirectory(QString directory) +{ + Q_D(QQuickWebEngineDownloadItem); + if (d->downloadState != QQuickWebEngineDownloadItem::DownloadRequested) { + qWarning("Setting the download directory is not allowed after the download has been accepted."); + return; + } + + QString changeDirectory = d->downloadDirectory; + if (!directory.isEmpty() && changeDirectory != directory) { + changeDirectory = directory; + + if (d->downloadDirectory != changeDirectory) { + d->downloadDirectory = changeDirectory; + Q_EMIT pathChanged(); + Q_EMIT downloadDirectoryChanged(); + } + + QString newFileName = QFileInfo(d->profile->d_ptr->profileAdapter()->updateDownloadPath(d->downloadId, + d->downloadDirectory, + d->suggestedFileName)).fileName(); + if (d->downloadFileName != newFileName) { + d->downloadFileName = newFileName; + Q_EMIT pathChanged(); + Q_EMIT downloadFileNameChanged(); + } + } +} + +/*! + \qmlproperty string WebEngineDownloadItem::downloadFileName + \since QtWebEngine 1.10 + + Holds the name of the file to which data is being downloaded. + + The download file name can only be set in the + \l{WebEngineProfile::downloadRequested}{downloadRequested} handler before + the download is accepted. + + \sa WebEngineProfile::downloadRequested(), accept() +*/ + +QString QQuickWebEngineDownloadItem::downloadFileName() const +{ + Q_D(const QQuickWebEngineDownloadItem); + return d->downloadFileName; +} + +void QQuickWebEngineDownloadItem::setDownloadFileName(QString fileName) +{ + Q_D(QQuickWebEngineDownloadItem); + if (d->downloadState != QQuickWebEngineDownloadItem::DownloadRequested) { + qWarning("Setting the download file name is not allowed after the download has been accepted."); + return; + } + + if (d->downloadFileName != fileName && !fileName.isEmpty()) { + d->downloadFileName = fileName; Q_EMIT pathChanged(); + Q_EMIT downloadFileNameChanged(); } } + /*! \qmlproperty string WebEngineDownloadItem::suggestedFileName \since QtWebEngine 1.10 diff --git a/src/webengine/api/qquickwebenginedownloaditem_p.h b/src/webengine/api/qquickwebenginedownloaditem_p.h index 613b0173d..cef99e534 100644 --- a/src/webengine/api/qquickwebenginedownloaditem_p.h +++ b/src/webengine/api/qquickwebenginedownloaditem_p.h @@ -139,6 +139,8 @@ public: Q_PROPERTY(QQuickWebEngineView *view READ view CONSTANT REVISION 7 FINAL) Q_PROPERTY(QUrl url READ url CONSTANT REVISION 8 FINAL) Q_PROPERTY(QString suggestedFileName READ suggestedFileName CONSTANT REVISION 8 FINAL) + Q_PROPERTY(QString downloadDirectory READ downloadDirectory WRITE setDownloadDirectory NOTIFY downloadDirectoryChanged REVISION 8 FINAL) + Q_PROPERTY(QString downloadFileName READ downloadFileName WRITE setDownloadFileName NOTIFY downloadFileNameChanged REVISION 8 FINAL) Q_INVOKABLE void accept(); Q_INVOKABLE void cancel(); @@ -150,8 +152,8 @@ public: qint64 totalBytes() const; qint64 receivedBytes() const; QString mimeType() const; - QString path() const; - void setPath(QString path); + QString Q_DECL_DEPRECATED path() const; + void Q_DECL_DEPRECATED setPath(QString path); SavePageFormat savePageFormat() const; void setSavePageFormat(SavePageFormat format); DownloadType Q_DECL_DEPRECATED type() const; @@ -163,6 +165,10 @@ public: QQuickWebEngineView *view() const; QUrl url() const; QString suggestedFileName() const; + QString downloadDirectory() const; + void setDownloadDirectory(QString directory); + QString downloadFileName() const; + void setDownloadFileName(QString fileName); Q_SIGNALS: void stateChanged(); @@ -170,11 +176,13 @@ Q_SIGNALS: void receivedBytesChanged(); void totalBytesChanged(); Q_REVISION(1) void mimeTypeChanged(); - void pathChanged(); + void Q_DECL_DEPRECATED pathChanged(); Q_REVISION(3) void typeChanged(); Q_REVISION(4) void interruptReasonChanged(); Q_REVISION(5) void isFinishedChanged(); Q_REVISION(5) void isPausedChanged(); + Q_REVISION(8) void downloadDirectoryChanged(); + Q_REVISION(8) void downloadFileNameChanged(); private: QQuickWebEngineDownloadItem(QQuickWebEngineDownloadItemPrivate*, QObject *parent = 0); diff --git a/src/webengine/api/qquickwebenginedownloaditem_p_p.h b/src/webengine/api/qquickwebenginedownloaditem_p_p.h index e4d90d8ef..51deee18b 100644 --- a/src/webengine/api/qquickwebenginedownloaditem_p_p.h +++ b/src/webengine/api/qquickwebenginedownloaditem_p_p.h @@ -84,6 +84,8 @@ public: QQuickWebEngineView *view; QUrl downloadUrl; QString suggestedFileName; + QString downloadDirectory; + QString downloadFileName; void update(const QtWebEngineCore::ProfileAdapterClient::DownloadItemInfo &info); void updateState(QQuickWebEngineDownloadItem::DownloadState newState); diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp index 2dbd0e94b..57434e296 100644 --- a/src/webengine/api/qquickwebengineprofile.cpp +++ b/src/webengine/api/qquickwebengineprofile.cpp @@ -48,6 +48,8 @@ #include "qwebenginecookiestore.h" #include "qwebenginenotification.h" +#include <QFileInfo> +#include <QDir> #include <QQmlEngine> #include "profile_adapter.h" @@ -242,7 +244,8 @@ void QQuickWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info) itemPrivate->downloadState = QQuickWebEngineDownloadItem::DownloadRequested; itemPrivate->totalBytes = info.totalBytes; itemPrivate->mimeType = info.mimeType; - itemPrivate->downloadPath = info.path; + itemPrivate->downloadDirectory = QFileInfo(info.path).path(); + itemPrivate->downloadFileName = QFileInfo(info.path).fileName(); itemPrivate->suggestedFileName = info.suggestedFileName; itemPrivate->savePageFormat = static_cast<QQuickWebEngineDownloadItem::SavePageFormat>( info.savePageFormat); @@ -261,7 +264,7 @@ void QQuickWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info) Q_EMIT q->downloadRequested(download); QQuickWebEngineDownloadItem::DownloadState state = download->state(); - info.path = download->path(); + info.path = QDir(download->downloadDirectory()).filePath(download->downloadFileName()); info.savePageFormat = itemPrivate->savePageFormat; info.accepted = state != QQuickWebEngineDownloadItem::DownloadCancelled && state != QQuickWebEngineDownloadItem::DownloadRequested; diff --git a/src/webenginewidgets/api/qwebenginedownloaditem.cpp b/src/webenginewidgets/api/qwebenginedownloaditem.cpp index 6df131066..7ce572e2f 100644 --- a/src/webenginewidgets/api/qwebenginedownloaditem.cpp +++ b/src/webenginewidgets/api/qwebenginedownloaditem.cpp @@ -43,6 +43,7 @@ #include "profile_adapter.h" #include "qwebengineprofile_p.h" +#include <QDir> #include "QFileInfo" QT_BEGIN_NAMESPACE @@ -508,6 +509,7 @@ QString QWebEngineDownloadItem::mimeType() const } /*! + \obsolete Returns the full target path where data is being downloaded to. The path includes the file name. The default suggested path is the standard download location @@ -517,7 +519,7 @@ QString QWebEngineDownloadItem::mimeType() const QString QWebEngineDownloadItem::path() const { Q_D(const QWebEngineDownloadItem); - return d->downloadPath; + return QDir::cleanPath(QDir(d->downloadDirectory).filePath(d->downloadFileName)); } /*! @@ -534,19 +536,99 @@ void QWebEngineDownloadItem::setPath(QString path) qWarning("Setting the download path is not allowed after the download has been accepted."); return; } + if (QDir(d->downloadDirectory).filePath(d->downloadFileName) != path) { + if (QFileInfo(path).fileName().isEmpty()) { + qWarning("The download path does not include file name."); + return; + } + + if (QFileInfo(path).isDir()) { + qWarning("The download path matches with an already existing directory path."); + return; + } + + if (QFileInfo(path).fileName() == path) { + d->downloadDirectory = QStringLiteral(""); + d->downloadFileName = path; + } else { + d->downloadDirectory = QFileInfo(path).filePath(); + d->downloadFileName = QFileInfo(path).fileName(); + } + } +} + +/*! + \since 5.14 + + Returns the download directory path. +*/ + +QString QWebEngineDownloadItem::downloadDirectory() const +{ + Q_D(const QWebEngineDownloadItem); + return d->downloadDirectory; +} - if (QFileInfo(path).fileName().isEmpty()) { - qWarning("The download path does not include file name."); +/*! + \since 5.14 + + Sets the directory path to download the file to. + + The download directory path can only be set in response to the QWebEngineProfile::downloadRequested() + signal before the download is accepted. Past that point, this function has no effect on the + download item's state. +*/ + +void QWebEngineDownloadItem::setDownloadDirectory(QString directory) +{ + Q_D(QWebEngineDownloadItem); + if (d->downloadState != QWebEngineDownloadItem::DownloadRequested) { + qWarning("Setting the download directory is not allowed after the download has been accepted."); return; - } + } + + if (!directory.isEmpty() && d->downloadDirectory != directory) + d->downloadDirectory = directory; - if (QFileInfo(path).isDir()) { - qWarning("The download path matches with an already existing directory path."); + d->downloadFileName = QFileInfo(d->profile->profileAdapter()->updateDownloadPath(d->downloadId, + d->downloadDirectory, + d->suggestedFileName)).fileName(); +} + +/*! + \since 5.14 + + Returns the suggested file name. +*/ + +QString QWebEngineDownloadItem::downloadFileName() const +{ + Q_D(const QWebEngineDownloadItem); + return d->downloadFileName; +} + +/*! + \since 5.14 + + Sets the file name to download the file to. + + The download file name can only be set in response to the QWebEngineProfile::downloadRequested() + signal before the download is accepted. Past that point, this function has no effect on the + download item's state. +*/ + +void QWebEngineDownloadItem::setDownloadFileName(QString fileName) +{ + Q_D(QWebEngineDownloadItem); + if (d->downloadState != QWebEngineDownloadItem::DownloadRequested) { + qWarning("Setting the download file name is not allowed after the download has been accepted."); return; } - d->downloadPath = path; + if (!fileName.isEmpty()) + d->downloadFileName = fileName; } + /*! \since 5.14 diff --git a/src/webenginewidgets/api/qwebenginedownloaditem.h b/src/webenginewidgets/api/qwebenginedownloaditem.h index 0ae6b2575..169d80553 100644 --- a/src/webenginewidgets/api/qwebenginedownloaditem.h +++ b/src/webenginewidgets/api/qwebenginedownloaditem.h @@ -118,8 +118,8 @@ public: qint64 receivedBytes() const; QUrl url() const; QString mimeType() const; - QString path() const; - void setPath(QString path); + QString Q_DECL_DEPRECATED path() const; + void Q_DECL_DEPRECATED setPath(QString path); bool isFinished() const; bool isPaused() const; SavePageFormat savePageFormat() const; @@ -129,6 +129,10 @@ public: QString interruptReasonString() const; bool isSavePageDownload() const; QString suggestedFileName() const; + QString downloadDirectory() const; + void setDownloadDirectory(QString directory); + QString downloadFileName() const; + void setDownloadFileName(QString fileName); QWebEnginePage *page() const; diff --git a/src/webenginewidgets/api/qwebenginedownloaditem_p.h b/src/webenginewidgets/api/qwebenginedownloaditem_p.h index ac5db7dcd..08e478736 100644 --- a/src/webenginewidgets/api/qwebenginedownloaditem_p.h +++ b/src/webenginewidgets/api/qwebenginedownloaditem_p.h @@ -79,6 +79,8 @@ public: QString mimeType; bool downloadPaused; QString suggestedFileName; + QString downloadDirectory; + QString downloadFileName; qint64 totalBytes; qint64 receivedBytes; diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index 6e7191355..6e2db533e 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -53,6 +53,7 @@ #include "visited_links_manager_qt.h" #include "web_engine_settings.h" +#include <QDir> #include <QtWebEngineCore/qwebengineurlscheme.h> QT_BEGIN_NAMESPACE @@ -226,7 +227,8 @@ void QWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info) itemPrivate->downloadId = info.id; itemPrivate->downloadState = info.accepted ? QWebEngineDownloadItem::DownloadInProgress : QWebEngineDownloadItem::DownloadRequested; - itemPrivate->downloadPath = info.path; + itemPrivate->downloadDirectory = QFileInfo(info.path).path(); + itemPrivate->downloadFileName = QFileInfo(info.path).fileName(); itemPrivate->suggestedFileName = info.suggestedFileName; itemPrivate->mimeType = info.mimeType; itemPrivate->savePageFormat = static_cast<QWebEngineDownloadItem::SavePageFormat>(info.savePageFormat); @@ -245,7 +247,7 @@ void QWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info) QWebEngineDownloadItem::DownloadState state = download->state(); - info.path = download->path(); + info.path = QDir(download->downloadDirectory()).filePath(download->downloadFileName()); info.savePageFormat = static_cast<QtWebEngineCore::ProfileAdapterClient::SavePageFormat>( download->savePageFormat()); info.accepted = state != QWebEngineDownloadItem::DownloadCancelled; diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 8b8eb8636..f464d58d0 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -262,6 +262,10 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineDownloadItem.view --> QQuickWebEngineView*" << "QQuickWebEngineDownloadItem.url --> QUrl" << "QQuickWebEngineDownloadItem.suggestedFileName --> QString" + << "QQuickWebEngineDownloadItem.downloadDirectory --> QString" + << "QQuickWebEngineDownloadItem.downloadDirectoryChanged() --> void" + << "QQuickWebEngineDownloadItem.downloadFileName --> QString" + << "QQuickWebEngineDownloadItem.downloadFileNameChanged() --> void" << "QQuickWebEngineFileDialogRequest.FileModeOpen --> FileMode" << "QQuickWebEngineFileDialogRequest.FileModeOpenMultiple --> FileMode" << "QQuickWebEngineFileDialogRequest.FileModeSave --> FileMode" diff --git a/tests/auto/quick/qmltests/data/tst_download.qml b/tests/auto/quick/qmltests/data/tst_download.qml index c8f783b03..ff57c76c7 100644 --- a/tests/auto/quick/qmltests/data/tst_download.qml +++ b/tests/auto/quick/qmltests/data/tst_download.qml @@ -44,6 +44,12 @@ TestWebEngineView { property var downloadInterruptReason: null property url downloadUrl: "" property string suggestedFileName: "" + property string downloadDirectory: "" + property string downloadFileName: "" + property string downloadedPath: "" + property int downloadDirectoryChanged: 0 + property int downloadFileNameChanged: 0 + property int downloadPathChanged: 0 function urlToPath(url) { var path = url.toString() @@ -68,23 +74,36 @@ TestWebEngineView { ignoreUnknownSignals: true onStateChanged: downloadState.push(target.state) onInterruptReasonChanged: downloadInterruptReason = target.interruptReason + onDownloadDirectoryChanged: downloadDirectoryChanged++ + onDownloadFileNameChanged: downloadFileNameChanged++ + onPathChanged: downloadPathChanged++ } WebEngineProfile { id: testDownloadProfile onDownloadRequested: { + testDownloadProfile.downloadPath = urlToPath(StandardPaths.writableLocation(StandardPaths.TempLocation)) downloadState.push(download.state) downloadItemConnections.target = download if (cancelDownload) { download.cancel() } else { totalBytes = download.totalBytes - download.path = "testfile.zip" + download.downloadDirectory = testDownloadProfile.downloadPath + download.downloadFileName = "testfile.zip" + + if (downloadDirectory.length != 0) + download.downloadDirectory = testDownloadProfile.downloadPath + downloadDirectory + + if (downloadFileName.length != 0) + download.downloadFileName = downloadFileName + download.accept() } downloadUrl = download.url suggestedFileName = download.suggestedFileName + downloadedPath = download.downloadDirectory + download.downloadFileName } onDownloadFinished: { receivedBytes = download.receivedBytes; @@ -103,6 +122,9 @@ TestWebEngineView { downloadItemConnections.target = null downloadState = [] downloadInterruptReason = null + downloadDirectoryChanged = 0 + downloadFileNameChanged = 0 + downloadPathChanged = 0 } function test_downloadRequest() { @@ -165,5 +187,28 @@ TestWebEngineView { testDownloadProfile.downloadPath = downloadPath; compare(testDownloadProfile.downloadPath, downloadPath); } + + function test_downloadToDirectoryWithFileName() { + compare(downLoadRequestedSpy.count, 0); + compare(downloadDirectoryChanged, 0); + compare(downloadFileNameChanged, 0); + downloadDirectory = "/test/"; + downloadFileName = "test.zip"; + webEngineView.url = Qt.resolvedUrl("download.zip"); + downLoadRequestedSpy.wait(); + compare(downLoadRequestedSpy.count, 1); + compare(downloadUrl, webEngineView.url); + compare(suggestedFileName, "download.zip"); + compare(downloadState[0], WebEngineDownloadItem.DownloadRequested); + tryCompare(downloadState, "1", WebEngineDownloadItem.DownloadInProgress); + compare(downloadedPath, testDownloadProfile.downloadPath + downloadDirectory + downloadFileName); + compare(downloadDirectoryChanged, 1); + compare(downloadFileNameChanged, 3); + compare(downloadPathChanged, 4); + downloadFinishedSpy.wait(); + compare(totalBytes, receivedBytes); + tryCompare(downloadState, "2", WebEngineDownloadItem.DownloadCompleted); + verify(!downloadInterruptReason); + } } } diff --git a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp index e614f3751..c0bb8d5c5 100644 --- a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp +++ b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp @@ -238,7 +238,8 @@ void tst_LoadSignals::fileDownloadDoesNotTriggerLoadSignals_qtbug66661() connect(item, &QWebEngineDownloadItem::stateChanged, [&downloadState](QWebEngineDownloadItem::DownloadState newState){ downloadState = newState; }); - item->setPath(tempDir.filePath(QFileInfo(item->path()).fileName())); + item->setDownloadDirectory(tempDir.filePath(QFileInfo(item->path()).path())); + item->setDownloadFileName(QFileInfo(item->path()).fileName()); item->accept(); }); diff --git a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp index 8ae2e9aea..6dc7f03c1 100644 --- a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp +++ b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp @@ -78,6 +78,7 @@ private Q_SLOTS: void downloadToNonExistentDir(); void downloadToReadOnlyDir(); void downloadPathValidation(); + void downloadToDirectoryWithFileName(); private: void saveLink(QPoint linkPos); @@ -447,6 +448,8 @@ void tst_QWebEngineDownloadItem::downloadLink() QByteArray slashFileName = QByteArrayLiteral("/") + fileName; QString suggestedPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + slashFileName; + QString downloadDirectory = tmpDir.path(); + QString downloadFileName = fileName; QString downloadPath = tmpDir.path() + slashFileName; QUrl downloadUrl = m_server->url(slashFileName); int acceptedCount = 0; @@ -460,7 +463,7 @@ void tst_QWebEngineDownloadItem::downloadLink() QCOMPARE(item->type(), expectedDownloadType(userAction, fileDisposition)); QCOMPARE(item->isSavePageDownload(), false); QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected)); - QCOMPARE(item->path(), suggestedPath); + QCOMPARE(QDir(item->downloadDirectory()).filePath(item->downloadFileName()), suggestedPath); QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat); QCOMPARE(item->url(), downloadUrl); QCOMPARE(item->page(), m_page); @@ -474,14 +477,15 @@ void tst_QWebEngineDownloadItem::downloadLink() QCOMPARE(item->type(), expectedDownloadType(userAction, fileDisposition)); QCOMPARE(item->isSavePageDownload(), false); QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected)); - QCOMPARE(item->path(), downloadPath); + QCOMPARE(QDir(item->downloadDirectory()).filePath(item->downloadFileName()), downloadPath); QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat); QCOMPARE(item->url(), downloadUrl); QCOMPARE(item->page(), m_page); finishedCount++; }); - item->setPath(downloadPath); + item->setDownloadDirectory(downloadDirectory); + item->setDownloadFileName(downloadFileName); item->accept(); acceptedCount++; @@ -575,7 +579,8 @@ void tst_QWebEngineDownloadItem::downloadTwoLinks() QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat); QCOMPARE(item->mimeType(), QStringLiteral("text/plain")); QString filePart = QChar('/') + item->url().fileName(); - QCOMPARE(item->path(), standardDir + filePart); + QString fileName = item->url().fileName(); + QCOMPARE(QDir(item->downloadDirectory()).filePath(item->downloadFileName()), standardDir + filePart); // type() is broken due to race condition in DownloadManagerDelegateQt if (action1 == ClickLink && action2 == ClickLink) { @@ -590,7 +595,8 @@ void tst_QWebEngineDownloadItem::downloadTwoLinks() connect(item, &QWebEngineDownloadItem::finished, [&]() { finishedCount++; }); - item->setPath(tmpDir.path() + filePart); + item->setDownloadDirectory(tmpDir.path()); + item->setDownloadFileName(fileName); item->accept(); acceptedCount++; @@ -655,7 +661,7 @@ void tst_QWebEngineDownloadItem::downloadPage() QCOMPARE(item->isSavePageDownload(), true); // FIXME(juvaldma): why is mimeType always the same? QCOMPARE(item->mimeType(), QStringLiteral("application/x-mimearchive")); - QCOMPARE(item->path(), downloadPath); + QCOMPARE(QDir(item->downloadDirectory()).filePath(item->downloadFileName()), downloadPath); QCOMPARE(item->savePageFormat(), savePageFormat); QCOMPARE(item->url(), downloadUrl); QCOMPARE(item->page(), m_page); @@ -670,7 +676,7 @@ void tst_QWebEngineDownloadItem::downloadPage() QCOMPARE(item->type(), QWebEngineDownloadItem::SavePage); QCOMPARE(item->isSavePageDownload(), true); QCOMPARE(item->mimeType(), QStringLiteral("application/x-mimearchive")); - QCOMPARE(item->path(), downloadPath); + QCOMPARE(QDir(item->downloadDirectory()).filePath(item->downloadFileName()), downloadPath); QCOMPARE(item->savePageFormat(), savePageFormat); QCOMPARE(item->url(), downloadUrl); QCOMPARE(item->page(), m_page); @@ -901,7 +907,7 @@ void tst_QWebEngineDownloadItem::downloadUniqueFilename() QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); QCOMPARE(item->type(), QWebEngineDownloadItem::Attachment); QCOMPARE(item->isSavePageDownload(), false); - downloadedFilePath = item->path(); + downloadedFilePath = QDir(item->downloadDirectory()).filePath(item->downloadFileName()); downloadFinished = true; }); }); @@ -959,7 +965,7 @@ void tst_QWebEngineDownloadItem::downloadUniqueFilenameWithTimestamp() QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); QCOMPARE(item->page(), m_page); downloadFinished = true; - downloadedFilePath = item->path(); + downloadedFilePath = QDir(item->downloadDirectory()).filePath(item->downloadFileName()); }); }); @@ -1053,7 +1059,7 @@ void tst_QWebEngineDownloadItem::downloadToNonExistentDir() QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); QCOMPARE(item->page(), m_page); downloadFinished = true; - downloadedFilePath = item->path(); + downloadedFilePath = QDir(item->downloadDirectory()).filePath(item->downloadFileName()); }); }); @@ -1246,5 +1252,135 @@ void tst_QWebEngineDownloadItem::downloadPathValidation() QDir::setCurrent(oldPath); } +void tst_QWebEngineDownloadItem::downloadToDirectoryWithFileName() +{ + QString downloadDirectory; + QString downloadFileName; + QString downloadedFilePath; + QString downloadedSuggestedFileName; + QString fileName = "test.txt"; + QString uniqueFileName = "test (1).txt"; + + 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) { + + if (!downloadDirectory.isEmpty()) { + item->setDownloadDirectory(downloadDirectory); + QCOMPARE(item->downloadDirectory(), downloadDirectory); + } + + if (!downloadFileName.isEmpty()) { + item->setDownloadFileName(downloadFileName); + QCOMPARE(item->downloadFileName(), downloadFileName); + } + + QCOMPARE(item->path(), QDir(item->downloadDirectory()).filePath(item->downloadFileName())); + 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 = QDir(item->downloadDirectory()).filePath(item->downloadFileName()); + downloadedSuggestedFileName = item->suggestedFileName(); + }); + }); + + // Download file to the default download directory. + downloadDirectory = ""; + downloadFileName = ""; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, QDir(m_profile->downloadPath()).filePath(fileName)); + QCOMPARE(downloadedSuggestedFileName, fileName); + + // Download the same file to another directory + downloadFinished = false; + downloadDirectory = m_profile->downloadPath() + QDir::separator() + "test1" + QDir::separator(); + downloadFileName = ""; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, QDir(downloadDirectory).filePath(fileName)); + QCOMPARE(downloadedSuggestedFileName, fileName); + + // Download the same file to the same directory and the file name must be unique. + downloadFinished = false; + downloadDirectory = m_profile->downloadPath() + QDir::separator() + "test1" + QDir::separator(); + downloadFileName = ""; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, QDir(downloadDirectory).filePath(uniqueFileName)); + QCOMPARE(downloadedSuggestedFileName, fileName); + + // Download another file to the same directory and set file name by + // QWebEngineDownloadItem::setDownloadDirectory() and setDownloadFileName() to avoid uniquification. + downloadFinished = false; + downloadDirectory = m_profile->downloadPath() + QDir::separator() + "test1" + QDir::separator(); + downloadFileName = "test1.txt"; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, QDir(downloadDirectory).filePath(downloadFileName)); + QCOMPARE(downloadedSuggestedFileName, fileName); + + // Download the same file to another directory without uniquifying the file name + downloadFinished = false; + downloadDirectory = m_profile->downloadPath() + QDir::separator() + "test2" + QDir::separator(); + downloadFileName = "test1.txt"; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, QDir(downloadDirectory).filePath(downloadFileName)); + QCOMPARE(downloadedSuggestedFileName, fileName); + + // Download the same file to same directory and set file name by + // QWebEngineDownloadItem::setDownloadDirectory() and setDownloadFileName() to avoid uniquification. + downloadFinished = false; + downloadDirectory = m_profile->downloadPath() + QDir::separator() + "test2" + QDir::separator(); + downloadFileName = "test1.txt"; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, QDir(downloadDirectory).filePath(downloadFileName)); + QCOMPARE(downloadedSuggestedFileName, fileName); + + // Download the same file in the same directory. + // Use the suggested file name (test.txt) and the file name will not be unique because this file name don't yet exists. + downloadFinished = false; + downloadDirectory = m_profile->downloadPath() + QDir::separator() + "test2" + QDir::separator(); + downloadFileName = ""; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, QDir(downloadDirectory).filePath(fileName)); + QCOMPARE(downloadedSuggestedFileName, fileName); +} + QTEST_MAIN(tst_QWebEngineDownloadItem) #include "tst_qwebenginedownloaditem.moc" |