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