#include "config.h"
#if ENABLE(BLOB)
#include "FileReader.h"
#include "CrossThreadTask.h"
#include "File.h"
#include "Logging.h"
#include "ProgressEvent.h"
#include "ScriptExecutionContext.h"
#include <wtf/ArrayBuffer.h>
#include <wtf/CurrentTime.h>
#include <wtf/text/CString.h>
namespace WebCore {
static const double progressNotificationIntervalMS = 50;
FileReader::FileReader(ScriptExecutionContext* context)
: ActiveDOMObject(context, this)
, m_state(None)
, m_readType(FileReaderLoader::ReadAsBinaryString)
, m_lastProgressNotificationTimeMS(0)
{
}
FileReader::~FileReader()
{
terminate();
}
bool FileReader::hasPendingActivity() const
{
return (m_state != None && m_state != Completed) || ActiveDOMObject::hasPendingActivity();
}
bool FileReader::canSuspend() const
{
return false;
}
void FileReader::stop()
{
terminate();
}
void FileReader::readAsArrayBuffer(Blob* blob)
{
if (!blob)
return;
LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", blob->url().string().utf8().data(), blob->isFile() ? static_cast<File*>(blob)->path().utf8().data() : "");
readInternal(blob, FileReaderLoader::ReadAsArrayBuffer);
}
void FileReader::readAsBinaryString(Blob* blob)
{
if (!blob)
return;
LOG(FileAPI, "FileReader: reading as binary: %s %s\n", blob->url().string().utf8().data(), blob->isFile() ? static_cast<File*>(blob)->path().utf8().data() : "");
readInternal(blob, FileReaderLoader::ReadAsBinaryString);
}
void FileReader::readAsText(Blob* blob, const String& encoding)
{
if (!blob)
return;
LOG(FileAPI, "FileReader: reading as text: %s %s\n", blob->url().string().utf8().data(), blob->isFile() ? static_cast<File*>(blob)->path().utf8().data() : "");
m_encoding = encoding;
readInternal(blob, FileReaderLoader::ReadAsText);
}
void FileReader::readAsDataURL(Blob* blob)
{
if (!blob)
return;
LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", blob->url().string().utf8().data(), blob->isFile() ? static_cast<File*>(blob)->path().utf8().data() : "");
readInternal(blob, FileReaderLoader::ReadAsDataURL);
}
static void delayedStart(ScriptExecutionContext*, FileReader* reader)
{
reader->start();
}
void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type)
{
if (m_state != None && m_state != Starting)
return;
if (m_state == None)
scriptExecutionContext()->postTask(createCallbackTask(&delayedStart, AllowAccessLater(this)));
m_blob = blob;
m_readType = type;
m_state = Starting;
}
static void delayedAbort(ScriptExecutionContext*, FileReader* reader)
{
reader->doAbort();
}
void FileReader::abort()
{
LOG(FileAPI, "FileReader: aborting\n");
if (m_state == Aborting)
return;
m_state = Aborting;
scriptExecutionContext()->postTask(
createCallbackTask(&delayedAbort, AllowAccessLater(this)));
}
void FileReader::doAbort()
{
terminate();
m_error = FileError::create(FileError::ABORT_ERR);
fireEvent(eventNames().errorEvent);
fireEvent(eventNames().abortEvent);
fireEvent(eventNames().loadendEvent);
}
void FileReader::terminate()
{
if (m_loader) {
m_loader->cancel();
m_loader = nullptr;
}
m_state = Completed;
}
void FileReader::start()
{
m_state = Opening;
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.get());
}
void FileReader::didStartLoading()
{
m_state = Reading;
fireEvent(eventNames().loadstartEvent);
}
void FileReader::didReceiveData()
{
double now = currentTimeMS();
if (!m_lastProgressNotificationTimeMS)
m_lastProgressNotificationTimeMS = now;
else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) {
fireEvent(eventNames().progressEvent);
m_lastProgressNotificationTimeMS = now;
}
}
void FileReader::didFinishLoading()
{
m_state = Completed;
fireEvent(eventNames().loadEvent);
fireEvent(eventNames().loadendEvent);
}
void FileReader::didFail(int errorCode)
{
if (m_state == Aborting)
return;
m_state = Completed;
m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
fireEvent(eventNames().errorEvent);
fireEvent(eventNames().loadendEvent);
}
void FileReader::fireEvent(const AtomicString& type)
{
dispatchEvent(ProgressEvent::create(type, true, m_loader ? m_loader->bytesLoaded() : 0, m_loader ? m_loader->totalBytes() : 0));
}
FileReader::ReadyState FileReader::readyState() const
{
switch (m_state) {
case None:
case Starting:
return EMPTY;
case Opening:
case Reading:
case Aborting:
return LOADING;
case Completed:
return DONE;
}
ASSERT_NOT_REACHED();
return EMPTY;
}
PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const
{
return m_loader ? m_loader->arrayBufferResult() : 0;
}
String FileReader::stringResult()
{
return m_loader ? m_loader->stringResult() : "";
}
}
#endif // ENABLE(BLOB)