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 #if ENABLE(BLOB)
34
35 #include "FileReaderLoader.h"
36
37 #include "ArrayBuffer.h"
38 #include "Base64.h"
39 #include "Blob.h"
40 #include "BlobURL.h"
41 #include "FileReaderLoaderClient.h"
42 #include "ResourceRequest.h"
43 #include "ResourceResponse.h"
44 #include "ScriptExecutionContext.h"
45 #include "TextResourceDecoder.h"
46 #include "ThreadableBlobRegistry.h"
47 #include "ThreadableLoader.h"
48 #include <wtf/PassRefPtr.h>
49 #include <wtf/RefPtr.h>
50 #include <wtf/Vector.h>
51 #include <wtf/text/StringBuilder.h>
52
53 using namespace std;
54
55 namespace WebCore {
56
FileReaderLoader(ReadType readType,FileReaderLoaderClient * client)57 FileReaderLoader::FileReaderLoader(ReadType readType, FileReaderLoaderClient* client)
58 : m_readType(readType)
59 , m_client(client)
60 , m_isRawDataConverted(false)
61 , m_stringResult("")
62 , m_bytesLoaded(0)
63 , m_totalBytes(0)
64 , m_errorCode(0)
65 {
66 }
67
~FileReaderLoader()68 FileReaderLoader::~FileReaderLoader()
69 {
70 terminate();
71 ThreadableBlobRegistry::unregisterBlobURL(m_urlForReading);
72 }
73
start(ScriptExecutionContext * scriptExecutionContext,Blob * blob)74 void FileReaderLoader::start(ScriptExecutionContext* scriptExecutionContext, Blob* blob)
75 {
76 // The blob is read by routing through the request handling layer given a temporary public url.
77 m_urlForReading = BlobURL::createPublicURL(scriptExecutionContext->securityOrigin());
78 if (m_urlForReading.isEmpty()) {
79 failed(FileError::SECURITY_ERR);
80 return;
81 }
82 ThreadableBlobRegistry::registerBlobURL(m_urlForReading, blob->url());
83
84 // Construct and load the request.
85 ResourceRequest request(m_urlForReading);
86 request.setHTTPMethod("GET");
87
88 ThreadableLoaderOptions options;
89 options.sendLoadCallbacks = true;
90 options.sniffContent = false;
91 options.forcePreflight = false;
92 options.allowCredentials = true;
93 options.crossOriginRequestPolicy = DenyCrossOriginRequests;
94
95 if (m_client)
96 m_loader = ThreadableLoader::create(scriptExecutionContext, this, request, options);
97 else
98 ThreadableLoader::loadResourceSynchronously(scriptExecutionContext, request, *this, options);
99 }
100
cancel()101 void FileReaderLoader::cancel()
102 {
103 m_errorCode = FileError::ABORT_ERR;
104 terminate();
105 }
106
terminate()107 void FileReaderLoader::terminate()
108 {
109 if (m_loader) {
110 m_loader->cancel();
111 cleanup();
112 }
113 }
114
cleanup()115 void FileReaderLoader::cleanup()
116 {
117 m_loader = 0;
118
119 // If we get any error, we do not need to keep a buffer around.
120 if (m_errorCode) {
121 m_rawData = 0;
122 m_stringResult = "";
123 }
124 }
125
didReceiveResponse(const ResourceResponse & response)126 void FileReaderLoader::didReceiveResponse(const ResourceResponse& response)
127 {
128 if (response.httpStatusCode() != 200) {
129 failed(httpStatusCodeToErrorCode(response.httpStatusCode()));
130 return;
131 }
132
133 unsigned long long length = response.expectedContentLength();
134
135 // Check that we can cast to unsigned since we have to do
136 // so to call ArrayBuffer's create function.
137 // FIXME: Support reading more than the current size limit of ArrayBuffer.
138 if (length > numeric_limits<unsigned>::max()) {
139 failed(FileError::NOT_READABLE_ERR);
140 return;
141 }
142
143 ASSERT(!m_rawData);
144 m_rawData = ArrayBuffer::create(static_cast<unsigned>(length), 1);
145
146 if (!m_rawData) {
147 failed(FileError::NOT_READABLE_ERR);
148 return;
149 }
150
151 m_totalBytes = static_cast<unsigned>(length);
152
153 if (m_client)
154 m_client->didStartLoading();
155 }
156
didReceiveData(const char * data,int dataLength)157 void FileReaderLoader::didReceiveData(const char* data, int dataLength)
158 {
159 ASSERT(data);
160 ASSERT(dataLength > 0);
161
162 // Bail out if we already encountered an error.
163 if (m_errorCode)
164 return;
165
166 int length = dataLength;
167 unsigned remainingBufferSpace = m_totalBytes - m_bytesLoaded;
168 if (length > static_cast<long long>(remainingBufferSpace))
169 length = static_cast<int>(remainingBufferSpace);
170
171 if (length <= 0)
172 return;
173
174 memcpy(static_cast<char*>(m_rawData->data()) + m_bytesLoaded, data, length);
175 m_bytesLoaded += length;
176
177 m_isRawDataConverted = false;
178
179 if (m_client)
180 m_client->didReceiveData();
181 }
182
didFinishLoading(unsigned long,double)183 void FileReaderLoader::didFinishLoading(unsigned long, double)
184 {
185 cleanup();
186 if (m_client)
187 m_client->didFinishLoading();
188 }
189
didFail(const ResourceError &)190 void FileReaderLoader::didFail(const ResourceError&)
191 {
192 // If we're aborting, do not proceed with normal error handling since it is covered in aborting code.
193 if (m_errorCode == FileError::ABORT_ERR)
194 return;
195
196 failed(FileError::NOT_READABLE_ERR);
197 }
198
failed(int errorCode)199 void FileReaderLoader::failed(int errorCode)
200 {
201 m_errorCode = errorCode;
202 cleanup();
203 if (m_client)
204 m_client->didFail(m_errorCode);
205 }
206
httpStatusCodeToErrorCode(int httpStatusCode)207 FileError::ErrorCode FileReaderLoader::httpStatusCodeToErrorCode(int httpStatusCode)
208 {
209 switch (httpStatusCode) {
210 case 403:
211 return FileError::SECURITY_ERR;
212 case 404:
213 return FileError::NOT_FOUND_ERR;
214 default:
215 return FileError::NOT_READABLE_ERR;
216 }
217 }
218
arrayBufferResult() const219 PassRefPtr<ArrayBuffer> FileReaderLoader::arrayBufferResult() const
220 {
221 ASSERT(m_readType == ReadAsArrayBuffer);
222
223 // If the loading is not started or an error occurs, return an empty result.
224 if (!m_rawData || m_errorCode)
225 return 0;
226
227 // If completed, we can simply return our buffer.
228 if (isCompleted())
229 return m_rawData;
230
231 // Otherwise, return a copy.
232 return ArrayBuffer::create(m_rawData.get());
233 }
234
stringResult()235 String FileReaderLoader::stringResult()
236 {
237 ASSERT(m_readType != ReadAsArrayBuffer);
238
239 // If the loading is not started or an error occurs, return an empty result.
240 if (!m_rawData || m_errorCode)
241 return m_stringResult;
242
243 // If already converted from the raw data, return the result now.
244 if (m_isRawDataConverted)
245 return m_stringResult;
246
247 switch (m_readType) {
248 case ReadAsArrayBuffer:
249 // No conversion is needed.
250 break;
251 case ReadAsBinaryString:
252 m_stringResult = String(static_cast<const char*>(m_rawData->data()), m_bytesLoaded);
253 break;
254 case ReadAsText:
255 convertToText();
256 break;
257 case ReadAsDataURL:
258 // Partial data is not supported when reading as data URL.
259 if (isCompleted())
260 convertToDataURL();
261 break;
262 default:
263 ASSERT_NOT_REACHED();
264 }
265
266 return m_stringResult;
267 }
268
convertToText()269 void FileReaderLoader::convertToText()
270 {
271 if (!m_bytesLoaded)
272 return;
273
274 // Decode the data.
275 // The File API spec says that we should use the supplied encoding if it is valid. However, we choose to ignore this
276 // requirement in order to be consistent with how WebKit decodes the web content: always has the BOM override the
277 // provided encoding.
278 // FIXME: consider supporting incremental decoding to improve the perf.
279 StringBuilder builder;
280 if (!m_decoder)
281 m_decoder = TextResourceDecoder::create("text/plain", m_encoding.isValid() ? m_encoding : UTF8Encoding());
282 builder.append(m_decoder->decode(static_cast<const char*>(m_rawData->data()), m_bytesLoaded));
283
284 if (isCompleted())
285 builder.append(m_decoder->flush());
286
287 m_stringResult = builder.toString();
288 }
289
convertToDataURL()290 void FileReaderLoader::convertToDataURL()
291 {
292 StringBuilder builder;
293 builder.append("data:");
294
295 if (!m_bytesLoaded) {
296 m_stringResult = builder.toString();
297 return;
298 }
299
300 if (!m_dataType.isEmpty()) {
301 builder.append(m_dataType);
302 builder.append(";base64,");
303 } else
304 builder.append("base64,");
305
306 Vector<char> out;
307 base64Encode(static_cast<const char*>(m_rawData->data()), m_bytesLoaded, out);
308 out.append('\0');
309 builder.append(out.data());
310
311 m_stringResult = builder.toString();
312 }
313
isCompleted() const314 bool FileReaderLoader::isCompleted() const
315 {
316 return m_bytesLoaded == m_totalBytes;
317 }
318
setEncoding(const String & encoding)319 void FileReaderLoader::setEncoding(const String& encoding)
320 {
321 if (!encoding.isEmpty())
322 m_encoding = TextEncoding(encoding);
323 }
324
325 } // namespace WebCore
326
327 #endif // ENABLE(BLOB)
328