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)
68 ScriptWrappable::init(this);
71 FileWriter::~FileWriter()
73 ASSERT(!m_recursionDepth);
74 if (m_readyState == WRITING)
78 const AtomicString& FileWriter::interfaceName() const
80 return EventTargetNames::FileWriter;
83 void FileWriter::stop()
85 // Make sure we've actually got something to stop, and haven't already called abort().
86 if (!writer() || m_readyState != WRITING)
88 doOperation(OperationAbort);
92 bool FileWriter::hasPendingActivity() const
94 return m_operationInProgress != OperationNone || m_queuedOperation != OperationNone || m_readyState == WRITING;
97 void FileWriter::write(Blob* data, ExceptionState& exceptionState)
101 ASSERT(m_truncateLength == -1);
102 if (m_readyState == WRITING) {
103 setError(FileError::INVALID_STATE_ERR, exceptionState);
107 setError(FileError::TYPE_MISMATCH_ERR, exceptionState);
110 if (m_recursionDepth > kMaxRecursionDepth) {
111 setError(FileError::SECURITY_ERR, exceptionState);
115 m_blobBeingWritten = data;
116 m_readyState = WRITING;
118 m_bytesToWrite = data->size();
119 ASSERT(m_queuedOperation == OperationNone);
120 if (m_operationInProgress != OperationNone) {
121 // We must be waiting for an abort to complete, since m_readyState wasn't WRITING.
122 ASSERT(m_operationInProgress == OperationAbort);
123 m_queuedOperation = OperationWrite;
125 doOperation(OperationWrite);
127 fireEvent(EventTypeNames::writestart);
130 void FileWriter::seek(long long position, ExceptionState& exceptionState)
133 if (m_readyState == WRITING) {
134 setError(FileError::INVALID_STATE_ERR, exceptionState);
138 ASSERT(m_truncateLength == -1);
139 ASSERT(m_queuedOperation == OperationNone);
140 seekInternal(position);
143 void FileWriter::truncate(long long position, ExceptionState& exceptionState)
146 ASSERT(m_truncateLength == -1);
147 if (m_readyState == WRITING || position < 0) {
148 setError(FileError::INVALID_STATE_ERR, exceptionState);
151 if (m_recursionDepth > kMaxRecursionDepth) {
152 setError(FileError::SECURITY_ERR, exceptionState);
156 m_readyState = WRITING;
159 m_truncateLength = position;
160 ASSERT(m_queuedOperation == OperationNone);
161 if (m_operationInProgress != OperationNone) {
162 // We must be waiting for an abort to complete, since m_readyState wasn't WRITING.
163 ASSERT(m_operationInProgress == OperationAbort);
164 m_queuedOperation = OperationTruncate;
166 doOperation(OperationTruncate);
167 fireEvent(EventTypeNames::writestart);
170 void FileWriter::abort(ExceptionState& exceptionState)
173 if (m_readyState != WRITING)
177 doOperation(OperationAbort);
178 signalCompletion(FileError::ABORT_ERR);
181 void FileWriter::didWrite(long long bytes, bool complete)
183 if (m_operationInProgress == OperationAbort) {
187 ASSERT(m_readyState == WRITING);
188 ASSERT(m_truncateLength == -1);
189 ASSERT(m_operationInProgress == OperationWrite);
190 ASSERT(!m_bytesToWrite || bytes + m_bytesWritten > 0);
191 ASSERT(bytes + m_bytesWritten <= m_bytesToWrite);
192 m_bytesWritten += bytes;
193 ASSERT((m_bytesWritten == m_bytesToWrite) || !complete);
194 setPosition(position() + bytes);
195 if (position() > length())
196 setLength(position());
198 m_blobBeingWritten.clear();
199 m_operationInProgress = OperationNone;
202 int numAborts = m_numAborts;
203 // We could get an abort in the handler for this event. If we do, it's
204 // already handled the cleanup and signalCompletion call.
205 double now = currentTimeMS();
206 if (complete || !m_lastProgressNotificationTimeMS || (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS)) {
207 m_lastProgressNotificationTimeMS = now;
208 fireEvent(EventTypeNames::progress);
212 if (numAborts == m_numAborts)
213 signalCompletion(FileError::OK);
217 void FileWriter::didTruncate()
219 if (m_operationInProgress == OperationAbort) {
223 ASSERT(m_operationInProgress == OperationTruncate);
224 ASSERT(m_truncateLength >= 0);
225 setLength(m_truncateLength);
226 if (position() > length())
227 setPosition(length());
228 m_operationInProgress = OperationNone;
229 signalCompletion(FileError::OK);
232 void FileWriter::didFail(WebFileError code)
234 ASSERT(m_operationInProgress != OperationNone);
235 ASSERT(static_cast<FileError::ErrorCode>(code) != FileError::OK);
236 if (m_operationInProgress == OperationAbort) {
240 ASSERT(m_queuedOperation == OperationNone);
241 ASSERT(m_readyState == WRITING);
242 m_blobBeingWritten.clear();
243 m_operationInProgress = OperationNone;
244 signalCompletion(static_cast<FileError::ErrorCode>(code));
247 void FileWriter::completeAbort()
249 ASSERT(m_operationInProgress == OperationAbort);
250 m_operationInProgress = OperationNone;
251 Operation operation = m_queuedOperation;
252 m_queuedOperation = OperationNone;
253 doOperation(operation);
256 void FileWriter::doOperation(Operation operation)
258 m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(executionContext(), "FileWriter", m_asyncOperationId);
261 ASSERT(m_operationInProgress == OperationNone);
262 ASSERT(m_truncateLength == -1);
263 ASSERT(m_blobBeingWritten.get());
264 ASSERT(m_readyState == WRITING);
265 writer()->write(position(), m_blobBeingWritten->uuid());
267 case OperationTruncate:
268 ASSERT(m_operationInProgress == OperationNone);
269 ASSERT(m_truncateLength >= 0);
270 ASSERT(m_readyState == WRITING);
271 writer()->truncate(m_truncateLength);
274 ASSERT(m_operationInProgress == OperationNone);
275 ASSERT(m_truncateLength == -1);
276 ASSERT(!m_blobBeingWritten.get());
277 ASSERT(m_readyState == DONE);
280 if (m_operationInProgress == OperationWrite || m_operationInProgress == OperationTruncate)
282 else if (m_operationInProgress != OperationAbort)
283 operation = OperationNone;
284 m_queuedOperation = OperationNone;
285 m_blobBeingWritten.clear();
286 m_truncateLength = -1;
289 ASSERT(m_queuedOperation == OperationNone);
290 m_operationInProgress = operation;
293 void FileWriter::signalCompletion(FileError::ErrorCode code)
296 m_truncateLength = -1;
297 if (FileError::OK != code) {
298 m_error = FileError::create(code);
299 if (FileError::ABORT_ERR == code)
300 fireEvent(EventTypeNames::abort);
302 fireEvent(EventTypeNames::error);
304 fireEvent(EventTypeNames::write);
305 fireEvent(EventTypeNames::writeend);
307 InspectorInstrumentation::traceAsyncOperationCompleted(executionContext(), m_asyncOperationId);
308 m_asyncOperationId = 0;
311 void FileWriter::fireEvent(const AtomicString& type)
313 InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(executionContext(), m_asyncOperationId);
315 dispatchEvent(ProgressEvent::create(type, true, m_bytesWritten, m_bytesToWrite));
317 ASSERT(m_recursionDepth >= 0);
318 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
321 void FileWriter::setError(FileError::ErrorCode errorCode, ExceptionState& exceptionState)
324 FileError::throwDOMException(exceptionState, errorCode);
325 m_error = FileError::create(errorCode);
328 void FileWriter::trace(Visitor* visitor)
330 visitor->trace(m_error);
331 visitor->trace(m_blobBeingWritten);
332 FileWriterBase::trace(visitor);
333 EventTargetWithInlineData::trace(visitor);