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 #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 
     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 
     68 FileReaderLoader::~FileReaderLoader()
     69 {
     70     terminate();
     71     ThreadableBlobRegistry::unregisterBlobURL(m_urlForReading);
     72 }
     73 
     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 
    101 void FileReaderLoader::cancel()
    102 {
    103     m_errorCode = FileError::ABORT_ERR;
    104     terminate();
    105 }
    106 
    107 void FileReaderLoader::terminate()
    108 {
    109     if (m_loader) {
    110         m_loader->cancel();
    111         cleanup();
    112     }
    113 }
    114 
    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 
    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 
    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 
    183 void FileReaderLoader::didFinishLoading(unsigned long, double)
    184 {
    185     cleanup();
    186     if (m_client)
    187         m_client->didFinishLoading();
    188 }
    189 
    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 
    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 
    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 
    219 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 
    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 
    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 
    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 
    314 bool FileReaderLoader::isCompleted() const
    315 {
    316     return m_bytesLoaded == m_totalBytes;
    317 }
    318 
    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