Upstream version 10.39.225.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 }
232
233 FileReader::~FileReader()
234 {
235     terminate();
236 }
237
238 const AtomicString& FileReader::interfaceName() const
239 {
240     return EventTargetNames::FileReader;
241 }
242
243 void FileReader::stop()
244 {
245     // The delayed abort task tidies up and advances to the DONE state.
246     if (m_loadingState == LoadingStateAborted)
247         return;
248
249     if (hasPendingActivity())
250         ThrottlingController::finishReader(executionContext(), this, ThrottlingController::removeReader(executionContext(), this));
251     terminate();
252 }
253
254 bool FileReader::hasPendingActivity() const
255 {
256     return m_state == LOADING;
257 }
258
259 void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& exceptionState)
260 {
261     ASSERT(blob);
262     WTF_LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
263
264     readInternal(blob, FileReaderLoader::ReadAsArrayBuffer, exceptionState);
265 }
266
267 void FileReader::readAsBinaryString(Blob* blob, ExceptionState& exceptionState)
268 {
269     ASSERT(blob);
270     WTF_LOG(FileAPI, "FileReader: reading as binary: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
271
272     readInternal(blob, FileReaderLoader::ReadAsBinaryString, exceptionState);
273 }
274
275 void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionState& exceptionState)
276 {
277     ASSERT(blob);
278     WTF_LOG(FileAPI, "FileReader: reading as text: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
279
280     m_encoding = encoding;
281     readInternal(blob, FileReaderLoader::ReadAsText, exceptionState);
282 }
283
284 void FileReader::readAsText(Blob* blob, ExceptionState& exceptionState)
285 {
286     readAsText(blob, String(), exceptionState);
287 }
288
289 void FileReader::readAsDataURL(Blob* blob, ExceptionState& exceptionState)
290 {
291     ASSERT(blob);
292     WTF_LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
293
294     readInternal(blob, FileReaderLoader::ReadAsDataURL, exceptionState);
295 }
296
297 void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionState& exceptionState)
298 {
299     // If multiple concurrent read methods are called on the same FileReader, InvalidStateError should be thrown when the state is LOADING.
300     if (m_state == LOADING) {
301         exceptionState.throwDOMException(InvalidStateError, "The object is already busy reading Blobs.");
302         return;
303     }
304
305     if (blob->hasBeenClosed()) {
306         exceptionState.throwDOMException(InvalidStateError, String(blob->isFile() ? "File" : "Blob") + " has been closed.");
307         return;
308     }
309
310     if (!ThrottlingController::from(executionContext())) {
311         exceptionState.throwDOMException(AbortError, "Reading from a Document-detached FileReader is not supported.");
312         return;
313     }
314
315     // "Snapshot" the Blob data rather than the Blob itself as ongoing
316     // read operations should not be affected if close() is called on
317     // the Blob being read.
318     m_blobDataHandle = blob->blobDataHandle();
319     m_blobType = blob->type();
320     m_readType = type;
321     m_state = LOADING;
322     m_loadingState = LoadingStatePending;
323     m_error = nullptr;
324     ThrottlingController::pushReader(executionContext(), this);
325 }
326
327 void FileReader::executePendingRead()
328 {
329     ASSERT(m_loadingState == LoadingStatePending);
330     m_loadingState = LoadingStateLoading;
331
332     m_loader = adoptPtr(new FileReaderLoader(m_readType, this));
333     m_loader->setEncoding(m_encoding);
334     m_loader->setDataType(m_blobType);
335     m_loader->start(executionContext(), m_blobDataHandle);
336     m_blobDataHandle = nullptr;
337 }
338
339 static void delayedAbort(ExecutionContext*, FileReader* reader)
340 {
341     reader->doAbort();
342 }
343
344 void FileReader::abort()
345 {
346     WTF_LOG(FileAPI, "FileReader: aborting\n");
347
348     if (m_loadingState != LoadingStateLoading
349         && m_loadingState != LoadingStatePending) {
350         return;
351     }
352     m_loadingState = LoadingStateAborted;
353
354     // 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.
355     executionContext()->postTask(
356         createCrossThreadTask(&delayedAbort, AllowAccessLater(this)));
357 }
358
359 void FileReader::doAbort()
360 {
361     ASSERT(m_state != DONE);
362
363     terminate();
364
365     m_error = FileError::create(FileError::ABORT_ERR);
366
367     // Unregister the reader.
368     ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
369
370     fireEvent(EventTypeNames::error);
371     fireEvent(EventTypeNames::abort);
372     fireEvent(EventTypeNames::loadend);
373
374     // All possible events have fired and we're done, no more pending activity.
375     ThrottlingController::finishReader(executionContext(), this, finalStep);
376 }
377
378 void FileReader::terminate()
379 {
380     if (m_loader) {
381         m_loader->cancel();
382         m_loader = nullptr;
383     }
384     m_state = DONE;
385     m_loadingState = LoadingStateNone;
386 }
387
388 void FileReader::didStartLoading()
389 {
390     fireEvent(EventTypeNames::loadstart);
391 }
392
393 void FileReader::didReceiveData()
394 {
395     // Fire the progress event at least every 50ms.
396     double now = currentTimeMS();
397     if (!m_lastProgressNotificationTimeMS)
398         m_lastProgressNotificationTimeMS = now;
399     else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) {
400         fireEvent(EventTypeNames::progress);
401         m_lastProgressNotificationTimeMS = now;
402     }
403 }
404
405 void FileReader::didFinishLoading()
406 {
407     if (m_loadingState == LoadingStateAborted)
408         return;
409     ASSERT(m_loadingState == LoadingStateLoading);
410
411     // It's important that we change m_loadingState before firing any events
412     // since any of the events could call abort(), which internally checks
413     // if we're still loading (therefore we need abort process) or not.
414     m_loadingState = LoadingStateNone;
415
416     fireEvent(EventTypeNames::progress);
417
418     ASSERT(m_state != DONE);
419     m_state = DONE;
420
421     // Unregister the reader.
422     ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
423
424     fireEvent(EventTypeNames::load);
425     fireEvent(EventTypeNames::loadend);
426
427     // All possible events have fired and we're done, no more pending activity.
428     ThrottlingController::finishReader(executionContext(), this, finalStep);
429 }
430
431 void FileReader::didFail(FileError::ErrorCode errorCode)
432 {
433     if (m_loadingState == LoadingStateAborted)
434         return;
435     ASSERT(m_loadingState == LoadingStateLoading);
436     m_loadingState = LoadingStateNone;
437
438     ASSERT(m_state != DONE);
439     m_state = DONE;
440
441     m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
442
443     // Unregister the reader.
444     ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
445
446     fireEvent(EventTypeNames::error);
447     fireEvent(EventTypeNames::loadend);
448
449     // All possible events have fired and we're done, no more pending activity.
450     ThrottlingController::finishReader(executionContext(), this, finalStep);
451 }
452
453 void FileReader::fireEvent(const AtomicString& type)
454 {
455     InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(executionContext(), m_asyncOperationId);
456     if (!m_loader) {
457         dispatchEvent(ProgressEvent::create(type, false, 0, 0));
458         InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
459         return;
460     }
461
462     if (m_loader->totalBytes() >= 0)
463         dispatchEvent(ProgressEvent::create(type, true, m_loader->bytesLoaded(), m_loader->totalBytes()));
464     else
465         dispatchEvent(ProgressEvent::create(type, false, m_loader->bytesLoaded(), 0));
466
467     InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
468 }
469
470 PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const
471 {
472     if (!m_loader || m_error)
473         return nullptr;
474     return m_loader->arrayBufferResult();
475 }
476
477 String FileReader::stringResult()
478 {
479     if (!m_loader || m_error)
480         return String();
481     return m_loader->stringResult();
482 }
483
484 void FileReader::trace(Visitor* visitor)
485 {
486     visitor->trace(m_error);
487     EventTargetWithInlineData::trace(visitor);
488 }
489
490 } // namespace blink