blob: 04ef2b1b8dbad01e23c64ab6cee7d02f17028d01 [file] [log] [blame]
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "SerializedScriptValue.h"
#include "Blob.h"
#include "ByteArray.h"
#include "CanvasPixelArray.h"
#include "ExceptionCode.h"
#include "File.h"
#include "FileList.h"
#include "ImageData.h"
#include "SharedBuffer.h"
#include "V8Binding.h"
#include "V8Blob.h"
#include "V8File.h"
#include "V8FileList.h"
#include "V8ImageData.h"
#include "V8Proxy.h"
#include "V8Utilities.h"
#include <wtf/Assertions.h>
#include <wtf/RefCounted.h>
#include <wtf/Vector.h>
// FIXME: consider crashing in debug mode on deserialization errors
namespace WebCore {
namespace {
typedef UChar BufferValueType;
// Serialization format is a sequence of (tag, optional data)
// pairs. Tag always takes exactly one byte.
enum SerializationTag {
InvalidTag = '!',
PaddingTag = '\0',
UndefinedTag = '_',
NullTag = '0',
TrueTag = 'T',
FalseTag = 'F',
StringTag = 'S',
Int32Tag = 'I',
Uint32Tag = 'U',
DateTag = 'D',
NumberTag = 'N',
BlobTag = 'b',
FileTag = 'f',
FileListTag = 'l',
ImageDataTag = '#',
ArrayTag = '[',
ObjectTag = '{',
SparseArrayTag = '@',
RegExpTag = 'R',
};
static bool shouldCheckForCycles(int depth)
{
ASSERT(depth >= 0);
// Since we are not required to spot the cycle as soon as it
// happens we can check for cycles only when the current depth
// is a power of two.
return !(depth & (depth - 1));
}
static const int maxDepth = 20000;
// VarInt encoding constants.
static const int varIntShift = 7;
static const int varIntMask = (1 << varIntShift) - 1;
// ZigZag encoding helps VarInt encoding stay small for negative
// numbers with small absolute values.
class ZigZag {
public:
static uint32_t encode(uint32_t value)
{
if (value & (1U << 31))
value = ((~value) << 1) + 1;
else
value <<= 1;
return value;
}
static uint32_t decode(uint32_t value)
{
if (value & 1)
value = ~(value >> 1);
else
value >>= 1;
return value;
}
private:
ZigZag();
};
// Writer is responsible for serializing primitive types and storing
// information used to reconstruct composite types.
class Writer {
WTF_MAKE_NONCOPYABLE(Writer);
public:
Writer()
: m_position(0)
{
}
// Write functions for primitive types.
void writeUndefined() { append(UndefinedTag); }
void writeNull() { append(NullTag); }
void writeTrue() { append(TrueTag); }
void writeFalse() { append(FalseTag); }
void writeString(const char* data, int length)
{
ASSERT(length >= 0);
append(StringTag);
doWriteString(data, length);
}
void writeWebCoreString(const String& string)
{
// Uses UTF8 encoding so we can read it back as either V8 or
// WebCore string.
append(StringTag);
doWriteWebCoreString(string);
}
void writeInt32(int32_t value)
{
append(Int32Tag);
doWriteUint32(ZigZag::encode(static_cast<uint32_t>(value)));
}
void writeUint32(uint32_t value)
{
append(Uint32Tag);
doWriteUint32(value);
}
void writeDate(double numberValue)
{
append(DateTag);
doWriteNumber(numberValue);
}
void writeNumber(double number)
{
append(NumberTag);
doWriteNumber(number);
}
void writeBlob(const String& url, const String& type, unsigned long long size)
{
append(BlobTag);
doWriteWebCoreString(url);
doWriteWebCoreString(type);
doWriteUint64(size);
}
void writeFile(const String& path, const String& url, const String& type)
{
append(FileTag);
doWriteWebCoreString(path);
doWriteWebCoreString(url);
doWriteWebCoreString(type);
}
void writeFileList(const FileList& fileList)
{
append(FileListTag);
uint32_t length = fileList.length();
doWriteUint32(length);
for (unsigned i = 0; i < length; ++i) {
doWriteWebCoreString(fileList.item(i)->path());
doWriteWebCoreString(fileList.item(i)->url().string());
doWriteWebCoreString(fileList.item(i)->type());
}
}
void writeImageData(uint32_t width, uint32_t height, const uint8_t* pixelData, uint32_t pixelDataLength)
{
append(ImageDataTag);
doWriteUint32(width);
doWriteUint32(height);
doWriteUint32(pixelDataLength);
append(pixelData, pixelDataLength);
}
void writeRegExp(v8::Local<v8::String> pattern, v8::RegExp::Flags flags)
{
append(RegExpTag);
v8::String::Utf8Value patternUtf8Value(pattern);
doWriteString(*patternUtf8Value, patternUtf8Value.length());
doWriteUint32(static_cast<uint32_t>(flags));
}
void writeArray(uint32_t length)
{
append(ArrayTag);
doWriteUint32(length);
}
void writeObject(uint32_t numProperties)
{
append(ObjectTag);
doWriteUint32(numProperties);
}
void writeSparseArray(uint32_t numProperties, uint32_t length)
{
append(SparseArrayTag);
doWriteUint32(numProperties);
doWriteUint32(length);
}
Vector<BufferValueType>& data()
{
fillHole();
return m_buffer;
}
private:
void doWriteString(const char* data, int length)
{
doWriteUint32(static_cast<uint32_t>(length));
append(reinterpret_cast<const uint8_t*>(data), length);
}
void doWriteWebCoreString(const String& string)
{
RefPtr<SharedBuffer> buffer = utf8Buffer(string);
doWriteString(buffer->data(), buffer->size());
}
template<class T>
void doWriteUintHelper(T value)
{
while (true) {
uint8_t b = (value & varIntMask);
value >>= varIntShift;
if (!value) {
append(b);
break;
}
append(b | (1 << varIntShift));
}
}
void doWriteUint32(uint32_t value)
{
doWriteUintHelper(value);
}
void doWriteUint64(uint64_t value)
{
doWriteUintHelper(value);
}
void doWriteNumber(double number)
{
append(reinterpret_cast<uint8_t*>(&number), sizeof(number));
}
void append(SerializationTag tag)
{
append(static_cast<uint8_t>(tag));
}
void append(uint8_t b)
{
ensureSpace(1);
*byteAt(m_position++) = b;
}
void append(const uint8_t* data, int length)
{
ensureSpace(length);
memcpy(byteAt(m_position), data, length);
m_position += length;
}
void ensureSpace(int extra)
{
COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes);
m_buffer.grow((m_position + extra + 1) / 2); // "+ 1" to round up.
}
void fillHole()
{
COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes);
// If the writer is at odd position in the buffer, then one of
// the bytes in the last UChar is not initialized.
if (m_position % 2)
*byteAt(m_position) = static_cast<uint8_t>(PaddingTag);
}
uint8_t* byteAt(int position) { return reinterpret_cast<uint8_t*>(m_buffer.data()) + position; }
Vector<BufferValueType> m_buffer;
unsigned m_position;
};
class Serializer {
class StateBase;
public:
enum Status {
Success,
InputError,
JSException,
JSFailure
};
Serializer(Writer& writer, v8::TryCatch& tryCatch)
: m_writer(writer)
, m_tryCatch(tryCatch)
, m_depth(0)
, m_status(Success)
{
ASSERT(!tryCatch.HasCaught());
}
Status serialize(v8::Handle<v8::Value> value)
{
v8::HandleScope scope;
StateBase* state = doSerialize(value, 0);
while (state)
state = state->advance(*this);
return m_status;
}
// Functions used by serialization states.
StateBase* doSerialize(v8::Handle<v8::Value> value, StateBase* next);
StateBase* checkException(StateBase* state)
{
return m_tryCatch.HasCaught() ? handleError(JSException, state) : 0;
}
StateBase* reportFailure(StateBase* state)
{
return handleError(JSFailure, state);
}
StateBase* writeArray(uint32_t length, StateBase* state)
{
m_writer.writeArray(length);
return pop(state);
}
StateBase* writeObject(uint32_t numProperties, StateBase* state)
{
m_writer.writeObject(numProperties);
return pop(state);
}
StateBase* writeSparseArray(uint32_t numProperties, uint32_t length, StateBase* state)
{
m_writer.writeSparseArray(numProperties, length);
return pop(state);
}
private:
class StateBase {
WTF_MAKE_NONCOPYABLE(StateBase);
public:
virtual ~StateBase() { }
// Link to the next state to form a stack.
StateBase* nextState() { return m_next; }
// Composite object we're processing in this state.
v8::Handle<v8::Value> composite() { return m_composite; }
// Serializes (a part of) the current composite and returns
// the next state to process or null when this is the final
// state.
virtual StateBase* advance(Serializer&) = 0;
protected:
StateBase(v8::Handle<v8::Value> composite, StateBase* next)
: m_composite(composite)
, m_next(next)
{
}
private:
v8::Handle<v8::Value> m_composite;
StateBase* m_next;
};
// Dummy state that is used to signal serialization errors.
class ErrorState : public StateBase {
public:
ErrorState()
: StateBase(v8::Handle<v8::Value>(), 0)
{
}
virtual StateBase* advance(Serializer&)
{
delete this;
return 0;
}
};
template <typename T>
class State : public StateBase {
public:
v8::Handle<T> composite() { return v8::Handle<T>::Cast(StateBase::composite()); }
protected:
State(v8::Handle<T> composite, StateBase* next)
: StateBase(composite, next)
{
}
};
#if 0
// Currently unused, see comment in newArrayState.
class ArrayState : public State<v8::Array> {
public:
ArrayState(v8::Handle<v8::Array> array, StateBase* next)
: State<v8::Array>(array, next)
, m_index(-1)
{
}
virtual StateBase* advance(Serializer& serializer)
{
++m_index;
for (; m_index < composite()->Length(); ++m_index) {
v8::Handle<v8::Value> value = composite()->Get(m_index);
if (StateBase* newState = serializer.checkException(this))
return newState;
if (StateBase* newState = serializer.doSerialize(value, this))
return newState;
}
return serializer.writeArray(composite()->Length(), this);
}
private:
unsigned m_index;
};
#endif
class AbstractObjectState : public State<v8::Object> {
public:
AbstractObjectState(v8::Handle<v8::Object> object, StateBase* next)
: State<v8::Object>(object, next)
, m_index(0)
, m_numSerializedProperties(0)
, m_nameDone(false)
{
}
virtual StateBase* advance(Serializer& serializer)
{
if (!m_index) {
m_propertyNames = composite()->GetPropertyNames();
if (StateBase* newState = serializer.checkException(this))
return newState;
if (m_propertyNames.IsEmpty())
return serializer.reportFailure(this);
}
while (m_index < m_propertyNames->Length()) {
if (!m_nameDone) {
v8::Local<v8::Value> propertyName = m_propertyNames->Get(m_index);
if (StateBase* newState = serializer.checkException(this))
return newState;
if (propertyName.IsEmpty())
return serializer.reportFailure(this);
bool hasStringProperty = propertyName->IsString() && composite()->HasRealNamedProperty(propertyName.As<v8::String>());
if (StateBase* newState = serializer.checkException(this))
return newState;
bool hasIndexedProperty = !hasStringProperty && propertyName->IsUint32() && composite()->HasRealIndexedProperty(propertyName->Uint32Value());
if (StateBase* newState = serializer.checkException(this))
return newState;
if (hasStringProperty || hasIndexedProperty)
m_propertyName = propertyName;
else {
++m_index;
continue;
}
}
ASSERT(!m_propertyName.IsEmpty());
if (!m_nameDone) {
m_nameDone = true;
if (StateBase* newState = serializer.doSerialize(m_propertyName, this))
return newState;
}
v8::Local<v8::Value> value = composite()->Get(m_propertyName);
if (StateBase* newState = serializer.checkException(this))
return newState;
m_nameDone = false;
m_propertyName.Clear();
++m_index;
++m_numSerializedProperties;
if (StateBase* newState = serializer.doSerialize(value, this))
return newState;
}
return objectDone(m_numSerializedProperties, serializer);
}
protected:
virtual StateBase* objectDone(unsigned numProperties, Serializer&) = 0;
private:
v8::Local<v8::Array> m_propertyNames;
v8::Local<v8::Value> m_propertyName;
unsigned m_index;
unsigned m_numSerializedProperties;
bool m_nameDone;
};
class ObjectState : public AbstractObjectState {
public:
ObjectState(v8::Handle<v8::Object> object, StateBase* next)
: AbstractObjectState(object, next)
{
}
protected:
virtual StateBase* objectDone(unsigned numProperties, Serializer& serializer)
{
return serializer.writeObject(numProperties, this);
}
};
class SparseArrayState : public AbstractObjectState {
public:
SparseArrayState(v8::Handle<v8::Array> array, StateBase* next)
: AbstractObjectState(array, next)
{
}
protected:
virtual StateBase* objectDone(unsigned numProperties, Serializer& serializer)
{
return serializer.writeSparseArray(numProperties, composite().As<v8::Array>()->Length(), this);
}
};
StateBase* push(StateBase* state)
{
ASSERT(state);
++m_depth;
return checkComposite(state) ? state : handleError(InputError, state);
}
StateBase* pop(StateBase* state)
{
ASSERT(state);
--m_depth;
StateBase* next = state->nextState();
delete state;
return next;
}
StateBase* handleError(Status errorStatus, StateBase* state)
{
ASSERT(errorStatus != Success);
m_status = errorStatus;
while (state) {
StateBase* tmp = state->nextState();
delete state;
state = tmp;
}
return new ErrorState;
}
bool checkComposite(StateBase* top)
{
ASSERT(top);
if (m_depth > maxDepth)
return false;
if (!shouldCheckForCycles(m_depth))
return true;
v8::Handle<v8::Value> composite = top->composite();
for (StateBase* state = top->nextState(); state; state = state->nextState()) {
if (state->composite() == composite)
return false;
}
return true;
}
void writeString(v8::Handle<v8::Value> value)
{
v8::String::Utf8Value stringValue(value);
m_writer.writeString(*stringValue, stringValue.length());
}
void writeBlob(v8::Handle<v8::Value> value)
{
Blob* blob = V8Blob::toNative(value.As<v8::Object>());
if (!blob)
return;
m_writer.writeBlob(blob->url().string(), blob->type(), blob->size());
}
void writeFile(v8::Handle<v8::Value> value)
{
File* file = V8File::toNative(value.As<v8::Object>());
if (!file)
return;
m_writer.writeFile(file->path(), file->url().string(), file->type());
}
void writeFileList(v8::Handle<v8::Value> value)
{
FileList* fileList = V8FileList::toNative(value.As<v8::Object>());
if (!fileList)
return;
m_writer.writeFileList(*fileList);
}
void writeImageData(v8::Handle<v8::Value> value)
{
ImageData* imageData = V8ImageData::toNative(value.As<v8::Object>());
if (!imageData)
return;
WTF::ByteArray* pixelArray = imageData->data()->data();
m_writer.writeImageData(imageData->width(), imageData->height(), pixelArray->data(), pixelArray->length());
}
void writeRegExp(v8::Handle<v8::Value> value)
{
v8::Handle<v8::RegExp> regExp = value.As<v8::RegExp>();
m_writer.writeRegExp(regExp->GetSource(), regExp->GetFlags());
}
static StateBase* newArrayState(v8::Handle<v8::Array> array, StateBase* next)
{
// FIXME: use plain Array state when we can quickly check that
// an array is not sparse and has only indexed properties.
return new SparseArrayState(array, next);
}
static StateBase* newObjectState(v8::Handle<v8::Object> object, StateBase* next)
{
// FIXME: check not a wrapper
return new ObjectState(object, next);
}
Writer& m_writer;
v8::TryCatch& m_tryCatch;
int m_depth;
Status m_status;
};
Serializer::StateBase* Serializer::doSerialize(v8::Handle<v8::Value> value, StateBase* next)
{
if (value.IsEmpty())
return reportFailure(next);
if (value->IsUndefined())
m_writer.writeUndefined();
else if (value->IsNull())
m_writer.writeNull();
else if (value->IsTrue())
m_writer.writeTrue();
else if (value->IsFalse())
m_writer.writeFalse();
else if (value->IsInt32())
m_writer.writeInt32(value->Int32Value());
else if (value->IsUint32())
m_writer.writeUint32(value->Uint32Value());
else if (value->IsDate())
m_writer.writeDate(value->NumberValue());
else if (value->IsNumber())
m_writer.writeNumber(value.As<v8::Number>()->Value());
else if (value->IsString())
writeString(value);
else if (value->IsArray())
return push(newArrayState(value.As<v8::Array>(), next));
else if (V8File::HasInstance(value))
writeFile(value);
else if (V8Blob::HasInstance(value))
writeBlob(value);
else if (V8FileList::HasInstance(value))
writeFileList(value);
else if (V8ImageData::HasInstance(value))
writeImageData(value);
else if (value->IsRegExp())
writeRegExp(value);
else if (value->IsObject())
return push(newObjectState(value.As<v8::Object>(), next));
return 0;
}
// Interface used by Reader to create objects of composite types.
class CompositeCreator {
public:
virtual ~CompositeCreator() { }
virtual bool createArray(uint32_t length, v8::Handle<v8::Value>* value) = 0;
virtual bool createObject(uint32_t numProperties, v8::Handle<v8::Value>* value) = 0;
virtual bool createSparseArray(uint32_t numProperties, uint32_t length, v8::Handle<v8::Value>* value) = 0;
};
// Reader is responsible for deserializing primitive types and
// restoring information about saved objects of composite types.
class Reader {
public:
Reader(const uint8_t* buffer, int length)
: m_buffer(buffer)
, m_length(length)
, m_position(0)
{
ASSERT(length >= 0);
}
bool isEof() const { return m_position >= m_length; }
bool read(v8::Handle<v8::Value>* value, CompositeCreator& creator)
{
SerializationTag tag;
if (!readTag(&tag))
return false;
switch (tag) {
case InvalidTag:
return false;
case PaddingTag:
return true;
case UndefinedTag:
*value = v8::Undefined();
break;
case NullTag:
*value = v8::Null();
break;
case TrueTag:
*value = v8::True();
break;
case FalseTag:
*value = v8::False();
break;
case StringTag:
if (!readString(value))
return false;
break;
case Int32Tag:
if (!readInt32(value))
return false;
break;
case Uint32Tag:
if (!readUint32(value))
return false;
break;
case DateTag:
if (!readDate(value))
return false;
break;
case NumberTag:
if (!readNumber(value))
return false;
break;
case BlobTag:
if (!readBlob(value))
return false;
break;
case FileTag:
if (!readFile(value))
return false;
break;
case FileListTag:
if (!readFileList(value))
return false;
break;
case ImageDataTag:
if (!readImageData(value))
return false;
break;
case ArrayTag: {
uint32_t length;
if (!doReadUint32(&length))
return false;
if (!creator.createArray(length, value))
return false;
break;
}
case RegExpTag:
if (!readRegExp(value))
return false;
break;
case ObjectTag: {
uint32_t numProperties;
if (!doReadUint32(&numProperties))
return false;
if (!creator.createObject(numProperties, value))
return false;
break;
}
case SparseArrayTag: {
uint32_t numProperties;
uint32_t length;
if (!doReadUint32(&numProperties))
return false;
if (!doReadUint32(&length))
return false;
if (!creator.createSparseArray(numProperties, length, value))
return false;
break;
}
default:
return false;
}
return !value->IsEmpty();
}
private:
bool readTag(SerializationTag* tag)
{
if (m_position >= m_length)
return false;
*tag = static_cast<SerializationTag>(m_buffer[m_position++]);
return true;
}
bool readString(v8::Handle<v8::Value>* value)
{
uint32_t length;
if (!doReadUint32(&length))
return false;
if (m_position + length > m_length)
return false;
*value = v8::String::New(reinterpret_cast<const char*>(m_buffer + m_position), length);
m_position += length;
return true;
}
bool readWebCoreString(String* string)
{
uint32_t length;
if (!doReadUint32(&length))
return false;
if (m_position + length > m_length)
return false;
*string = String::fromUTF8(reinterpret_cast<const char*>(m_buffer + m_position), length);
m_position += length;
return true;
}
bool readInt32(v8::Handle<v8::Value>* value)
{
uint32_t rawValue;
if (!doReadUint32(&rawValue))
return false;
*value = v8::Integer::New(static_cast<int32_t>(ZigZag::decode(rawValue)));
return true;
}
bool readUint32(v8::Handle<v8::Value>* value)
{
uint32_t rawValue;
if (!doReadUint32(&rawValue))
return false;
*value = v8::Integer::NewFromUnsigned(rawValue);
return true;
}
bool readDate(v8::Handle<v8::Value>* value)
{
double numberValue;
if (!doReadNumber(&numberValue))
return false;
*value = v8::Date::New(numberValue);
return true;
}
bool readNumber(v8::Handle<v8::Value>* value)
{
double number;
if (!doReadNumber(&number))
return false;
*value = v8::Number::New(number);
return true;
}
bool readImageData(v8::Handle<v8::Value>* value)
{
uint32_t width;
uint32_t height;
uint32_t pixelDataLength;
if (!doReadUint32(&width))
return false;
if (!doReadUint32(&height))
return false;
if (!doReadUint32(&pixelDataLength))
return false;
if (m_position + pixelDataLength > m_length)
return false;
RefPtr<ImageData> imageData = ImageData::create(IntSize(width, height));
WTF::ByteArray* pixelArray = imageData->data()->data();
ASSERT(pixelArray);
ASSERT(pixelArray->length() >= pixelDataLength);
memcpy(pixelArray->data(), m_buffer + m_position, pixelDataLength);
m_position += pixelDataLength;
*value = toV8(imageData.release());
return true;
}
bool readRegExp(v8::Handle<v8::Value>* value)
{
v8::Handle<v8::Value> pattern;
if (!readString(&pattern))
return false;
uint32_t flags;
if (!doReadUint32(&flags))
return false;
*value = v8::RegExp::New(pattern.As<v8::String>(), static_cast<v8::RegExp::Flags>(flags));
return true;
}
bool readBlob(v8::Handle<v8::Value>* value)
{
String url;
String type;
uint64_t size;
if (!readWebCoreString(&url))
return false;
if (!readWebCoreString(&type))
return false;
if (!doReadUint64(&size))
return false;
PassRefPtr<Blob> blob = Blob::create(KURL(ParsedURLString, url), type, size);
*value = toV8(blob);
return true;
}
bool readFile(v8::Handle<v8::Value>* value)
{
String path;
String url;
String type;
if (!readWebCoreString(&path))
return false;
if (!readWebCoreString(&url))
return false;
if (!readWebCoreString(&type))
return false;
PassRefPtr<File> file = File::create(path, KURL(ParsedURLString, url), type);
*value = toV8(file);
return true;
}
bool readFileList(v8::Handle<v8::Value>* value)
{
uint32_t length;
if (!doReadUint32(&length))
return false;
PassRefPtr<FileList> fileList = FileList::create();
for (unsigned i = 0; i < length; ++i) {
String path;
String urlString;
String type;
if (!readWebCoreString(&path))
return false;
if (!readWebCoreString(&urlString))
return false;
if (!readWebCoreString(&type))
return false;
fileList->append(File::create(path, KURL(ParsedURLString, urlString), type));
}
*value = toV8(fileList);
return true;
}
template<class T>
bool doReadUintHelper(T* value)
{
*value = 0;
uint8_t currentByte;
int shift = 0;
do {
if (m_position >= m_length)
return false;
currentByte = m_buffer[m_position++];
*value |= ((currentByte & varIntMask) << shift);
shift += varIntShift;
} while (currentByte & (1 << varIntShift));
return true;
}
bool doReadUint32(uint32_t* value)
{
return doReadUintHelper(value);
}
bool doReadUint64(uint64_t* value)
{
return doReadUintHelper(value);
}
bool doReadNumber(double* number)
{
if (m_position + sizeof(double) > m_length)
return false;
uint8_t* numberAsByteArray = reinterpret_cast<uint8_t*>(number);
for (unsigned i = 0; i < sizeof(double); ++i)
numberAsByteArray[i] = m_buffer[m_position++];
return true;
}
const uint8_t* m_buffer;
const unsigned m_length;
unsigned m_position;
};
class Deserializer : public CompositeCreator {
public:
explicit Deserializer(Reader& reader)
: m_reader(reader)
{
}
v8::Handle<v8::Value> deserialize()
{
v8::HandleScope scope;
while (!m_reader.isEof()) {
if (!doDeserialize())
return v8::Null();
}
if (stackDepth() != 1)
return v8::Null();
return scope.Close(element(0));
}
virtual bool createArray(uint32_t length, v8::Handle<v8::Value>* value)
{
if (length > stackDepth())
return false;
v8::Local<v8::Array> array = v8::Array::New(length);
if (array.IsEmpty())
return false;
const int depth = stackDepth() - length;
for (unsigned i = 0; i < length; ++i)
array->Set(i, element(depth + i));
pop(length);
*value = array;
return true;
}
virtual bool createObject(uint32_t numProperties, v8::Handle<v8::Value>* value)
{
v8::Local<v8::Object> object = v8::Object::New();
if (object.IsEmpty())
return false;
return initializeObject(object, numProperties, value);
}
virtual bool createSparseArray(uint32_t numProperties, uint32_t length, v8::Handle<v8::Value>* value)
{
v8::Local<v8::Array> array = v8::Array::New(length);
if (array.IsEmpty())
return false;
return initializeObject(array, numProperties, value);
}
private:
bool initializeObject(v8::Handle<v8::Object> object, uint32_t numProperties, v8::Handle<v8::Value>* value)
{
unsigned length = 2 * numProperties;
if (length > stackDepth())
return false;
for (unsigned i = stackDepth() - length; i < stackDepth(); i += 2) {
v8::Local<v8::Value> propertyName = element(i);
v8::Local<v8::Value> propertyValue = element(i + 1);
object->Set(propertyName, propertyValue);
}
pop(length);
*value = object;
return true;
}
bool doDeserialize()
{
v8::Local<v8::Value> value;
if (!m_reader.read(&value, *this))
return false;
if (!value.IsEmpty())
push(value);
return true;
}
void push(v8::Local<v8::Value> value) { m_stack.append(value); }
void pop(unsigned length)
{
ASSERT(length <= m_stack.size());
m_stack.shrink(m_stack.size() - length);
}
unsigned stackDepth() const { return m_stack.size(); }
v8::Local<v8::Value> element(unsigned index)
{
ASSERT(index < m_stack.size());
return m_stack[index];
}
Reader& m_reader;
Vector<v8::Local<v8::Value> > m_stack;
};
} // namespace
void SerializedScriptValue::deserializeAndSetProperty(v8::Handle<v8::Object> object, const char* propertyName,
v8::PropertyAttribute attribute, SerializedScriptValue* value)
{
if (!value)
return;
v8::Handle<v8::Value> deserialized = value->deserialize();
object->ForceSet(v8::String::NewSymbol(propertyName), deserialized, attribute);
}
void SerializedScriptValue::deserializeAndSetProperty(v8::Handle<v8::Object> object, const char* propertyName,
v8::PropertyAttribute attribute, PassRefPtr<SerializedScriptValue> value)
{
deserializeAndSetProperty(object, propertyName, attribute, value.get());
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(v8::Handle<v8::Value> value, bool& didThrow)
{
return adoptRef(new SerializedScriptValue(value, didThrow));
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(v8::Handle<v8::Value> value)
{
bool didThrow;
return adoptRef(new SerializedScriptValue(value, didThrow));
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::createFromWire(String data)
{
return adoptRef(new SerializedScriptValue(data));
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(String data)
{
Writer writer;
writer.writeWebCoreString(data);
String wireData = StringImpl::adopt(writer.data());
return adoptRef(new SerializedScriptValue(wireData));
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create()
{
return adoptRef(new SerializedScriptValue());
}
SerializedScriptValue* SerializedScriptValue::nullValue()
{
DEFINE_STATIC_LOCAL(RefPtr<SerializedScriptValue>, nullValue, (0));
if (!nullValue) {
Writer writer;
writer.writeNull();
String wireData = StringImpl::adopt(writer.data());
nullValue = adoptRef(new SerializedScriptValue(wireData));
}
return nullValue.get();
}
SerializedScriptValue* SerializedScriptValue::undefinedValue()
{
DEFINE_STATIC_LOCAL(RefPtr<SerializedScriptValue>, undefinedValue, (0));
if (!undefinedValue) {
Writer writer;
writer.writeUndefined();
String wireData = StringImpl::adopt(writer.data());
undefinedValue = adoptRef(new SerializedScriptValue(wireData));
}
return undefinedValue.get();
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::release()
{
RefPtr<SerializedScriptValue> result = adoptRef(new SerializedScriptValue(m_data));
m_data = String().crossThreadString();
return result.release();
}
SerializedScriptValue::SerializedScriptValue()
{
}
SerializedScriptValue::SerializedScriptValue(v8::Handle<v8::Value> value, bool& didThrow)
{
didThrow = false;
Writer writer;
Serializer::Status status;
{
v8::TryCatch tryCatch;
Serializer serializer(writer, tryCatch);
status = serializer.serialize(value);
if (status == Serializer::JSException) {
// If there was a JS exception thrown, re-throw it.
didThrow = true;
tryCatch.ReThrow();
return;
}
}
if (status == Serializer::InputError) {
// If there was an input error, throw a new exception outside
// of the TryCatch scope.
didThrow = true;
throwError(NOT_SUPPORTED_ERR);
return;
}
if (status == Serializer::JSFailure) {
// If there was a JS failure (but no exception), there's not
// much we can do except for unwinding the C++ stack by
// pretending there was a JS exception.
didThrow = true;
return;
}
ASSERT(status == Serializer::Success);
m_data = String(StringImpl::adopt(writer.data())).crossThreadString();
}
SerializedScriptValue::SerializedScriptValue(String wireData)
{
m_data = wireData.crossThreadString();
}
v8::Handle<v8::Value> SerializedScriptValue::deserialize()
{
if (!m_data.impl())
return v8::Null();
COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes);
Reader reader(reinterpret_cast<const uint8_t*>(m_data.impl()->characters()), 2 * m_data.length());
Deserializer deserializer(reader);
return deserializer.deserialize();
}
} // namespace WebCore