#include "config.h"
#if ENABLE(FILE_READER)
#include "FileReader.h"
#include "Base64.h"
#include "Blob.h"
#include "File.h"
#include "FileStreamProxy.h"
#include "Logging.h"
#include "ProgressEvent.h"
#include "ScriptExecutionContext.h"
#include <wtf/CurrentTime.h>
namespace WebCore {
const unsigned bufferSize = 1024;
const double progressNotificationIntervalMS = 50;
FileReader::FileReader(ScriptExecutionContext* context)
: ActiveDOMObject(context, this)
, m_state(Empty)
, m_readType(ReadFileAsBinaryString)
, m_result("")
, m_isRawDataConverted(false)
, m_bytesLoaded(0)
, m_totalBytes(0)
, m_lastProgressNotificationTimeMS(0)
, m_alreadyStarted(false)
{
m_buffer.resize(bufferSize);
}
FileReader::~FileReader()
{
terminate();
}
bool FileReader::hasPendingActivity() const
{
return m_state == Loading || ActiveDOMObject::hasPendingActivity();
}
bool FileReader::canSuspend() const
{
return false;
}
void FileReader::stop()
{
terminate();
}
void FileReader::readAsBinaryString(Blob* fileBlob)
{
LOG(FileAPI, "FileReader: reading as binary: %s\n", fileBlob->path().utf8().data());
readInternal(fileBlob, ReadFileAsBinaryString);
}
void FileReader::readAsText(Blob* fileBlob, const String& encoding)
{
LOG(FileAPI, "FileReader: reading as text: %s\n", fileBlob->path().utf8().data());
if (!encoding.isEmpty())
m_encoding = TextEncoding(encoding);
readInternal(fileBlob, ReadFileAsText);
}
void FileReader::readAsDataURL(File* file)
{
LOG(FileAPI, "FileReader: reading as data URL: %s\n", file->path().utf8().data());
m_fileType = file->type();
readInternal(file, ReadFileAsDataURL);
}
void FileReader::readInternal(Blob* fileBlob, ReadType type)
{
if (m_alreadyStarted)
return;
m_fileBlob = fileBlob;
m_readType = type;
if (!m_streamProxy.get())
m_streamProxy = FileStreamProxy::create(scriptExecutionContext(), this);
}
void FileReader::abort()
{
LOG(FileAPI, "FileReader: aborting\n");
terminate();
m_result = "";
m_error = FileError::create(ABORT_ERR);
fireEvent(eventNames().errorEvent);
fireEvent(eventNames().abortEvent);
fireEvent(eventNames().loadendEvent);
}
void FileReader::terminate()
{
if (m_streamProxy) {
m_streamProxy->stop();
m_streamProxy = 0;
}
m_state = Done;
}
void FileReader::didStart()
{
m_alreadyStarted = true;
m_streamProxy->openForRead(m_fileBlob.get());
}
void FileReader::didGetSize(long long size)
{
m_state = Loading;
fireEvent(eventNames().loadstartEvent);
m_totalBytes = size;
m_streamProxy->read(&m_buffer.at(0), m_buffer.size());
}
void FileReader::didRead(const char* data, int bytesRead)
{
ASSERT(data && bytesRead);
if (m_state == Done)
return;
switch (m_readType) {
case ReadFileAsBinaryString:
m_result += String(data, static_cast<unsigned>(bytesRead));
break;
case ReadFileAsText:
case ReadFileAsDataURL:
m_rawData.append(data, static_cast<unsigned>(bytesRead));
m_isRawDataConverted = false;
break;
default:
ASSERT_NOT_REACHED();
}
m_bytesLoaded += bytesRead;
double now = WTF::currentTimeMS();
if (!m_lastProgressNotificationTimeMS)
m_lastProgressNotificationTimeMS = now;
else if (now - m_lastProgressNotificationTimeMS > progressNotificationIntervalMS) {
fireEvent(eventNames().progressEvent);
m_lastProgressNotificationTimeMS = now;
}
m_streamProxy->read(&m_buffer.at(0), m_buffer.size());
}
void FileReader::didFinish()
{
m_state = Done;
m_streamProxy->close();
fireEvent(eventNames().loadEvent);
fireEvent(eventNames().loadendEvent);
}
void FileReader::didFail(ExceptionCode ec)
{
m_state = Done;
m_error = FileError::create(ec);
m_streamProxy->close();
fireEvent(eventNames().errorEvent);
fireEvent(eventNames().loadendEvent);
}
void FileReader::fireEvent(const AtomicString& type)
{
dispatchEvent(ProgressEvent::create(type, true, static_cast<unsigned>(m_bytesLoaded), static_cast<unsigned>(m_totalBytes)));
}
const ScriptString& FileReader::result()
{
if (m_readType == ReadFileAsBinaryString)
return m_result;
if (m_isRawDataConverted)
return m_result;
m_isRawDataConverted = true;
if (m_readType == ReadFileAsText)
convertToText();
else if (m_readType == ReadFileAsDataURL && m_state == Done)
convertToDataURL();
return m_result;
}
void FileReader::convertToText()
{
if (!m_rawData.size()) {
m_result = "";
return;
}
int offset = 0;
if (!m_encoding.isValid()) {
if (m_rawData.size() >= 2 && m_rawData[0] == '\xFE' && m_rawData[1] == '\xFF') {
offset = 2;
m_encoding = UTF16BigEndianEncoding();
} else if (m_rawData.size() >= 2 && m_rawData[0] == '\xFF' && m_rawData[1] == '\xFE') {
offset = 2;
m_encoding = UTF16LittleEndianEncoding();
} else if (m_rawData.size() >= 2 && m_rawData[0] == '\xEF' && m_rawData[1] == '\xBB' && m_rawData[2] == '\xBF') {
offset = 3;
m_encoding = UTF8Encoding();
} else
m_encoding = UTF8Encoding();
}
m_result = m_encoding.decode(&m_rawData.at(0) + offset, m_rawData.size() - offset);
}
void FileReader::convertToDataURL()
{
m_result = "data:";
if (!m_rawData.size())
return;
m_result += m_fileType;
if (!m_fileType.isEmpty())
m_result += ";";
m_result += "base64,";
Vector<char> out;
base64Encode(m_rawData, out);
out.append('\0');
m_result += out.data();
}
}
#endif // ENABLE(FILE_READER)