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