summaryrefslogtreecommitdiff
path: root/chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp')
-rw-r--r--chromium/third_party/WebKit/Source/core/fileapi/FileReader.cpp203
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