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 "FileReader.h" 36 37#include "ArrayBuffer.h" 38#include "CrossThreadTask.h" 39#include "File.h" 40#include "Logging.h" 41#include "ProgressEvent.h" 42#include "ScriptExecutionContext.h" 43#include <wtf/CurrentTime.h> 44#include <wtf/text/CString.h> 45 46namespace WebCore { 47 48static const double progressNotificationIntervalMS = 50; 49 50FileReader::FileReader(ScriptExecutionContext* context) 51 : ActiveDOMObject(context, this) 52 , m_state(None) 53 , m_readType(FileReaderLoader::ReadAsBinaryString) 54 , m_lastProgressNotificationTimeMS(0) 55{ 56} 57 58FileReader::~FileReader() 59{ 60 terminate(); 61} 62 63bool FileReader::hasPendingActivity() const 64{ 65 return (m_state != None && m_state != Completed) || ActiveDOMObject::hasPendingActivity(); 66} 67 68bool FileReader::canSuspend() const 69{ 70 // FIXME: It is not currently possible to suspend a FileReader, so pages with FileReader can not go into page cache. 71 return false; 72} 73 74void FileReader::stop() 75{ 76 terminate(); 77} 78 79void FileReader::readAsArrayBuffer(Blob* blob) 80{ 81 if (!blob) 82 return; 83 84 LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", blob->url().string().utf8().data(), blob->isFile() ? static_cast<File*>(blob)->path().utf8().data() : ""); 85 86 readInternal(blob, FileReaderLoader::ReadAsArrayBuffer); 87} 88 89void FileReader::readAsBinaryString(Blob* blob) 90{ 91 if (!blob) 92 return; 93 94 LOG(FileAPI, "FileReader: reading as binary: %s %s\n", blob->url().string().utf8().data(), blob->isFile() ? static_cast<File*>(blob)->path().utf8().data() : ""); 95 96 readInternal(blob, FileReaderLoader::ReadAsBinaryString); 97} 98 99void FileReader::readAsText(Blob* blob, const String& encoding) 100{ 101 if (!blob) 102 return; 103 104 LOG(FileAPI, "FileReader: reading as text: %s %s\n", blob->url().string().utf8().data(), blob->isFile() ? static_cast<File*>(blob)->path().utf8().data() : ""); 105 106 m_encoding = encoding; 107 readInternal(blob, FileReaderLoader::ReadAsText); 108} 109 110void FileReader::readAsDataURL(Blob* blob) 111{ 112 if (!blob) 113 return; 114 115 LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", blob->url().string().utf8().data(), blob->isFile() ? static_cast<File*>(blob)->path().utf8().data() : ""); 116 117 readInternal(blob, FileReaderLoader::ReadAsDataURL); 118} 119 120static void delayedStart(ScriptExecutionContext*, FileReader* reader) 121{ 122 reader->start(); 123} 124 125void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type) 126{ 127 // readAs*** methods() can be called multiple times. Only the last call before the actual reading happens is processed. 128 if (m_state != None && m_state != Starting) 129 return; 130 131 if (m_state == None) 132 scriptExecutionContext()->postTask(createCallbackTask(&delayedStart, this)); 133 134 m_blob = blob; 135 m_readType = type; 136 m_state = Starting; 137} 138 139static void delayedAbort(ScriptExecutionContext*, FileReader* reader) 140{ 141 reader->doAbort(); 142} 143 144void FileReader::abort() 145{ 146 LOG(FileAPI, "FileReader: aborting\n"); 147 148 if (m_state == Aborting) 149 return; 150 m_state = Aborting; 151 152 // 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. 153 scriptExecutionContext()->postTask(createCallbackTask(&delayedAbort, this)); 154} 155 156void FileReader::doAbort() 157{ 158 terminate(); 159 160 m_error = FileError::create(FileError::ABORT_ERR); 161 162 fireEvent(eventNames().errorEvent); 163 fireEvent(eventNames().abortEvent); 164 fireEvent(eventNames().loadendEvent); 165} 166 167void FileReader::terminate() 168{ 169 if (m_loader) { 170 m_loader->cancel(); 171 m_loader = 0; 172 } 173 m_state = Completed; 174} 175 176void FileReader::start() 177{ 178 m_state = Opening; 179 180 m_loader = adoptPtr(new FileReaderLoader(m_readType, this)); 181 m_loader->setEncoding(m_encoding); 182 m_loader->setDataType(m_blob->type()); 183 m_loader->start(scriptExecutionContext(), m_blob.get()); 184} 185 186void FileReader::didStartLoading() 187{ 188 m_state = Reading; 189 fireEvent(eventNames().loadstartEvent); 190} 191 192void FileReader::didReceiveData() 193{ 194 // Fire the progress event at least every 50ms. 195 double now = currentTimeMS(); 196 if (!m_lastProgressNotificationTimeMS) 197 m_lastProgressNotificationTimeMS = now; 198 else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) { 199 fireEvent(eventNames().progressEvent); 200 m_lastProgressNotificationTimeMS = now; 201 } 202} 203 204void FileReader::didFinishLoading() 205{ 206 m_state = Completed; 207 208 fireEvent(eventNames().loadEvent); 209 fireEvent(eventNames().loadendEvent); 210} 211 212void FileReader::didFail(int errorCode) 213{ 214 // If we're aborting, do not proceed with normal error handling since it is covered in aborting code. 215 if (m_state == Aborting) 216 return; 217 218 m_state = Completed; 219 220 m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode)); 221 fireEvent(eventNames().errorEvent); 222 fireEvent(eventNames().loadendEvent); 223} 224 225void FileReader::fireEvent(const AtomicString& type) 226{ 227 dispatchEvent(ProgressEvent::create(type, true, m_loader ? m_loader->bytesLoaded() : 0, m_loader ? m_loader->totalBytes() : 0)); 228} 229 230FileReader::ReadyState FileReader::readyState() const 231{ 232 switch (m_state) { 233 case None: 234 case Starting: 235 return EMPTY; 236 case Opening: 237 case Reading: 238 case Aborting: 239 return LOADING; 240 case Completed: 241 return DONE; 242 } 243 ASSERT_NOT_REACHED(); 244 return EMPTY; 245} 246 247PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const 248{ 249 return m_loader ? m_loader->arrayBufferResult() : 0; 250} 251 252String FileReader::stringResult() 253{ 254 return m_loader ? m_loader->stringResult() : ""; 255} 256 257} // namespace WebCore 258 259#endif // ENABLE(BLOB) 260