[Qt][WK2] QtFileDownloader ctor shouldn't call QtFileDownloader::onReadyRead()
[WebKit.git] / Source / WebKit2 / WebProcess / Downloads / qt / QtFileDownloader.cpp
1 /*
2  * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this program; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  *
19  */
20 #include "config.h"
21 #include "QtFileDownloader.h"
22
23 #include "DataReference.h"
24 #include "Download.h"
25 #include "HTTPParsers.h"
26 #include "MIMETypeRegistry.h"
27 #include <QCoreApplication>
28 #include <QFile>
29 #include <QFileInfo>
30 #include <QNetworkAccessManager>
31 #include <WebCore/QNetworkReplyHandler.h>
32 #include <WebCore/ResourceError.h>
33 #include <WebCore/ResourceResponse.h>
34
35 using namespace WebCore;
36 using namespace WTF;
37
38 namespace WebKit {
39
40 QtFileDownloader::QtFileDownloader(Download* download, PassOwnPtr<QNetworkReply> reply)
41     : m_download(download)
42     , m_reply(reply)
43     , m_error(QNetworkReply::NoError)
44     , m_headersRead(false)
45 {
46 }
47
48 QtFileDownloader::~QtFileDownloader()
49 {
50     if (!m_destinationFile)
51         return;
52
53     abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorAborted);
54 }
55
56 void QtFileDownloader::start()
57 {
58     connect(m_reply.get(), SIGNAL(readyRead()), SLOT(onReadyRead()));
59     connect(m_reply.get(), SIGNAL(finished()), SLOT(onFinished()));
60     connect(m_reply.get(), SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onError(QNetworkReply::NetworkError)));
61
62     // Call onReadyRead just in case some data is already waiting.
63     onReadyRead();
64 }
65
66 void QtFileDownloader::determineFilename()
67 {
68     ASSERT(!m_destinationFile);
69
70     QString fileNameCandidate = filenameFromHTTPContentDisposition(QString::fromLatin1(m_reply->rawHeader("Content-Disposition")));
71     if (fileNameCandidate.isEmpty()) {
72         KURL kurl = m_reply->url();
73         fileNameCandidate = decodeURLEscapeSequences(kurl.lastPathComponent());
74     }
75
76     if (fileNameCandidate.isEmpty()) {
77         abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotDetermineFilename);
78         return;
79     }
80
81     // Make sure that we remove possible "../.." parts in the given file name.
82     QFileInfo fileNameFilter(fileNameCandidate);
83     QString fileName = fileNameFilter.fileName();
84
85     if (fileName.isEmpty()) {
86         abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotDetermineFilename);
87         return;
88     }
89
90     bool allowOverwrite;
91     m_download->decideDestinationWithSuggestedFilename(fileName, allowOverwrite);
92 }
93
94 void QtFileDownloader::decidedDestination(const QString& decidedFilePath, bool allowOverwrite)
95 {
96     ASSERT(!m_destinationFile);
97
98     // Error might have occured during destination query.
99     if (m_error != QNetworkReply::NoError) {
100         abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorNetworkFailure);
101         return;
102     }
103
104     if (decidedFilePath.isEmpty()) {
105         abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCancelledByCaller);
106         return;
107     }
108
109     OwnPtr<QFile> downloadFile = adoptPtr(new QFile(decidedFilePath));
110
111     if (!allowOverwrite && downloadFile->exists()) {
112         abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorFileAlreadyExists);
113         return;
114     }
115
116     if (!downloadFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
117         abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotOpenFile);
118         return;
119     }
120
121     // Assigning to m_destinationFile flags that either error or
122     // finished shall be called in the end.
123     m_destinationFile = downloadFile.release();
124
125     m_download->didCreateDestination(m_destinationFile->fileName());
126
127     // We might have gotten readyRead already even before this function
128     // was called.
129     if (m_reply->bytesAvailable())
130         onReadyRead();
131
132     // We might have gotten finished already even before this
133     // function was called.
134     if (m_reply->isFinished())
135         onFinished();
136
137 }
138
139 void QtFileDownloader::abortDownloadWritingAndEmitError(QtFileDownloader::DownloadError errorCode)
140 {
141     m_reply->abort();
142
143     // On network failures it's QNetworkReplyHandler::errorForReply who will handle errors.
144     if (errorCode == QtFileDownloader::DownloadErrorNetworkFailure) {
145         m_download->didFail(QNetworkReplyHandler::errorForReply(m_reply.get()), CoreIPC::DataReference(0, 0));
146         return;
147     }
148
149     QString translatedErrorMessage;
150     switch (errorCode) {
151     case QtFileDownloader::DownloadErrorAborted:
152         translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Download aborted");
153         break;
154     case QtFileDownloader::DownloadErrorCannotWriteToFile:
155         translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot write to file");
156         break;
157     case QtFileDownloader::DownloadErrorCannotOpenFile:
158         translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot open file for writing");
159         break;
160     case QtFileDownloader::DownloadErrorFileAlreadyExists:
161         translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "File already exists");
162         break;
163     case QtFileDownloader::DownloadErrorCancelledByCaller:
164         translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Download cancelled by caller");
165         break;
166     case QtFileDownloader::DownloadErrorCannotDetermineFilename:
167         translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot determine filename");
168         break;
169     default:
170         ASSERT_NOT_REACHED();
171     }
172
173     ResourceError downloadError("Download", errorCode, m_reply->url().toString(), translatedErrorMessage);
174
175     m_download->didFail(downloadError, CoreIPC::DataReference(0, 0));
176 }
177
178 void QtFileDownloader::onReadyRead()
179 {
180     if (m_destinationFile) {
181         QByteArray content = m_reply->readAll();
182         if (content.size() <= 0)
183             return;
184
185         qint64 bytesWritten = m_destinationFile->write(content);
186
187         if (bytesWritten == -1) {
188             abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotWriteToFile);
189             return;
190         }
191
192         // There might a corner case to be fixed here if bytesWritten != content.size()
193         // does not actually represent an error.
194         ASSERT(bytesWritten == content.size());
195
196         m_download->didReceiveData(bytesWritten);
197     } else if (!m_headersRead) {
198         // By API contract, QNetworkReply::metaDataChanged cannot really be trusted.
199         // Thus we need to call this upon receiving first data.
200         String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
201         String encoding = extractCharsetFromMediaType(contentType);
202         String mimeType = extractMIMETypeFromMediaType(contentType);
203
204         // Let's try to guess from the extension.
205         if (mimeType.isEmpty())
206             mimeType = MIMETypeRegistry::getMIMETypeForPath(m_reply->url().path());
207
208         ResourceResponse response(m_reply->url(), mimeType, m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), encoding, String());
209         m_download->didReceiveResponse(response);
210
211         determineFilename();
212
213         m_headersRead = true;
214     }
215 }
216
217 void QtFileDownloader::onFinished()
218 {
219     if (!m_destinationFile)
220         return;
221
222     m_destinationFile.clear();
223
224     if (m_error == QNetworkReply::NoError)
225         m_download->didFinish();
226     else
227         abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorNetworkFailure);
228 }
229
230 void QtFileDownloader::onError(QNetworkReply::NetworkError code)
231 {
232     m_error = code;
233 }
234
235 void QtFileDownloader::cancel()
236 {
237     m_reply->abort();
238 }
239
240 } // namespace WebKit
241 #include "moc_QtFileDownloader.cpp"