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