Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / fileapi / FileReader.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 "core/fileapi/FileReader.h"
33
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "core/dom/CrossThreadTask.h"
36 #include "core/dom/Document.h"
37 #include "core/dom/ExceptionCode.h"
38 #include "core/dom/ExecutionContext.h"
39 #include "core/events/ProgressEvent.h"
40 #include "core/fileapi/File.h"
41 #include "core/frame/LocalFrame.h"
42 #include "core/inspector/InspectorInstrumentation.h"
43 #include "core/workers/WorkerClients.h"
44 #include "core/workers/WorkerGlobalScope.h"
45 #include "platform/Logging.h"
46 #include "platform/Supplementable.h"
47 #include "wtf/ArrayBuffer.h"
48 #include "wtf/CurrentTime.h"
49 #include "wtf/Deque.h"
50 #include "wtf/HashSet.h"
51 #include "wtf/text/CString.h"
52
53 namespace blink {
54
55 namespace {
56
57 #if !LOG_DISABLED
58 const CString utf8BlobUUID(Blob* blob)
59 {
60     return blob->uuid().utf8();
61 }
62
63 const CString utf8FilePath(Blob* blob)
64 {
65     return blob->hasBackingFile() ? toFile(blob)->path().utf8() : "";
66 }
67 #endif
68
69 } // namespace
70
71 // Embedders like chromium limit the number of simultaneous requests to avoid
72 // excessive IPC congestion. We limit this to 100 per thread to throttle the
73 // requests (the value is arbitrarily chosen).
74 static const size_t kMaxOutstandingRequestsPerThread = 100;
75 static const double progressNotificationIntervalMS = 50;
76
77 typedef PersistentHeapDequeWillBeHeapDeque<Member<FileReader> > FileReaderDeque;
78 typedef PersistentHeapHashSetWillBeHeapHashSet<Member<FileReader> > FileReaderHashSet;
79
80 // FIXME: Oilpan: if ExecutionContext is moved to the heap, consider
81 // making this object an ExecutionContext supplement (only.)
82 class FileReader::ThrottlingController final : public NoBaseWillBeGarbageCollectedFinalized<FileReader::ThrottlingController>, public WillBeHeapSupplement<LocalFrame>, public WillBeHeapSupplement<WorkerClients> {
83     WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(FileReader::ThrottlingController);
84 public:
85     static ThrottlingController* from(ExecutionContext* context)
86     {
87         if (!context)
88             return 0;
89
90         if (context->isDocument()) {
91             Document* document = toDocument(context);
92             if (!document->frame())
93                 return 0;
94
95             ThrottlingController* controller = static_cast<ThrottlingController*>(WillBeHeapSupplement<LocalFrame>::from(document->frame(), supplementName()));
96             if (controller)
97                 return controller;
98
99             controller = new ThrottlingController();
100             WillBeHeapSupplement<LocalFrame>::provideTo(*document->frame(), supplementName(), adoptPtrWillBeNoop(controller));
101             return controller;
102         }
103         ASSERT(!isMainThread());
104         ASSERT(context->isWorkerGlobalScope());
105         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
106         ThrottlingController* controller = static_cast<ThrottlingController*>(WillBeHeapSupplement<WorkerClients>::from(workerGlobalScope->clients(), supplementName()));
107         if (controller)
108             return controller;
109
110         controller = new ThrottlingController();
111         WillBeHeapSupplement<WorkerClients>::provideTo(*workerGlobalScope->clients(), supplementName(), adoptPtrWillBeNoop(controller));
112         return controller;
113     }
114
115     ~ThrottlingController() { }
116
117     enum FinishReaderType { DoNotRunPendingReaders, RunPendingReaders };
118
119     static void pushReader(ExecutionContext* context, FileReader* reader)
120     {
121         ThrottlingController* controller = from(context);
122         if (!controller)
123             return;
124
125         reader->m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(context, "FileReader");
126         controller->pushReader(reader);
127     }
128
129     static FinishReaderType removeReader(ExecutionContext* context, FileReader* reader)
130     {
131         ThrottlingController* controller = from(context);
132         if (!controller)
133             return DoNotRunPendingReaders;
134
135         return controller->removeReader(reader);
136     }
137
138     static void finishReader(ExecutionContext* context, FileReader* reader, FinishReaderType nextStep)
139     {
140         InspectorInstrumentation::traceAsyncOperationCompleted(context, reader->m_asyncOperationId);
141
142         ThrottlingController* controller = from(context);
143         if (!controller)
144             return;
145
146         controller->finishReader(reader, nextStep);
147     }
148
149     void trace(Visitor* visitor)
150     {
151 #if ENABLE(OILPAN)
152         visitor->trace(m_pendingReaders);
153         visitor->trace(m_runningReaders);
154 #endif
155         WillBeHeapSupplement<LocalFrame>::trace(visitor);
156         WillBeHeapSupplement<WorkerClients>::trace(visitor);
157     }
158
159 private:
160     ThrottlingController()
161         : m_maxRunningReaders(kMaxOutstandingRequestsPerThread)
162     {
163     }
164
165     void pushReader(FileReader* reader)
166     {
167         if (m_pendingReaders.isEmpty()
168             && m_runningReaders.size() < m_maxRunningReaders) {
169             reader->executePendingRead();
170             ASSERT(!m_runningReaders.contains(reader));
171             m_runningReaders.add(reader);
172             return;
173         }
174         m_pendingReaders.append(reader);
175         executeReaders();
176     }
177
178     FinishReaderType removeReader(FileReader* reader)
179     {
180         FileReaderHashSet::const_iterator hashIter = m_runningReaders.find(reader);
181         if (hashIter != m_runningReaders.end()) {
182             m_runningReaders.remove(hashIter);
183             return RunPendingReaders;
184         }
185         FileReaderDeque::const_iterator dequeEnd = m_pendingReaders.end();
186         for (FileReaderDeque::const_iterator it = m_pendingReaders.begin(); it != dequeEnd; ++it) {
187             if (*it == reader) {
188                 m_pendingReaders.remove(it);
189                 break;
190             }
191         }
192         return DoNotRunPendingReaders;
193     }
194
195     void finishReader(FileReader* reader, FinishReaderType nextStep)
196     {
197         if (nextStep == RunPendingReaders)
198             executeReaders();
199     }
200
201     void executeReaders()
202     {
203         while (m_runningReaders.size() < m_maxRunningReaders) {
204             if (m_pendingReaders.isEmpty())
205                 return;
206             FileReader* reader = m_pendingReaders.takeFirst();
207             reader->executePendingRead();
208             m_runningReaders.add(reader);
209         }
210     }
211
212     static const char* supplementName() { return "FileReaderThrottlingController"; }
213
214     const size_t m_maxRunningReaders;
215     FileReaderDeque m_pendingReaders;
216     FileReaderHashSet m_runningReaders;
217 };
218
219 FileReader* FileReader::create(ExecutionContext* context)
220 {
221     FileReader* fileReader = new FileReader(context);
222     fileReader->suspendIfNeeded();
223     return fileReader;
224 }
225
226 FileReader::FileReader(ExecutionContext* context)
227     : ActiveDOMObject(context)
228     , m_state(EMPTY)
229     , m_loadingState(LoadingStateNone)
230     , m_readType(FileReaderLoader::ReadAsBinaryString)
231     , m_lastProgressNotificationTimeMS(0)
232     , m_asyncOperationId(0)
233 {
234 }
235
236 FileReader::~FileReader()
237 {
238     terminate();
239 }
240
241 const AtomicString& FileReader::interfaceName() const
242 {
243     return EventTargetNames::FileReader;
244 }
245
246 void FileReader::stop()
247 {
248     // The delayed abort task tidies up and advances to the DONE state.
249     if (m_loadingState == LoadingStateAborted)
250         return;
251
252     if (hasPendingActivity())
253         ThrottlingController::finishReader(executionContext(), this, ThrottlingController::removeReader(executionContext(), this));
254     terminate();
255 }
256
257 bool FileReader::hasPendingActivity() const
258 {
259     return m_state == LOADING;
260 }
261
262 void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& exceptionState)
263 {
264     ASSERT(blob);
265     WTF_LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
266
267     readInternal(blob, FileReaderLoader::ReadAsArrayBuffer, exceptionState);
268 }
269
270 void FileReader::readAsBinaryString(Blob* blob, ExceptionState& exceptionState)
271 {
272     ASSERT(blob);
273     WTF_LOG(FileAPI, "FileReader: reading as binary: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
274
275     readInternal(blob, FileReaderLoader::ReadAsBinaryString, exceptionState);
276 }
277
278 void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionState& exceptionState)
279 {
280     ASSERT(blob);
281     WTF_LOG(FileAPI, "FileReader: reading as text: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
282
283     m_encoding = encoding;
284     readInternal(blob, FileReaderLoader::ReadAsText, exceptionState);
285 }
286
287 void FileReader::readAsText(Blob* blob, ExceptionState& exceptionState)
288 {
289     readAsText(blob, String(), exceptionState);
290 }
291
292 void FileReader::readAsDataURL(Blob* blob, ExceptionState& exceptionState)
293 {
294     ASSERT(blob);
295     WTF_LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
296
297     readInternal(blob, FileReaderLoader::ReadAsDataURL, exceptionState);
298 }
299
300 void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionState& exceptionState)
301 {
302     // If multiple concurrent read methods are called on the same FileReader, InvalidStateError should be thrown when the state is LOADING.
303     if (m_state == LOADING) {
304         exceptionState.throwDOMException(InvalidStateError, "The object is already busy reading Blobs.");
305         return;
306     }
307
308     if (blob->hasBeenClosed()) {
309         exceptionState.throwDOMException(InvalidStateError, String(blob->isFile() ? "File" : "Blob") + " has been closed.");
310         return;
311     }
312
313     if (!ThrottlingController::from(executionContext())) {
314         exceptionState.throwDOMException(AbortError, "Reading from a Document-detached FileReader is not supported.");
315         return;
316     }
317
318     // "Snapshot" the Blob data rather than the Blob itself as ongoing
319     // read operations should not be affected if close() is called on
320     // the Blob being read.
321     m_blobDataHandle = blob->blobDataHandle();
322     m_blobType = blob->type();
323     m_readType = type;
324     m_state = LOADING;
325     m_loadingState = LoadingStatePending;
326     m_error = nullptr;
327     ThrottlingController::pushReader(executionContext(), this);
328 }
329
330 void FileReader::executePendingRead()
331 {
332     ASSERT(m_loadingState == LoadingStatePending);
333     m_loadingState = LoadingStateLoading;
334
335     m_loader = adoptPtr(new FileReaderLoader(m_readType, this));
336     m_loader->setEncoding(m_encoding);
337     m_loader->setDataType(m_blobType);
338     m_loader->start(executionContext(), m_blobDataHandle);
339     m_blobDataHandle = nullptr;
340 }
341
342 static void delayedAbort(ExecutionContext*, FileReader* reader)
343 {
344     reader->doAbort();
345 }
346
347 void FileReader::abort()
348 {
349     WTF_LOG(FileAPI, "FileReader: aborting\n");
350
351     if (m_loadingState != LoadingStateLoading
352         && m_loadingState != LoadingStatePending) {
353         return;
354     }
355     m_loadingState = LoadingStateAborted;
356
357     // Schedule to have the abort done later since abort() might be called from the event handler and we do not want the resource loading code to be in the stack.
358     executionContext()->postTask(
359         createCrossThreadTask(&delayedAbort, AllowAccessLater(this)));
360 }
361
362 void FileReader::doAbort()
363 {
364     ASSERT(m_state != DONE);
365
366     terminate();
367
368     m_error = FileError::create(FileError::ABORT_ERR);
369
370     // Unregister the reader.
371     ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
372
373     fireEvent(EventTypeNames::error);
374     fireEvent(EventTypeNames::abort);
375     fireEvent(EventTypeNames::loadend);
376
377     // All possible events have fired and we're done, no more pending activity.
378     ThrottlingController::finishReader(executionContext(), this, finalStep);
379 }
380
381 void FileReader::terminate()
382 {
383     if (m_loader) {
384         m_loader->cancel();
385         m_loader = nullptr;
386     }
387     m_state = DONE;
388     m_loadingState = LoadingStateNone;
389 }
390
391 void FileReader::didStartLoading()
392 {
393     fireEvent(EventTypeNames::loadstart);
394 }
395
396 void FileReader::didReceiveData()
397 {
398     // Fire the progress event at least every 50ms.
399     double now = currentTimeMS();
400     if (!m_lastProgressNotificationTimeMS)
401         m_lastProgressNotificationTimeMS = now;
402     else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) {
403         fireEvent(EventTypeNames::progress);
404         m_lastProgressNotificationTimeMS = now;
405     }
406 }
407
408 void FileReader::didFinishLoading()
409 {
410     if (m_loadingState == LoadingStateAborted)
411         return;
412     ASSERT(m_loadingState == LoadingStateLoading);
413
414     // It's important that we change m_loadingState before firing any events
415     // since any of the events could call abort(), which internally checks
416     // if we're still loading (therefore we need abort process) or not.
417     m_loadingState = LoadingStateNone;
418
419     fireEvent(EventTypeNames::progress);
420
421     ASSERT(m_state != DONE);
422     m_state = DONE;
423
424     // Unregister the reader.
425     ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
426
427     fireEvent(EventTypeNames::load);
428     fireEvent(EventTypeNames::loadend);
429
430     // All possible events have fired and we're done, no more pending activity.
431     ThrottlingController::finishReader(executionContext(), this, finalStep);
432 }
433
434 void FileReader::didFail(FileError::ErrorCode errorCode)
435 {
436     if (m_loadingState == LoadingStateAborted)
437         return;
438     ASSERT(m_loadingState == LoadingStateLoading);
439     m_loadingState = LoadingStateNone;
440
441     ASSERT(m_state != DONE);
442     m_state = DONE;
443
444     m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
445
446     // Unregister the reader.
447     ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
448
449     fireEvent(EventTypeNames::error);
450     fireEvent(EventTypeNames::loadend);
451
452     // All possible events have fired and we're done, no more pending activity.
453     ThrottlingController::finishReader(executionContext(), this, finalStep);
454 }
455
456 void FileReader::fireEvent(const AtomicString& type)
457 {
458     InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(executionContext(), m_asyncOperationId);
459     if (!m_loader) {
460         dispatchEvent(ProgressEvent::create(type, false, 0, 0));
461         InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
462         return;
463     }
464
465     if (m_loader->totalBytes() >= 0)
466         dispatchEvent(ProgressEvent::create(type, true, m_loader->bytesLoaded(), m_loader->totalBytes()));
467     else
468         dispatchEvent(ProgressEvent::create(type, false, m_loader->bytesLoaded(), 0));
469
470     InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
471 }
472
473 PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const
474 {
475     if (!m_loader || m_error)
476         return nullptr;
477     return m_loader->arrayBufferResult();
478 }
479
480 String FileReader::stringResult()
481 {
482     if (!m_loader || m_error)
483         return String();
484     return m_loader->stringResult();
485 }
486
487 void FileReader::trace(Visitor* visitor)
488 {
489     visitor->trace(m_error);
490     EventTargetWithInlineData::trace(visitor);
491 }
492
493 } // namespace blink