Upstream version 9.38.198.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     ScriptWrappable::init(this);
69 }
70
71 FileWriter::~FileWriter()
72 {
73     ASSERT(!m_recursionDepth);
74     if (m_readyState == WRITING)
75         stop();
76 }
77
78 const AtomicString& FileWriter::interfaceName() const
79 {
80     return EventTargetNames::FileWriter;
81 }
82
83 void FileWriter::stop()
84 {
85     // Make sure we've actually got something to stop, and haven't already called abort().
86     if (!writer() || m_readyState != WRITING)
87         return;
88     doOperation(OperationAbort);
89     m_readyState = DONE;
90 }
91
92 bool FileWriter::hasPendingActivity() const
93 {
94     return m_operationInProgress != OperationNone || m_queuedOperation != OperationNone || m_readyState == WRITING;
95 }
96
97 void FileWriter::write(Blob* data, ExceptionState& exceptionState)
98 {
99     ASSERT(writer());
100     ASSERT(data);
101     ASSERT(m_truncateLength == -1);
102     if (m_readyState == WRITING) {
103         setError(FileError::INVALID_STATE_ERR, exceptionState);
104         return;
105     }
106     if (!data) {
107         setError(FileError::TYPE_MISMATCH_ERR, exceptionState);
108         return;
109     }
110     if (m_recursionDepth > kMaxRecursionDepth) {
111         setError(FileError::SECURITY_ERR, exceptionState);
112         return;
113     }
114
115     m_blobBeingWritten = data;
116     m_readyState = WRITING;
117     m_bytesWritten = 0;
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;
124     } else
125         doOperation(OperationWrite);
126
127     fireEvent(EventTypeNames::writestart);
128 }
129
130 void FileWriter::seek(long long position, ExceptionState& exceptionState)
131 {
132     ASSERT(writer());
133     if (m_readyState == WRITING) {
134         setError(FileError::INVALID_STATE_ERR, exceptionState);
135         return;
136     }
137
138     ASSERT(m_truncateLength == -1);
139     ASSERT(m_queuedOperation == OperationNone);
140     seekInternal(position);
141 }
142
143 void FileWriter::truncate(long long position, ExceptionState& exceptionState)
144 {
145     ASSERT(writer());
146     ASSERT(m_truncateLength == -1);
147     if (m_readyState == WRITING || position < 0) {
148         setError(FileError::INVALID_STATE_ERR, exceptionState);
149         return;
150     }
151     if (m_recursionDepth > kMaxRecursionDepth) {
152         setError(FileError::SECURITY_ERR, exceptionState);
153         return;
154     }
155
156     m_readyState = WRITING;
157     m_bytesWritten = 0;
158     m_bytesToWrite = 0;
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;
165     } else
166         doOperation(OperationTruncate);
167     fireEvent(EventTypeNames::writestart);
168 }
169
170 void FileWriter::abort(ExceptionState& exceptionState)
171 {
172     ASSERT(writer());
173     if (m_readyState != WRITING)
174         return;
175     ++m_numAborts;
176
177     doOperation(OperationAbort);
178     signalCompletion(FileError::ABORT_ERR);
179 }
180
181 void FileWriter::didWrite(long long bytes, bool complete)
182 {
183     if (m_operationInProgress == OperationAbort) {
184         completeAbort();
185         return;
186     }
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());
197     if (complete) {
198         m_blobBeingWritten.clear();
199         m_operationInProgress = OperationNone;
200     }
201
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);
209     }
210
211     if (complete) {
212       if (numAborts == m_numAborts)
213           signalCompletion(FileError::OK);
214     }
215 }
216
217 void FileWriter::didTruncate()
218 {
219     if (m_operationInProgress == OperationAbort) {
220         completeAbort();
221         return;
222     }
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);
230 }
231
232 void FileWriter::didFail(WebFileError code)
233 {
234     ASSERT(m_operationInProgress != OperationNone);
235     ASSERT(static_cast<FileError::ErrorCode>(code) != FileError::OK);
236     if (m_operationInProgress == OperationAbort) {
237         completeAbort();
238         return;
239     }
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));
245 }
246
247 void FileWriter::completeAbort()
248 {
249     ASSERT(m_operationInProgress == OperationAbort);
250     m_operationInProgress = OperationNone;
251     Operation operation = m_queuedOperation;
252     m_queuedOperation = OperationNone;
253     doOperation(operation);
254 }
255
256 void FileWriter::doOperation(Operation operation)
257 {
258     m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(executionContext(), "FileWriter", m_asyncOperationId);
259     switch (operation) {
260     case OperationWrite:
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());
266         break;
267     case OperationTruncate:
268         ASSERT(m_operationInProgress == OperationNone);
269         ASSERT(m_truncateLength >= 0);
270         ASSERT(m_readyState == WRITING);
271         writer()->truncate(m_truncateLength);
272         break;
273     case OperationNone:
274         ASSERT(m_operationInProgress == OperationNone);
275         ASSERT(m_truncateLength == -1);
276         ASSERT(!m_blobBeingWritten.get());
277         ASSERT(m_readyState == DONE);
278         break;
279     case OperationAbort:
280         if (m_operationInProgress == OperationWrite || m_operationInProgress == OperationTruncate)
281             writer()->cancel();
282         else if (m_operationInProgress != OperationAbort)
283             operation = OperationNone;
284         m_queuedOperation = OperationNone;
285         m_blobBeingWritten.clear();
286         m_truncateLength = -1;
287         break;
288     }
289     ASSERT(m_queuedOperation == OperationNone);
290     m_operationInProgress = operation;
291 }
292
293 void FileWriter::signalCompletion(FileError::ErrorCode code)
294 {
295     m_readyState = DONE;
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);
301         else
302             fireEvent(EventTypeNames::error);
303     } else
304         fireEvent(EventTypeNames::write);
305     fireEvent(EventTypeNames::writeend);
306
307     InspectorInstrumentation::traceAsyncOperationCompleted(executionContext(), m_asyncOperationId);
308     m_asyncOperationId = 0;
309 }
310
311 void FileWriter::fireEvent(const AtomicString& type)
312 {
313     InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(executionContext(), m_asyncOperationId);
314     ++m_recursionDepth;
315     dispatchEvent(ProgressEvent::create(type, true, m_bytesWritten, m_bytesToWrite));
316     --m_recursionDepth;
317     ASSERT(m_recursionDepth >= 0);
318     InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
319 }
320
321 void FileWriter::setError(FileError::ErrorCode errorCode, ExceptionState& exceptionState)
322 {
323     ASSERT(errorCode);
324     FileError::throwDOMException(exceptionState, errorCode);
325     m_error = FileError::create(errorCode);
326 }
327
328 void FileWriter::trace(Visitor* visitor)
329 {
330     visitor->trace(m_error);
331     visitor->trace(m_blobBeingWritten);
332     FileWriterBase::trace(visitor);
333     EventTargetWithInlineData::trace(visitor);
334 }
335
336 } // namespace blink