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 "core/dom/DOMArrayBuffer.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/fetch/FetchInitiatorTypeNames.h"
38 #include "core/fileapi/Blob.h"
39 #include "core/fileapi/FileReaderLoaderClient.h"
40 #include "core/html/parser/TextResourceDecoder.h"
41 #include "core/loader/ThreadableLoader.h"
42 #include "core/streams/Stream.h"
43 #include "platform/blob/BlobRegistry.h"
44 #include "platform/blob/BlobURL.h"
45 #include "platform/network/ResourceRequest.h"
46 #include "platform/network/ResourceResponse.h"
47 #include "public/platform/WebURLRequest.h"
48 #include "wtf/PassOwnPtr.h"
49 #include "wtf/PassRefPtr.h"
50 #include "wtf/RefPtr.h"
51 #include "wtf/Vector.h"
52 #include "wtf/text/Base64.h"
53 #include "wtf/text/StringBuilder.h"
57 FileReaderLoader::FileReaderLoader(ReadType readType
, FileReaderLoaderClient
* client
)
58 : m_readType(readType
)
60 , m_urlForReadingIsStream(false)
61 , m_isRawDataConverted(false)
63 , m_finishedLoading(false)
69 , m_errorCode(FileError::OK
)
73 FileReaderLoader::~FileReaderLoader()
76 if (!m_urlForReading
.isEmpty()) {
77 if (m_urlForReadingIsStream
)
78 BlobRegistry::unregisterStreamURL(m_urlForReading
);
80 BlobRegistry::revokePublicBlobURL(m_urlForReading
);
84 void FileReaderLoader::startInternal(ExecutionContext
& executionContext
, const Stream
* stream
, PassRefPtr
<BlobDataHandle
> blobData
)
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
);
95 BlobRegistry::registerPublicBlobURL(executionContext
.securityOrigin(), m_urlForReading
, blobData
);
98 BlobRegistry::registerStreamURL(executionContext
.securityOrigin(), m_urlForReading
, stream
->url());
101 // Construct and load the request.
102 ResourceRequest
request(m_urlForReading
);
104 // FIXME: Should this really be 'internal'? Do we know anything about the actual request that generated this fetch?
105 request
.setRequestContext(WebURLRequest::RequestContextInternal
);
107 request
.setHTTPMethod("GET");
109 request
.setHTTPHeaderField("Range", AtomicString(String::format("bytes=%d-%d", m_rangeStart
, m_rangeEnd
)));
111 ThreadableLoaderOptions options
;
112 options
.preflightPolicy
= ConsiderPreflight
;
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
;
119 ResourceLoaderOptions resourceLoaderOptions
;
120 resourceLoaderOptions
.allowCredentials
= AllowStoredCredentials
;
123 m_loader
= ThreadableLoader::create(executionContext
, this, request
, options
, resourceLoaderOptions
);
125 ThreadableLoader::loadResourceSynchronously(executionContext
, request
, *this, options
, resourceLoaderOptions
);
128 void FileReaderLoader::start(ExecutionContext
* executionContext
, PassRefPtr
<BlobDataHandle
> blobData
)
130 ASSERT(executionContext
);
131 m_urlForReadingIsStream
= false;
132 startInternal(*executionContext
, 0, blobData
);
135 void FileReaderLoader::start(ExecutionContext
* executionContext
, const Stream
& stream
, unsigned readSize
)
137 ASSERT(executionContext
);
141 m_rangeEnd
= readSize
- 1; // End is inclusive so (0,0) is a 1-byte read.
144 m_urlForReadingIsStream
= true;
145 startInternal(*executionContext
, &stream
, nullptr);
148 void FileReaderLoader::cancel()
150 m_errorCode
= FileError::ABORT_ERR
;
154 void FileReaderLoader::terminate()
162 void FileReaderLoader::cleanup()
166 // If we get any error, we do not need to keep a buffer around.
170 m_isRawDataConverted
= true;
175 void FileReaderLoader::didReceiveResponse(unsigned long, const ResourceResponse
& response
, PassOwnPtr
<WebDataConsumerHandle
> handle
)
177 ASSERT_UNUSED(handle
, !handle
);
178 if (response
.httpStatusCode() != 200) {
179 failed(httpStatusCodeToErrorCode(response
.httpStatusCode()));
183 // A negative value means that the content length wasn't specified.
184 m_totalBytes
= response
.expectedContentLength();
186 long long initialBufferLength
= -1;
188 if (m_totalBytes
>= 0) {
189 initialBufferLength
= m_totalBytes
;
190 } else if (m_hasRange
) {
191 // Set m_totalBytes and allocate a buffer based on the specified range.
192 m_totalBytes
= 1LL + m_rangeEnd
- m_rangeStart
;
193 initialBufferLength
= m_totalBytes
;
195 // Nothing is known about the size of the resource. Normalize
196 // m_totalBytes to -1 and initialize the buffer for receiving with the
203 if (m_readType
!= ReadByClient
) {
204 // Check that we can cast to unsigned since we have to do
205 // so to call ArrayBuffer's create function.
206 // FIXME: Support reading more than the current size limit of ArrayBuffer.
207 if (initialBufferLength
> std::numeric_limits
<unsigned>::max()) {
208 failed(FileError::NOT_READABLE_ERR
);
212 if (initialBufferLength
< 0)
213 m_rawData
= adoptPtr(new ArrayBufferBuilder());
215 m_rawData
= adoptPtr(new ArrayBufferBuilder(static_cast<unsigned>(initialBufferLength
)));
217 if (!m_rawData
|| !m_rawData
->isValid()) {
218 failed(FileError::NOT_READABLE_ERR
);
222 if (initialBufferLength
>= 0) {
223 // Total size is known. Set m_rawData to ignore overflowed data.
224 m_rawData
->setVariableCapacity(false);
229 m_client
->didStartLoading();
232 void FileReaderLoader::didReceiveData(const char* data
, unsigned dataLength
)
236 // Bail out if we already encountered an error.
240 if (m_readType
== ReadByClient
) {
241 m_bytesLoaded
+= dataLength
;
244 m_client
->didReceiveDataForClient(data
, dataLength
);
248 unsigned bytesAppended
= m_rawData
->append(data
, dataLength
);
249 if (!bytesAppended
) {
252 failed(FileError::NOT_READABLE_ERR
);
255 m_bytesLoaded
+= bytesAppended
;
256 m_isRawDataConverted
= false;
259 m_client
->didReceiveData();
262 void FileReaderLoader::didFinishLoading(unsigned long, double)
264 if (m_readType
!= ReadByClient
&& m_rawData
) {
265 m_rawData
->shrinkToFit();
266 m_isRawDataConverted
= false;
269 if (m_totalBytes
== -1) {
270 // Update m_totalBytes only when in dynamic buffer grow mode.
271 m_totalBytes
= m_bytesLoaded
;
274 m_finishedLoading
= true;
278 m_client
->didFinishLoading();
281 void FileReaderLoader::didFail(const ResourceError
&)
283 // If we're aborting, do not proceed with normal error handling since it is covered in aborting code.
284 if (m_errorCode
== FileError::ABORT_ERR
)
287 failed(FileError::NOT_READABLE_ERR
);
290 void FileReaderLoader::failed(FileError::ErrorCode errorCode
)
292 m_errorCode
= errorCode
;
295 m_client
->didFail(m_errorCode
);
298 FileError::ErrorCode
FileReaderLoader::httpStatusCodeToErrorCode(int httpStatusCode
)
300 switch (httpStatusCode
) {
302 return FileError::SECURITY_ERR
;
304 return FileError::NOT_FOUND_ERR
;
306 return FileError::NOT_READABLE_ERR
;
310 PassRefPtr
<DOMArrayBuffer
> FileReaderLoader::arrayBufferResult() const
312 ASSERT(m_readType
== ReadAsArrayBuffer
);
314 // If the loading is not started or an error occurs, return an empty result.
315 if (!m_rawData
|| m_errorCode
)
318 return DOMArrayBuffer::create(m_rawData
->toArrayBuffer());
321 String
FileReaderLoader::stringResult()
323 ASSERT(m_readType
!= ReadAsArrayBuffer
&& m_readType
!= ReadByClient
);
325 // If the loading is not started or an error occurs, return an empty result.
326 if (!m_rawData
|| m_errorCode
)
327 return m_stringResult
;
329 // If already converted from the raw data, return the result now.
330 if (m_isRawDataConverted
)
331 return m_stringResult
;
333 switch (m_readType
) {
334 case ReadAsArrayBuffer
:
335 // No conversion is needed.
337 case ReadAsBinaryString
:
338 m_stringResult
= m_rawData
->toString();
339 m_isRawDataConverted
= true;
345 // Partial data is not supported when reading as data URL.
346 if (m_finishedLoading
)
350 ASSERT_NOT_REACHED();
353 return m_stringResult
;
356 void FileReaderLoader::convertToText()
358 m_isRawDataConverted
= true;
360 if (!m_bytesLoaded
) {
366 // The File API spec says that we should use the supplied encoding if it is valid. However, we choose to ignore this
367 // requirement in order to be consistent with how WebKit decodes the web content: always has the BOM override the
368 // provided encoding.
369 // FIXME: consider supporting incremental decoding to improve the perf.
370 StringBuilder builder
;
372 m_decoder
= TextResourceDecoder::create("text/plain", m_encoding
.isValid() ? m_encoding
: UTF8Encoding());
373 builder
.append(m_decoder
->decode(static_cast<const char*>(m_rawData
->data()), m_rawData
->byteLength()));
375 if (m_finishedLoading
)
376 builder
.append(m_decoder
->flush());
378 m_stringResult
= builder
.toString();
381 void FileReaderLoader::convertToDataURL()
383 m_isRawDataConverted
= true;
385 StringBuilder builder
;
386 builder
.appendLiteral("data:");
388 if (!m_bytesLoaded
) {
389 m_stringResult
= builder
.toString();
393 builder
.append(m_dataType
);
394 builder
.appendLiteral(";base64,");
397 base64Encode(static_cast<const char*>(m_rawData
->data()), m_rawData
->byteLength(), out
);
399 builder
.append(out
.data());
401 m_stringResult
= builder
.toString();
404 void FileReaderLoader::setEncoding(const String
& encoding
)
406 if (!encoding
.isEmpty())
407 m_encoding
= WTF::TextEncoding(encoding
);