Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / filesystem / FileWriter.cpp
1 /*
2  * Copyright (C) 2010 Google Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
17  *
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.
29  */
30
31 #include "config.h"
32 #include "modules/filesystem/FileWriter.h"
33
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"
42
43 namespace blink {
44
45 static const int kMaxRecursionDepth = 3;
46 static const double progressNotificationIntervalMS = 50;
47
48 FileWriter* FileWriter::create(ExecutionContext* context)
49 {
50     FileWriter* fileWriter = adoptRefCountedGarbageCollectedWillBeNoop(new FileWriter(context));
51     fileWriter->suspendIfNeeded();
52     return fileWriter;
53 }
54
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)
67 {
68 }
69
70 FileWriter::~FileWriter()
71 {
72     ASSERT(!m_recursionDepth);
73     if (m_readyState == WRITING)
74         stop();
75 }
76
77 const AtomicString& FileWriter::interfaceName() const
78 {
79     return EventTargetNames::FileWriter;
80 }
81
82 void 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
91 bool FileWriter::hasPendingActivity() const
92 {
93     return m_operationInProgress != OperationNone || m_queuedOperation != OperationNone || m_readyState == WRITING;
94 }
95
96 void 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
129 void 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
142 void 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
169 void 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
180 void 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
216 void 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
231 void 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
246 void 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
255 void 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
292 void 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
310 void 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
320 void FileWriter::setError(FileError::ErrorCode errorCode, ExceptionState& exceptionState)
321 {
322     ASSERT(errorCode);
323     FileError::throwDOMException(exceptionState, errorCode);
324     m_error = FileError::create(errorCode);
325 }
326
327 void 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