Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 "core/fileapi/FileReaderLoader.h"
34
35 #include "FetchInitiatorTypeNames.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/fileapi/Blob.h"
38 #include "core/fileapi/FileReaderLoaderClient.h"
39 #include "core/fileapi/Stream.h"
40 #include "core/html/parser/TextResourceDecoder.h"
41 #include "core/loader/ThreadableLoader.h"
42 #include "platform/blob/BlobRegistry.h"
43 #include "platform/blob/BlobURL.h"
44 #include "platform/network/ResourceRequest.h"
45 #include "platform/network/ResourceResponse.h"
46 #include "wtf/PassOwnPtr.h"
47 #include "wtf/PassRefPtr.h"
48 #include "wtf/RefPtr.h"
49 #include "wtf/Vector.h"
50 #include "wtf/text/Base64.h"
51 #include "wtf/text/StringBuilder.h"
52
53 using namespace std;
54
55 namespace WebCore {
56
57 FileReaderLoader::FileReaderLoader(ReadType readType, FileReaderLoaderClient* client)
58     : m_readType(readType)
59     , m_client(client)
60     , m_urlForReadingIsStream(false)
61     , m_isRawDataConverted(false)
62     , m_stringResult("")
63     , m_finishedLoading(false)
64     , m_bytesLoaded(0)
65     , m_totalBytes(-1)
66     , m_hasRange(false)
67     , m_rangeStart(0)
68     , m_rangeEnd(0)
69     , m_errorCode(FileError::OK)
70 {
71 }
72
73 FileReaderLoader::~FileReaderLoader()
74 {
75     terminate();
76     if (!m_urlForReading.isEmpty()) {
77         if (m_urlForReadingIsStream)
78             BlobRegistry::unregisterStreamURL(m_urlForReading);
79         else
80             BlobRegistry::revokePublicBlobURL(m_urlForReading);
81     }
82 }
83
84 void FileReaderLoader::startInternal(ExecutionContext& executionContext, const Stream* stream, PassRefPtr<BlobDataHandle> blobData)
85 {
86     // The blob is read by routing through the request handling layer given a temporary public url.
87     m_urlForReading = BlobURL::createPublicURL(executionContext.securityOrigin());
88     if (m_urlForReading.isEmpty()) {
89         failed(FileError::SECURITY_ERR);
90         return;
91     }
92
93     if (blobData) {
94         ASSERT(!stream);
95         BlobRegistry::registerPublicBlobURL(executionContext.securityOrigin(), m_urlForReading, blobData);
96     } else {
97         ASSERT(stream);
98         BlobRegistry::registerStreamURL(executionContext.securityOrigin(), m_urlForReading, stream->url());
99     }
100
101     // Construct and load the request.
102     ResourceRequest request(m_urlForReading);
103     request.setHTTPMethod("GET");
104     if (m_hasRange)
105         request.setHTTPHeaderField("Range", AtomicString(String::format("bytes=%d-%d", m_rangeStart, m_rangeEnd)));
106
107     ThreadableLoaderOptions options;
108     options.sniffContent = DoNotSniffContent;
109     options.preflightPolicy = ConsiderPreflight;
110     options.allowCredentials = AllowStoredCredentials;
111     options.crossOriginRequestPolicy = DenyCrossOriginRequests;
112     // FIXME: Is there a directive to which this load should be subject?
113     options.contentSecurityPolicyEnforcement = DoNotEnforceContentSecurityPolicy;
114     // Use special initiator to hide the request from the inspector.
115     options.initiator = FetchInitiatorTypeNames::internal;
116
117     if (m_client)
118         m_loader = ThreadableLoader::create(executionContext, this, request, options);
119     else
120         ThreadableLoader::loadResourceSynchronously(executionContext, request, *this, options);
121 }
122
123 void FileReaderLoader::start(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobData)
124 {
125     ASSERT(executionContext);
126     m_urlForReadingIsStream = false;
127     startInternal(*executionContext, 0, blobData);
128 }
129
130 void FileReaderLoader::start(ExecutionContext* executionContext, const Stream& stream, unsigned readSize)
131 {
132     ASSERT(executionContext);
133     if (readSize > 0) {
134         m_hasRange = true;
135         m_rangeStart = 0;
136         m_rangeEnd = readSize - 1; // End is inclusive so (0,0) is a 1-byte read.
137     }
138
139     m_urlForReadingIsStream = true;
140     startInternal(*executionContext, &stream, nullptr);
141 }
142
143 void FileReaderLoader::cancel()
144 {
145     m_errorCode = FileError::ABORT_ERR;
146     terminate();
147 }
148
149 void FileReaderLoader::terminate()
150 {
151     if (m_loader) {
152         m_loader->cancel();
153         cleanup();
154     }
155 }
156
157 void FileReaderLoader::cleanup()
158 {
159     m_loader = nullptr;
160
161     // If we get any error, we do not need to keep a buffer around.
162     if (m_errorCode) {
163         m_rawData.clear();
164         m_stringResult = "";
165         m_isRawDataConverted = true;
166         m_decoder.clear();
167     }
168 }
169
170 void FileReaderLoader::didReceiveResponse(unsigned long, const ResourceResponse& response)
171 {
172     if (response.httpStatusCode() != 200) {
173         failed(httpStatusCodeToErrorCode(response.httpStatusCode()));
174         return;
175     }
176
177     // A negative value means that the content length wasn't specified.
178     m_totalBytes = response.expectedContentLength();
179
180     long long initialBufferLength = -1;
181
182     if (m_totalBytes >= 0) {
183         initialBufferLength = m_totalBytes;
184     } else if (m_hasRange) {
185         // Set m_totalBytes and allocate a buffer based on the specified range.
186         m_totalBytes = 1LL + m_rangeEnd - m_rangeStart;
187         initialBufferLength = m_totalBytes;
188     } else {
189         // Nothing is known about the size of the resource. Normalize
190         // m_totalBytes to -1 and initialize the buffer for receiving with the
191         // default size.
192         m_totalBytes = -1;
193     }
194
195     ASSERT(!m_rawData);
196
197     if (m_readType != ReadByClient) {
198         // Check that we can cast to unsigned since we have to do
199         // so to call ArrayBuffer's create function.
200         // FIXME: Support reading more than the current size limit of ArrayBuffer.
201         if (initialBufferLength > numeric_limits<unsigned>::max()) {
202             failed(FileError::NOT_READABLE_ERR);
203             return;
204         }
205
206         if (initialBufferLength < 0)
207             m_rawData = adoptPtr(new ArrayBufferBuilder());
208         else
209             m_rawData = adoptPtr(new ArrayBufferBuilder(static_cast<unsigned>(initialBufferLength)));
210
211         if (!m_rawData || !m_rawData->isValid()) {
212             failed(FileError::NOT_READABLE_ERR);
213             return;
214         }
215
216         if (initialBufferLength >= 0) {
217             // Total size is known. Set m_rawData to ignore overflowed data.
218             m_rawData->setVariableCapacity(false);
219         }
220     }
221
222     if (m_client)
223         m_client->didStartLoading();
224 }
225
226 void FileReaderLoader::didReceiveData(const char* data, int dataLength)
227 {
228     ASSERT(data);
229     ASSERT(dataLength > 0);
230
231     // Bail out if we already encountered an error.
232     if (m_errorCode)
233         return;
234
235     if (m_readType == ReadByClient) {
236         m_bytesLoaded += dataLength;
237
238         if (m_client)
239             m_client->didReceiveDataForClient(data, dataLength);
240         return;
241     }
242
243     unsigned bytesAppended = m_rawData->append(data, static_cast<unsigned>(dataLength));
244     if (!bytesAppended) {
245         m_rawData.clear();
246         m_bytesLoaded = 0;
247         failed(FileError::NOT_READABLE_ERR);
248         return;
249     }
250     m_bytesLoaded += bytesAppended;
251     m_isRawDataConverted = false;
252
253     if (m_client)
254         m_client->didReceiveData();
255 }
256
257 void FileReaderLoader::didFinishLoading(unsigned long, double)
258 {
259     if (m_readType != ReadByClient && m_rawData) {
260         m_rawData->shrinkToFit();
261         m_isRawDataConverted = false;
262     }
263
264     if (m_totalBytes == -1) {
265         // Update m_totalBytes only when in dynamic buffer grow mode.
266         m_totalBytes = m_bytesLoaded;
267     }
268
269     m_finishedLoading = true;
270
271     cleanup();
272     if (m_client)
273         m_client->didFinishLoading();
274 }
275
276 void FileReaderLoader::didFail(const ResourceError&)
277 {
278     // If we're aborting, do not proceed with normal error handling since it is covered in aborting code.
279     if (m_errorCode == FileError::ABORT_ERR)
280         return;
281
282     failed(FileError::NOT_READABLE_ERR);
283 }
284
285 void FileReaderLoader::failed(FileError::ErrorCode errorCode)
286 {
287     m_errorCode = errorCode;
288     cleanup();
289     if (m_client)
290         m_client->didFail(m_errorCode);
291 }
292
293 FileError::ErrorCode FileReaderLoader::httpStatusCodeToErrorCode(int httpStatusCode)
294 {
295     switch (httpStatusCode) {
296     case 403:
297         return FileError::SECURITY_ERR;
298     case 404:
299         return FileError::NOT_FOUND_ERR;
300     default:
301         return FileError::NOT_READABLE_ERR;
302     }
303 }
304
305 PassRefPtr<ArrayBuffer> FileReaderLoader::arrayBufferResult() const
306 {
307     ASSERT(m_readType == ReadAsArrayBuffer);
308
309     // If the loading is not started or an error occurs, return an empty result.
310     if (!m_rawData || m_errorCode)
311         return nullptr;
312
313     return m_rawData->toArrayBuffer();
314 }
315
316 String FileReaderLoader::stringResult()
317 {
318     ASSERT(m_readType != ReadAsArrayBuffer && m_readType != ReadAsBlob && m_readType != ReadByClient);
319
320     // If the loading is not started or an error occurs, return an empty result.
321     if (!m_rawData || m_errorCode)
322         return m_stringResult;
323
324     // If already converted from the raw data, return the result now.
325     if (m_isRawDataConverted)
326         return m_stringResult;
327
328     switch (m_readType) {
329     case ReadAsArrayBuffer:
330         // No conversion is needed.
331         break;
332     case ReadAsBinaryString:
333         m_stringResult = m_rawData->toString();
334         m_isRawDataConverted = true;
335         break;
336     case ReadAsText:
337         convertToText();
338         break;
339     case ReadAsDataURL:
340         // Partial data is not supported when reading as data URL.
341         if (m_finishedLoading)
342             convertToDataURL();
343         break;
344     default:
345         ASSERT_NOT_REACHED();
346     }
347
348     return m_stringResult;
349 }
350
351 void FileReaderLoader::convertToText()
352 {
353     m_isRawDataConverted = true;
354
355     if (!m_bytesLoaded) {
356         m_stringResult = "";
357         return;
358     }
359
360     // Decode the data.
361     // The File API spec says that we should use the supplied encoding if it is valid. However, we choose to ignore this
362     // requirement in order to be consistent with how WebKit decodes the web content: always has the BOM override the
363     // provided encoding.
364     // FIXME: consider supporting incremental decoding to improve the perf.
365     StringBuilder builder;
366     if (!m_decoder)
367         m_decoder = TextResourceDecoder::create("text/plain", m_encoding.isValid() ? m_encoding : UTF8Encoding());
368     builder.append(m_decoder->decode(static_cast<const char*>(m_rawData->data()), m_rawData->byteLength()));
369
370     if (m_finishedLoading)
371         builder.append(m_decoder->flush());
372
373     m_stringResult = builder.toString();
374 }
375
376 void FileReaderLoader::convertToDataURL()
377 {
378     m_isRawDataConverted = true;
379
380     StringBuilder builder;
381     builder.append("data:");
382
383     if (!m_bytesLoaded) {
384         m_stringResult = builder.toString();
385         return;
386     }
387
388     builder.append(m_dataType);
389     builder.append(";base64,");
390
391     Vector<char> out;
392     base64Encode(static_cast<const char*>(m_rawData->data()), m_rawData->byteLength(), out);
393     out.append('\0');
394     builder.append(out.data());
395
396     m_stringResult = builder.toString();
397 }
398
399 void FileReaderLoader::setEncoding(const String& encoding)
400 {
401     if (!encoding.isEmpty())
402         m_encoding = WTF::TextEncoding(encoding);
403 }
404
405 } // namespace WebCore