2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "core/fileapi/FileReaderLoader.h"
35 #include "FetchInitiatorTypeNames.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/fetch/TextResourceDecoder.h"
38 #include "core/fileapi/Blob.h"
39 #include "core/fileapi/FileReaderLoaderClient.h"
40 #include "core/fileapi/Stream.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/ArrayBuffer.h"
47 #include "wtf/PassOwnPtr.h"
48 #include "wtf/PassRefPtr.h"
49 #include "wtf/RefPtr.h"
50 #include "wtf/Vector.h"
51 #include "wtf/text/Base64.h"
52 #include "wtf/text/StringBuilder.h"
58 FileReaderLoader::FileReaderLoader(ReadType readType, FileReaderLoaderClient* client)
59 : m_readType(readType)
61 , m_urlForReadingIsStream(false)
62 , m_isRawDataConverted(false)
64 , m_finishedLoading(false)
70 , m_errorCode(FileError::OK)
74 FileReaderLoader::~FileReaderLoader()
77 if (!m_urlForReading.isEmpty()) {
78 if (m_urlForReadingIsStream)
79 BlobRegistry::unregisterStreamURL(m_urlForReading);
81 BlobRegistry::revokePublicBlobURL(m_urlForReading);
85 void FileReaderLoader::startInternal(ExecutionContext* executionContext, const Stream* stream, PassRefPtr<BlobDataHandle> blobData)
87 // The blob is read by routing through the request handling layer given a temporary public url.
88 m_urlForReading = BlobURL::createPublicURL(executionContext->securityOrigin());
89 if (m_urlForReading.isEmpty()) {
90 failed(FileError::SECURITY_ERR);
96 BlobRegistry::registerPublicBlobURL(executionContext->securityOrigin(), m_urlForReading, blobData);
99 BlobRegistry::registerStreamURL(executionContext->securityOrigin(), m_urlForReading, stream->url());
102 // Construct and load the request.
103 ResourceRequest request(m_urlForReading);
104 request.setHTTPMethod("GET");
106 request.setHTTPHeaderField("Range", String::format("bytes=%d-%d", m_rangeStart, m_rangeEnd));
108 ThreadableLoaderOptions options;
109 options.sendLoadCallbacks = SendCallbacks;
110 options.sniffContent = DoNotSniffContent;
111 options.preflightPolicy = ConsiderPreflight;
112 options.allowCredentials = AllowStoredCredentials;
113 options.crossOriginRequestPolicy = DenyCrossOriginRequests;
114 // FIXME: Is there a directive to which this load should be subject?
115 options.contentSecurityPolicyEnforcement = DoNotEnforceContentSecurityPolicy;
116 // Use special initiator to hide the request from the inspector.
117 options.initiator = FetchInitiatorTypeNames::internal;
120 m_loader = ThreadableLoader::create(executionContext, this, request, options);
122 ThreadableLoader::loadResourceSynchronously(executionContext, request, *this, options);
125 void FileReaderLoader::start(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobData)
127 m_urlForReadingIsStream = false;
128 startInternal(executionContext, 0, blobData);
131 void FileReaderLoader::start(ExecutionContext* executionContext, const Stream& stream, unsigned readSize)
136 m_rangeEnd = readSize - 1; // End is inclusive so (0,0) is a 1-byte read.
139 m_urlForReadingIsStream = true;
140 startInternal(executionContext, &stream, 0);
143 void FileReaderLoader::cancel()
145 m_errorCode = FileError::ABORT_ERR;
149 void FileReaderLoader::terminate()
157 void FileReaderLoader::cleanup()
161 // If we get any error, we do not need to keep a buffer around.
165 m_isRawDataConverted = true;
170 void FileReaderLoader::didReceiveResponse(unsigned long, const ResourceResponse& response)
172 if (response.httpStatusCode() != 200) {
173 failed(httpStatusCodeToErrorCode(response.httpStatusCode()));
177 // A negative value means that the content length wasn't specified.
178 m_totalBytes = response.expectedContentLength();
180 long long initialBufferLength = -1;
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;
189 // Nothing is known about the size of the resource. Normalize
190 // m_totalBytes to -1 and initialize the buffer for receiving with the
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);
206 if (initialBufferLength < 0) {
207 m_rawData = adoptPtr(new ArrayBufferBuilder());
209 m_rawData = adoptPtr(new ArrayBufferBuilder(static_cast<unsigned>(initialBufferLength)));
210 // Total size is known. Set m_rawData to ignore overflowed data.
211 m_rawData->setVariableCapacity(false);
215 failed(FileError::NOT_READABLE_ERR);
221 m_client->didStartLoading();
224 void FileReaderLoader::didReceiveData(const char* data, int dataLength)
227 ASSERT(dataLength > 0);
229 // Bail out if we already encountered an error.
233 if (m_readType == ReadByClient) {
234 m_bytesLoaded += dataLength;
237 m_client->didReceiveDataForClient(data, dataLength);
241 unsigned bytesAppended = m_rawData->append(data, static_cast<unsigned>(dataLength));
242 if (!bytesAppended) {
245 failed(FileError::NOT_READABLE_ERR);
248 m_bytesLoaded += bytesAppended;
249 m_isRawDataConverted = false;
252 m_client->didReceiveData();
255 void FileReaderLoader::didFinishLoading(unsigned long, double)
257 if (m_readType != ReadByClient && m_rawData) {
258 m_rawData->shrinkToFit();
259 m_isRawDataConverted = false;
262 if (m_totalBytes == -1) {
263 // Update m_totalBytes only when in dynamic buffer grow mode.
264 m_totalBytes = m_bytesLoaded;
267 m_finishedLoading = true;
271 m_client->didFinishLoading();
274 void FileReaderLoader::didFail(const ResourceError&)
276 // If we're aborting, do not proceed with normal error handling since it is covered in aborting code.
277 if (m_errorCode == FileError::ABORT_ERR)
280 failed(FileError::NOT_READABLE_ERR);
283 void FileReaderLoader::failed(FileError::ErrorCode errorCode)
285 m_errorCode = errorCode;
288 m_client->didFail(m_errorCode);
291 FileError::ErrorCode FileReaderLoader::httpStatusCodeToErrorCode(int httpStatusCode)
293 switch (httpStatusCode) {
295 return FileError::SECURITY_ERR;
297 return FileError::NOT_FOUND_ERR;
299 return FileError::NOT_READABLE_ERR;
303 PassRefPtr<ArrayBuffer> FileReaderLoader::arrayBufferResult() const
305 ASSERT(m_readType == ReadAsArrayBuffer);
307 // If the loading is not started or an error occurs, return an empty result.
308 if (!m_rawData || m_errorCode)
311 return m_rawData->toArrayBuffer();
314 String FileReaderLoader::stringResult()
316 ASSERT(m_readType != ReadAsArrayBuffer && m_readType != ReadAsBlob && m_readType != ReadByClient);
318 // If the loading is not started or an error occurs, return an empty result.
319 if (!m_rawData || m_errorCode)
320 return m_stringResult;
322 // If already converted from the raw data, return the result now.
323 if (m_isRawDataConverted)
324 return m_stringResult;
326 switch (m_readType) {
327 case ReadAsArrayBuffer:
328 // No conversion is needed.
330 case ReadAsBinaryString:
331 m_stringResult = m_rawData->toString();
332 m_isRawDataConverted = true;
338 // Partial data is not supported when reading as data URL.
339 if (m_finishedLoading)
343 ASSERT_NOT_REACHED();
346 return m_stringResult;
349 void FileReaderLoader::convertToText()
351 m_isRawDataConverted = true;
353 if (!m_bytesLoaded) {
359 // The File API spec says that we should use the supplied encoding if it is valid. However, we choose to ignore this
360 // requirement in order to be consistent with how WebKit decodes the web content: always has the BOM override the
361 // provided encoding.
362 // FIXME: consider supporting incremental decoding to improve the perf.
363 StringBuilder builder;
365 m_decoder = TextResourceDecoder::create("text/plain", m_encoding.isValid() ? m_encoding : UTF8Encoding());
366 builder.append(m_decoder->decode(static_cast<const char*>(m_rawData->data()), m_rawData->byteLength()));
368 if (m_finishedLoading)
369 builder.append(m_decoder->flush());
371 m_stringResult = builder.toString();
374 void FileReaderLoader::convertToDataURL()
376 m_isRawDataConverted = true;
378 StringBuilder builder;
379 builder.append("data:");
381 if (!m_bytesLoaded) {
382 m_stringResult = builder.toString();
386 builder.append(m_dataType);
387 builder.append(";base64,");
390 base64Encode(static_cast<const char*>(m_rawData->data()), m_rawData->byteLength(), out);
392 builder.append(out.data());
394 m_stringResult = builder.toString();
397 void FileReaderLoader::setEncoding(const String& encoding)
399 if (!encoding.isEmpty())
400 m_encoding = WTF::TextEncoding(encoding);
403 } // namespace WebCore