• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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