blob: b3115076ca8186e397a9eca4cc3648ba58992a8d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ScriptValueSerializer_h
#define ScriptValueSerializer_h
#include "bindings/core/v8/SerializationTag.h"
#include "bindings/core/v8/SerializedScriptValue.h"
#include "bindings/core/v8/V8Binding.h"
#include "core/CoreExport.h"
#include "wtf/HashMap.h"
#include "wtf/Noncopyable.h"
#include "wtf/Vector.h"
#include "wtf/text/WTFString.h"
#include "wtf/typed_arrays/ArrayBufferContents.h"
#include <v8.h>
namespace blink {
class CompositorProxy;
class DOMArrayBuffer;
class DOMArrayBufferView;
class DOMSharedArrayBuffer;
class File;
class FileList;
class ImageData;
class StaticBitmapImage;
typedef Vector<WTF::ArrayBufferContents, 1> ArrayBufferContentsArray;
typedef Vector<RefPtr<StaticBitmapImage>, 1> ImageBitmapContentsArray;
// V8ObjectMap is a map from V8 objects to arbitrary values of type T.
// V8 objects (or handles to V8 objects) cannot be used as keys in ordinary wtf::HashMaps;
// this class should be used instead. GCObject must be a subtype of v8::Object.
// Suggested usage:
// V8ObjectMap<v8::Object, int> map;
// v8::Local<v8::Object> obj = ...;
// map.set(obj, 42);
template<typename GCObject, typename T>
class V8ObjectMap {
STACK_ALLOCATED();
public:
bool contains(const v8::Local<GCObject>& handle)
{
return m_map.contains(*handle);
}
bool tryGet(const v8::Local<GCObject>& handle, T* valueOut)
{
typename HandleToT::iterator result = m_map.find(*handle);
if (result != m_map.end()) {
*valueOut = result->value;
return true;
}
return false;
}
void set(const v8::Local<GCObject>& handle, const T& value)
{
m_map.set(*handle, value);
}
private:
// This implementation uses GetIdentityHash(), which sets a hidden property on the object containing
// a random integer (or returns the one that had been previously set). This ensures that the table
// never needs to be rebuilt across garbage collections at the expense of doing additional allocation
// and making more round trips into V8. Note that since GetIdentityHash() is defined only on
// v8::Objects, this V8ObjectMap cannot be used to map v8::Strings to T (because the public V8 API
// considers a v8::String to be a v8::Primitive).
// If V8 exposes a way to get at the address of the object held by a handle, then we can produce
// an alternate implementation that does not need to do any V8-side allocation; however, it will
// need to rehash after every garbage collection because a key object may have been moved.
template<typename G>
struct V8HandlePtrHash {
STATIC_ONLY(V8HandlePtrHash);
static v8::Local<G> unsafeHandleFromRawValue(const G* value)
{
const v8::Local<G>* handle = reinterpret_cast<const v8::Local<G>*>(&value);
return *handle;
}
static unsigned hash(const G* key)
{
return static_cast<unsigned>(unsafeHandleFromRawValue(key)->GetIdentityHash());
}
static bool equal(const G* a, const G* b)
{
return unsafeHandleFromRawValue(a) == unsafeHandleFromRawValue(b);
}
// For HashArg.
static const bool safeToCompareToEmptyOrDeleted = false;
};
typedef WTF::HashMap<GCObject*, T, V8HandlePtrHash<GCObject>> HandleToT;
HandleToT m_map;
};
// SerializedScriptValueWriter is responsible for serializing primitive types and storing
// information used to reconstruct composite types.
class CORE_EXPORT SerializedScriptValueWriter {
STACK_ALLOCATED();
WTF_MAKE_NONCOPYABLE(SerializedScriptValueWriter);
public:
typedef UChar BufferValueType;
SerializedScriptValueWriter()
: m_position(0)
{
}
// Write functions for primitive types.
void writeUndefined();
void writeNull();
void writeTrue();
void writeFalse();
void writeBooleanObject(bool value);
void writeOneByteString(v8::Local<v8::String>&);
void writeUCharString(v8::Local<v8::String>&);
void writeStringObject(const char* data, int length);
void writeWebCoreString(const String&);
void writeVersion();
void writeInt32(int32_t value);
void writeUint32(uint32_t value);
void writeDate(double numberValue);
void writeNumber(double number);
void writeNumberObject(double number);
void writeBlob(const String& uuid, const String& type, unsigned long long size);
void writeBlobIndex(int blobIndex);
void writeCompositorProxy(const CompositorProxy&);
void writeFile(const File&);
void writeFileIndex(int blobIndex);
void writeFileList(const FileList&);
void writeFileListIndex(const Vector<int>& blobIndices);
void writeArrayBuffer(const DOMArrayBuffer&);
void writeArrayBufferView(const DOMArrayBufferView&);
void doWriteImageData(uint32_t width, uint32_t height, const uint8_t* pixelData, uint32_t pixelDataLength);
void writeImageData(uint32_t width, uint32_t height, const uint8_t* pixelData, uint32_t pixelDataLength);
void writeImageBitmap(uint32_t width, uint32_t height, const uint8_t* pixelData, uint32_t pixelDataLength);
void writeRegExp(v8::Local<v8::String> pattern, v8::RegExp::Flags);
void writeTransferredMessagePort(uint32_t index);
void writeTransferredArrayBuffer(uint32_t index);
void writeTransferredImageBitmap(uint32_t index);
void writeTransferredOffscreenCanvas(uint32_t index, uint32_t width, uint32_t height, uint32_t id);
void writeTransferredSharedArrayBuffer(uint32_t index);
void writeObjectReference(uint32_t reference);
void writeObject(uint32_t numProperties);
void writeSparseArray(uint32_t numProperties, uint32_t length);
void writeDenseArray(uint32_t numProperties, uint32_t length);
String takeWireString();
void writeReferenceCount(uint32_t numberOfReferences);
void writeGenerateFreshObject();
void writeGenerateFreshSparseArray(uint32_t length);
void writeGenerateFreshDenseArray(uint32_t length);
void writeGenerateFreshMap();
void writeGenerateFreshSet();
void writeMap(uint32_t length);
void writeSet(uint32_t length);
protected:
void doWriteFile(const File&);
void doWriteArrayBuffer(const DOMArrayBuffer&);
void doWriteString(const char* data, int length);
void doWriteWebCoreString(const String&);
int bytesNeededToWireEncode(uint32_t value);
template<class T>
void doWriteUintHelper(T value)
{
while (true) {
uint8_t b = (value & SerializedScriptValue::varIntMask);
value >>= SerializedScriptValue::varIntShift;
if (!value) {
append(b);
break;
}
append(b | (1 << SerializedScriptValue::varIntShift));
}
}
void doWriteUint32(uint32_t value);
void doWriteUint64(uint64_t value);
void doWriteNumber(double number);
void append(SerializationTag);
void append(uint8_t b);
void append(const uint8_t* data, int length);
void ensureSpace(unsigned extra);
void fillHole();
uint8_t* byteAt(int position);
int v8StringWriteOptions();
private:
Vector<BufferValueType> m_buffer;
unsigned m_position;
};
class CORE_EXPORT ScriptValueSerializer {
STACK_ALLOCATED();
WTF_MAKE_NONCOPYABLE(ScriptValueSerializer);
protected:
class StateBase;
public:
enum Status {
Success,
InputError,
DataCloneError,
JSException
};
ScriptValueSerializer(SerializedScriptValueWriter&, const Transferables*, WebBlobInfoArray*, BlobDataHandleMap& blobDataHandles, v8::TryCatch&, ScriptState*);
v8::Isolate* isolate() { return m_scriptState->isolate(); }
v8::Local<v8::Context> context() { return m_scriptState->context(); }
Status serialize(v8::Local<v8::Value>);
String errorMessage() { return m_errorMessage; }
protected:
class StateBase {
USING_FAST_MALLOC(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::Local<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(ScriptValueSerializer&) = 0;
protected:
StateBase(v8::Local<v8::Value> composite, StateBase* next)
: m_composite(composite)
, m_next(next)
{
}
private:
v8::Local<v8::Value> m_composite;
StateBase* m_next;
};
// Dummy state that is used to signal serialization errors.
class ErrorState final : public StateBase {
public:
ErrorState()
: StateBase(v8Undefined(), 0)
{
}
StateBase* advance(ScriptValueSerializer&) override
{
delete this;
return 0;
}
};
template <typename T>
class State : public StateBase {
public:
v8::Local<T> composite() { return v8::Local<T>::Cast(StateBase::composite()); }
protected:
State(v8::Local<T> composite, StateBase* next)
: StateBase(composite, next)
{
}
};
class AbstractObjectState : public State<v8::Object> {
public:
AbstractObjectState(v8::Local<v8::Object> object, StateBase* next)
: State<v8::Object>(object, next)
, m_index(0)
, m_numSerializedProperties(0)
, m_nameDone(false)
{
}
protected:
virtual StateBase* objectDone(unsigned numProperties, ScriptValueSerializer&) = 0;
StateBase* serializeProperties(bool ignoreIndexed, ScriptValueSerializer&);
v8::Local<v8::Array> m_propertyNames;
private:
v8::Local<v8::Value> m_propertyName;
unsigned m_index;
unsigned m_numSerializedProperties;
bool m_nameDone;
};
class ObjectState final : public AbstractObjectState {
public:
ObjectState(v8::Local<v8::Object> object, StateBase* next)
: AbstractObjectState(object, next)
{
}
StateBase* advance(ScriptValueSerializer&) override;
protected:
StateBase* objectDone(unsigned numProperties, ScriptValueSerializer&) override;
};
class DenseArrayState final : public AbstractObjectState {
public:
DenseArrayState(v8::Local<v8::Array> array, v8::Local<v8::Array> propertyNames, StateBase* next, v8::Isolate* isolate)
: AbstractObjectState(array, next)
, m_arrayIndex(0)
, m_arrayLength(array->Length())
{
m_propertyNames = v8::Local<v8::Array>::New(isolate, propertyNames);
}
StateBase* advance(ScriptValueSerializer&) override;
protected:
StateBase* objectDone(unsigned numProperties, ScriptValueSerializer&) override;
private:
uint32_t m_arrayIndex;
uint32_t m_arrayLength;
};
class SparseArrayState final : public AbstractObjectState {
public:
SparseArrayState(v8::Local<v8::Array> array, v8::Local<v8::Array> propertyNames, StateBase* next, v8::Isolate* isolate)
: AbstractObjectState(array, next)
{
m_propertyNames = v8::Local<v8::Array>::New(isolate, propertyNames);
}
StateBase* advance(ScriptValueSerializer&) override;
protected:
StateBase* objectDone(unsigned numProperties, ScriptValueSerializer&) override;
};
template <typename T>
class CollectionState : public State<T> {
public:
CollectionState(v8::Local<T> collection, StateBase* next)
: State<T>(collection, next)
, m_entries(collection->AsArray())
, m_index(0)
, m_length(m_entries->Length())
{
}
StateBase* advance(ScriptValueSerializer&) override;
private:
v8::Local<v8::Array> m_entries;
uint32_t m_index;
uint32_t m_length;
};
typedef CollectionState<v8::Map> MapState;
typedef CollectionState<v8::Set> SetState;
// Functions used by serialization states.
virtual StateBase* doSerializeValue(v8::Local<v8::Value>, StateBase* next);
private:
StateBase* doSerialize(v8::Local<v8::Value>, StateBase* next);
StateBase* doSerializeArrayBuffer(v8::Local<v8::Value> arrayBuffer, StateBase* next);
StateBase* checkException(StateBase*);
StateBase* writeObject(uint32_t numProperties, StateBase*);
StateBase* writeSparseArray(uint32_t numProperties, uint32_t length, StateBase*);
StateBase* writeDenseArray(uint32_t numProperties, uint32_t length, StateBase*);
template <typename T> StateBase* writeCollection(uint32_t length, StateBase*);
StateBase* push(StateBase* state)
{
ASSERT(state);
++m_depth;
return checkComposite(state) ? state : handleError(InputError, "Value being cloned is either cyclic or too deeply nested.", state);
}
StateBase* pop(StateBase* state)
{
ASSERT(state);
--m_depth;
StateBase* next = state->nextState();
delete state;
return next;
}
bool checkComposite(StateBase* top);
void writeString(v8::Local<v8::Value>);
void writeStringObject(v8::Local<v8::Value>);
void writeNumberObject(v8::Local<v8::Value>);
void writeBooleanObject(v8::Local<v8::Value>);
StateBase* writeBlob(v8::Local<v8::Value>, StateBase* next);
StateBase* writeCompositorProxy(v8::Local<v8::Value>, StateBase* next);
StateBase* writeFile(v8::Local<v8::Value>, StateBase* next);
StateBase* writeFileList(v8::Local<v8::Value>, StateBase* next);
void writeImageData(v8::Local<v8::Value>);
StateBase* writeImageBitmap(v8::Local<v8::Value>, StateBase* next);
void writeRegExp(v8::Local<v8::Value>);
StateBase* writeAndGreyArrayBufferView(v8::Local<v8::Object>, StateBase* next);
StateBase* writeArrayBuffer(v8::Local<v8::Value>, StateBase* next);
StateBase* writeTransferredArrayBuffer(v8::Local<v8::Value>, uint32_t index, StateBase* next);
StateBase* writeTransferredImageBitmap(v8::Local<v8::Value>, uint32_t index, StateBase* next);
StateBase* writeTransferredOffscreenCanvas(v8::Local<v8::Value>, uint32_t index, StateBase* next);
StateBase* writeTransferredSharedArrayBuffer(v8::Local<v8::Value>, uint32_t index, StateBase* next);
static bool shouldSerializeDensely(uint32_t length, uint32_t propertyCount);
StateBase* startArrayState(v8::Local<v8::Array>, StateBase* next);
StateBase* startObjectState(v8::Local<v8::Object>, StateBase* next);
StateBase* startMapState(v8::Local<v8::Map>, StateBase* next);
StateBase* startSetState(v8::Local<v8::Set>, StateBase* next);
bool appendBlobInfo(const String& uuid, const String& type, unsigned long long size, int* index);
bool appendFileInfo(const File*, int* index);
protected:
// Marks object as having been visited by the serializer and assigns it a unique object reference ID.
// An object may only be greyed once.
void greyObject(const v8::Local<v8::Object>&);
StateBase* handleError(Status errorStatus, const String& message, StateBase*);
SerializedScriptValueWriter& writer() { return m_writer; }
uint32_t nextObjectReference() const { return m_nextObjectReference; }
private:
void copyTransferables(const Transferables&);
RefPtr<ScriptState> m_scriptState;
SerializedScriptValueWriter& m_writer;
v8::TryCatch& m_tryCatch;
int m_depth;
Status m_status;
String m_errorMessage;
typedef V8ObjectMap<v8::Object, uint32_t> ObjectPool;
ObjectPool m_objectPool;
ObjectPool m_transferredMessagePorts;
ObjectPool m_transferredArrayBuffers;
ObjectPool m_transferredImageBitmaps;
ObjectPool m_transferredOffscreenCanvas;
uint32_t m_nextObjectReference;
WebBlobInfoArray* m_blobInfo;
BlobDataHandleMap& m_blobDataHandles;
};
// Interface used by SerializedScriptValueReader to create objects of composite types.
class CORE_EXPORT ScriptValueCompositeCreator {
STACK_ALLOCATED();
WTF_MAKE_NONCOPYABLE(ScriptValueCompositeCreator);
public:
ScriptValueCompositeCreator() { }
virtual ~ScriptValueCompositeCreator() { }
virtual bool consumeTopOfStack(v8::Local<v8::Value>*) = 0;
virtual uint32_t objectReferenceCount() = 0;
virtual void pushObjectReference(const v8::Local<v8::Value>&) = 0;
virtual bool tryGetObjectFromObjectReference(uint32_t reference, v8::Local<v8::Value>*) = 0;
virtual bool tryGetTransferredMessagePort(uint32_t index, v8::Local<v8::Value>*) = 0;
virtual bool tryGetTransferredArrayBuffer(uint32_t index, v8::Local<v8::Value>*) = 0;
virtual bool tryGetTransferredImageBitmap(uint32_t index, v8::Local<v8::Value>*) = 0;
virtual bool tryGetTransferredOffscreenCanvas(uint32_t index, uint32_t width, uint32_t height, uint32_t id, v8::Local<v8::Value>*) = 0;
virtual bool tryGetTransferredSharedArrayBuffer(uint32_t index, v8::Local<v8::Value>*) = 0;
virtual bool newSparseArray(uint32_t length) = 0;
virtual bool newDenseArray(uint32_t length) = 0;
virtual bool newMap() = 0;
virtual bool newSet() = 0;
virtual bool newObject() = 0;
virtual bool completeObject(uint32_t numProperties, v8::Local<v8::Value>*) = 0;
virtual bool completeSparseArray(uint32_t numProperties, uint32_t length, v8::Local<v8::Value>*) = 0;
virtual bool completeDenseArray(uint32_t numProperties, uint32_t length, v8::Local<v8::Value>*) = 0;
virtual bool completeMap(uint32_t length, v8::Local<v8::Value>*) = 0;
virtual bool completeSet(uint32_t length, v8::Local<v8::Value>*) = 0;
};
// SerializedScriptValueReader is responsible for deserializing primitive types and
// restoring information about saved objects of composite types.
class CORE_EXPORT SerializedScriptValueReader {
STACK_ALLOCATED();
WTF_MAKE_NONCOPYABLE(SerializedScriptValueReader);
public:
SerializedScriptValueReader(const uint8_t* buffer, int length, const WebBlobInfoArray* blobInfo, BlobDataHandleMap& blobDataHandles, ScriptState* scriptState)
: m_scriptState(scriptState)
, m_buffer(buffer)
, m_length(length)
, m_position(0)
, m_version(0)
, m_blobInfo(blobInfo)
, m_blobDataHandles(blobDataHandles)
{
ASSERT(!(reinterpret_cast<size_t>(buffer) & 1));
ASSERT(length >= 0);
}
bool isEof() const { return m_position >= m_length; }
ScriptState* getScriptState() const { return m_scriptState.get(); }
protected:
v8::Isolate* isolate() const { return m_scriptState->isolate(); }
v8::Local<v8::Context> context() const { return m_scriptState->context(); }
unsigned length() const { return m_length; }
unsigned position() const { return m_position; }
const uint8_t* allocate(uint32_t size)
{
const uint8_t* allocated = m_buffer + m_position;
m_position += size;
return allocated;
}
public:
virtual bool read(v8::Local<v8::Value>*, ScriptValueCompositeCreator&);
bool readVersion(uint32_t& version);
void setVersion(uint32_t);
protected:
bool readWithTag(SerializationTag, v8::Local<v8::Value>*, ScriptValueCompositeCreator&);
bool readTag(SerializationTag*);
bool readWebCoreString(String*);
bool readUint32(v8::Local<v8::Value>*);
bool doReadUint32(uint32_t* value);
private:
void undoReadTag();
bool readArrayBufferViewSubTag(ArrayBufferViewSubTag*);
bool readString(v8::Local<v8::Value>*);
bool readUCharString(v8::Local<v8::Value>*);
bool readStringObject(v8::Local<v8::Value>*);
bool readInt32(v8::Local<v8::Value>*);
bool readDate(v8::Local<v8::Value>*);
bool readNumber(v8::Local<v8::Value>*);
bool readNumberObject(v8::Local<v8::Value>*);
ImageData* doReadImageData();
bool readImageData(v8::Local<v8::Value>*);
bool readImageBitmap(v8::Local<v8::Value>*);
bool readCompositorProxy(v8::Local<v8::Value>*);
DOMArrayBuffer* doReadArrayBuffer();
bool readArrayBuffer(v8::Local<v8::Value>*);
bool readArrayBufferView(v8::Local<v8::Value>*, ScriptValueCompositeCreator&);
bool readRegExp(v8::Local<v8::Value>*);
bool readBlob(v8::Local<v8::Value>*, bool isIndexed);
bool readFile(v8::Local<v8::Value>*, bool isIndexed);
bool readFileList(v8::Local<v8::Value>*, bool isIndexed);
File* readFileHelper();
File* readFileIndexHelper();
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 & SerializedScriptValue::varIntMask) << shift);
shift += SerializedScriptValue::varIntShift;
} while (currentByte & (1 << SerializedScriptValue::varIntShift));
return true;
}
bool doReadUint64(uint64_t* value);
bool doReadNumber(double* number);
PassRefPtr<BlobDataHandle> getOrCreateBlobDataHandle(const String& uuid, const String& type, long long size = -1);
private:
RefPtr<ScriptState> m_scriptState;
const uint8_t* m_buffer;
const unsigned m_length;
unsigned m_position;
uint32_t m_version;
const WebBlobInfoArray* m_blobInfo;
const BlobDataHandleMap& m_blobDataHandles;
};
class CORE_EXPORT ScriptValueDeserializer : public ScriptValueCompositeCreator {
STACK_ALLOCATED();
WTF_MAKE_NONCOPYABLE(ScriptValueDeserializer);
public:
ScriptValueDeserializer(SerializedScriptValueReader& reader, MessagePortArray* messagePorts, ArrayBufferContentsArray* arrayBufferContents, ImageBitmapContentsArray* imageBitmapContents)
: m_reader(reader)
, m_transferredMessagePorts(messagePorts)
, m_arrayBufferContents(arrayBufferContents)
, m_imageBitmapContents(imageBitmapContents)
, m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0)
, m_imageBitmaps(imageBitmapContents ? imageBitmapContents->size() : 0)
, m_version(0)
{
}
v8::Local<v8::Value> deserialize();
bool newSparseArray(uint32_t) override;
bool newDenseArray(uint32_t length) override;
bool newMap() override;
bool newSet() override;
bool consumeTopOfStack(v8::Local<v8::Value>*) override;
bool newObject() override;
bool completeObject(uint32_t numProperties, v8::Local<v8::Value>*) override;
bool completeSparseArray(uint32_t numProperties, uint32_t length, v8::Local<v8::Value>*) override;
bool completeDenseArray(uint32_t numProperties, uint32_t length, v8::Local<v8::Value>*) override;
bool completeMap(uint32_t length, v8::Local<v8::Value>*) override;
bool completeSet(uint32_t length, v8::Local<v8::Value>*) override;
void pushObjectReference(const v8::Local<v8::Value>&) override;
bool tryGetTransferredMessagePort(uint32_t index, v8::Local<v8::Value>*) override;
bool tryGetTransferredArrayBuffer(uint32_t index, v8::Local<v8::Value>*) override;
bool tryGetTransferredImageBitmap(uint32_t index, v8::Local<v8::Value>*) override;
bool tryGetTransferredOffscreenCanvas(uint32_t index, uint32_t width, uint32_t height, uint32_t id, v8::Local<v8::Value>*) override;
bool tryGetTransferredSharedArrayBuffer(uint32_t index, v8::Local<v8::Value>*) override;
bool tryGetObjectFromObjectReference(uint32_t reference, v8::Local<v8::Value>*) override;
uint32_t objectReferenceCount() override;
protected:
SerializedScriptValueReader& reader() { return m_reader; }
virtual bool read(v8::Local<v8::Value>*);
private:
bool initializeObject(v8::Local<v8::Object>, uint32_t numProperties, v8::Local<v8::Value>*);
bool doDeserialize();
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);
void openComposite(const v8::Local<v8::Value>&);
bool closeComposite(v8::Local<v8::Value>*);
SerializedScriptValueReader& m_reader;
Vector<v8::Local<v8::Value>> m_stack;
Vector<v8::Local<v8::Value>> m_objectPool;
Vector<uint32_t> m_openCompositeReferenceStack;
MessagePortArray* m_transferredMessagePorts;
ArrayBufferContentsArray* m_arrayBufferContents;
ImageBitmapContentsArray* m_imageBitmapContents;
Vector<v8::Local<v8::Value>> m_arrayBuffers;
Vector<v8::Local<v8::Value>> m_imageBitmaps;
uint32_t m_version;
};
} // namespace blink
#endif // ScriptValueSerializer_h