summaryrefslogtreecommitdiffstats
path: root/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp')
-rw-r--r--Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp249
1 files changed, 249 insertions, 0 deletions
diff --git a/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp b/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp
new file mode 100644
index 000000000..0ec67704c
--- /dev/null
+++ b/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#include "config.h"
+#include "QtFileDownloader.h"
+
+#include "DataReference.h"
+#include "Download.h"
+#include "HTTPParsers.h"
+#include "MIMETypeRegistry.h"
+#include <QCoreApplication>
+#include <QFile>
+#include <QFileInfo>
+#include <QNetworkAccessManager>
+#include <WebCore/QNetworkReplyHandler.h>
+#include <WebCore/ResourceError.h>
+#include <WebCore/ResourceResponse.h>
+
+using namespace WebCore;
+using namespace WTF;
+
+namespace WebKit {
+
+QtFileDownloader::QtFileDownloader(Download* download, QNetworkReply* reply)
+ : m_download(download)
+ , m_reply(reply)
+ , m_error(QNetworkReply::NoError)
+ , m_headersRead(false)
+{
+}
+
+QtFileDownloader::~QtFileDownloader()
+{
+ if (!m_destinationFile)
+ return;
+
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorAborted);
+}
+
+void QtFileDownloader::init()
+{
+ connect(m_reply.get(), SIGNAL(readyRead()), SLOT(onReadyRead()));
+ connect(m_reply.get(), SIGNAL(finished()), SLOT(onFinished()));
+ connect(m_reply.get(), SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onError(QNetworkReply::NetworkError)));
+}
+
+QString QtFileDownloader::determineFilename()
+{
+ ASSERT(!m_destinationFile);
+
+ QString filenameCandidate = filenameFromHTTPContentDisposition(QString::fromLatin1(m_reply->rawHeader("Content-Disposition")));
+ if (filenameCandidate.isEmpty()) {
+ URL kurl = m_reply->url();
+ filenameCandidate = decodeURLEscapeSequences(kurl.lastPathComponent());
+ }
+
+ if (filenameCandidate.isEmpty()) {
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotDetermineFilename);
+ return QString();
+ }
+
+ // Make sure that we remove possible "../.." parts in the given file name.
+ QFileInfo filenameFilter(filenameCandidate);
+ QString filename = filenameFilter.fileName();
+
+ if (filename.isEmpty()) {
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotDetermineFilename);
+ return QString();
+ }
+
+ return filename;
+}
+
+void QtFileDownloader::startTransfer(const QString& decidedFilePath)
+{
+ ASSERT(!m_destinationFile);
+
+ // Error might have occured during destination query.
+ if (m_error != QNetworkReply::NoError) {
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorNetworkFailure);
+ return;
+ }
+
+ if (decidedFilePath.isEmpty()) {
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCancelled);
+ return;
+ }
+
+ auto downloadFile = std::make_unique<QFile>(decidedFilePath);
+
+ if (!downloadFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotOpenFile);
+ return;
+ }
+
+ // Assigning to m_destinationFile flags that either error or
+ // finished shall be called in the end.
+ m_destinationFile = WTFMove(downloadFile);
+
+ m_download->didCreateDestination(m_destinationFile->fileName());
+
+ // We might have gotten readyRead already even before this function
+ // was called.
+ if (m_reply->bytesAvailable())
+ onReadyRead();
+
+ // We might have gotten finished already even before this
+ // function was called.
+ if (m_reply->isFinished())
+ onFinished();
+}
+
+void QtFileDownloader::abortDownloadWritingAndEmitError(QtFileDownloader::DownloadError errorCode)
+{
+ m_reply->abort();
+
+ // On network failures it's QNetworkReplyHandler::errorForReply who will handle errors.
+ if (errorCode == QtFileDownloader::DownloadErrorNetworkFailure) {
+ m_download->didFail(QNetworkReplyHandler::errorForReply(m_reply.get()), IPC::DataReference(0, 0));
+ return;
+ }
+
+ QString translatedErrorMessage;
+ switch (errorCode) {
+ case QtFileDownloader::DownloadErrorAborted:
+ translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Download aborted");
+ break;
+ case QtFileDownloader::DownloadErrorCannotWriteToFile:
+ translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot write to file");
+ break;
+ case QtFileDownloader::DownloadErrorCannotOpenFile:
+ translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot open file for writing");
+ break;
+ case QtFileDownloader::DownloadErrorDestinationAlreadyExists:
+ translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Destination already exists");
+ break;
+ case QtFileDownloader::DownloadErrorCancelled:
+ translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Download cancelled by caller");
+ break;
+ case QtFileDownloader::DownloadErrorCannotDetermineFilename:
+ translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot determine filename");
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ ResourceError downloadError("Download", errorCode, m_reply->url(), translatedErrorMessage);
+
+ m_download->didFail(downloadError, IPC::DataReference(0, 0));
+}
+
+void QtFileDownloader::handleDownloadResponse()
+{
+ // By API contract, QNetworkReply::metaDataChanged cannot really be trusted.
+ // Thus we need to call this upon receiving first data.
+ String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
+ String encoding = extractCharsetFromMediaType(contentType);
+ String mimeType = extractMIMETypeFromMediaType(contentType);
+ String filename = determineFilename();
+
+ // If filename is empty it means determineFilename aborted and emitted an error.
+ if (filename.isEmpty())
+ return;
+
+ // Let's try to guess from the extension.
+ if (mimeType.isEmpty())
+ mimeType = MIMETypeRegistry::getMIMETypeForPath(m_reply->url().path());
+
+ ResourceResponse response(m_reply->url(), mimeType, m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), encoding, filename);
+ m_download->didReceiveResponse(response);
+}
+
+void QtFileDownloader::onReadyRead()
+{
+ if (m_destinationFile) {
+ QByteArray content = m_reply->readAll();
+ if (content.size() <= 0)
+ return;
+
+ qint64 bytesWritten = m_destinationFile->write(content);
+
+ if (bytesWritten == -1) {
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotWriteToFile);
+ return;
+ }
+
+ // There might a corner case to be fixed here if bytesWritten != content.size()
+ // does not actually represent an error.
+ ASSERT(bytesWritten == content.size());
+
+ m_download->didReceiveData(bytesWritten);
+ } else if (!m_headersRead) {
+ handleDownloadResponse();
+ m_headersRead = true;
+ }
+}
+
+void QtFileDownloader::onFinished()
+{
+ // If it's finished and we haven't even read the headers, it means we never got to onReadyRead and that we are
+ // probably dealing with the download of a local file or of a small file that was started with a handle.
+ if (!m_headersRead) {
+ handleDownloadResponse();
+ m_headersRead = true;
+ return;
+ }
+
+ if (!m_destinationFile)
+ return;
+
+ m_destinationFile = nullptr;
+
+ if (m_error == QNetworkReply::NoError)
+ m_download->didFinish();
+ else if (m_error == QNetworkReply::OperationCanceledError)
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCancelled);
+ else
+ abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorNetworkFailure);
+}
+
+void QtFileDownloader::onError(QNetworkReply::NetworkError code)
+{
+ m_error = code;
+}
+
+void QtFileDownloader::cancel()
+{
+ m_reply->abort();
+ // QtFileDownloader::onFinished() will be called and will raise a DownloadErrorCancelled.
+}
+
+} // namespace WebKit
+#include "moc_QtFileDownloader.cpp"