| /* |
| * 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: |
| // - catch V8 exceptions |
| // - 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 : Noncopyable { |
| 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: |
| explicit Serializer(Writer& writer) |
| : m_writer(writer) |
| , m_depth(0) |
| , m_hasError(false) |
| { |
| } |
| |
| bool serialize(v8::Handle<v8::Value> value) |
| { |
| v8::HandleScope scope; |
| StateBase* state = doSerialize(value, 0); |
| while (state) |
| state = state->advance(*this); |
| return !m_hasError; |
| } |
| |
| // Functions used by serialization states. |
| |
| StateBase* doSerialize(v8::Handle<v8::Value> value, StateBase* next); |
| |
| 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 : public Noncopyable { |
| 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) { |
| if (StateBase* newState = serializer.doSerialize(composite()->Get(m_index), 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_propertyNames(object->GetPropertyNames()) |
| , m_index(-1) |
| , m_numSerializedProperties(0) |
| , m_nameDone(false) |
| { |
| } |
| |
| virtual StateBase* advance(Serializer& serializer) |
| { |
| ++m_index; |
| for (; m_index < m_propertyNames->Length(); ++m_index) { |
| if (m_propertyName.IsEmpty()) { |
| v8::Local<v8::Value> propertyName = m_propertyNames->Get(m_index); |
| if ((propertyName->IsString() && composite()->HasRealNamedProperty(propertyName.As<v8::String>())) |
| || (propertyName->IsUint32() && composite()->HasRealIndexedProperty(propertyName->Uint32Value()))) { |
| m_propertyName = propertyName; |
| } else |
| 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); |
| m_nameDone = false; |
| m_propertyName.Clear(); |
| ++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(state); |
| } |
| |
| StateBase* pop(StateBase* state) |
| { |
| ASSERT(state); |
| --m_depth; |
| StateBase* next = state->nextState(); |
| delete state; |
| return next; |
| } |
| |
| StateBase* handleError(StateBase* state) |
| { |
| m_hasError = true; |
| 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 |
| // - support File, etc. |
| return new ObjectState(object, next); |
| } |
| |
| Writer& m_writer; |
| int m_depth; |
| bool m_hasError; |
| }; |
| |
| Serializer::StateBase* Serializer::doSerialize(v8::Handle<v8::Value> value, StateBase* 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); |
| } |
| |
| 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, WireData)); |
| } |
| |
| PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(String data) |
| { |
| return adoptRef(new SerializedScriptValue(data, StringValue)); |
| } |
| |
| PassRefPtr<SerializedScriptValue> SerializedScriptValue::create() |
| { |
| return adoptRef(new SerializedScriptValue()); |
| } |
| |
| SerializedScriptValue* SerializedScriptValue::nullValue() |
| { |
| DEFINE_STATIC_LOCAL(RefPtr<SerializedScriptValue>, nullValue, (SerializedScriptValue::create())); |
| return nullValue.get(); |
| } |
| |
| PassRefPtr<SerializedScriptValue> SerializedScriptValue::release() |
| { |
| RefPtr<SerializedScriptValue> result = adoptRef(new SerializedScriptValue(m_data, WireData)); |
| m_data = String().crossThreadString(); |
| return result.release(); |
| } |
| |
| SerializedScriptValue::SerializedScriptValue() |
| { |
| } |
| |
| SerializedScriptValue::SerializedScriptValue(v8::Handle<v8::Value> value, bool& didThrow) |
| { |
| didThrow = false; |
| Writer writer; |
| Serializer serializer(writer); |
| if (!serializer.serialize(value)) { |
| throwError(NOT_SUPPORTED_ERR); |
| didThrow = true; |
| return; |
| } |
| m_data = String(StringImpl::adopt(writer.data())).crossThreadString(); |
| } |
| |
| SerializedScriptValue::SerializedScriptValue(String data, StringDataMode mode) |
| { |
| if (mode == WireData) |
| m_data = data.crossThreadString(); |
| else { |
| ASSERT(mode == StringValue); |
| Writer writer; |
| writer.writeWebCoreString(data); |
| m_data = String(StringImpl::adopt(writer.data())).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 |