Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / core / fileapi / FileReader.cpp
blobe7bbdf5156a4c1757d03592e926fcb402c0f117e
1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
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.
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.
31 #include "config.h"
32 #include "core/fileapi/FileReader.h"
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "bindings/core/v8/UnionTypesCore.h"
36 #include "core/dom/CrossThreadTask.h"
37 #include "core/dom/DOMArrayBuffer.h"
38 #include "core/dom/Document.h"
39 #include "core/dom/ExceptionCode.h"
40 #include "core/dom/ExecutionContext.h"
41 #include "core/events/ProgressEvent.h"
42 #include "core/fileapi/File.h"
43 #include "core/inspector/InspectorInstrumentation.h"
44 #include "platform/Logging.h"
45 #include "platform/Supplementable.h"
46 #include "wtf/CurrentTime.h"
47 #include "wtf/Deque.h"
48 #include "wtf/HashSet.h"
49 #include "wtf/text/CString.h"
51 namespace blink {
53 namespace {
55 #if !LOG_DISABLED
56 const CString utf8BlobUUID(Blob* blob)
58 return blob->uuid().utf8();
61 const CString utf8FilePath(Blob* blob)
63 return blob->hasBackingFile() ? toFile(blob)->path().utf8() : "";
65 #endif
67 } // namespace
69 // Embedders like chromium limit the number of simultaneous requests to avoid
70 // excessive IPC congestion. We limit this to 100 per thread to throttle the
71 // requests (the value is arbitrarily chosen).
72 static const size_t kMaxOutstandingRequestsPerThread = 100;
73 static const double progressNotificationIntervalMS = 50;
75 class FileReader::ThrottlingController final : public NoBaseWillBeGarbageCollectedFinalized<FileReader::ThrottlingController>, public WillBeHeapSupplement<ExecutionContext> {
76 WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(FileReader::ThrottlingController);
77 public:
78 static ThrottlingController* from(ExecutionContext* context)
80 if (!context)
81 return 0;
83 ThrottlingController* controller = static_cast<ThrottlingController*>(WillBeHeapSupplement<ExecutionContext>::from(*context, supplementName()));
84 if (!controller) {
85 controller = new ThrottlingController();
86 WillBeHeapSupplement<ExecutionContext>::provideTo(*context, supplementName(), adoptPtrWillBeNoop(controller));
88 return controller;
91 ~ThrottlingController() { }
93 enum FinishReaderType { DoNotRunPendingReaders, RunPendingReaders };
95 static void pushReader(ExecutionContext* context, FileReader* reader)
97 ThrottlingController* controller = from(context);
98 if (!controller)
99 return;
101 reader->m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(context, "FileReader");
102 controller->pushReader(reader);
105 static FinishReaderType removeReader(ExecutionContext* context, FileReader* reader)
107 ThrottlingController* controller = from(context);
108 if (!controller)
109 return DoNotRunPendingReaders;
111 return controller->removeReader(reader);
114 static void finishReader(ExecutionContext* context, FileReader* reader, FinishReaderType nextStep)
116 InspectorInstrumentation::traceAsyncOperationCompleted(context, reader->m_asyncOperationId);
118 ThrottlingController* controller = from(context);
119 if (!controller)
120 return;
122 controller->finishReader(reader, nextStep);
125 DEFINE_INLINE_TRACE()
127 #if ENABLE(OILPAN)
128 visitor->trace(m_pendingReaders);
129 visitor->trace(m_runningReaders);
130 #endif
131 WillBeHeapSupplement<ExecutionContext>::trace(visitor);
134 private:
135 ThrottlingController()
136 : m_maxRunningReaders(kMaxOutstandingRequestsPerThread)
140 void pushReader(FileReader* reader)
142 if (m_pendingReaders.isEmpty()
143 && m_runningReaders.size() < m_maxRunningReaders) {
144 reader->executePendingRead();
145 ASSERT(!m_runningReaders.contains(reader));
146 m_runningReaders.add(reader);
147 return;
149 m_pendingReaders.append(reader);
150 executeReaders();
153 FinishReaderType removeReader(FileReader* reader)
155 FileReaderHashSet::const_iterator hashIter = m_runningReaders.find(reader);
156 if (hashIter != m_runningReaders.end()) {
157 m_runningReaders.remove(hashIter);
158 return RunPendingReaders;
160 FileReaderDeque::const_iterator dequeEnd = m_pendingReaders.end();
161 for (FileReaderDeque::const_iterator it = m_pendingReaders.begin(); it != dequeEnd; ++it) {
162 if (*it == reader) {
163 m_pendingReaders.remove(it);
164 break;
167 return DoNotRunPendingReaders;
170 void finishReader(FileReader* reader, FinishReaderType nextStep)
172 if (nextStep == RunPendingReaders)
173 executeReaders();
176 void executeReaders()
178 while (m_runningReaders.size() < m_maxRunningReaders) {
179 if (m_pendingReaders.isEmpty())
180 return;
181 FileReader* reader = m_pendingReaders.takeFirst();
182 reader->executePendingRead();
183 m_runningReaders.add(reader);
187 static const char* supplementName() { return "FileReaderThrottlingController"; }
189 const size_t m_maxRunningReaders;
191 using FileReaderDeque = PersistentHeapDequeWillBeHeapDeque<Member<FileReader>>;
192 using FileReaderHashSet = PersistentHeapHashSetWillBeHeapHashSet<Member<FileReader>>;
194 FileReaderDeque m_pendingReaders;
195 FileReaderHashSet m_runningReaders;
198 FileReader* FileReader::create(ExecutionContext* context)
200 FileReader* fileReader = new FileReader(context);
201 fileReader->suspendIfNeeded();
202 return fileReader;
205 FileReader::FileReader(ExecutionContext* context)
206 : ActiveDOMObject(context)
207 , m_state(EMPTY)
208 , m_loadingState(LoadingStateNone)
209 , m_readType(FileReaderLoader::ReadAsBinaryString)
210 , m_lastProgressNotificationTimeMS(0)
211 , m_asyncOperationId(0)
215 FileReader::~FileReader()
217 terminate();
220 const AtomicString& FileReader::interfaceName() const
222 return EventTargetNames::FileReader;
225 void FileReader::stop()
227 // The delayed abort task tidies up and advances to the DONE state.
228 if (m_loadingState == LoadingStateAborted)
229 return;
231 if (hasPendingActivity())
232 ThrottlingController::finishReader(executionContext(), this, ThrottlingController::removeReader(executionContext(), this));
233 terminate();
236 bool FileReader::hasPendingActivity() const
238 return m_state == LOADING;
241 void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& exceptionState)
243 ASSERT(blob);
244 WTF_LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
246 readInternal(blob, FileReaderLoader::ReadAsArrayBuffer, exceptionState);
249 void FileReader::readAsBinaryString(Blob* blob, ExceptionState& exceptionState)
251 ASSERT(blob);
252 WTF_LOG(FileAPI, "FileReader: reading as binary: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
254 readInternal(blob, FileReaderLoader::ReadAsBinaryString, exceptionState);
257 void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionState& exceptionState)
259 ASSERT(blob);
260 WTF_LOG(FileAPI, "FileReader: reading as text: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
262 m_encoding = encoding;
263 readInternal(blob, FileReaderLoader::ReadAsText, exceptionState);
266 void FileReader::readAsText(Blob* blob, ExceptionState& exceptionState)
268 readAsText(blob, String(), exceptionState);
271 void FileReader::readAsDataURL(Blob* blob, ExceptionState& exceptionState)
273 ASSERT(blob);
274 WTF_LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
276 readInternal(blob, FileReaderLoader::ReadAsDataURL, exceptionState);
279 void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionState& exceptionState)
281 // If multiple concurrent read methods are called on the same FileReader, InvalidStateError should be thrown when the state is LOADING.
282 if (m_state == LOADING) {
283 exceptionState.throwDOMException(InvalidStateError, "The object is already busy reading Blobs.");
284 return;
287 if (blob->hasBeenClosed()) {
288 exceptionState.throwDOMException(InvalidStateError, String(blob->isFile() ? "File" : "Blob") + " has been closed.");
289 return;
292 ExecutionContext* context = executionContext();
293 if (!context) {
294 exceptionState.throwDOMException(AbortError, "Reading from a detached FileReader is not supported.");
295 return;
298 // A document loader will not load new resources once the Document has detached from its frame.
299 if (context->isDocument() && !toDocument(context)->frame()) {
300 exceptionState.throwDOMException(AbortError, "Reading from a Document-detached FileReader is not supported.");
301 return;
304 // "Snapshot" the Blob data rather than the Blob itself as ongoing
305 // read operations should not be affected if close() is called on
306 // the Blob being read.
307 m_blobDataHandle = blob->blobDataHandle();
308 m_blobType = blob->type();
309 m_readType = type;
310 m_state = LOADING;
311 m_loadingState = LoadingStatePending;
312 m_error = nullptr;
313 ASSERT(ThrottlingController::from(context));
314 ThrottlingController::pushReader(context, this);
317 void FileReader::executePendingRead()
319 ASSERT(m_loadingState == LoadingStatePending);
320 m_loadingState = LoadingStateLoading;
322 m_loader = FileReaderLoader::create(m_readType, this);
323 m_loader->setEncoding(m_encoding);
324 m_loader->setDataType(m_blobType);
325 m_loader->start(executionContext(), m_blobDataHandle);
326 m_blobDataHandle = nullptr;
329 static void delayedAbort(FileReader* reader)
331 reader->doAbort();
334 void FileReader::abort()
336 WTF_LOG(FileAPI, "FileReader: aborting\n");
338 if (m_loadingState != LoadingStateLoading
339 && m_loadingState != LoadingStatePending) {
340 return;
342 m_loadingState = LoadingStateAborted;
344 // 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.
345 executionContext()->postTask(
346 FROM_HERE, createSameThreadTask(&delayedAbort, this));
349 void FileReader::doAbort()
351 ASSERT(m_state != DONE);
353 terminate();
355 m_error = FileError::create(FileError::ABORT_ERR);
357 // Unregister the reader.
358 ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
360 fireEvent(EventTypeNames::error);
361 fireEvent(EventTypeNames::abort);
362 fireEvent(EventTypeNames::loadend);
364 // All possible events have fired and we're done, no more pending activity.
365 ThrottlingController::finishReader(executionContext(), this, finalStep);
368 void FileReader::result(StringOrArrayBuffer& resultAttribute) const
370 if (!m_loader || m_error)
371 return;
373 if (m_readType == FileReaderLoader::ReadAsArrayBuffer)
374 resultAttribute.setArrayBuffer(m_loader->arrayBufferResult());
375 else
376 resultAttribute.setString(m_loader->stringResult());
379 void FileReader::terminate()
381 if (m_loader) {
382 m_loader->cancel();
383 m_loader = nullptr;
385 m_state = DONE;
386 m_loadingState = LoadingStateNone;
389 void FileReader::didStartLoading()
391 fireEvent(EventTypeNames::loadstart);
394 void FileReader::didReceiveData()
396 // Fire the progress event at least every 50ms.
397 double now = currentTimeMS();
398 if (!m_lastProgressNotificationTimeMS) {
399 m_lastProgressNotificationTimeMS = now;
400 } else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) {
401 fireEvent(EventTypeNames::progress);
402 m_lastProgressNotificationTimeMS = now;
406 void FileReader::didFinishLoading()
408 if (m_loadingState == LoadingStateAborted)
409 return;
410 ASSERT(m_loadingState == LoadingStateLoading);
412 // It's important that we change m_loadingState before firing any events
413 // since any of the events could call abort(), which internally checks
414 // if we're still loading (therefore we need abort process) or not.
415 m_loadingState = LoadingStateNone;
417 fireEvent(EventTypeNames::progress);
419 ASSERT(m_state != DONE);
420 m_state = DONE;
422 // Unregister the reader.
423 ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
425 fireEvent(EventTypeNames::load);
426 fireEvent(EventTypeNames::loadend);
428 // All possible events have fired and we're done, no more pending activity.
429 ThrottlingController::finishReader(executionContext(), this, finalStep);
432 void FileReader::didFail(FileError::ErrorCode errorCode)
434 if (m_loadingState == LoadingStateAborted)
435 return;
436 ASSERT(m_loadingState == LoadingStateLoading);
437 m_loadingState = LoadingStateNone;
439 ASSERT(m_state != DONE);
440 m_state = DONE;
442 m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
444 // Unregister the reader.
445 ThrottlingController::FinishReaderType finalStep = ThrottlingController::removeReader(executionContext(), this);
447 fireEvent(EventTypeNames::error);
448 fireEvent(EventTypeNames::loadend);
450 // All possible events have fired and we're done, no more pending activity.
451 ThrottlingController::finishReader(executionContext(), this, finalStep);
454 void FileReader::fireEvent(const AtomicString& type)
456 InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(executionContext(), m_asyncOperationId);
457 if (!m_loader) {
458 dispatchEvent(ProgressEvent::create(type, false, 0, 0));
459 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
460 return;
463 if (m_loader->totalBytes() >= 0)
464 dispatchEvent(ProgressEvent::create(type, true, m_loader->bytesLoaded(), m_loader->totalBytes()));
465 else
466 dispatchEvent(ProgressEvent::create(type, false, m_loader->bytesLoaded(), 0));
468 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
471 DEFINE_TRACE(FileReader)
473 visitor->trace(m_error);
474 RefCountedGarbageCollectedEventTargetWithInlineData<FileReader>::trace(visitor);
475 ActiveDOMObject::trace(visitor);
478 } // namespace blink