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