Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / modules / filesystem / FileWriter.cpp
blobbf9f657e32ffa9d82ac0cae0414546e328b64929
1 /*
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
6 * met:
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.
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.
31 #include "config.h"
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"
43 namespace blink {
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();
52 return fileWriter;
55 FileWriter::FileWriter(ExecutionContext* context)
56 : ActiveDOMObject(context)
57 , m_readyState(INIT)
58 , m_operationInProgress(OperationNone)
59 , m_queuedOperation(OperationNone)
60 , m_bytesWritten(0)
61 , m_bytesToWrite(0)
62 , m_truncateLength(-1)
63 , m_numAborts(0)
64 , m_recursionDepth(0)
65 , m_lastProgressNotificationTimeMS(0)
66 , m_asyncOperationId(0)
70 FileWriter::~FileWriter()
72 ASSERT(!m_recursionDepth);
73 if (m_readyState == WRITING)
74 stop();
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)
86 return;
87 doOperation(OperationAbort);
88 m_readyState = DONE;
91 bool FileWriter::hasPendingActivity() const
93 return m_operationInProgress != OperationNone || m_queuedOperation != OperationNone || m_readyState == WRITING;
96 void FileWriter::write(Blob* data, ExceptionState& exceptionState)
98 ASSERT(writer());
99 ASSERT(m_truncateLength == -1);
100 if (m_readyState == WRITING) {
101 setError(FileError::INVALID_STATE_ERR, exceptionState);
102 return;
104 if (!data) {
105 setError(FileError::TYPE_MISMATCH_ERR, exceptionState);
106 return;
108 if (m_recursionDepth > kMaxRecursionDepth) {
109 setError(FileError::SECURITY_ERR, exceptionState);
110 return;
113 m_blobBeingWritten = data;
114 m_readyState = WRITING;
115 m_bytesWritten = 0;
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;
122 } else
123 doOperation(OperationWrite);
125 fireEvent(EventTypeNames::writestart);
128 void FileWriter::seek(long long position, ExceptionState& exceptionState)
130 ASSERT(writer());
131 if (m_readyState == WRITING) {
132 setError(FileError::INVALID_STATE_ERR, exceptionState);
133 return;
136 ASSERT(m_truncateLength == -1);
137 ASSERT(m_queuedOperation == OperationNone);
138 seekInternal(position);
141 void FileWriter::truncate(long long position, ExceptionState& exceptionState)
143 ASSERT(writer());
144 ASSERT(m_truncateLength == -1);
145 if (m_readyState == WRITING || position < 0) {
146 setError(FileError::INVALID_STATE_ERR, exceptionState);
147 return;
149 if (m_recursionDepth > kMaxRecursionDepth) {
150 setError(FileError::SECURITY_ERR, exceptionState);
151 return;
154 m_readyState = WRITING;
155 m_bytesWritten = 0;
156 m_bytesToWrite = 0;
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;
163 } else
164 doOperation(OperationTruncate);
165 fireEvent(EventTypeNames::writestart);
168 void FileWriter::abort(ExceptionState& exceptionState)
170 ASSERT(writer());
171 if (m_readyState != WRITING)
172 return;
173 ++m_numAborts;
175 doOperation(OperationAbort);
176 signalCompletion(FileError::ABORT_ERR);
179 void FileWriter::didWrite(long long bytes, bool complete)
181 if (m_operationInProgress == OperationAbort) {
182 completeAbort();
183 return;
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());
195 if (complete) {
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);
209 if (complete) {
210 if (numAborts == m_numAborts)
211 signalCompletion(FileError::OK);
215 void FileWriter::didTruncate()
217 if (m_operationInProgress == OperationAbort) {
218 completeAbort();
219 return;
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) {
235 completeAbort();
236 return;
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);
257 switch (operation) {
258 case OperationWrite:
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());
264 break;
265 case OperationTruncate:
266 ASSERT(m_operationInProgress == OperationNone);
267 ASSERT(m_truncateLength >= 0);
268 ASSERT(m_readyState == WRITING);
269 writer()->truncate(m_truncateLength);
270 break;
271 case OperationNone:
272 ASSERT(m_operationInProgress == OperationNone);
273 ASSERT(m_truncateLength == -1);
274 ASSERT(!m_blobBeingWritten.get());
275 ASSERT(m_readyState == DONE);
276 break;
277 case OperationAbort:
278 if (m_operationInProgress == OperationWrite || m_operationInProgress == OperationTruncate)
279 writer()->cancel();
280 else if (m_operationInProgress != OperationAbort)
281 operation = OperationNone;
282 m_queuedOperation = OperationNone;
283 m_blobBeingWritten.clear();
284 m_truncateLength = -1;
285 break;
287 ASSERT(m_queuedOperation == OperationNone);
288 m_operationInProgress = operation;
291 void FileWriter::signalCompletion(FileError::ErrorCode code)
293 m_readyState = DONE;
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);
299 else
300 fireEvent(EventTypeNames::error);
301 } else
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);
312 ++m_recursionDepth;
313 dispatchEvent(ProgressEvent::create(type, true, m_bytesWritten, m_bytesToWrite));
314 --m_recursionDepth;
315 ASSERT(m_recursionDepth >= 0);
316 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
319 void FileWriter::setError(FileError::ErrorCode errorCode, ExceptionState& exceptionState)
321 ASSERT(errorCode);
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);
335 } // namespace blink