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