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
= 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
)
99 ASSERT(m_truncateLength
== -1);
100 if (m_readyState
== WRITING
) {
101 setError(FileError::INVALID_STATE_ERR
, exceptionState
);
105 setError(FileError::TYPE_MISMATCH_ERR
, exceptionState
);
108 if (m_recursionDepth
> kMaxRecursionDepth
) {
109 setError(FileError::SECURITY_ERR
, exceptionState
);
113 m_blobBeingWritten
= data
;
114 m_readyState
= WRITING
;
116 m_bytesToWrite
= data
->size();
117 ASSERT(m_queuedOperation
== OperationNone
);
118 if (m_operationInProgress
!= OperationNone
) {
119 // We must be waiting for an abort to complete, since m_readyState wasn't WRITING.
120 ASSERT(m_operationInProgress
== OperationAbort
);
121 m_queuedOperation
= OperationWrite
;
123 doOperation(OperationWrite
);
125 fireEvent(EventTypeNames::writestart
);
128 void FileWriter::seek(long long position
, ExceptionState
& exceptionState
)
131 if (m_readyState
== WRITING
) {
132 setError(FileError::INVALID_STATE_ERR
, exceptionState
);
136 ASSERT(m_truncateLength
== -1);
137 ASSERT(m_queuedOperation
== OperationNone
);
138 seekInternal(position
);
141 void FileWriter::truncate(long long position
, ExceptionState
& exceptionState
)
144 ASSERT(m_truncateLength
== -1);
145 if (m_readyState
== WRITING
|| position
< 0) {
146 setError(FileError::INVALID_STATE_ERR
, exceptionState
);
149 if (m_recursionDepth
> kMaxRecursionDepth
) {
150 setError(FileError::SECURITY_ERR
, exceptionState
);
154 m_readyState
= WRITING
;
157 m_truncateLength
= position
;
158 ASSERT(m_queuedOperation
== OperationNone
);
159 if (m_operationInProgress
!= OperationNone
) {
160 // We must be waiting for an abort to complete, since m_readyState wasn't WRITING.
161 ASSERT(m_operationInProgress
== OperationAbort
);
162 m_queuedOperation
= OperationTruncate
;
164 doOperation(OperationTruncate
);
165 fireEvent(EventTypeNames::writestart
);
168 void FileWriter::abort(ExceptionState
& exceptionState
)
171 if (m_readyState
!= WRITING
)
175 doOperation(OperationAbort
);
176 signalCompletion(FileError::ABORT_ERR
);
179 void FileWriter::didWrite(long long bytes
, bool complete
)
181 if (m_operationInProgress
== OperationAbort
) {
185 ASSERT(m_readyState
== WRITING
);
186 ASSERT(m_truncateLength
== -1);
187 ASSERT(m_operationInProgress
== OperationWrite
);
188 ASSERT(!m_bytesToWrite
|| bytes
+ m_bytesWritten
> 0);
189 ASSERT(bytes
+ m_bytesWritten
<= m_bytesToWrite
);
190 m_bytesWritten
+= bytes
;
191 ASSERT((m_bytesWritten
== m_bytesToWrite
) || !complete
);
192 setPosition(position() + bytes
);
193 if (position() > length())
194 setLength(position());
196 m_blobBeingWritten
.clear();
197 m_operationInProgress
= OperationNone
;
200 int numAborts
= m_numAborts
;
201 // We could get an abort in the handler for this event. If we do, it's
202 // already handled the cleanup and signalCompletion call.
203 double now
= currentTimeMS();
204 if (complete
|| !m_lastProgressNotificationTimeMS
|| (now
- m_lastProgressNotificationTimeMS
> progressNotificationIntervalMS
)) {
205 m_lastProgressNotificationTimeMS
= now
;
206 fireEvent(EventTypeNames::progress
);
210 if (numAborts
== m_numAborts
)
211 signalCompletion(FileError::OK
);
215 void FileWriter::didTruncate()
217 if (m_operationInProgress
== OperationAbort
) {
221 ASSERT(m_operationInProgress
== OperationTruncate
);
222 ASSERT(m_truncateLength
>= 0);
223 setLength(m_truncateLength
);
224 if (position() > length())
225 setPosition(length());
226 m_operationInProgress
= OperationNone
;
227 signalCompletion(FileError::OK
);
230 void FileWriter::didFail(WebFileError code
)
232 ASSERT(m_operationInProgress
!= OperationNone
);
233 ASSERT(static_cast<FileError::ErrorCode
>(code
) != FileError::OK
);
234 if (m_operationInProgress
== OperationAbort
) {
238 ASSERT(m_queuedOperation
== OperationNone
);
239 ASSERT(m_readyState
== WRITING
);
240 m_blobBeingWritten
.clear();
241 m_operationInProgress
= OperationNone
;
242 signalCompletion(static_cast<FileError::ErrorCode
>(code
));
245 void FileWriter::completeAbort()
247 ASSERT(m_operationInProgress
== OperationAbort
);
248 m_operationInProgress
= OperationNone
;
249 Operation operation
= m_queuedOperation
;
250 m_queuedOperation
= OperationNone
;
251 doOperation(operation
);
254 void FileWriter::doOperation(Operation operation
)
256 m_asyncOperationId
= InspectorInstrumentation::traceAsyncOperationStarting(executionContext(), "FileWriter", m_asyncOperationId
);
259 ASSERT(m_operationInProgress
== OperationNone
);
260 ASSERT(m_truncateLength
== -1);
261 ASSERT(m_blobBeingWritten
.get());
262 ASSERT(m_readyState
== WRITING
);
263 writer()->write(position(), m_blobBeingWritten
->uuid());
265 case OperationTruncate
:
266 ASSERT(m_operationInProgress
== OperationNone
);
267 ASSERT(m_truncateLength
>= 0);
268 ASSERT(m_readyState
== WRITING
);
269 writer()->truncate(m_truncateLength
);
272 ASSERT(m_operationInProgress
== OperationNone
);
273 ASSERT(m_truncateLength
== -1);
274 ASSERT(!m_blobBeingWritten
.get());
275 ASSERT(m_readyState
== DONE
);
278 if (m_operationInProgress
== OperationWrite
|| m_operationInProgress
== OperationTruncate
)
280 else if (m_operationInProgress
!= OperationAbort
)
281 operation
= OperationNone
;
282 m_queuedOperation
= OperationNone
;
283 m_blobBeingWritten
.clear();
284 m_truncateLength
= -1;
287 ASSERT(m_queuedOperation
== OperationNone
);
288 m_operationInProgress
= operation
;
291 void FileWriter::signalCompletion(FileError::ErrorCode code
)
294 m_truncateLength
= -1;
295 if (FileError::OK
!= code
) {
296 m_error
= FileError::create(code
);
297 if (FileError::ABORT_ERR
== code
)
298 fireEvent(EventTypeNames::abort
);
300 fireEvent(EventTypeNames::error
);
302 fireEvent(EventTypeNames::write
);
303 fireEvent(EventTypeNames::writeend
);
305 InspectorInstrumentation::traceAsyncOperationCompleted(executionContext(), m_asyncOperationId
);
306 m_asyncOperationId
= 0;
309 void FileWriter::fireEvent(const AtomicString
& type
)
311 InspectorInstrumentationCookie cookie
= InspectorInstrumentation::traceAsyncCallbackStarting(executionContext(), m_asyncOperationId
);
313 dispatchEvent(ProgressEvent::create(type
, true, m_bytesWritten
, m_bytesToWrite
));
315 ASSERT(m_recursionDepth
>= 0);
316 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie
);
319 void FileWriter::setError(FileError::ErrorCode errorCode
, ExceptionState
& exceptionState
)
322 FileError::throwDOMException(exceptionState
, errorCode
);
323 m_error
= FileError::create(errorCode
);
326 DEFINE_TRACE(FileWriter
)
328 visitor
->trace(m_error
);
329 visitor
->trace(m_blobBeingWritten
);
330 EventTargetWithInlineData::trace(visitor
);
331 FileWriterBase::trace(visitor
);
332 ActiveDOMObject::trace(visitor
);