15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright (C) 2010 Google Inc. All rights reserved. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Redistribution and use in source and binary forms, with or without 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * modification, are permitted provided that the following conditions are 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * met: 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * * Redistributions of source code must retain the above copyright 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * notice, this list of conditions and the following disclaimer. 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * * Redistributions in binary form must reproduce the above 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * copyright notice, this list of conditions and the following disclaimer 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * in the documentation and/or other materials provided with the 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * distribution. 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * * Neither the name of Google Inc. nor the names of its 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * contributors may be used to endorse or promote products derived from 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * this software without specific prior written permission. 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "config.h" 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "modules/filesystem/FileWriter.h" 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "bindings/core/v8/ExceptionState.h" 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "core/dom/ExceptionCode.h" 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "core/events/ProgressEvent.h" 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "core/fileapi/Blob.h" 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "core/inspector/InspectorInstrumentation.h" 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "public/platform/WebFileWriter.h" 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "public/platform/WebURL.h" 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "wtf/CurrentTime.h" 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace blink { 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxRecursionDepth = 3; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const double progressNotificationIntervalMS = 50; 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FileWriter* FileWriter::create(ExecutionContext* context) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){ 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileWriter* fileWriter = adoptRefCountedGarbageCollectedWillBeNoop(new FileWriter(context)); 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fileWriter->suspendIfNeeded(); 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return fileWriter; 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FileWriter::FileWriter(ExecutionContext* context) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : ActiveDOMObject(context) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_readyState(INIT) 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_operationInProgress(OperationNone) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_queuedOperation(OperationNone) 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_bytesWritten(0) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_bytesToWrite(0) 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_truncateLength(-1) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_numAborts(0) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_recursionDepth(0) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_lastProgressNotificationTimeMS(0) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) , m_asyncOperationId(0) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){ 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FileWriter::~FileWriter() 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){ 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ASSERT(!m_recursionDepth); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (m_readyState == WRITING) 74 stop(); 75} 76 77const AtomicString& FileWriter::interfaceName() const 78{ 79 return EventTargetNames::FileWriter; 80} 81 82void FileWriter::stop() 83{ 84 // Make sure we've actually got something to stop, and haven't already called abort(). 85 if (!writer() || m_readyState != WRITING) 86 return; 87 doOperation(OperationAbort); 88 m_readyState = DONE; 89} 90 91bool FileWriter::hasPendingActivity() const 92{ 93 return m_operationInProgress != OperationNone || m_queuedOperation != OperationNone || m_readyState == WRITING; 94} 95 96void FileWriter::write(Blob* data, ExceptionState& exceptionState) 97{ 98 ASSERT(writer()); 99 ASSERT(data); 100 ASSERT(m_truncateLength == -1); 101 if (m_readyState == WRITING) { 102 setError(FileError::INVALID_STATE_ERR, exceptionState); 103 return; 104 } 105 if (!data) { 106 setError(FileError::TYPE_MISMATCH_ERR, exceptionState); 107 return; 108 } 109 if (m_recursionDepth > kMaxRecursionDepth) { 110 setError(FileError::SECURITY_ERR, exceptionState); 111 return; 112 } 113 114 m_blobBeingWritten = data; 115 m_readyState = WRITING; 116 m_bytesWritten = 0; 117 m_bytesToWrite = data->size(); 118 ASSERT(m_queuedOperation == OperationNone); 119 if (m_operationInProgress != OperationNone) { 120 // We must be waiting for an abort to complete, since m_readyState wasn't WRITING. 121 ASSERT(m_operationInProgress == OperationAbort); 122 m_queuedOperation = OperationWrite; 123 } else 124 doOperation(OperationWrite); 125 126 fireEvent(EventTypeNames::writestart); 127} 128 129void FileWriter::seek(long long position, ExceptionState& exceptionState) 130{ 131 ASSERT(writer()); 132 if (m_readyState == WRITING) { 133 setError(FileError::INVALID_STATE_ERR, exceptionState); 134 return; 135 } 136 137 ASSERT(m_truncateLength == -1); 138 ASSERT(m_queuedOperation == OperationNone); 139 seekInternal(position); 140} 141 142void FileWriter::truncate(long long position, ExceptionState& exceptionState) 143{ 144 ASSERT(writer()); 145 ASSERT(m_truncateLength == -1); 146 if (m_readyState == WRITING || position < 0) { 147 setError(FileError::INVALID_STATE_ERR, exceptionState); 148 return; 149 } 150 if (m_recursionDepth > kMaxRecursionDepth) { 151 setError(FileError::SECURITY_ERR, exceptionState); 152 return; 153 } 154 155 m_readyState = WRITING; 156 m_bytesWritten = 0; 157 m_bytesToWrite = 0; 158 m_truncateLength = position; 159 ASSERT(m_queuedOperation == OperationNone); 160 if (m_operationInProgress != OperationNone) { 161 // We must be waiting for an abort to complete, since m_readyState wasn't WRITING. 162 ASSERT(m_operationInProgress == OperationAbort); 163 m_queuedOperation = OperationTruncate; 164 } else 165 doOperation(OperationTruncate); 166 fireEvent(EventTypeNames::writestart); 167} 168 169void FileWriter::abort(ExceptionState& exceptionState) 170{ 171 ASSERT(writer()); 172 if (m_readyState != WRITING) 173 return; 174 ++m_numAborts; 175 176 doOperation(OperationAbort); 177 signalCompletion(FileError::ABORT_ERR); 178} 179 180void FileWriter::didWrite(long long bytes, bool complete) 181{ 182 if (m_operationInProgress == OperationAbort) { 183 completeAbort(); 184 return; 185 } 186 ASSERT(m_readyState == WRITING); 187 ASSERT(m_truncateLength == -1); 188 ASSERT(m_operationInProgress == OperationWrite); 189 ASSERT(!m_bytesToWrite || bytes + m_bytesWritten > 0); 190 ASSERT(bytes + m_bytesWritten <= m_bytesToWrite); 191 m_bytesWritten += bytes; 192 ASSERT((m_bytesWritten == m_bytesToWrite) || !complete); 193 setPosition(position() + bytes); 194 if (position() > length()) 195 setLength(position()); 196 if (complete) { 197 m_blobBeingWritten.clear(); 198 m_operationInProgress = OperationNone; 199 } 200 201 int numAborts = m_numAborts; 202 // We could get an abort in the handler for this event. If we do, it's 203 // already handled the cleanup and signalCompletion call. 204 double now = currentTimeMS(); 205 if (complete || !m_lastProgressNotificationTimeMS || (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS)) { 206 m_lastProgressNotificationTimeMS = now; 207 fireEvent(EventTypeNames::progress); 208 } 209 210 if (complete) { 211 if (numAborts == m_numAborts) 212 signalCompletion(FileError::OK); 213 } 214} 215 216void FileWriter::didTruncate() 217{ 218 if (m_operationInProgress == OperationAbort) { 219 completeAbort(); 220 return; 221 } 222 ASSERT(m_operationInProgress == OperationTruncate); 223 ASSERT(m_truncateLength >= 0); 224 setLength(m_truncateLength); 225 if (position() > length()) 226 setPosition(length()); 227 m_operationInProgress = OperationNone; 228 signalCompletion(FileError::OK); 229} 230 231void FileWriter::didFail(WebFileError code) 232{ 233 ASSERT(m_operationInProgress != OperationNone); 234 ASSERT(static_cast<FileError::ErrorCode>(code) != FileError::OK); 235 if (m_operationInProgress == OperationAbort) { 236 completeAbort(); 237 return; 238 } 239 ASSERT(m_queuedOperation == OperationNone); 240 ASSERT(m_readyState == WRITING); 241 m_blobBeingWritten.clear(); 242 m_operationInProgress = OperationNone; 243 signalCompletion(static_cast<FileError::ErrorCode>(code)); 244} 245 246void FileWriter::completeAbort() 247{ 248 ASSERT(m_operationInProgress == OperationAbort); 249 m_operationInProgress = OperationNone; 250 Operation operation = m_queuedOperation; 251 m_queuedOperation = OperationNone; 252 doOperation(operation); 253} 254 255void FileWriter::doOperation(Operation operation) 256{ 257 m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(executionContext(), "FileWriter", m_asyncOperationId); 258 switch (operation) { 259 case OperationWrite: 260 ASSERT(m_operationInProgress == OperationNone); 261 ASSERT(m_truncateLength == -1); 262 ASSERT(m_blobBeingWritten.get()); 263 ASSERT(m_readyState == WRITING); 264 writer()->write(position(), m_blobBeingWritten->uuid()); 265 break; 266 case OperationTruncate: 267 ASSERT(m_operationInProgress == OperationNone); 268 ASSERT(m_truncateLength >= 0); 269 ASSERT(m_readyState == WRITING); 270 writer()->truncate(m_truncateLength); 271 break; 272 case OperationNone: 273 ASSERT(m_operationInProgress == OperationNone); 274 ASSERT(m_truncateLength == -1); 275 ASSERT(!m_blobBeingWritten.get()); 276 ASSERT(m_readyState == DONE); 277 break; 278 case OperationAbort: 279 if (m_operationInProgress == OperationWrite || m_operationInProgress == OperationTruncate) 280 writer()->cancel(); 281 else if (m_operationInProgress != OperationAbort) 282 operation = OperationNone; 283 m_queuedOperation = OperationNone; 284 m_blobBeingWritten.clear(); 285 m_truncateLength = -1; 286 break; 287 } 288 ASSERT(m_queuedOperation == OperationNone); 289 m_operationInProgress = operation; 290} 291 292void FileWriter::signalCompletion(FileError::ErrorCode code) 293{ 294 m_readyState = DONE; 295 m_truncateLength = -1; 296 if (FileError::OK != code) { 297 m_error = FileError::create(code); 298 if (FileError::ABORT_ERR == code) 299 fireEvent(EventTypeNames::abort); 300 else 301 fireEvent(EventTypeNames::error); 302 } else 303 fireEvent(EventTypeNames::write); 304 fireEvent(EventTypeNames::writeend); 305 306 InspectorInstrumentation::traceAsyncOperationCompleted(executionContext(), m_asyncOperationId); 307 m_asyncOperationId = 0; 308} 309 310void FileWriter::fireEvent(const AtomicString& type) 311{ 312 InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(executionContext(), m_asyncOperationId); 313 ++m_recursionDepth; 314 dispatchEvent(ProgressEvent::create(type, true, m_bytesWritten, m_bytesToWrite)); 315 --m_recursionDepth; 316 ASSERT(m_recursionDepth >= 0); 317 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie); 318} 319 320void FileWriter::setError(FileError::ErrorCode errorCode, ExceptionState& exceptionState) 321{ 322 ASSERT(errorCode); 323 FileError::throwDOMException(exceptionState, errorCode); 324 m_error = FileError::create(errorCode); 325} 326 327void FileWriter::trace(Visitor* visitor) 328{ 329 visitor->trace(m_error); 330 visitor->trace(m_blobBeingWritten); 331 FileWriterBase::trace(visitor); 332 EventTargetWithInlineData::trace(visitor); 333} 334 335} // namespace blink 336