[Resource Timing] Gather timing information with reliable responseEnd time
[WebKit-https.git] / Source / WebCore / fileapi / FileReaderLoader.cpp
1 /*
2  * Copyright (C) 2010 Google Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #include "FileReaderLoader.h"
34
35 #include "Blob.h"
36 #include "BlobURL.h"
37 #include "FileReaderLoaderClient.h"
38 #include "HTTPHeaderNames.h"
39 #include "ResourceError.h"
40 #include "ResourceRequest.h"
41 #include "ResourceResponse.h"
42 #include "ScriptExecutionContext.h"
43 #include "TextResourceDecoder.h"
44 #include "ThreadableBlobRegistry.h"
45 #include "ThreadableLoader.h"
46 #include <runtime/ArrayBuffer.h>
47 #include <wtf/RefPtr.h>
48 #include <wtf/Vector.h>
49 #include <wtf/text/Base64.h>
50 #include <wtf/text/StringBuilder.h>
51
52 namespace WebCore {
53
54 const int defaultBufferLength = 32768;
55
56 FileReaderLoader::FileReaderLoader(ReadType readType, FileReaderLoaderClient* client)
57     : m_readType(readType)
58     , m_client(client)
59     , m_isRawDataConverted(false)
60     , m_stringResult(emptyString())
61     , m_variableLength(false)
62     , m_bytesLoaded(0)
63     , m_totalBytes(0)
64     , m_errorCode(0)
65 {
66 }
67
68 FileReaderLoader::~FileReaderLoader()
69 {
70     terminate();
71     if (!m_urlForReading.isEmpty())
72         ThreadableBlobRegistry::unregisterBlobURL(m_urlForReading);
73 }
74
75 void FileReaderLoader::start(ScriptExecutionContext* scriptExecutionContext, Blob& blob)
76 {
77     ASSERT(scriptExecutionContext);
78
79     // The blob is read by routing through the request handling layer given a temporary public url.
80     m_urlForReading = BlobURL::createPublicURL(scriptExecutionContext->securityOrigin());
81     if (m_urlForReading.isEmpty()) {
82         failed(FileError::SECURITY_ERR);
83         return;
84     }
85     ThreadableBlobRegistry::registerBlobURL(scriptExecutionContext->securityOrigin(), m_urlForReading, blob.url());
86
87     // Construct and load the request.
88     ResourceRequest request(m_urlForReading);
89     request.setHTTPMethod("GET");
90
91     ThreadableLoaderOptions options;
92     options.sendLoadCallbacks = SendCallbacks;
93     options.dataBufferingPolicy = DoNotBufferData;
94     options.credentials = FetchOptions::Credentials::Include;
95     options.mode = FetchOptions::Mode::SameOrigin;
96     options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce;
97
98     if (m_client)
99         m_loader = ThreadableLoader::create(*scriptExecutionContext, *this, WTFMove(request), options);
100     else
101         ThreadableLoader::loadResourceSynchronously(*scriptExecutionContext, WTFMove(request), *this, options);
102 }
103
104 void FileReaderLoader::cancel()
105 {
106     m_errorCode = FileError::ABORT_ERR;
107     terminate();
108 }
109
110 void FileReaderLoader::terminate()
111 {
112     if (m_loader) {
113         m_loader->cancel();
114         cleanup();
115     }
116 }
117
118 void FileReaderLoader::cleanup()
119 {
120     m_loader = nullptr;
121
122     // If we get any error, we do not need to keep a buffer around.
123     if (m_errorCode) {
124         m_rawData = nullptr;
125         m_stringResult = emptyString();
126     }
127 }
128
129 void FileReaderLoader::didReceiveResponse(unsigned long, const ResourceResponse& response)
130 {
131     if (response.httpStatusCode() != 200) {
132         failed(httpStatusCodeToErrorCode(response.httpStatusCode()));
133         return;
134     }
135
136     long long length = response.expectedContentLength();
137
138     // A negative value means that the content length wasn't specified, so the buffer will need to be dynamically grown.
139     if (length < 0) {
140         m_variableLength = true;
141         length = defaultBufferLength;
142     }
143
144     // Check that we can cast to unsigned since we have to do
145     // so to call ArrayBuffer's create function.
146     // FIXME: Support reading more than the current size limit of ArrayBuffer.
147     if (length > std::numeric_limits<unsigned>::max()) {
148         failed(FileError::NOT_READABLE_ERR);
149         return;
150     }
151
152     ASSERT(!m_rawData);
153     m_rawData = ArrayBuffer::tryCreate(static_cast<unsigned>(length), 1);
154
155     if (!m_rawData) {
156         failed(FileError::NOT_READABLE_ERR);
157         return;
158     }
159
160     m_totalBytes = static_cast<unsigned>(length);
161
162     if (m_client)
163         m_client->didStartLoading();
164 }
165
166 void FileReaderLoader::didReceiveData(const char* data, int dataLength)
167 {
168     ASSERT(data);
169     ASSERT(dataLength > 0);
170
171     // Bail out if we already encountered an error.
172     if (m_errorCode)
173         return;
174
175     int length = dataLength;
176     unsigned remainingBufferSpace = m_totalBytes - m_bytesLoaded;
177     if (length > static_cast<long long>(remainingBufferSpace)) {
178         // If the buffer has hit maximum size, it can't be grown any more.
179         if (m_totalBytes >= std::numeric_limits<unsigned>::max()) {
180             failed(FileError::NOT_READABLE_ERR);
181             return;
182         }
183         if (m_variableLength) {
184             unsigned newLength = m_totalBytes + static_cast<unsigned>(dataLength);
185             if (newLength < m_totalBytes) {
186                 failed(FileError::NOT_READABLE_ERR);
187                 return;
188             }
189             newLength = std::max(newLength, m_totalBytes + m_totalBytes / 4 + 1);
190             auto newData = ArrayBuffer::tryCreate(newLength, 1);
191             if (!newData) {
192                 // Not enough memory.
193                 failed(FileError::NOT_READABLE_ERR);
194                 return;
195             }
196             memcpy(static_cast<char*>(newData->data()), static_cast<char*>(m_rawData->data()), m_bytesLoaded);
197
198             m_rawData = newData;
199             m_totalBytes = static_cast<unsigned>(newLength);
200         } else {
201             // This can only happen if we get more data than indicated in expected content length (i.e. never, unless the networking layer is buggy).
202             length = remainingBufferSpace;
203         }
204     }
205
206     if (length <= 0)
207         return;
208
209     memcpy(static_cast<char*>(m_rawData->data()) + m_bytesLoaded, data, length);
210     m_bytesLoaded += length;
211
212     m_isRawDataConverted = false;
213
214     if (m_client)
215         m_client->didReceiveData();
216 }
217
218 void FileReaderLoader::didFinishLoading(unsigned long)
219 {
220     if (m_variableLength && m_totalBytes > m_bytesLoaded) {
221         RefPtr<ArrayBuffer> newData = m_rawData->slice(0, m_bytesLoaded);
222
223         m_rawData = newData;
224         m_totalBytes = m_bytesLoaded;
225     }
226     cleanup();
227     if (m_client)
228         m_client->didFinishLoading();
229 }
230
231 void FileReaderLoader::didFail(const ResourceError& error)
232 {
233     // If we're aborting, do not proceed with normal error handling since it is covered in aborting code.
234     if (m_errorCode == FileError::ABORT_ERR)
235         return;
236
237     failed(toErrorCode(static_cast<BlobResourceHandle::Error>(error.errorCode())));
238 }
239
240 void FileReaderLoader::failed(int errorCode)
241 {
242     m_errorCode = errorCode;
243     cleanup();
244     if (m_client)
245         m_client->didFail(m_errorCode);
246 }
247
248 FileError::ErrorCode FileReaderLoader::toErrorCode(BlobResourceHandle::Error error)
249 {
250     switch (error) {
251     case BlobResourceHandle::Error::NotFoundError:
252         return FileError::NOT_FOUND_ERR;
253     default:
254         return FileError::NOT_READABLE_ERR;
255     }
256 }
257
258 FileError::ErrorCode FileReaderLoader::httpStatusCodeToErrorCode(int httpStatusCode)
259 {
260     switch (httpStatusCode) {
261     case 403:
262         return FileError::SECURITY_ERR;
263     default:
264         return FileError::NOT_READABLE_ERR;
265     }
266 }
267
268 RefPtr<ArrayBuffer> FileReaderLoader::arrayBufferResult() const
269 {
270     ASSERT(m_readType == ReadAsArrayBuffer);
271
272     // If the loading is not started or an error occurs, return an empty result.
273     if (!m_rawData || m_errorCode)
274         return nullptr;
275
276     // If completed, we can simply return our buffer.
277     if (isCompleted())
278         return m_rawData;
279
280     // Otherwise, return a copy.
281     return ArrayBuffer::create(*m_rawData);
282 }
283
284 String FileReaderLoader::stringResult()
285 {
286     ASSERT(m_readType != ReadAsArrayBuffer && m_readType != ReadAsBlob);
287
288     // If the loading is not started or an error occurs, return an empty result.
289     if (!m_rawData || m_errorCode)
290         return m_stringResult;
291
292     // If already converted from the raw data, return the result now.
293     if (m_isRawDataConverted)
294         return m_stringResult;
295
296     switch (m_readType) {
297     case ReadAsArrayBuffer:
298         // No conversion is needed.
299         break;
300     case ReadAsBinaryString:
301         m_stringResult = String(static_cast<const char*>(m_rawData->data()), m_bytesLoaded);
302         break;
303     case ReadAsText:
304         convertToText();
305         break;
306     case ReadAsDataURL:
307         // Partial data is not supported when reading as data URL.
308         if (isCompleted())
309             convertToDataURL();
310         break;
311     default:
312         ASSERT_NOT_REACHED();
313     }
314     
315     return m_stringResult;
316 }
317
318 void FileReaderLoader::convertToText()
319 {
320     if (!m_bytesLoaded)
321         return;
322
323     // Decode the data.
324     // The File API spec says that we should use the supplied encoding if it is valid. However, we choose to ignore this
325     // requirement in order to be consistent with how WebKit decodes the web content: always has the BOM override the
326     // provided encoding.     
327     // FIXME: consider supporting incremental decoding to improve the perf.
328     if (!m_decoder)
329         m_decoder = TextResourceDecoder::create("text/plain", m_encoding.isValid() ? m_encoding : UTF8Encoding());
330     if (isCompleted())
331         m_stringResult = m_decoder->decodeAndFlush(static_cast<const char*>(m_rawData->data()), m_bytesLoaded);
332     else
333         m_stringResult = m_decoder->decode(static_cast<const char*>(m_rawData->data()), m_bytesLoaded);
334 }
335
336 void FileReaderLoader::convertToDataURL()
337 {
338     StringBuilder builder;
339     builder.appendLiteral("data:");
340
341     if (!m_bytesLoaded) {
342         m_stringResult = builder.toString();
343         return;
344     }
345
346     builder.append(m_dataType);
347     builder.appendLiteral(";base64,");
348
349     Vector<char> out;
350     base64Encode(m_rawData->data(), m_bytesLoaded, out);
351     out.append('\0');
352     builder.append(out.data());
353
354     m_stringResult = builder.toString();
355 }
356
357 bool FileReaderLoader::isCompleted() const
358 {
359     return m_bytesLoaded == m_totalBytes;
360 }
361
362 void FileReaderLoader::setEncoding(const String& encoding)
363 {
364     if (!encoding.isEmpty())
365         m_encoding = TextEncoding(encoding);
366 }
367
368 } // namespace WebCore