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 #include "core/fileapi/FileReader.h"
     33 
     34 #include "bindings/v8/ExceptionState.h"
     35 #include "core/dom/CrossThreadTask.h"
     36 #include "core/dom/ExceptionCode.h"
     37 #include "core/dom/ProgressEvent.h"
     38 #include "core/dom/ScriptExecutionContext.h"
     39 #include "core/fileapi/File.h"
     40 #include "core/platform/Logging.h"
     41 #include "wtf/ArrayBuffer.h"
     42 #include "wtf/CurrentTime.h"
     43 #include "wtf/text/CString.h"
     44 
     45 namespace WebCore {
     46 
     47 namespace {
     48 
     49 const CString utf8BlobURL(Blob* blob)
     50 {
     51     return blob->url().string().utf8();
     52 }
     53 
     54 const CString utf8FilePath(Blob* blob)
     55 {
     56     return blob->isFile() ? toFile(blob)->path().utf8() : "";
     57 }
     58 
     59 } // namespace
     60 
     61 static const double progressNotificationIntervalMS = 50;
     62 
     63 PassRefPtr<FileReader> FileReader::create(ScriptExecutionContext* context)
     64 {
     65     RefPtr<FileReader> fileReader(adoptRef(new FileReader(context)));
     66     fileReader->suspendIfNeeded();
     67     return fileReader.release();
     68 }
     69 
     70 FileReader::FileReader(ScriptExecutionContext* context)
     71     : ActiveDOMObject(context)
     72     , m_state(EMPTY)
     73     , m_loadingState(LoadingStateNone)
     74     , m_readType(FileReaderLoader::ReadAsBinaryString)
     75     , m_lastProgressNotificationTimeMS(0)
     76 {
     77     ScriptWrappable::init(this);
     78 }
     79 
     80 FileReader::~FileReader()
     81 {
     82     terminate();
     83 }
     84 
     85 const AtomicString& FileReader::interfaceName() const
     86 {
     87     return eventNames().interfaceForFileReader;
     88 }
     89 
     90 bool FileReader::canSuspend() const
     91 {
     92     // FIXME: It is not currently possible to suspend a FileReader, so pages with FileReader can not go into page cache.
     93     return false;
     94 }
     95 
     96 void FileReader::stop()
     97 {
     98     terminate();
     99 }
    100 
    101 void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& es)
    102 {
    103     if (!blob)
    104         return;
    105 
    106     LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", utf8BlobURL(blob).data(), utf8FilePath(blob).data());
    107 
    108     readInternal(blob, FileReaderLoader::ReadAsArrayBuffer, es);
    109 }
    110 
    111 void FileReader::readAsBinaryString(Blob* blob, ExceptionState& es)
    112 {
    113     if (!blob)
    114         return;
    115 
    116     LOG(FileAPI, "FileReader: reading as binary: %s %s\n", utf8BlobURL(blob).data(), utf8FilePath(blob).data());
    117 
    118     readInternal(blob, FileReaderLoader::ReadAsBinaryString, es);
    119 }
    120 
    121 void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionState& es)
    122 {
    123     if (!blob)
    124         return;
    125 
    126     LOG(FileAPI, "FileReader: reading as text: %s %s\n", utf8BlobURL(blob).data(), utf8FilePath(blob).data());
    127 
    128     m_encoding = encoding;
    129     readInternal(blob, FileReaderLoader::ReadAsText, es);
    130 }
    131 
    132 void FileReader::readAsText(Blob* blob, ExceptionState& es)
    133 {
    134     readAsText(blob, String(), es);
    135 }
    136 
    137 void FileReader::readAsDataURL(Blob* blob, ExceptionState& es)
    138 {
    139     if (!blob)
    140         return;
    141 
    142     LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", utf8BlobURL(blob).data(), utf8FilePath(blob).data());
    143 
    144     readInternal(blob, FileReaderLoader::ReadAsDataURL, es);
    145 }
    146 
    147 void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionState& es)
    148 {
    149     // If multiple concurrent read methods are called on the same FileReader, InvalidStateError should be thrown when the state is LOADING.
    150     if (m_state == LOADING) {
    151         es.throwDOMException(InvalidStateError);
    152         return;
    153     }
    154 
    155     setPendingActivity(this);
    156 
    157     m_blob = blob;
    158     m_readType = type;
    159     m_state = LOADING;
    160     m_loadingState = LoadingStateLoading;
    161     m_error = 0;
    162 
    163     m_loader = adoptPtr(new FileReaderLoader(m_readType, this));
    164     m_loader->setEncoding(m_encoding);
    165     m_loader->setDataType(m_blob->type());
    166     m_loader->start(scriptExecutionContext(), *m_blob);
    167 }
    168 
    169 static void delayedAbort(ScriptExecutionContext*, FileReader* reader)
    170 {
    171     reader->doAbort();
    172 }
    173 
    174 void FileReader::abort()
    175 {
    176     LOG(FileAPI, "FileReader: aborting\n");
    177 
    178     if (m_loadingState != LoadingStateLoading)
    179         return;
    180     m_loadingState = LoadingStateAborted;
    181 
    182     // Schedule to have the abort done later since abort() might be called from the event handler and we do not want the resource loading code to be in the stack.
    183     scriptExecutionContext()->postTask(
    184         createCallbackTask(&delayedAbort, AllowAccessLater(this)));
    185 }
    186 
    187 void FileReader::doAbort()
    188 {
    189     ASSERT(m_state != DONE);
    190 
    191     terminate();
    192 
    193     m_error = FileError::create(FileError::ABORT_ERR);
    194 
    195     fireEvent(eventNames().errorEvent);
    196     fireEvent(eventNames().abortEvent);
    197     fireEvent(eventNames().loadendEvent);
    198 
    199     // All possible events have fired and we're done, no more pending activity.
    200     unsetPendingActivity(this);
    201 }
    202 
    203 void FileReader::terminate()
    204 {
    205     if (m_loader) {
    206         m_loader->cancel();
    207         m_loader = nullptr;
    208     }
    209     m_state = DONE;
    210     m_loadingState = LoadingStateNone;
    211 }
    212 
    213 void FileReader::didStartLoading()
    214 {
    215     fireEvent(eventNames().loadstartEvent);
    216 }
    217 
    218 void FileReader::didReceiveData()
    219 {
    220     // Fire the progress event at least every 50ms.
    221     double now = currentTimeMS();
    222     if (!m_lastProgressNotificationTimeMS)
    223         m_lastProgressNotificationTimeMS = now;
    224     else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) {
    225         fireEvent(eventNames().progressEvent);
    226         m_lastProgressNotificationTimeMS = now;
    227     }
    228 }
    229 
    230 void FileReader::didFinishLoading()
    231 {
    232     if (m_loadingState == LoadingStateAborted)
    233         return;
    234     ASSERT(m_loadingState == LoadingStateLoading);
    235     m_loadingState = LoadingStateNone;
    236 
    237     ASSERT(m_state != DONE);
    238     m_state = DONE;
    239 
    240     fireEvent(eventNames().progressEvent);
    241     fireEvent(eventNames().loadEvent);
    242     fireEvent(eventNames().loadendEvent);
    243 
    244     // All possible events have fired and we're done, no more pending activity.
    245     unsetPendingActivity(this);
    246 }
    247 
    248 void FileReader::didFail(FileError::ErrorCode errorCode)
    249 {
    250     if (m_loadingState == LoadingStateAborted)
    251         return;
    252     ASSERT(m_loadingState == LoadingStateLoading);
    253     m_loadingState = LoadingStateNone;
    254 
    255     ASSERT(m_state != DONE);
    256     m_state = DONE;
    257 
    258     m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
    259     fireEvent(eventNames().errorEvent);
    260     fireEvent(eventNames().loadendEvent);
    261 
    262     // All possible events have fired and we're done, no more pending activity.
    263     unsetPendingActivity(this);
    264 }
    265 
    266 void FileReader::fireEvent(const AtomicString& type)
    267 {
    268     dispatchEvent(ProgressEvent::create(type, true, m_loader ? m_loader->bytesLoaded() : 0, m_loader ? m_loader->totalBytes() : 0));
    269 }
    270 
    271 PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const
    272 {
    273     if (!m_loader || m_error)
    274         return 0;
    275     return m_loader->arrayBufferResult();
    276 }
    277 
    278 String FileReader::stringResult()
    279 {
    280     if (!m_loader || m_error)
    281         return String();
    282     return m_loader->stringResult();
    283 }
    284 
    285 } // namespace WebCore
    286