diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp | 203 |
1 files changed, 146 insertions, 57 deletions
diff --git a/chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp b/chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp index 6568c2f1d1b..c94eadb93f6 100644 --- a/chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp +++ b/chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp @@ -34,40 +34,104 @@ #include "bindings/v8/ExceptionState.h" #include "core/dom/CrossThreadTask.h" #include "core/dom/ExceptionCode.h" -#include "core/dom/ProgressEvent.h" -#include "core/dom/ScriptExecutionContext.h" +#include "core/dom/ExecutionContext.h" +#include "core/events/ProgressEvent.h" #include "core/fileapi/File.h" -#include "core/platform/Logging.h" +#include "platform/Logging.h" #include "wtf/ArrayBuffer.h" #include "wtf/CurrentTime.h" +#include "wtf/Deque.h" +#include "wtf/HashSet.h" +#include "wtf/ThreadSpecific.h" +#include "wtf/Threading.h" #include "wtf/text/CString.h" namespace WebCore { namespace { -const CString utf8BlobURL(Blob* blob) +#if !LOG_DISABLED +const CString utf8BlobUUID(Blob* blob) { - return blob->url().string().utf8(); + return blob->uuid().utf8(); } const CString utf8FilePath(Blob* blob) { - return blob->isFile() ? toFile(blob)->path().utf8() : ""; + return blob->hasBackingFile() ? toFile(blob)->path().utf8() : ""; } +#endif } // namespace +// Embedders like chromium limit the number of simultaneous requests to avoid +// excessive IPC congestion. We limit this to 100 per thread to throttle the +// requests (the value is arbitrarily chosen). +static const size_t kMaxOutstandingRequestsPerThread = 100; static const double progressNotificationIntervalMS = 50; -PassRefPtr<FileReader> FileReader::create(ScriptExecutionContext* context) +class FileReader::ThrottlingController { +public: + ThrottlingController() : m_maxRunningReaders(kMaxOutstandingRequestsPerThread) { } + ~ThrottlingController() { } + + void pushReader(FileReader* reader) + { + reader->setPendingActivity(reader); + if (m_pendingReaders.isEmpty() + && m_runningReaders.size() < m_maxRunningReaders) { + reader->executePendingRead(); + m_runningReaders.add(reader); + return; + } + m_pendingReaders.append(reader); + executeReaders(); + } + + void removeReader(FileReader* reader) + { + HashSet<FileReader*>::const_iterator hashIter = m_runningReaders.find(reader); + if (hashIter != m_runningReaders.end()) { + m_runningReaders.remove(hashIter); + reader->unsetPendingActivity(reader); + executeReaders(); + return; + } + Deque<FileReader*>::const_iterator dequeEnd = m_pendingReaders.end(); + for (Deque<FileReader*>::const_iterator it = m_pendingReaders.begin(); it != dequeEnd; ++it) { + if (*it == reader) { + m_pendingReaders.remove(it); + reader->unsetPendingActivity(reader); + return; + } + } + } + +private: + void executeReaders() + { + while (m_runningReaders.size() < m_maxRunningReaders) { + if (m_pendingReaders.isEmpty()) + return; + FileReader* reader = m_pendingReaders.takeFirst(); + reader->executePendingRead(); + m_runningReaders.add(reader); + } + } + + const size_t m_maxRunningReaders; + Deque<FileReader*> m_pendingReaders; + HashSet<FileReader*> m_runningReaders; +}; + +PassRefPtr<FileReader> FileReader::create(ExecutionContext* context) { RefPtr<FileReader> fileReader(adoptRef(new FileReader(context))); fileReader->suspendIfNeeded(); return fileReader.release(); } -FileReader::FileReader(ScriptExecutionContext* context) +FileReader::FileReader(ExecutionContext* context) : ActiveDOMObject(context) , m_state(EMPTY) , m_loadingState(LoadingStateNone) @@ -84,103 +148,114 @@ FileReader::~FileReader() const AtomicString& FileReader::interfaceName() const { - return eventNames().interfaceForFileReader; -} - -bool FileReader::canSuspend() const -{ - // FIXME: It is not currently possible to suspend a FileReader, so pages with FileReader can not go into page cache. - return false; + return EventTargetNames::FileReader; } void FileReader::stop() { + if (m_loadingState == LoadingStateLoading || m_loadingState == LoadingStatePending) + throttlingController()->removeReader(this); terminate(); } -void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& es) +void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& exceptionState) { - if (!blob) + if (!blob) { + exceptionState.throwTypeError("The argument is not a Blob."); return; + } - LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", utf8BlobURL(blob).data(), utf8FilePath(blob).data()); + WTF_LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data()); - readInternal(blob, FileReaderLoader::ReadAsArrayBuffer, es); + readInternal(blob, FileReaderLoader::ReadAsArrayBuffer, exceptionState); } -void FileReader::readAsBinaryString(Blob* blob, ExceptionState& es) +void FileReader::readAsBinaryString(Blob* blob, ExceptionState& exceptionState) { - if (!blob) + if (!blob) { + exceptionState.throwTypeError("The argument is not a Blob."); return; + } - LOG(FileAPI, "FileReader: reading as binary: %s %s\n", utf8BlobURL(blob).data(), utf8FilePath(blob).data()); + WTF_LOG(FileAPI, "FileReader: reading as binary: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data()); - readInternal(blob, FileReaderLoader::ReadAsBinaryString, es); + readInternal(blob, FileReaderLoader::ReadAsBinaryString, exceptionState); } -void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionState& es) +void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionState& exceptionState) { - if (!blob) + if (!blob) { + exceptionState.throwTypeError("The argument is not a Blob."); return; + } - LOG(FileAPI, "FileReader: reading as text: %s %s\n", utf8BlobURL(blob).data(), utf8FilePath(blob).data()); + WTF_LOG(FileAPI, "FileReader: reading as text: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data()); m_encoding = encoding; - readInternal(blob, FileReaderLoader::ReadAsText, es); + readInternal(blob, FileReaderLoader::ReadAsText, exceptionState); } -void FileReader::readAsText(Blob* blob, ExceptionState& es) +void FileReader::readAsText(Blob* blob, ExceptionState& exceptionState) { - readAsText(blob, String(), es); + readAsText(blob, String(), exceptionState); } -void FileReader::readAsDataURL(Blob* blob, ExceptionState& es) +void FileReader::readAsDataURL(Blob* blob, ExceptionState& exceptionState) { - if (!blob) + if (!blob) { + exceptionState.throwTypeError("The argument is not a Blob."); return; + } - LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", utf8BlobURL(blob).data(), utf8FilePath(blob).data()); + WTF_LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data()); - readInternal(blob, FileReaderLoader::ReadAsDataURL, es); + readInternal(blob, FileReaderLoader::ReadAsDataURL, exceptionState); } -void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionState& es) +void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionState& exceptionState) { // If multiple concurrent read methods are called on the same FileReader, InvalidStateError should be thrown when the state is LOADING. if (m_state == LOADING) { - es.throwDOMException(InvalidStateError); + exceptionState.throwDOMException(InvalidStateError, "The object is already busy reading Blobs."); return; } - setPendingActivity(this); - m_blob = blob; m_readType = type; m_state = LOADING; - m_loadingState = LoadingStateLoading; + m_loadingState = LoadingStatePending; m_error = 0; + throttlingController()->pushReader(this); +} + +void FileReader::executePendingRead() +{ + ASSERT(m_loadingState == LoadingStatePending); + m_loadingState = LoadingStateLoading; m_loader = adoptPtr(new FileReaderLoader(m_readType, this)); m_loader->setEncoding(m_encoding); m_loader->setDataType(m_blob->type()); - m_loader->start(scriptExecutionContext(), *m_blob); + m_loader->start(executionContext(), m_blob->blobDataHandle()); } -static void delayedAbort(ScriptExecutionContext*, FileReader* reader) +static void delayedAbort(ExecutionContext*, FileReader* reader) { reader->doAbort(); } void FileReader::abort() { - LOG(FileAPI, "FileReader: aborting\n"); + WTF_LOG(FileAPI, "FileReader: aborting\n"); - if (m_loadingState != LoadingStateLoading) + if (m_loadingState != LoadingStateLoading + && m_loadingState != LoadingStatePending) { return; + } m_loadingState = LoadingStateAborted; // 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. - scriptExecutionContext()->postTask( + executionContext()->postTask( createCallbackTask(&delayedAbort, AllowAccessLater(this))); } @@ -192,12 +267,12 @@ void FileReader::doAbort() m_error = FileError::create(FileError::ABORT_ERR); - fireEvent(eventNames().errorEvent); - fireEvent(eventNames().abortEvent); - fireEvent(eventNames().loadendEvent); + fireEvent(EventTypeNames::error); + fireEvent(EventTypeNames::abort); + fireEvent(EventTypeNames::loadend); // All possible events have fired and we're done, no more pending activity. - unsetPendingActivity(this); + throttlingController()->removeReader(this); } void FileReader::terminate() @@ -212,7 +287,7 @@ void FileReader::terminate() void FileReader::didStartLoading() { - fireEvent(eventNames().loadstartEvent); + fireEvent(EventTypeNames::loadstart); } void FileReader::didReceiveData() @@ -222,7 +297,7 @@ void FileReader::didReceiveData() if (!m_lastProgressNotificationTimeMS) m_lastProgressNotificationTimeMS = now; else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) { - fireEvent(eventNames().progressEvent); + fireEvent(EventTypeNames::progress); m_lastProgressNotificationTimeMS = now; } } @@ -238,16 +313,16 @@ void FileReader::didFinishLoading() // if we're still loading (therefore we need abort process) or not. m_loadingState = LoadingStateNone; - fireEvent(eventNames().progressEvent); + fireEvent(EventTypeNames::progress); ASSERT(m_state != DONE); m_state = DONE; - fireEvent(eventNames().loadEvent); - fireEvent(eventNames().loadendEvent); + fireEvent(EventTypeNames::load); + fireEvent(EventTypeNames::loadend); // All possible events have fired and we're done, no more pending activity. - unsetPendingActivity(this); + throttlingController()->removeReader(this); } void FileReader::didFail(FileError::ErrorCode errorCode) @@ -261,16 +336,30 @@ void FileReader::didFail(FileError::ErrorCode errorCode) m_state = DONE; m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode)); - fireEvent(eventNames().errorEvent); - fireEvent(eventNames().loadendEvent); + fireEvent(EventTypeNames::error); + fireEvent(EventTypeNames::loadend); // All possible events have fired and we're done, no more pending activity. - unsetPendingActivity(this); + throttlingController()->removeReader(this); } void FileReader::fireEvent(const AtomicString& type) { - dispatchEvent(ProgressEvent::create(type, true, m_loader ? m_loader->bytesLoaded() : 0, m_loader ? m_loader->totalBytes() : 0)); + if (!m_loader) { + dispatchEvent(ProgressEvent::create(type, false, 0, 0)); + return; + } + + if (m_loader->totalBytes() >= 0) + dispatchEvent(ProgressEvent::create(type, true, m_loader->bytesLoaded(), m_loader->totalBytes())); + else + dispatchEvent(ProgressEvent::create(type, false, m_loader->bytesLoaded(), 0)); +} + +ThreadSpecific<FileReader::ThrottlingController>& FileReader::throttlingController() +{ + AtomicallyInitializedStatic(ThreadSpecific<FileReader::ThrottlingController>*, controller = new ThreadSpecific<FileReader::ThrottlingController>); + return *controller; } PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const |