This source file includes following definitions.
- utf8BlobUUID
- utf8FilePath
- ThrottlingController
- ThrottlingController
- pushReader
- removeReader
- finishReader
- executeReaders
- create
- m_lastProgressNotificationTimeMS
- interfaceName
- stop
- readAsArrayBuffer
- readAsBinaryString
- readAsText
- readAsText
- readAsDataURL
- readInternal
- executePendingRead
- delayedAbort
- abort
- doAbort
- terminate
- didStartLoading
- didReceiveData
- didFinishLoading
- didFail
- fireEvent
- throttlingController
- arrayBufferResult
- stringResult
- trace
#include "config.h"
#include "core/fileapi/FileReader.h"
#include "bindings/v8/ExceptionState.h"
#include "core/dom/CrossThreadTask.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/ExecutionContext.h"
#include "core/events/ProgressEvent.h"
#include "core/fileapi/File.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 {
#if !LOG_DISABLED
const CString utf8BlobUUID(Blob* blob)
{
return blob->uuid().utf8();
}
const CString utf8FilePath(Blob* blob)
{
return blob->hasBackingFile() ? toFile(blob)->path().utf8() : "";
}
#endif
}
static const size_t kMaxOutstandingRequestsPerThread = 100;
static const double progressNotificationIntervalMS = 50;
class FileReader::ThrottlingController {
public:
ThrottlingController() : m_maxRunningReaders(kMaxOutstandingRequestsPerThread) { }
~ThrottlingController() { }
enum FinishReaderType { DoNotRunPendingReaders, RunPendingReaders };
void pushReader(FileReader* reader)
{
reader->setPendingActivity(reader);
if (m_pendingReaders.isEmpty()
&& m_runningReaders.size() < m_maxRunningReaders) {
reader->executePendingRead();
ASSERT(!m_runningReaders.contains(reader));
m_runningReaders.add(reader);
return;
}
m_pendingReaders.append(reader);
executeReaders();
}
FinishReaderType removeReader(FileReader* reader)
{
HashSet<FileReader*>::const_iterator hashIter = m_runningReaders.find(reader);
if (hashIter != m_runningReaders.end()) {
m_runningReaders.remove(hashIter);
return RunPendingReaders;
}
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);
break;
}
}
return DoNotRunPendingReaders;
}
void finishReader(FileReader* reader, FinishReaderType nextStep)
{
reader->unsetPendingActivity(reader);
if (nextStep == RunPendingReaders)
executeReaders();
}
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;
};
PassRefPtrWillBeRawPtr<FileReader> FileReader::create(ExecutionContext* context)
{
RefPtrWillBeRawPtr<FileReader> fileReader(adoptRefWillBeRefCountedGarbageCollected(new FileReader(context)));
fileReader->suspendIfNeeded();
return fileReader.release();
}
FileReader::FileReader(ExecutionContext* context)
: ActiveDOMObject(context)
, m_state(EMPTY)
, m_loadingState(LoadingStateNone)
, m_readType(FileReaderLoader::ReadAsBinaryString)
, m_lastProgressNotificationTimeMS(0)
{
ScriptWrappable::init(this);
}
FileReader::~FileReader()
{
terminate();
}
const AtomicString& FileReader::interfaceName() const
{
return EventTargetNames::FileReader;
}
void FileReader::stop()
{
if (m_loadingState == LoadingStateLoading || m_loadingState == LoadingStatePending)
throttlingController()->finishReader(this, throttlingController()->removeReader(this));
terminate();
}
void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& exceptionState)
{
if (!blob) {
exceptionState.throwTypeError("The argument is not a Blob.");
return;
}
WTF_LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
readInternal(blob, FileReaderLoader::ReadAsArrayBuffer, exceptionState);
}
void FileReader::readAsBinaryString(Blob* blob, ExceptionState& exceptionState)
{
if (!blob) {
exceptionState.throwTypeError("The argument is not a Blob.");
return;
}
WTF_LOG(FileAPI, "FileReader: reading as binary: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
readInternal(blob, FileReaderLoader::ReadAsBinaryString, exceptionState);
}
void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionState& exceptionState)
{
if (!blob) {
exceptionState.throwTypeError("The argument is not a Blob.");
return;
}
WTF_LOG(FileAPI, "FileReader: reading as text: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
m_encoding = encoding;
readInternal(blob, FileReaderLoader::ReadAsText, exceptionState);
}
void FileReader::readAsText(Blob* blob, ExceptionState& exceptionState)
{
readAsText(blob, String(), exceptionState);
}
void FileReader::readAsDataURL(Blob* blob, ExceptionState& exceptionState)
{
if (!blob) {
exceptionState.throwTypeError("The argument is not a Blob.");
return;
}
WTF_LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", utf8BlobUUID(blob).data(), utf8FilePath(blob).data());
readInternal(blob, FileReaderLoader::ReadAsDataURL, exceptionState);
}
void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionState& exceptionState)
{
if (m_state == LOADING) {
exceptionState.throwDOMException(InvalidStateError, "The object is already busy reading Blobs.");
return;
}
if (blob->hasBeenClosed()) {
exceptionState.throwDOMException(InvalidStateError, String(blob->isFile() ? "File" : "Blob") + " has been closed.");
return;
}
m_blobDataHandle = blob->blobDataHandle();
m_blobType = blob->type();
m_readType = type;
m_state = LOADING;
m_loadingState = LoadingStatePending;
m_error = nullptr;
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_blobType);
m_loader->start(executionContext(), m_blobDataHandle);
m_blobDataHandle = nullptr;
}
static void delayedAbort(ExecutionContext*, FileReader* reader)
{
reader->doAbort();
}
void FileReader::abort()
{
WTF_LOG(FileAPI, "FileReader: aborting\n");
if (m_loadingState != LoadingStateLoading
&& m_loadingState != LoadingStatePending) {
return;
}
m_loadingState = LoadingStateAborted;
executionContext()->postTask(
createCallbackTask(&delayedAbort, AllowAccessLater(this)));
}
void FileReader::doAbort()
{
ASSERT(m_state != DONE);
terminate();
m_error = FileError::create(FileError::ABORT_ERR);
ThrottlingController::FinishReaderType finalStep = throttlingController()->removeReader(this);
fireEvent(EventTypeNames::error);
fireEvent(EventTypeNames::abort);
fireEvent(EventTypeNames::loadend);
throttlingController()->finishReader(this, finalStep);
}
void FileReader::terminate()
{
if (m_loader) {
m_loader->cancel();
m_loader = nullptr;
}
m_state = DONE;
m_loadingState = LoadingStateNone;
}
void FileReader::didStartLoading()
{
fireEvent(EventTypeNames::loadstart);
}
void FileReader::didReceiveData()
{
double now = currentTimeMS();
if (!m_lastProgressNotificationTimeMS)
m_lastProgressNotificationTimeMS = now;
else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) {
fireEvent(EventTypeNames::progress);
m_lastProgressNotificationTimeMS = now;
}
}
void FileReader::didFinishLoading()
{
if (m_loadingState == LoadingStateAborted)
return;
ASSERT(m_loadingState == LoadingStateLoading);
m_loadingState = LoadingStateNone;
fireEvent(EventTypeNames::progress);
ASSERT(m_state != DONE);
m_state = DONE;
ThrottlingController::FinishReaderType finalStep = throttlingController()->removeReader(this);
fireEvent(EventTypeNames::load);
fireEvent(EventTypeNames::loadend);
throttlingController()->finishReader(this, finalStep);
}
void FileReader::didFail(FileError::ErrorCode errorCode)
{
if (m_loadingState == LoadingStateAborted)
return;
ASSERT(m_loadingState == LoadingStateLoading);
m_loadingState = LoadingStateNone;
ASSERT(m_state != DONE);
m_state = DONE;
m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
ThrottlingController::FinishReaderType finalStep = throttlingController()->removeReader(this);
fireEvent(EventTypeNames::error);
fireEvent(EventTypeNames::loadend);
throttlingController()->finishReader(this, finalStep);
}
void FileReader::fireEvent(const AtomicString& type)
{
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
{
if (!m_loader || m_error)
return nullptr;
return m_loader->arrayBufferResult();
}
String FileReader::stringResult()
{
if (!m_loader || m_error)
return String();
return m_loader->stringResult();
}
void FileReader::trace(Visitor* visitor)
{
visitor->trace(m_error);
}
}