2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "modules/filesystem/FileWriter.h"
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "core/dom/ExceptionCode.h"
36 #include "core/events/ProgressEvent.h"
37 #include "core/fileapi/Blob.h"
38 #include "core/inspector/InspectorInstrumentation.h"
39 #include "public/platform/WebFileWriter.h"
40 #include "public/platform/WebURL.h"
41 #include "wtf/CurrentTime.h"
45 static const int kMaxRecursionDepth = 3;
46 static const double progressNotificationIntervalMS = 50;
48 FileWriter* FileWriter::create(ExecutionContext* context)
50 FileWriter* fileWriter = adoptRefCountedGarbageCollectedWillBeNoop(new FileWriter(context));
51 fileWriter->suspendIfNeeded();
55 FileWriter::FileWriter(ExecutionContext* context)
56 : ActiveDOMObject(context)
58 , m_operationInProgress(OperationNone)
59 , m_queuedOperation(OperationNone)
62 , m_truncateLength(-1)
65 , m_lastProgressNotificationTimeMS(0)
66 , m_asyncOperationId(0)
70 FileWriter::~FileWriter()
72 ASSERT(!m_recursionDepth);
73 if (m_readyState == WRITING)
77 const AtomicString& FileWriter::interfaceName() const
79 return EventTargetNames::FileWriter;
82 void FileWriter::stop()
84 // Make sure we've actually got something to stop, and haven't already called abort().
85 if (!writer() || m_readyState != WRITING)
87 doOperation(OperationAbort);
91 bool FileWriter::hasPendingActivity() const
93 return m_operationInProgress != OperationNone || m_queuedOperation != OperationNone || m_readyState == WRITING;
96 void FileWriter::write(Blob* data, ExceptionState& exceptionState)
100 ASSERT(m_truncateLength == -1);
101 if (m_readyState == WRITING) {
102 setError(FileError::INVALID_STATE_ERR, exceptionState);
106 setError(FileError::TYPE_MISMATCH_ERR, exceptionState);
109 if (m_recursionDepth > kMaxRecursionDepth) {
110 setError(FileError::SECURITY_ERR, exceptionState);
114 m_blobBeingWritten = data;
115 m_readyState = WRITING;
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;
124 doOperation(OperationWrite);
126 fireEvent(EventTypeNames::writestart);
129 void FileWriter::seek(long long position, ExceptionState& exceptionState)
132 if (m_readyState == WRITING) {
133 setError(FileError::INVALID_STATE_ERR, exceptionState);
137 ASSERT(m_truncateLength == -1);
138 ASSERT(m_queuedOperation == OperationNone);
139 seekInternal(position);
142 void FileWriter::truncate(long long position, ExceptionState& exceptionState)
145 ASSERT(m_truncateLength == -1);
146 if (m_readyState == WRITING || position < 0) {
147 setError(FileError::INVALID_STATE_ERR, exceptionState);
150 if (m_recursionDepth > kMaxRecursionDepth) {
151 setError(FileError::SECURITY_ERR, exceptionState);
155 m_readyState = WRITING;
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;
165 doOperation(OperationTruncate);
166 fireEvent(EventTypeNames::writestart);
169 void FileWriter::abort(ExceptionState& exceptionState)
172 if (m_readyState != WRITING)
176 doOperation(OperationAbort);
177 signalCompletion(FileError::ABORT_ERR);
180 void FileWriter::didWrite(long long bytes, bool complete)
182 if (m_operationInProgress == OperationAbort) {
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());
197 m_blobBeingWritten.clear();
198 m_operationInProgress = OperationNone;
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);
211 if (numAborts == m_numAborts)
212 signalCompletion(FileError::OK);
216 void FileWriter::didTruncate()
218 if (m_operationInProgress == OperationAbort) {
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);
231 void FileWriter::didFail(WebFileError code)
233 ASSERT(m_operationInProgress != OperationNone);
234 ASSERT(static_cast<FileError::ErrorCode>(code) != FileError::OK);
235 if (m_operationInProgress == OperationAbort) {
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));
246 void FileWriter::completeAbort()
248 ASSERT(m_operationInProgress == OperationAbort);
249 m_operationInProgress = OperationNone;
250 Operation operation = m_queuedOperation;
251 m_queuedOperation = OperationNone;
252 doOperation(operation);
255 void FileWriter::doOperation(Operation operation)
257 m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(executionContext(), "FileWriter", m_asyncOperationId);
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());
266 case OperationTruncate:
267 ASSERT(m_operationInProgress == OperationNone);
268 ASSERT(m_truncateLength >= 0);
269 ASSERT(m_readyState == WRITING);
270 writer()->truncate(m_truncateLength);
273 ASSERT(m_operationInProgress == OperationNone);
274 ASSERT(m_truncateLength == -1);
275 ASSERT(!m_blobBeingWritten.get());
276 ASSERT(m_readyState == DONE);
279 if (m_operationInProgress == OperationWrite || m_operationInProgress == OperationTruncate)
281 else if (m_operationInProgress != OperationAbort)
282 operation = OperationNone;
283 m_queuedOperation = OperationNone;
284 m_blobBeingWritten.clear();
285 m_truncateLength = -1;
288 ASSERT(m_queuedOperation == OperationNone);
289 m_operationInProgress = operation;
292 void FileWriter::signalCompletion(FileError::ErrorCode code)
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);
301 fireEvent(EventTypeNames::error);
303 fireEvent(EventTypeNames::write);
304 fireEvent(EventTypeNames::writeend);
306 InspectorInstrumentation::traceAsyncOperationCompleted(executionContext(), m_asyncOperationId);
307 m_asyncOperationId = 0;
310 void FileWriter::fireEvent(const AtomicString& type)
312 InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(executionContext(), m_asyncOperationId);
314 dispatchEvent(ProgressEvent::create(type, true, m_bytesWritten, m_bytesToWrite));
316 ASSERT(m_recursionDepth >= 0);
317 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
320 void FileWriter::setError(FileError::ErrorCode errorCode, ExceptionState& exceptionState)
323 FileError::throwDOMException(exceptionState, errorCode);
324 m_error = FileError::create(errorCode);
327 void FileWriter::trace(Visitor* visitor)
329 visitor->trace(m_error);
330 visitor->trace(m_blobBeingWritten);
331 FileWriterBase::trace(visitor);
332 EventTargetWithInlineData::trace(visitor);