Index: Source/bindings/core/v8/SerializedScriptValue.cpp |
diff --git a/Source/bindings/core/v8/SerializedScriptValue.cpp b/Source/bindings/core/v8/SerializedScriptValue.cpp |
index 1458b60de49c71c0b65d0a1a480aacb1d4c80a9c..72ca9d1026e57ed5fe3525cd34bdfab62060536f 100644 |
--- a/Source/bindings/core/v8/SerializedScriptValue.cpp |
+++ b/Source/bindings/core/v8/SerializedScriptValue.cpp |
@@ -31,2865 +31,33 @@ |
#include "config.h" |
#include "bindings/core/v8/SerializedScriptValue.h" |
+#include "bindings/core/v8/DOMDataStore.h" |
+#include "bindings/core/v8/DOMWrapperWorld.h" |
#include "bindings/core/v8/ExceptionState.h" |
+#include "bindings/core/v8/ScriptValueSerializer.h" |
#include "bindings/core/v8/V8ArrayBuffer.h" |
#include "bindings/core/v8/V8ArrayBufferView.h" |
#include "bindings/core/v8/V8Binding.h" |
-#include "bindings/core/v8/V8Blob.h" |
-#include "bindings/core/v8/V8DataView.h" |
-#include "bindings/core/v8/V8File.h" |
-#include "bindings/core/v8/V8FileList.h" |
-#include "bindings/core/v8/V8Float32Array.h" |
-#include "bindings/core/v8/V8Float64Array.h" |
-#include "bindings/core/v8/V8ImageData.h" |
-#include "bindings/core/v8/V8Int16Array.h" |
-#include "bindings/core/v8/V8Int32Array.h" |
-#include "bindings/core/v8/V8Int8Array.h" |
#include "bindings/core/v8/V8MessagePort.h" |
-#include "bindings/core/v8/V8Uint16Array.h" |
-#include "bindings/core/v8/V8Uint32Array.h" |
-#include "bindings/core/v8/V8Uint8Array.h" |
-#include "bindings/core/v8/V8Uint8ClampedArray.h" |
-#include "bindings/core/v8/WorkerScriptController.h" |
-#include "bindings/modules/v8/V8CryptoKey.h" |
-#include "bindings/modules/v8/V8DOMFileSystem.h" |
+#include "core/dom/DOMArrayBuffer.h" |
#include "core/dom/ExceptionCode.h" |
-#include "core/dom/MessagePort.h" |
-#include "core/fileapi/Blob.h" |
-#include "core/fileapi/File.h" |
-#include "core/fileapi/FileList.h" |
-#include "core/html/ImageData.h" |
-#include "core/html/canvas/DataView.h" |
#include "platform/SharedBuffer.h" |
+#include "platform/blob/BlobData.h" |
#include "platform/heap/Handle.h" |
#include "public/platform/Platform.h" |
-#include "public/platform/WebBlobInfo.h" |
-#include "public/platform/WebCrypto.h" |
-#include "public/platform/WebCryptoKey.h" |
-#include "public/platform/WebCryptoKeyAlgorithm.h" |
#include "wtf/ArrayBuffer.h" |
#include "wtf/ArrayBufferContents.h" |
#include "wtf/ArrayBufferView.h" |
#include "wtf/Assertions.h" |
#include "wtf/ByteOrder.h" |
-#include "wtf/Float32Array.h" |
-#include "wtf/Float64Array.h" |
-#include "wtf/Int16Array.h" |
-#include "wtf/Int32Array.h" |
-#include "wtf/Int8Array.h" |
-#include "wtf/RefCounted.h" |
-#include "wtf/Uint16Array.h" |
-#include "wtf/Uint32Array.h" |
-#include "wtf/Uint8Array.h" |
#include "wtf/Uint8ClampedArray.h" |
#include "wtf/Vector.h" |
#include "wtf/text/StringBuffer.h" |
+#include "wtf/text/StringHash.h" |
#include "wtf/text/StringUTF8Adaptor.h" |
-// FIXME: consider crashing in debug mode on deserialization errors |
-// NOTE: be sure to change wireFormatVersion as necessary! |
- |
namespace blink { |
-namespace { |
- |
-// This code implements the HTML5 Structured Clone algorithm: |
-// http://www.whatwg.org/specs/web-apps/current-work/multipage/urls.html#safe-passing-of-structured-data |
- |
-// 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::Handle<v8::Object> obj = ...; |
-// map.set(obj, 42); |
-template<typename GCObject, typename T> |
-class V8ObjectMap { |
-public: |
- bool contains(const v8::Handle<GCObject>& handle) |
- { |
- return m_map.contains(*handle); |
- } |
- |
- bool tryGet(const v8::Handle<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::Handle<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 v8::Handle<G> unsafeHandleFromRawValue(const G* value) |
- { |
- const v8::Handle<G>* handle = reinterpret_cast<const v8::Handle<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; |
-}; |
- |
-typedef UChar BufferValueType; |
- |
-// Serialization format is a sequence of tags followed by zero or more data arguments. |
-// Tags always take exactly one byte. A serialized stream first begins with |
-// a complete VersionTag. If the stream does not begin with a VersionTag, we assume that |
-// the stream is in format 0. |
- |
-// This format is private to the implementation of SerializedScriptValue. Do not rely on it |
-// externally. It is safe to persist a SerializedScriptValue as a binary blob, but this |
-// code should always be used to interpret it. |
- |
-// WebCoreStrings are read as (length:uint32_t, string:UTF8[length]). |
-// RawStrings are read as (length:uint32_t, string:UTF8[length]). |
-// RawUCharStrings are read as (length:uint32_t, string:UChar[length/sizeof(UChar)]). |
-// RawFiles are read as (path:WebCoreString, url:WebCoreStrng, type:WebCoreString). |
-// There is a reference table that maps object references (uint32_t) to v8::Values. |
-// Tokens marked with (ref) are inserted into the reference table and given the next object reference ID after decoding. |
-// All tags except InvalidTag, PaddingTag, ReferenceCountTag, VersionTag, GenerateFreshObjectTag |
-// and GenerateFreshArrayTag push their results to the deserialization stack. |
-// There is also an 'open' stack that is used to resolve circular references. Objects or arrays may |
-// contain self-references. Before we begin to deserialize the contents of these values, they |
-// are first given object reference IDs (by GenerateFreshObjectTag/GenerateFreshArrayTag); |
-// these reference IDs are then used with ObjectReferenceTag to tie the recursive knot. |
-enum SerializationTag { |
- InvalidTag = '!', // Causes deserialization to fail. |
- PaddingTag = '\0', // Is ignored (but consumed). |
- UndefinedTag = '_', // -> <undefined> |
- NullTag = '0', // -> <null> |
- TrueTag = 'T', // -> <true> |
- FalseTag = 'F', // -> <false> |
- StringTag = 'S', // string:RawString -> string |
- StringUCharTag = 'c', // string:RawUCharString -> string |
- Int32Tag = 'I', // value:ZigZag-encoded int32 -> Integer |
- Uint32Tag = 'U', // value:uint32_t -> Integer |
- DateTag = 'D', // value:double -> Date (ref) |
- MessagePortTag = 'M', // index:int -> MessagePort. Fills the result with transferred MessagePort. |
- NumberTag = 'N', // value:double -> Number |
- BlobTag = 'b', // uuid:WebCoreString, type:WebCoreString, size:uint64_t -> Blob (ref) |
- BlobIndexTag = 'i', // index:int32_t -> Blob (ref) |
- FileTag = 'f', // file:RawFile -> File (ref) |
- FileIndexTag = 'e', // index:int32_t -> File (ref) |
- DOMFileSystemTag = 'd', // type:int32_t, name:WebCoreString, uuid:WebCoreString -> FileSystem (ref) |
- FileListTag = 'l', // length:uint32_t, files:RawFile[length] -> FileList (ref) |
- FileListIndexTag = 'L', // length:uint32_t, files:int32_t[length] -> FileList (ref) |
- ImageDataTag = '#', // width:uint32_t, height:uint32_t, pixelDataLength:uint32_t, data:byte[pixelDataLength] -> ImageData (ref) |
- ObjectTag = '{', // numProperties:uint32_t -> pops the last object from the open stack; |
- // fills it with the last numProperties name,value pairs pushed onto the deserialization stack |
- SparseArrayTag = '@', // numProperties:uint32_t, length:uint32_t -> pops the last object from the open stack; |
- // fills it with the last numProperties name,value pairs pushed onto the deserialization stack |
- DenseArrayTag = '$', // numProperties:uint32_t, length:uint32_t -> pops the last object from the open stack; |
- // fills it with the last length elements and numProperties name,value pairs pushed onto deserialization stack |
- RegExpTag = 'R', // pattern:RawString, flags:uint32_t -> RegExp (ref) |
- ArrayBufferTag = 'B', // byteLength:uint32_t, data:byte[byteLength] -> ArrayBuffer (ref) |
- ArrayBufferTransferTag = 't', // index:uint32_t -> ArrayBuffer. For ArrayBuffer transfer |
- ArrayBufferViewTag = 'V', // subtag:byte, byteOffset:uint32_t, byteLength:uint32_t -> ArrayBufferView (ref). Consumes an ArrayBuffer from the top of the deserialization stack. |
- CryptoKeyTag = 'K', // subtag:byte, props, usages:uint32_t, keyDataLength:uint32_t, keyData:byte[keyDataLength] |
- // If subtag=AesKeyTag: |
- // props = keyLengthBytes:uint32_t, algorithmId:uint32_t |
- // If subtag=HmacKeyTag: |
- // props = keyLengthBytes:uint32_t, hashId:uint32_t |
- // If subtag=RsaHashedKeyTag: |
- // props = algorithmId:uint32_t, type:uint32_t, modulusLengthBits:uint32_t, publicExponentLength:uint32_t, publicExponent:byte[publicExponentLength], hashId:uint32_t |
- // If subtag=EcKeyTag: |
- // props = algorithmId:uint32_t, type:uint32_t, namedCurve:uint32_t |
- ObjectReferenceTag = '^', // ref:uint32_t -> reference table[ref] |
- GenerateFreshObjectTag = 'o', // -> empty object allocated an object ID and pushed onto the open stack (ref) |
- GenerateFreshSparseArrayTag = 'a', // length:uint32_t -> empty array[length] allocated an object ID and pushed onto the open stack (ref) |
- GenerateFreshDenseArrayTag = 'A', // length:uint32_t -> empty array[length] allocated an object ID and pushed onto the open stack (ref) |
- ReferenceCountTag = '?', // refTableSize:uint32_t -> If the reference table is not refTableSize big, fails. |
- StringObjectTag = 's', // string:RawString -> new String(string) (ref) |
- NumberObjectTag = 'n', // value:double -> new Number(value) (ref) |
- TrueObjectTag = 'y', // new Boolean(true) (ref) |
- FalseObjectTag = 'x', // new Boolean(false) (ref) |
- VersionTag = 0xFF // version:uint32_t -> Uses this as the file version. |
-}; |
- |
-enum ArrayBufferViewSubTag { |
- ByteArrayTag = 'b', |
- UnsignedByteArrayTag = 'B', |
- UnsignedByteClampedArrayTag = 'C', |
- ShortArrayTag = 'w', |
- UnsignedShortArrayTag = 'W', |
- IntArrayTag = 'd', |
- UnsignedIntArrayTag = 'D', |
- FloatArrayTag = 'f', |
- DoubleArrayTag = 'F', |
- DataViewTag = '?' |
-}; |
- |
-enum CryptoKeySubTag { |
- AesKeyTag = 1, |
- HmacKeyTag = 2, |
- // ID 3 was used by RsaKeyTag, while still behind experimental flag. |
- RsaHashedKeyTag = 4, |
- EcKeyTag = 5, |
- // Maximum allowed value is 255 |
-}; |
- |
-enum AssymetricCryptoKeyType { |
- PublicKeyType = 1, |
- PrivateKeyType = 2, |
- // Maximum allowed value is 2^32-1 |
-}; |
- |
-enum CryptoKeyAlgorithmTag { |
- AesCbcTag = 1, |
- HmacTag = 2, |
- RsaSsaPkcs1v1_5Tag = 3, |
- // ID 4 was used by RsaEs, while still behind experimental flag. |
- Sha1Tag = 5, |
- Sha256Tag = 6, |
- Sha384Tag = 7, |
- Sha512Tag = 8, |
- AesGcmTag = 9, |
- RsaOaepTag = 10, |
- AesCtrTag = 11, |
- AesKwTag = 12, |
- RsaPssTag = 13, |
- EcdsaTag = 14, |
- // Maximum allowed value is 2^32-1 |
-}; |
- |
-enum NamedCurveTag { |
- P256Tag = 1, |
- P384Tag = 2, |
- P521Tag = 3, |
-}; |
- |
-enum CryptoKeyUsage { |
- // Extractability is not a "usage" in the WebCryptoKeyUsages sense, however |
- // it fits conveniently into this bitfield. |
- ExtractableUsage = 1 << 0, |
- |
- EncryptUsage = 1 << 1, |
- DecryptUsage = 1 << 2, |
- SignUsage = 1 << 3, |
- VerifyUsage = 1 << 4, |
- DeriveKeyUsage = 1 << 5, |
- WrapKeyUsage = 1 << 6, |
- UnwrapKeyUsage = 1 << 7, |
- DeriveBitsUsage = 1 << 8, |
- // Maximum allowed value is 1 << 31 |
-}; |
- |
-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 writeBooleanObject(bool value) |
- { |
- append(value ? TrueObjectTag : FalseObjectTag); |
- } |
- |
- void writeOneByteString(v8::Handle<v8::String>& string) |
- { |
- int stringLength = string->Length(); |
- int utf8Length = string->Utf8Length(); |
- ASSERT(stringLength >= 0 && utf8Length >= 0); |
- |
- append(StringTag); |
- doWriteUint32(static_cast<uint32_t>(utf8Length)); |
- ensureSpace(utf8Length); |
- |
- // ASCII fast path. |
- if (stringLength == utf8Length) { |
- string->WriteOneByte(byteAt(m_position), 0, utf8Length, v8StringWriteOptions()); |
- } else { |
- char* buffer = reinterpret_cast<char*>(byteAt(m_position)); |
- string->WriteUtf8(buffer, utf8Length, 0, v8StringWriteOptions()); |
- } |
- m_position += utf8Length; |
- } |
- |
- void writeUCharString(v8::Handle<v8::String>& string) |
- { |
- int length = string->Length(); |
- ASSERT(length >= 0); |
- |
- int size = length * sizeof(UChar); |
- int bytes = bytesNeededToWireEncode(static_cast<uint32_t>(size)); |
- if ((m_position + 1 + bytes) & 1) |
- append(PaddingTag); |
- |
- append(StringUCharTag); |
- doWriteUint32(static_cast<uint32_t>(size)); |
- ensureSpace(size); |
- |
- ASSERT(!(m_position & 1)); |
- uint16_t* buffer = reinterpret_cast<uint16_t*>(byteAt(m_position)); |
- string->Write(buffer, 0, length, v8StringWriteOptions()); |
- m_position += size; |
- } |
- |
- void writeStringObject(const char* data, int length) |
- { |
- ASSERT(length >= 0); |
- append(StringObjectTag); |
- 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 writeVersion() |
- { |
- append(VersionTag); |
- doWriteUint32(SerializedScriptValue::wireFormatVersion); |
- } |
- |
- 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 writeNumberObject(double number) |
- { |
- append(NumberObjectTag); |
- doWriteNumber(number); |
- } |
- |
- void writeBlob(const String& uuid, const String& type, unsigned long long size) |
- { |
- append(BlobTag); |
- doWriteWebCoreString(uuid); |
- doWriteWebCoreString(type); |
- doWriteUint64(size); |
- } |
- |
- void writeBlobIndex(int blobIndex) |
- { |
- ASSERT(blobIndex >= 0); |
- append(BlobIndexTag); |
- doWriteUint32(blobIndex); |
- } |
- |
- void writeDOMFileSystem(int type, const String& name, const String& url) |
- { |
- append(DOMFileSystemTag); |
- doWriteUint32(type); |
- doWriteWebCoreString(name); |
- doWriteWebCoreString(url); |
- } |
- |
- void writeFile(const File& file) |
- { |
- append(FileTag); |
- doWriteFile(file); |
- } |
- |
- void writeFileIndex(int blobIndex) |
- { |
- append(FileIndexTag); |
- doWriteUint32(blobIndex); |
- } |
- |
- void writeFileList(const FileList& fileList) |
- { |
- append(FileListTag); |
- uint32_t length = fileList.length(); |
- doWriteUint32(length); |
- for (unsigned i = 0; i < length; ++i) |
- doWriteFile(*fileList.item(i)); |
- } |
- |
- void writeFileListIndex(const Vector<int>& blobIndices) |
- { |
- append(FileListIndexTag); |
- uint32_t length = blobIndices.size(); |
- doWriteUint32(length); |
- for (unsigned i = 0; i < length; ++i) |
- doWriteUint32(blobIndices[i]); |
- } |
- |
- bool writeCryptoKey(const WebCryptoKey& key) |
- { |
- append(static_cast<uint8_t>(CryptoKeyTag)); |
- |
- switch (key.algorithm().paramsType()) { |
- case WebCryptoKeyAlgorithmParamsTypeAes: |
- doWriteAesKey(key); |
- break; |
- case WebCryptoKeyAlgorithmParamsTypeHmac: |
- doWriteHmacKey(key); |
- break; |
- case WebCryptoKeyAlgorithmParamsTypeRsaHashed: |
- doWriteRsaHashedKey(key); |
- break; |
- case WebCryptoKeyAlgorithmParamsTypeEc: |
- doWriteEcKey(key); |
- break; |
- case WebCryptoKeyAlgorithmParamsTypeNone: |
- ASSERT_NOT_REACHED(); |
- return false; |
- } |
- |
- doWriteKeyUsages(key.usages(), key.extractable()); |
- |
- WebVector<uint8_t> keyData; |
- if (!Platform::current()->crypto()->serializeKeyForClone(key, keyData)) |
- return false; |
- |
- doWriteUint32(keyData.size()); |
- append(keyData.data(), keyData.size()); |
- return true; |
- } |
- |
- void writeArrayBuffer(const ArrayBuffer& arrayBuffer) |
- { |
- append(ArrayBufferTag); |
- doWriteArrayBuffer(arrayBuffer); |
- } |
- |
- void writeArrayBufferView(const ArrayBufferView& arrayBufferView) |
- { |
- append(ArrayBufferViewTag); |
-#if ENABLE(ASSERT) |
- const ArrayBuffer& arrayBuffer = *arrayBufferView.buffer(); |
- ASSERT(static_cast<const uint8_t*>(arrayBuffer.data()) + arrayBufferView.byteOffset() == |
- static_cast<const uint8_t*>(arrayBufferView.baseAddress())); |
-#endif |
- ArrayBufferView::ViewType type = arrayBufferView.type(); |
- |
- if (type == ArrayBufferView::TypeInt8) |
- append(ByteArrayTag); |
- else if (type == ArrayBufferView::TypeUint8Clamped) |
- append(UnsignedByteClampedArrayTag); |
- else if (type == ArrayBufferView::TypeUint8) |
- append(UnsignedByteArrayTag); |
- else if (type == ArrayBufferView::TypeInt16) |
- append(ShortArrayTag); |
- else if (type == ArrayBufferView::TypeUint16) |
- append(UnsignedShortArrayTag); |
- else if (type == ArrayBufferView::TypeInt32) |
- append(IntArrayTag); |
- else if (type == ArrayBufferView::TypeUint32) |
- append(UnsignedIntArrayTag); |
- else if (type == ArrayBufferView::TypeFloat32) |
- append(FloatArrayTag); |
- else if (type == ArrayBufferView::TypeFloat64) |
- append(DoubleArrayTag); |
- else if (type == ArrayBufferView::TypeDataView) |
- append(DataViewTag); |
- else |
- ASSERT_NOT_REACHED(); |
- doWriteUint32(arrayBufferView.byteOffset()); |
- doWriteUint32(arrayBufferView.byteLength()); |
- } |
- |
- 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 writeTransferredMessagePort(uint32_t index) |
- { |
- append(MessagePortTag); |
- doWriteUint32(index); |
- } |
- |
- void writeTransferredArrayBuffer(uint32_t index) |
- { |
- append(ArrayBufferTransferTag); |
- doWriteUint32(index); |
- } |
- |
- void writeObjectReference(uint32_t reference) |
- { |
- append(ObjectReferenceTag); |
- doWriteUint32(reference); |
- } |
- |
- void writeObject(uint32_t numProperties) |
- { |
- append(ObjectTag); |
- doWriteUint32(numProperties); |
- } |
- |
- void writeSparseArray(uint32_t numProperties, uint32_t length) |
- { |
- append(SparseArrayTag); |
- doWriteUint32(numProperties); |
- doWriteUint32(length); |
- } |
- |
- void writeDenseArray(uint32_t numProperties, uint32_t length) |
- { |
- append(DenseArrayTag); |
- doWriteUint32(numProperties); |
- doWriteUint32(length); |
- } |
- |
- String takeWireString() |
- { |
- COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes); |
- fillHole(); |
- String data = String(m_buffer.data(), m_buffer.size()); |
- data.impl()->truncateAssumingIsolated((m_position + 1) / sizeof(BufferValueType)); |
- return data; |
- } |
- |
- void writeReferenceCount(uint32_t numberOfReferences) |
- { |
- append(ReferenceCountTag); |
- doWriteUint32(numberOfReferences); |
- } |
- |
- void writeGenerateFreshObject() |
- { |
- append(GenerateFreshObjectTag); |
- } |
- |
- void writeGenerateFreshSparseArray(uint32_t length) |
- { |
- append(GenerateFreshSparseArrayTag); |
- doWriteUint32(length); |
- } |
- |
- void writeGenerateFreshDenseArray(uint32_t length) |
- { |
- append(GenerateFreshDenseArrayTag); |
- doWriteUint32(length); |
- } |
- |
-private: |
- void doWriteFile(const File& file) |
- { |
- doWriteWebCoreString(file.hasBackingFile() ? file.path() : ""); |
- doWriteWebCoreString(file.name()); |
- doWriteWebCoreString(file.webkitRelativePath()); |
- doWriteWebCoreString(file.uuid()); |
- doWriteWebCoreString(file.type()); |
- |
- // FIXME don't use 1 byte to encode a flag. |
- if (file.hasValidSnapshotMetadata()) { |
- doWriteUint32(static_cast<uint8_t>(1)); |
- |
- long long size; |
- double lastModified; |
- file.captureSnapshot(size, lastModified); |
- doWriteUint64(static_cast<uint64_t>(size)); |
- doWriteNumber(lastModified); |
- } else { |
- doWriteUint32(static_cast<uint8_t>(0)); |
- } |
- |
- doWriteUint32(static_cast<uint8_t>((file.userVisibility() == File::IsUserVisible) ? 1 : 0)); |
- } |
- |
- void doWriteArrayBuffer(const ArrayBuffer& arrayBuffer) |
- { |
- uint32_t byteLength = arrayBuffer.byteLength(); |
- doWriteUint32(byteLength); |
- append(static_cast<const uint8_t*>(arrayBuffer.data()), byteLength); |
- } |
- |
- 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) |
- { |
- StringUTF8Adaptor stringUTF8(string); |
- doWriteString(stringUTF8.data(), stringUTF8.length()); |
- } |
- |
- void doWriteHmacKey(const WebCryptoKey& key) |
- { |
- ASSERT(key.algorithm().paramsType() == WebCryptoKeyAlgorithmParamsTypeHmac); |
- |
- append(static_cast<uint8_t>(HmacKeyTag)); |
- ASSERT(!(key.algorithm().hmacParams()->lengthBits() % 8)); |
- doWriteUint32(key.algorithm().hmacParams()->lengthBits() / 8); |
- doWriteAlgorithmId(key.algorithm().hmacParams()->hash().id()); |
- } |
- |
- void doWriteAesKey(const WebCryptoKey& key) |
- { |
- ASSERT(key.algorithm().paramsType() == WebCryptoKeyAlgorithmParamsTypeAes); |
- |
- append(static_cast<uint8_t>(AesKeyTag)); |
- doWriteAlgorithmId(key.algorithm().id()); |
- // Converting the key length from bits to bytes is lossless and makes |
- // it fit in 1 byte. |
- ASSERT(!(key.algorithm().aesParams()->lengthBits() % 8)); |
- doWriteUint32(key.algorithm().aesParams()->lengthBits() / 8); |
- } |
- |
- void doWriteRsaHashedKey(const WebCryptoKey& key) |
- { |
- ASSERT(key.algorithm().rsaHashedParams()); |
- append(static_cast<uint8_t>(RsaHashedKeyTag)); |
- |
- doWriteAlgorithmId(key.algorithm().id()); |
- doWriteAsymmetricKeyType(key.type()); |
- |
- const WebCryptoRsaHashedKeyAlgorithmParams* params = key.algorithm().rsaHashedParams(); |
- doWriteUint32(params->modulusLengthBits()); |
- doWriteUint32(params->publicExponent().size()); |
- append(params->publicExponent().data(), params->publicExponent().size()); |
- doWriteAlgorithmId(params->hash().id()); |
- } |
- |
- void doWriteEcKey(const WebCryptoKey& key) |
- { |
- ASSERT(key.algorithm().ecParams()); |
- append(static_cast<uint8_t>(EcKeyTag)); |
- |
- doWriteAlgorithmId(key.algorithm().id()); |
- doWriteAsymmetricKeyType(key.type()); |
- doWriteNamedCurve(key.algorithm().ecParams()->namedCurve()); |
- } |
- |
- void doWriteAlgorithmId(WebCryptoAlgorithmId id) |
- { |
- switch (id) { |
- case WebCryptoAlgorithmIdAesCbc: |
- return doWriteUint32(AesCbcTag); |
- case WebCryptoAlgorithmIdHmac: |
- return doWriteUint32(HmacTag); |
- case WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
- return doWriteUint32(RsaSsaPkcs1v1_5Tag); |
- case WebCryptoAlgorithmIdSha1: |
- return doWriteUint32(Sha1Tag); |
- case WebCryptoAlgorithmIdSha256: |
- return doWriteUint32(Sha256Tag); |
- case WebCryptoAlgorithmIdSha384: |
- return doWriteUint32(Sha384Tag); |
- case WebCryptoAlgorithmIdSha512: |
- return doWriteUint32(Sha512Tag); |
- case WebCryptoAlgorithmIdAesGcm: |
- return doWriteUint32(AesGcmTag); |
- case WebCryptoAlgorithmIdRsaOaep: |
- return doWriteUint32(RsaOaepTag); |
- case WebCryptoAlgorithmIdAesCtr: |
- return doWriteUint32(AesCtrTag); |
- case WebCryptoAlgorithmIdAesKw: |
- return doWriteUint32(AesKwTag); |
- case WebCryptoAlgorithmIdRsaPss: |
- return doWriteUint32(RsaPssTag); |
- case WebCryptoAlgorithmIdEcdsa: |
- return doWriteUint32(EcdsaTag); |
- } |
- ASSERT_NOT_REACHED(); |
- } |
- |
- void doWriteAsymmetricKeyType(WebCryptoKeyType keyType) |
- { |
- switch (keyType) { |
- case WebCryptoKeyTypePublic: |
- doWriteUint32(PublicKeyType); |
- break; |
- case WebCryptoKeyTypePrivate: |
- doWriteUint32(PrivateKeyType); |
- break; |
- case WebCryptoKeyTypeSecret: |
- ASSERT_NOT_REACHED(); |
- } |
- } |
- |
- void doWriteNamedCurve(WebCryptoNamedCurve namedCurve) |
- { |
- switch (namedCurve) { |
- case WebCryptoNamedCurveP256: |
- return doWriteUint32(P256Tag); |
- case WebCryptoNamedCurveP384: |
- return doWriteUint32(P384Tag); |
- case WebCryptoNamedCurveP521: |
- return doWriteUint32(P521Tag); |
- } |
- ASSERT_NOT_REACHED(); |
- } |
- |
- void doWriteKeyUsages(const WebCryptoKeyUsageMask usages, bool extractable) |
- { |
- // Reminder to update this when adding new key usages. |
- COMPILE_ASSERT(EndOfWebCryptoKeyUsage == (1 << 7) + 1, UpdateMe); |
- |
- uint32_t value = 0; |
- |
- if (extractable) |
- value |= ExtractableUsage; |
- |
- if (usages & WebCryptoKeyUsageEncrypt) |
- value |= EncryptUsage; |
- if (usages & WebCryptoKeyUsageDecrypt) |
- value |= DecryptUsage; |
- if (usages & WebCryptoKeyUsageSign) |
- value |= SignUsage; |
- if (usages & WebCryptoKeyUsageVerify) |
- value |= VerifyUsage; |
- if (usages & WebCryptoKeyUsageDeriveKey) |
- value |= DeriveKeyUsage; |
- if (usages & WebCryptoKeyUsageWrapKey) |
- value |= WrapKeyUsage; |
- if (usages & WebCryptoKeyUsageUnwrapKey) |
- value |= UnwrapKeyUsage; |
- if (usages & WebCryptoKeyUsageDeriveBits) |
- value |= DeriveBitsUsage; |
- |
- doWriteUint32(value); |
- } |
- |
- int bytesNeededToWireEncode(uint32_t value) |
- { |
- int bytes = 1; |
- while (true) { |
- value >>= varIntShift; |
- if (!value) |
- break; |
- ++bytes; |
- } |
- |
- return bytes; |
- } |
- |
- 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(unsigned extra) |
- { |
- COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes); |
- m_buffer.resize((m_position + extra + 1) / sizeof(BufferValueType)); // "+ 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; |
- } |
- |
- int v8StringWriteOptions() |
- { |
- return v8::String::NO_NULL_TERMINATION; |
- } |
- |
- Vector<BufferValueType> m_buffer; |
- unsigned m_position; |
-}; |
- |
-static v8::Handle<v8::Object> toV8Object(MessagePort* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) |
-{ |
- if (!impl) |
- return v8::Handle<v8::Object>(); |
- v8::Handle<v8::Value> wrapper = toV8(impl, creationContext, isolate); |
- ASSERT(wrapper->IsObject()); |
- return wrapper.As<v8::Object>(); |
-} |
- |
-static v8::Handle<v8::ArrayBuffer> toV8Object(DOMArrayBuffer* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) |
-{ |
- if (!impl) |
- return v8::Handle<v8::ArrayBuffer>(); |
- v8::Handle<v8::Value> wrapper = toV8(impl, creationContext, isolate); |
- ASSERT(wrapper->IsArrayBuffer()); |
- return wrapper.As<v8::ArrayBuffer>(); |
-} |
- |
-class Serializer { |
- class StateBase; |
-public: |
- enum Status { |
- Success, |
- InputError, |
- DataCloneError, |
- JSException |
- }; |
- |
- Serializer(Writer& writer, MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, WebBlobInfoArray* blobInfo, BlobDataHandleMap& blobDataHandles, v8::TryCatch& tryCatch, ScriptState* scriptState) |
- : m_scriptState(scriptState) |
- , m_writer(writer) |
- , m_tryCatch(tryCatch) |
- , m_depth(0) |
- , m_status(Success) |
- , m_nextObjectReference(0) |
- , m_blobInfo(blobInfo) |
- , m_blobDataHandles(blobDataHandles) |
- { |
- ASSERT(!tryCatch.HasCaught()); |
- v8::Handle<v8::Object> creationContext = m_scriptState->context()->Global(); |
- if (messagePorts) { |
- for (size_t i = 0; i < messagePorts->size(); i++) |
- m_transferredMessagePorts.set(toV8Object(messagePorts->at(i).get(), creationContext, isolate()), i); |
- } |
- if (arrayBuffers) { |
- for (size_t i = 0; i < arrayBuffers->size(); i++) { |
- v8::Handle<v8::Object> v8ArrayBuffer = toV8Object(arrayBuffers->at(i).get(), creationContext, isolate()); |
- // Coalesce multiple occurences of the same buffer to the first index. |
- if (!m_transferredArrayBuffers.contains(v8ArrayBuffer)) |
- m_transferredArrayBuffers.set(v8ArrayBuffer, i); |
- } |
- } |
- } |
- |
- v8::Isolate* isolate() { return m_scriptState->isolate(); } |
- |
- Status serialize(v8::Handle<v8::Value> value) |
- { |
- v8::HandleScope scope(isolate()); |
- m_writer.writeVersion(); |
- StateBase* state = doSerialize(value, 0); |
- while (state) |
- state = state->advance(*this); |
- return m_status; |
- } |
- |
- String errorMessage() { return m_errorMessage; } |
- |
- // Functions used by serialization states. |
- StateBase* doSerialize(v8::Handle<v8::Value>, StateBase* next); |
- |
- StateBase* doSerializeArrayBuffer(v8::Handle<v8::Value> arrayBuffer, StateBase* next) |
- { |
- return doSerialize(arrayBuffer, next); |
- } |
- |
- StateBase* checkException(StateBase* state) |
- { |
- return m_tryCatch.HasCaught() ? handleError(JSException, "", state) : 0; |
- } |
- |
- 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); |
- } |
- |
- StateBase* writeDenseArray(uint32_t numProperties, uint32_t length, StateBase* state) |
- { |
- m_writer.writeDenseArray(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 final : public StateBase { |
- public: |
- ErrorState() |
- : StateBase(v8Undefined(), 0) |
- { |
- } |
- |
- virtual StateBase* advance(Serializer&) override |
- { |
- 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) |
- { |
- } |
- }; |
- |
- 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) |
- { |
- } |
- |
- protected: |
- virtual StateBase* objectDone(unsigned numProperties, Serializer&) = 0; |
- |
- StateBase* serializeProperties(bool ignoreIndexed, Serializer& serializer) |
- { |
- 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.handleError(InputError, "Empty property names cannot be cloned.", 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 && !ignoreIndexed)) { |
- 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 we return early here, it's either because we have pushed a new state onto the |
- // serialization state stack or because we have encountered an error (and in both cases |
- // we are unwinding the native stack). |
- if (StateBase* newState = serializer.doSerialize(value, this)) |
- return newState; |
- } |
- return objectDone(m_numSerializedProperties, serializer); |
- } |
- |
- 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::Handle<v8::Object> object, StateBase* next) |
- : AbstractObjectState(object, next) |
- { |
- } |
- |
- virtual StateBase* advance(Serializer& serializer) override |
- { |
- if (m_propertyNames.IsEmpty()) { |
- m_propertyNames = composite()->GetPropertyNames(); |
- if (StateBase* newState = serializer.checkException(this)) |
- return newState; |
- if (m_propertyNames.IsEmpty()) |
- return serializer.handleError(InputError, "Empty property names cannot be cloned.", nextState()); |
- } |
- return serializeProperties(false, serializer); |
- } |
- |
- protected: |
- virtual StateBase* objectDone(unsigned numProperties, Serializer& serializer) override |
- { |
- return serializer.writeObject(numProperties, this); |
- } |
- }; |
- |
- class DenseArrayState final : public AbstractObjectState { |
- public: |
- DenseArrayState(v8::Handle<v8::Array> array, v8::Handle<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); |
- } |
- |
- virtual StateBase* advance(Serializer& serializer) override |
- { |
- while (m_arrayIndex < m_arrayLength) { |
- v8::Handle<v8::Value> value = composite().As<v8::Array>()->Get(m_arrayIndex); |
- m_arrayIndex++; |
- if (StateBase* newState = serializer.checkException(this)) |
- return newState; |
- if (StateBase* newState = serializer.doSerialize(value, this)) |
- return newState; |
- } |
- return serializeProperties(true, serializer); |
- } |
- |
- protected: |
- virtual StateBase* objectDone(unsigned numProperties, Serializer& serializer) override |
- { |
- return serializer.writeDenseArray(numProperties, m_arrayLength, this); |
- } |
- |
- private: |
- uint32_t m_arrayIndex; |
- uint32_t m_arrayLength; |
- }; |
- |
- class SparseArrayState final : public AbstractObjectState { |
- public: |
- SparseArrayState(v8::Handle<v8::Array> array, v8::Handle<v8::Array> propertyNames, StateBase* next, v8::Isolate* isolate) |
- : AbstractObjectState(array, next) |
- { |
- m_propertyNames = v8::Local<v8::Array>::New(isolate, propertyNames); |
- } |
- |
- virtual StateBase* advance(Serializer& serializer) override |
- { |
- return serializeProperties(false, serializer); |
- } |
- |
- protected: |
- virtual StateBase* objectDone(unsigned numProperties, Serializer& serializer) override |
- { |
- return serializer.writeSparseArray(numProperties, composite().As<v8::Array>()->Length(), this); |
- } |
- }; |
- |
- 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; |
- } |
- |
- StateBase* handleError(Status errorStatus, const String& message, StateBase* state) |
- { |
- ASSERT(errorStatus != Success); |
- m_status = errorStatus; |
- m_errorMessage = message; |
- 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::Handle<v8::String> string = value.As<v8::String>(); |
- if (!string->Length() || string->IsOneByte()) |
- m_writer.writeOneByteString(string); |
- else |
- m_writer.writeUCharString(string); |
- } |
- |
- void writeStringObject(v8::Handle<v8::Value> value) |
- { |
- v8::Handle<v8::StringObject> stringObject = value.As<v8::StringObject>(); |
- v8::String::Utf8Value stringValue(stringObject->ValueOf()); |
- m_writer.writeStringObject(*stringValue, stringValue.length()); |
- } |
- |
- void writeNumberObject(v8::Handle<v8::Value> value) |
- { |
- v8::Handle<v8::NumberObject> numberObject = value.As<v8::NumberObject>(); |
- m_writer.writeNumberObject(numberObject->ValueOf()); |
- } |
- |
- void writeBooleanObject(v8::Handle<v8::Value> value) |
- { |
- v8::Handle<v8::BooleanObject> booleanObject = value.As<v8::BooleanObject>(); |
- m_writer.writeBooleanObject(booleanObject->ValueOf()); |
- } |
- |
- StateBase* writeBlob(v8::Handle<v8::Value> value, StateBase* next) |
- { |
- Blob* blob = V8Blob::toImpl(value.As<v8::Object>()); |
- if (!blob) |
- return 0; |
- if (blob->hasBeenClosed()) |
- return handleError(DataCloneError, "A Blob object has been closed, and could therefore not be cloned.", next); |
- int blobIndex = -1; |
- m_blobDataHandles.set(blob->uuid(), blob->blobDataHandle()); |
- if (appendBlobInfo(blob->uuid(), blob->type(), blob->size(), &blobIndex)) |
- m_writer.writeBlobIndex(blobIndex); |
- else |
- m_writer.writeBlob(blob->uuid(), blob->type(), blob->size()); |
- return 0; |
- } |
- |
- StateBase* writeDOMFileSystem(v8::Handle<v8::Value> value, StateBase* next) |
- { |
- DOMFileSystem* fs = V8DOMFileSystem::toImpl(value.As<v8::Object>()); |
- if (!fs) |
- return 0; |
- if (!fs->clonable()) |
- return handleError(DataCloneError, "A FileSystem object could not be cloned.", next); |
- m_writer.writeDOMFileSystem(fs->type(), fs->name(), fs->rootURL().string()); |
- return 0; |
- } |
- |
- StateBase* writeFile(v8::Handle<v8::Value> value, StateBase* next) |
- { |
- File* file = V8File::toImpl(value.As<v8::Object>()); |
- if (!file) |
- return 0; |
- if (file->hasBeenClosed()) |
- return handleError(DataCloneError, "A File object has been closed, and could therefore not be cloned.", next); |
- int blobIndex = -1; |
- m_blobDataHandles.set(file->uuid(), file->blobDataHandle()); |
- if (appendFileInfo(file, &blobIndex)) { |
- ASSERT(blobIndex >= 0); |
- m_writer.writeFileIndex(blobIndex); |
- } else { |
- m_writer.writeFile(*file); |
- } |
- return 0; |
- } |
- |
- StateBase* writeFileList(v8::Handle<v8::Value> value, StateBase* next) |
- { |
- FileList* fileList = V8FileList::toImpl(value.As<v8::Object>()); |
- if (!fileList) |
- return 0; |
- unsigned length = fileList->length(); |
- Vector<int> blobIndices; |
- for (unsigned i = 0; i < length; ++i) { |
- int blobIndex = -1; |
- const File* file = fileList->item(i); |
- if (file->hasBeenClosed()) |
- return handleError(DataCloneError, "A File object has been closed, and could therefore not be cloned.", next); |
- m_blobDataHandles.set(file->uuid(), file->blobDataHandle()); |
- if (appendFileInfo(file, &blobIndex)) { |
- ASSERT(!i || blobIndex > 0); |
- ASSERT(blobIndex >= 0); |
- blobIndices.append(blobIndex); |
- } |
- } |
- if (!blobIndices.isEmpty()) |
- m_writer.writeFileListIndex(blobIndices); |
- else |
- m_writer.writeFileList(*fileList); |
- return 0; |
- } |
- |
- bool writeCryptoKey(v8::Handle<v8::Value> value) |
- { |
- CryptoKey* key = V8CryptoKey::toImpl(value.As<v8::Object>()); |
- if (!key) |
- return false; |
- return m_writer.writeCryptoKey(key->key()); |
- } |
- |
- void writeImageData(v8::Handle<v8::Value> value) |
- { |
- ImageData* imageData = V8ImageData::toImpl(value.As<v8::Object>()); |
- if (!imageData) |
- return; |
- Uint8ClampedArray* pixelArray = imageData->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()); |
- } |
- |
- StateBase* writeAndGreyArrayBufferView(v8::Handle<v8::Object> object, StateBase* next) |
- { |
- ASSERT(!object.IsEmpty()); |
- DOMArrayBufferView* arrayBufferView = V8ArrayBufferView::toImpl(object); |
- if (!arrayBufferView) |
- return 0; |
- if (!arrayBufferView->buffer()) |
- return handleError(DataCloneError, "An ArrayBuffer could not be cloned.", next); |
- v8::Handle<v8::Value> underlyingBuffer = toV8(arrayBufferView->buffer(), m_scriptState->context()->Global(), isolate()); |
- if (underlyingBuffer.IsEmpty()) |
- return handleError(DataCloneError, "An ArrayBuffer could not be cloned.", next); |
- StateBase* stateOut = doSerializeArrayBuffer(underlyingBuffer, next); |
- if (stateOut) |
- return stateOut; |
- m_writer.writeArrayBufferView(*arrayBufferView->view()); |
- // This should be safe: we serialize something that we know to be a wrapper (see |
- // the toV8 call above), so the call to doSerializeArrayBuffer should neither |
- // cause the system stack to overflow nor should it have potential to reach |
- // this ArrayBufferView again. |
- // |
- // We do need to grey the underlying buffer before we grey its view, however; |
- // ArrayBuffers may be shared, so they need to be given reference IDs, and an |
- // ArrayBufferView cannot be constructed without a corresponding ArrayBuffer |
- // (or without an additional tag that would allow us to do two-stage construction |
- // like we do for Objects and Arrays). |
- greyObject(object); |
- return 0; |
- } |
- |
- StateBase* writeArrayBuffer(v8::Handle<v8::Value> value, StateBase* next) |
- { |
- DOMArrayBuffer* arrayBuffer = V8ArrayBuffer::toImpl(value.As<v8::Object>()); |
- if (!arrayBuffer) |
- return 0; |
- if (arrayBuffer->isNeutered()) |
- return handleError(DataCloneError, "An ArrayBuffer is neutered and could not be cloned.", next); |
- ASSERT(!m_transferredArrayBuffers.contains(value.As<v8::Object>())); |
- m_writer.writeArrayBuffer(*arrayBuffer->buffer()); |
- return 0; |
- } |
- |
- StateBase* writeTransferredArrayBuffer(v8::Handle<v8::Value> value, uint32_t index, StateBase* next) |
- { |
- DOMArrayBuffer* arrayBuffer = V8ArrayBuffer::toImpl(value.As<v8::Object>()); |
- if (!arrayBuffer) |
- return 0; |
- if (arrayBuffer->isNeutered()) |
- return handleError(DataCloneError, "An ArrayBuffer is neutered and could not be cloned.", next); |
- m_writer.writeTransferredArrayBuffer(index); |
- return 0; |
- } |
- |
- static bool shouldSerializeDensely(uint32_t length, uint32_t propertyCount) |
- { |
- // Let K be the cost of serializing all property values that are there |
- // Cost of serializing sparsely: 5*propertyCount + K (5 bytes per uint32_t key) |
- // Cost of serializing densely: K + 1*(length - propertyCount) (1 byte for all properties that are not there) |
- // so densely is better than sparsly whenever 6*propertyCount > length |
- return 6 * propertyCount >= length; |
- } |
- |
- StateBase* startArrayState(v8::Handle<v8::Array> array, StateBase* next) |
- { |
- v8::Handle<v8::Array> propertyNames = array->GetPropertyNames(); |
- if (StateBase* newState = checkException(next)) |
- return newState; |
- uint32_t length = array->Length(); |
- |
- if (shouldSerializeDensely(length, propertyNames->Length())) { |
- m_writer.writeGenerateFreshDenseArray(length); |
- return push(new DenseArrayState(array, propertyNames, next, isolate())); |
- } |
- |
- m_writer.writeGenerateFreshSparseArray(length); |
- return push(new SparseArrayState(array, propertyNames, next, isolate())); |
- } |
- |
- StateBase* startObjectState(v8::Handle<v8::Object> object, StateBase* next) |
- { |
- m_writer.writeGenerateFreshObject(); |
- // FIXME: check not a wrapper |
- return push(new ObjectState(object, next)); |
- } |
- |
- // 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::Handle<v8::Object>& object) |
- { |
- ASSERT(!m_objectPool.contains(object)); |
- uint32_t objectReference = m_nextObjectReference++; |
- m_objectPool.set(object, objectReference); |
- } |
- |
- bool appendBlobInfo(const String& uuid, const String& type, unsigned long long size, int* index) |
- { |
- if (!m_blobInfo) |
- return false; |
- *index = m_blobInfo->size(); |
- m_blobInfo->append(WebBlobInfo(uuid, type, size)); |
- return true; |
- } |
- |
- bool appendFileInfo(const File* file, int* index) |
- { |
- if (!m_blobInfo) |
- return false; |
- |
- long long size = -1; |
- double lastModified = invalidFileTime(); |
- file->captureSnapshot(size, lastModified); |
- *index = m_blobInfo->size(); |
- m_blobInfo->append(WebBlobInfo(file->uuid(), file->path(), file->name(), file->type(), lastModified, size)); |
- return true; |
- } |
- |
- RefPtr<ScriptState> m_scriptState; |
- Writer& 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; |
- uint32_t m_nextObjectReference; |
- WebBlobInfoArray* m_blobInfo; |
- BlobDataHandleMap& m_blobDataHandles; |
-}; |
- |
-// Returns true if the provided object is to be considered a 'host object', as used in the |
-// HTML5 structured clone algorithm. |
-static bool isHostObject(v8::Handle<v8::Object> object) |
-{ |
- // If the object has any internal fields, then we won't be able to serialize or deserialize |
- // them; conveniently, this is also a quick way to detect DOM wrapper objects, because |
- // the mechanism for these relies on data stored in these fields. We should |
- // catch external array data as a special case. |
- return object->InternalFieldCount() || object->HasIndexedPropertiesInExternalArrayData(); |
-} |
- |
-Serializer::StateBase* Serializer::doSerialize(v8::Handle<v8::Value> value, StateBase* next) |
-{ |
- m_writer.writeReferenceCount(m_nextObjectReference); |
- uint32_t objectReference; |
- uint32_t arrayBufferIndex; |
- if ((value->IsObject() || value->IsDate() || value->IsRegExp()) |
- && m_objectPool.tryGet(value.As<v8::Object>(), &objectReference)) { |
- // Note that IsObject() also detects wrappers (eg, it will catch the things |
- // that we grey and write below). |
- ASSERT(!value->IsString()); |
- m_writer.writeObjectReference(objectReference); |
- } else if (value.IsEmpty()) { |
- return handleError(InputError, "The empty property name cannot be cloned.", next); |
- } else 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->IsNumber()) { |
- m_writer.writeNumber(value.As<v8::Number>()->Value()); |
- } else if (V8ArrayBufferView::hasInstance(value, isolate())) { |
- return writeAndGreyArrayBufferView(value.As<v8::Object>(), next); |
- } else if (value->IsString()) { |
- writeString(value); |
- } else if (V8MessagePort::hasInstance(value, isolate())) { |
- uint32_t messagePortIndex; |
- if (m_transferredMessagePorts.tryGet(value.As<v8::Object>(), &messagePortIndex)) { |
- m_writer.writeTransferredMessagePort(messagePortIndex); |
- } else { |
- return handleError(DataCloneError, "A MessagePort could not be cloned.", next); |
- } |
- } else if (V8ArrayBuffer::hasInstance(value, isolate()) && m_transferredArrayBuffers.tryGet(value.As<v8::Object>(), &arrayBufferIndex)) { |
- return writeTransferredArrayBuffer(value, arrayBufferIndex, next); |
- } else { |
- v8::Handle<v8::Object> jsObject = value.As<v8::Object>(); |
- if (jsObject.IsEmpty()) |
- return handleError(DataCloneError, "An object could not be cloned.", next); |
- greyObject(jsObject); |
- if (value->IsDate()) { |
- m_writer.writeDate(value->NumberValue()); |
- } else if (value->IsStringObject()) { |
- writeStringObject(value); |
- } else if (value->IsNumberObject()) { |
- writeNumberObject(value); |
- } else if (value->IsBooleanObject()) { |
- writeBooleanObject(value); |
- } else if (value->IsArray()) { |
- return startArrayState(value.As<v8::Array>(), next); |
- } else if (V8File::hasInstance(value, isolate())) { |
- return writeFile(value, next); |
- } else if (V8Blob::hasInstance(value, isolate())) { |
- return writeBlob(value, next); |
- } else if (V8DOMFileSystem::hasInstance(value, isolate())) { |
- return writeDOMFileSystem(value, next); |
- } else if (V8FileList::hasInstance(value, isolate())) { |
- return writeFileList(value, next); |
- } else if (V8CryptoKey::hasInstance(value, isolate())) { |
- if (!writeCryptoKey(value)) |
- return handleError(DataCloneError, "Couldn't serialize key data", next); |
- } else if (V8ImageData::hasInstance(value, isolate())) { |
- writeImageData(value); |
- } else if (value->IsRegExp()) { |
- writeRegExp(value); |
- } else if (V8ArrayBuffer::hasInstance(value, isolate())) { |
- return writeArrayBuffer(value, next); |
- } else if (value->IsObject()) { |
- if (isHostObject(jsObject) || jsObject->IsCallable() || value->IsNativeError()) |
- return handleError(DataCloneError, "An object could not be cloned.", next); |
- return startObjectState(jsObject, next); |
- } else { |
- return handleError(DataCloneError, "A value could not be cloned.", next); |
- } |
- } |
- return 0; |
-} |
- |
-// Interface used by Reader to create objects of composite types. |
-class CompositeCreator { |
- STACK_ALLOCATED(); |
-public: |
- virtual ~CompositeCreator() { } |
- |
- virtual bool consumeTopOfStack(v8::Handle<v8::Value>*) = 0; |
- virtual uint32_t objectReferenceCount() = 0; |
- virtual void pushObjectReference(const v8::Handle<v8::Value>&) = 0; |
- virtual bool tryGetObjectFromObjectReference(uint32_t reference, v8::Handle<v8::Value>*) = 0; |
- virtual bool tryGetTransferredMessagePort(uint32_t index, v8::Handle<v8::Value>*) = 0; |
- virtual bool tryGetTransferredArrayBuffer(uint32_t index, v8::Handle<v8::Value>*) = 0; |
- virtual bool newSparseArray(uint32_t length) = 0; |
- virtual bool newDenseArray(uint32_t length) = 0; |
- virtual bool newObject() = 0; |
- virtual bool completeObject(uint32_t numProperties, v8::Handle<v8::Value>*) = 0; |
- virtual bool completeSparseArray(uint32_t numProperties, uint32_t length, v8::Handle<v8::Value>*) = 0; |
- virtual bool completeDenseArray(uint32_t numProperties, uint32_t length, v8::Handle<v8::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, 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* scriptState() const { return m_scriptState.get(); } |
- |
-private: |
- v8::Isolate* isolate() const { return m_scriptState->isolate(); } |
- |
-public: |
- bool read(v8::Handle<v8::Value>* value, CompositeCreator& creator) |
- { |
- SerializationTag tag; |
- if (!readTag(&tag)) |
- return false; |
- switch (tag) { |
- case ReferenceCountTag: { |
- if (!m_version) |
- return false; |
- uint32_t referenceTableSize; |
- if (!doReadUint32(&referenceTableSize)) |
- return false; |
- // If this test fails, then the serializer and deserializer disagree about the assignment |
- // of object reference IDs. On the deserialization side, this means there are too many or too few |
- // calls to pushObjectReference. |
- if (referenceTableSize != creator.objectReferenceCount()) |
- return false; |
- return true; |
- } |
- case InvalidTag: |
- return false; |
- case PaddingTag: |
- return true; |
- case UndefinedTag: |
- *value = v8::Undefined(isolate()); |
- break; |
- case NullTag: |
- *value = v8::Null(isolate()); |
- break; |
- case TrueTag: |
- *value = v8Boolean(true, isolate()); |
- break; |
- case FalseTag: |
- *value = v8Boolean(false, isolate()); |
- break; |
- case TrueObjectTag: |
- *value = v8::BooleanObject::New(true); |
- creator.pushObjectReference(*value); |
- break; |
- case FalseObjectTag: |
- *value = v8::BooleanObject::New(false); |
- creator.pushObjectReference(*value); |
- break; |
- case StringTag: |
- if (!readString(value)) |
- return false; |
- break; |
- case StringUCharTag: |
- if (!readUCharString(value)) |
- return false; |
- break; |
- case StringObjectTag: |
- if (!readStringObject(value)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case Int32Tag: |
- if (!readInt32(value)) |
- return false; |
- break; |
- case Uint32Tag: |
- if (!readUint32(value)) |
- return false; |
- break; |
- case DateTag: |
- if (!readDate(value)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case NumberTag: |
- if (!readNumber(value)) |
- return false; |
- break; |
- case NumberObjectTag: |
- if (!readNumberObject(value)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case BlobTag: |
- case BlobIndexTag: |
- if (!readBlob(value, tag == BlobIndexTag)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case FileTag: |
- case FileIndexTag: |
- if (!readFile(value, tag == FileIndexTag)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case DOMFileSystemTag: |
- if (!readDOMFileSystem(value)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case FileListTag: |
- case FileListIndexTag: |
- if (!readFileList(value, tag == FileListIndexTag)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case CryptoKeyTag: |
- if (!readCryptoKey(value)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case ImageDataTag: |
- if (!readImageData(value)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- |
- case RegExpTag: |
- if (!readRegExp(value)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- case ObjectTag: { |
- uint32_t numProperties; |
- if (!doReadUint32(&numProperties)) |
- return false; |
- if (!creator.completeObject(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.completeSparseArray(numProperties, length, value)) |
- return false; |
- break; |
- } |
- case DenseArrayTag: { |
- uint32_t numProperties; |
- uint32_t length; |
- if (!doReadUint32(&numProperties)) |
- return false; |
- if (!doReadUint32(&length)) |
- return false; |
- if (!creator.completeDenseArray(numProperties, length, value)) |
- return false; |
- break; |
- } |
- case ArrayBufferViewTag: { |
- if (!m_version) |
- return false; |
- if (!readArrayBufferView(value, creator)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- } |
- case ArrayBufferTag: { |
- if (!m_version) |
- return false; |
- if (!readArrayBuffer(value)) |
- return false; |
- creator.pushObjectReference(*value); |
- break; |
- } |
- case GenerateFreshObjectTag: { |
- if (!m_version) |
- return false; |
- if (!creator.newObject()) |
- return false; |
- return true; |
- } |
- case GenerateFreshSparseArrayTag: { |
- if (!m_version) |
- return false; |
- uint32_t length; |
- if (!doReadUint32(&length)) |
- return false; |
- if (!creator.newSparseArray(length)) |
- return false; |
- return true; |
- } |
- case GenerateFreshDenseArrayTag: { |
- if (!m_version) |
- return false; |
- uint32_t length; |
- if (!doReadUint32(&length)) |
- return false; |
- if (!creator.newDenseArray(length)) |
- return false; |
- return true; |
- } |
- case MessagePortTag: { |
- if (!m_version) |
- return false; |
- uint32_t index; |
- if (!doReadUint32(&index)) |
- return false; |
- if (!creator.tryGetTransferredMessagePort(index, value)) |
- return false; |
- break; |
- } |
- case ArrayBufferTransferTag: { |
- if (!m_version) |
- return false; |
- uint32_t index; |
- if (!doReadUint32(&index)) |
- return false; |
- if (!creator.tryGetTransferredArrayBuffer(index, value)) |
- return false; |
- break; |
- } |
- case ObjectReferenceTag: { |
- if (!m_version) |
- return false; |
- uint32_t reference; |
- if (!doReadUint32(&reference)) |
- return false; |
- if (!creator.tryGetObjectFromObjectReference(reference, value)) |
- return false; |
- break; |
- } |
- default: |
- return false; |
- } |
- return !value->IsEmpty(); |
- } |
- |
- bool readVersion(uint32_t& version) |
- { |
- SerializationTag tag; |
- if (!readTag(&tag)) { |
- // This is a nullary buffer. We're still version 0. |
- version = 0; |
- return true; |
- } |
- if (tag != VersionTag) { |
- // Versions of the format past 0 start with the version tag. |
- version = 0; |
- // Put back the tag. |
- undoReadTag(); |
- return true; |
- } |
- // Version-bearing messages are obligated to finish the version tag. |
- return doReadUint32(&version); |
- } |
- |
- void setVersion(uint32_t version) |
- { |
- m_version = version; |
- } |
- |
-private: |
- bool readTag(SerializationTag* tag) |
- { |
- if (m_position >= m_length) |
- return false; |
- *tag = static_cast<SerializationTag>(m_buffer[m_position++]); |
- return true; |
- } |
- |
- void undoReadTag() |
- { |
- if (m_position > 0) |
- --m_position; |
- } |
- |
- bool readArrayBufferViewSubTag(ArrayBufferViewSubTag* tag) |
- { |
- if (m_position >= m_length) |
- return false; |
- *tag = static_cast<ArrayBufferViewSubTag>(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::NewFromUtf8(isolate(), reinterpret_cast<const char*>(m_buffer + m_position), v8::String::kNormalString, length); |
- m_position += length; |
- return true; |
- } |
- |
- bool readUCharString(v8::Handle<v8::Value>* value) |
- { |
- uint32_t length; |
- if (!doReadUint32(&length) || (length & 1)) |
- return false; |
- if (m_position + length > m_length) |
- return false; |
- ASSERT(!(m_position & 1)); |
- *value = v8::String::NewFromTwoByte(isolate(), reinterpret_cast<const uint16_t*>(m_buffer + m_position), v8::String::kNormalString, length / sizeof(UChar)); |
- m_position += length; |
- return true; |
- } |
- |
- bool readStringObject(v8::Handle<v8::Value>* value) |
- { |
- v8::Handle<v8::Value> stringValue; |
- if (!readString(&stringValue) || !stringValue->IsString()) |
- return false; |
- *value = v8::StringObject::New(stringValue.As<v8::String>()); |
- 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(isolate(), 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(isolate(), rawValue); |
- return true; |
- } |
- |
- bool readDate(v8::Handle<v8::Value>* value) |
- { |
- double numberValue; |
- if (!doReadNumber(&numberValue)) |
- return false; |
- *value = v8DateOrNaN(numberValue, isolate()); |
- return true; |
- } |
- |
- bool readNumber(v8::Handle<v8::Value>* value) |
- { |
- double number; |
- if (!doReadNumber(&number)) |
- return false; |
- *value = v8::Number::New(isolate(), number); |
- return true; |
- } |
- |
- bool readNumberObject(v8::Handle<v8::Value>* value) |
- { |
- double number; |
- if (!doReadNumber(&number)) |
- return false; |
- *value = v8::NumberObject::New(isolate(), 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; |
- RefPtrWillBeRawPtr<ImageData> imageData = ImageData::create(IntSize(width, height)); |
- Uint8ClampedArray* pixelArray = imageData->data(); |
- ASSERT(pixelArray); |
- ASSERT(pixelArray->length() >= pixelDataLength); |
- memcpy(pixelArray->data(), m_buffer + m_position, pixelDataLength); |
- m_position += pixelDataLength; |
- *value = toV8(imageData.release(), m_scriptState->context()->Global(), isolate()); |
- return true; |
- } |
- |
- PassRefPtr<ArrayBuffer> doReadArrayBuffer() |
- { |
- uint32_t byteLength; |
- if (!doReadUint32(&byteLength)) |
- return nullptr; |
- if (m_position + byteLength > m_length) |
- return nullptr; |
- const void* bufferStart = m_buffer + m_position; |
- m_position += byteLength; |
- return ArrayBuffer::create(bufferStart, byteLength); |
- } |
- |
- bool readArrayBuffer(v8::Handle<v8::Value>* value) |
- { |
- RefPtr<ArrayBuffer> arrayBuffer = doReadArrayBuffer(); |
- if (!arrayBuffer) |
- return false; |
- *value = toV8(DOMArrayBuffer::create(arrayBuffer.release()), m_scriptState->context()->Global(), isolate()); |
- return true; |
- } |
- |
- bool readArrayBufferView(v8::Handle<v8::Value>* value, CompositeCreator& creator) |
- { |
- ArrayBufferViewSubTag subTag; |
- uint32_t byteOffset; |
- uint32_t byteLength; |
- RefPtr<DOMArrayBuffer> arrayBuffer; |
- v8::Handle<v8::Value> arrayBufferV8Value; |
- if (!readArrayBufferViewSubTag(&subTag)) |
- return false; |
- if (!doReadUint32(&byteOffset)) |
- return false; |
- if (!doReadUint32(&byteLength)) |
- return false; |
- if (!creator.consumeTopOfStack(&arrayBufferV8Value)) |
- return false; |
- if (arrayBufferV8Value.IsEmpty()) |
- return false; |
- arrayBuffer = V8ArrayBuffer::toImpl(arrayBufferV8Value.As<v8::Object>()); |
- if (!arrayBuffer) |
- return false; |
- |
- v8::Handle<v8::Object> creationContext = m_scriptState->context()->Global(); |
- switch (subTag) { |
- case ByteArrayTag: |
- *value = toV8(DOMInt8Array::create(arrayBuffer.release(), byteOffset, byteLength), creationContext, isolate()); |
- break; |
- case UnsignedByteArrayTag: |
- *value = toV8(DOMUint8Array::create(arrayBuffer.release(), byteOffset, byteLength), creationContext, isolate()); |
- break; |
- case UnsignedByteClampedArrayTag: |
- *value = toV8(DOMUint8ClampedArray::create(arrayBuffer.release(), byteOffset, byteLength), creationContext, isolate()); |
- break; |
- case ShortArrayTag: { |
- uint32_t shortLength = byteLength / sizeof(int16_t); |
- if (shortLength * sizeof(int16_t) != byteLength) |
- return false; |
- *value = toV8(DOMInt16Array::create(arrayBuffer.release(), byteOffset, shortLength), creationContext, isolate()); |
- break; |
- } |
- case UnsignedShortArrayTag: { |
- uint32_t shortLength = byteLength / sizeof(uint16_t); |
- if (shortLength * sizeof(uint16_t) != byteLength) |
- return false; |
- *value = toV8(DOMUint16Array::create(arrayBuffer.release(), byteOffset, shortLength), creationContext, isolate()); |
- break; |
- } |
- case IntArrayTag: { |
- uint32_t intLength = byteLength / sizeof(int32_t); |
- if (intLength * sizeof(int32_t) != byteLength) |
- return false; |
- *value = toV8(DOMInt32Array::create(arrayBuffer.release(), byteOffset, intLength), creationContext, isolate()); |
- break; |
- } |
- case UnsignedIntArrayTag: { |
- uint32_t intLength = byteLength / sizeof(uint32_t); |
- if (intLength * sizeof(uint32_t) != byteLength) |
- return false; |
- *value = toV8(DOMUint32Array::create(arrayBuffer.release(), byteOffset, intLength), creationContext, isolate()); |
- break; |
- } |
- case FloatArrayTag: { |
- uint32_t floatLength = byteLength / sizeof(float); |
- if (floatLength * sizeof(float) != byteLength) |
- return false; |
- *value = toV8(DOMFloat32Array::create(arrayBuffer.release(), byteOffset, floatLength), creationContext, isolate()); |
- break; |
- } |
- case DoubleArrayTag: { |
- uint32_t floatLength = byteLength / sizeof(double); |
- if (floatLength * sizeof(double) != byteLength) |
- return false; |
- *value = toV8(DOMFloat64Array::create(arrayBuffer.release(), byteOffset, floatLength), creationContext, isolate()); |
- break; |
- } |
- case DataViewTag: |
- *value = toV8(DOMDataView::create(arrayBuffer.release(), byteOffset, byteLength), creationContext, isolate()); |
- break; |
- default: |
- return false; |
- } |
- // The various *Array::create() methods will return null if the range the view expects is |
- // mismatched with the range the buffer can provide or if the byte offset is not aligned |
- // to the size of the element type. |
- return !value->IsEmpty(); |
- } |
- |
- 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, bool isIndexed) |
- { |
- if (m_version < 3) |
- return false; |
- Blob* blob = nullptr; |
- if (isIndexed) { |
- if (m_version < 6) |
- return false; |
- ASSERT(m_blobInfo); |
- uint32_t index; |
- if (!doReadUint32(&index) || index >= m_blobInfo->size()) |
- return false; |
- const WebBlobInfo& info = (*m_blobInfo)[index]; |
- blob = Blob::create(getOrCreateBlobDataHandle(info.uuid(), info.type(), info.size())); |
- } else { |
- ASSERT(!m_blobInfo); |
- String uuid; |
- String type; |
- uint64_t size; |
- ASSERT(!m_blobInfo); |
- if (!readWebCoreString(&uuid)) |
- return false; |
- if (!readWebCoreString(&type)) |
- return false; |
- if (!doReadUint64(&size)) |
- return false; |
- blob = Blob::create(getOrCreateBlobDataHandle(uuid, type, size)); |
- } |
- *value = toV8(blob, m_scriptState->context()->Global(), isolate()); |
- return true; |
- } |
- |
- bool readDOMFileSystem(v8::Handle<v8::Value>* value) |
- { |
- uint32_t type; |
- String name; |
- String url; |
- if (!doReadUint32(&type)) |
- return false; |
- if (!readWebCoreString(&name)) |
- return false; |
- if (!readWebCoreString(&url)) |
- return false; |
- DOMFileSystem* fs = DOMFileSystem::create(m_scriptState->executionContext(), name, static_cast<FileSystemType>(type), KURL(ParsedURLString, url)); |
- *value = toV8(fs, m_scriptState->context()->Global(), isolate()); |
- return true; |
- } |
- |
- bool readFile(v8::Handle<v8::Value>* value, bool isIndexed) |
- { |
- File* file = nullptr; |
- if (isIndexed) { |
- if (m_version < 6) |
- return false; |
- file = readFileIndexHelper(); |
- } else { |
- file = readFileHelper(); |
- } |
- if (!file) |
- return false; |
- *value = toV8(file, m_scriptState->context()->Global(), isolate()); |
- return true; |
- } |
- |
- bool readFileList(v8::Handle<v8::Value>* value, bool isIndexed) |
- { |
- if (m_version < 3) |
- return false; |
- uint32_t length; |
- if (!doReadUint32(&length)) |
- return false; |
- FileList* fileList = FileList::create(); |
- for (unsigned i = 0; i < length; ++i) { |
- File* file = nullptr; |
- if (isIndexed) { |
- if (m_version < 6) |
- return false; |
- file = readFileIndexHelper(); |
- } else { |
- file = readFileHelper(); |
- } |
- if (!file) |
- return false; |
- fileList->append(file); |
- } |
- *value = toV8(fileList, m_scriptState->context()->Global(), isolate()); |
- return true; |
- } |
- |
- bool readCryptoKey(v8::Handle<v8::Value>* value) |
- { |
- uint32_t rawKeyType; |
- if (!doReadUint32(&rawKeyType)) |
- return false; |
- |
- WebCryptoKeyAlgorithm algorithm; |
- WebCryptoKeyType type = WebCryptoKeyTypeSecret; |
- |
- switch (static_cast<CryptoKeySubTag>(rawKeyType)) { |
- case AesKeyTag: |
- if (!doReadAesKey(algorithm, type)) |
- return false; |
- break; |
- case HmacKeyTag: |
- if (!doReadHmacKey(algorithm, type)) |
- return false; |
- break; |
- case RsaHashedKeyTag: |
- if (!doReadRsaHashedKey(algorithm, type)) |
- return false; |
- break; |
- case EcKeyTag: |
- if (!doReadEcKey(algorithm, type)) |
- return false; |
- break; |
- default: |
- return false; |
- } |
- |
- WebCryptoKeyUsageMask usages; |
- bool extractable; |
- if (!doReadKeyUsages(usages, extractable)) |
- return false; |
- |
- uint32_t keyDataLength; |
- if (!doReadUint32(&keyDataLength)) |
- return false; |
- |
- if (m_position + keyDataLength > m_length) |
- return false; |
- |
- const uint8_t* keyData = m_buffer + m_position; |
- m_position += keyDataLength; |
- |
- WebCryptoKey key = WebCryptoKey::createNull(); |
- if (!Platform::current()->crypto()->deserializeKeyForClone( |
- algorithm, type, extractable, usages, keyData, keyDataLength, key)) { |
- return false; |
- } |
- |
- *value = toV8(CryptoKey::create(key), m_scriptState->context()->Global(), isolate()); |
- return true; |
- } |
- |
- File* readFileHelper() |
- { |
- if (m_version < 3) |
- return nullptr; |
- ASSERT(!m_blobInfo); |
- String path; |
- String name; |
- String relativePath; |
- String uuid; |
- String type; |
- uint32_t hasSnapshot = 0; |
- uint64_t size = 0; |
- double lastModified = 0; |
- if (!readWebCoreString(&path)) |
- return nullptr; |
- if (m_version >= 4 && !readWebCoreString(&name)) |
- return nullptr; |
- if (m_version >= 4 && !readWebCoreString(&relativePath)) |
- return nullptr; |
- if (!readWebCoreString(&uuid)) |
- return nullptr; |
- if (!readWebCoreString(&type)) |
- return nullptr; |
- if (m_version >= 4 && !doReadUint32(&hasSnapshot)) |
- return nullptr; |
- if (hasSnapshot) { |
- if (!doReadUint64(&size)) |
- return nullptr; |
- if (!doReadNumber(&lastModified)) |
- return nullptr; |
- } |
- uint32_t isUserVisible = 1; |
- if (m_version >= 7 && !doReadUint32(&isUserVisible)) |
- return nullptr; |
- const File::UserVisibility userVisibility = (isUserVisible > 0) ? File::IsUserVisible : File::IsNotUserVisible; |
- return File::createFromSerialization(path, name, relativePath, userVisibility, hasSnapshot > 0, size, lastModified, getOrCreateBlobDataHandle(uuid, type)); |
- } |
- |
- File* readFileIndexHelper() |
- { |
- if (m_version < 3) |
- return nullptr; |
- ASSERT(m_blobInfo); |
- uint32_t index; |
- if (!doReadUint32(&index) || index >= m_blobInfo->size()) |
- return nullptr; |
- const WebBlobInfo& info = (*m_blobInfo)[index]; |
- return File::createFromIndexedSerialization(info.filePath(), info.fileName(), info.size(), info.lastModified(), getOrCreateBlobDataHandle(info.uuid(), info.type(), info.size())); |
- } |
- |
- 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; |
- } |
- |
- PassRefPtr<BlobDataHandle> getOrCreateBlobDataHandle(const String& uuid, const String& type, long long size = -1) |
- { |
- // The containing ssv may have a BDH for this uuid if this ssv is just being |
- // passed from main to worker thread (for example). We use those values when creating |
- // the new blob instead of cons'ing up a new BDH. |
- // |
- // FIXME: Maybe we should require that it work that way where the ssv must have a BDH for any |
- // blobs it comes across during deserialization. Would require callers to explicitly populate |
- // the collection of BDH's for blobs to work, which would encourage lifetimes to be considered |
- // when passing ssv's around cross process. At present, we get 'lucky' in some cases because |
- // the blob in the src process happens to still exist at the time the dest process is deserializing. |
- // For example in sharedWorker.postMessage(...). |
- BlobDataHandleMap::const_iterator it = m_blobDataHandles.find(uuid); |
- if (it != m_blobDataHandles.end()) { |
- // make assertions about type and size? |
- return it->value; |
- } |
- return BlobDataHandle::create(uuid, type, size); |
- } |
- |
- bool doReadHmacKey(WebCryptoKeyAlgorithm& algorithm, WebCryptoKeyType& type) |
- { |
- uint32_t lengthBytes; |
- if (!doReadUint32(&lengthBytes)) |
- return false; |
- WebCryptoAlgorithmId hash; |
- if (!doReadAlgorithmId(hash)) |
- return false; |
- algorithm = WebCryptoKeyAlgorithm::createHmac(hash, lengthBytes * 8); |
- type = WebCryptoKeyTypeSecret; |
- return !algorithm.isNull(); |
- } |
- |
- bool doReadAesKey(WebCryptoKeyAlgorithm& algorithm, WebCryptoKeyType& type) |
- { |
- WebCryptoAlgorithmId id; |
- if (!doReadAlgorithmId(id)) |
- return false; |
- uint32_t lengthBytes; |
- if (!doReadUint32(&lengthBytes)) |
- return false; |
- algorithm = WebCryptoKeyAlgorithm::createAes(id, lengthBytes * 8); |
- type = WebCryptoKeyTypeSecret; |
- return !algorithm.isNull(); |
- } |
- |
- bool doReadRsaHashedKey(WebCryptoKeyAlgorithm& algorithm, WebCryptoKeyType& type) |
- { |
- WebCryptoAlgorithmId id; |
- if (!doReadAlgorithmId(id)) |
- return false; |
- |
- if (!doReadAsymmetricKeyType(type)) |
- return false; |
- |
- uint32_t modulusLengthBits; |
- if (!doReadUint32(&modulusLengthBits)) |
- return false; |
- |
- uint32_t publicExponentSize; |
- if (!doReadUint32(&publicExponentSize)) |
- return false; |
- |
- if (m_position + publicExponentSize > m_length) |
- return false; |
- |
- const uint8_t* publicExponent = m_buffer + m_position; |
- m_position += publicExponentSize; |
- |
- WebCryptoAlgorithmId hash; |
- if (!doReadAlgorithmId(hash)) |
- return false; |
- algorithm = WebCryptoKeyAlgorithm::createRsaHashed(id, modulusLengthBits, publicExponent, publicExponentSize, hash); |
- |
- return !algorithm.isNull(); |
- } |
- |
- bool doReadEcKey(WebCryptoKeyAlgorithm& algorithm, WebCryptoKeyType& type) |
- { |
- WebCryptoAlgorithmId id; |
- if (!doReadAlgorithmId(id)) |
- return false; |
- |
- if (!doReadAsymmetricKeyType(type)) |
- return false; |
- |
- WebCryptoNamedCurve namedCurve; |
- if (!doReadNamedCurve(namedCurve)) |
- return false; |
- |
- algorithm = WebCryptoKeyAlgorithm::createEc(id, namedCurve); |
- return !algorithm.isNull(); |
- } |
- |
- bool doReadAlgorithmId(WebCryptoAlgorithmId& id) |
- { |
- uint32_t rawId; |
- if (!doReadUint32(&rawId)) |
- return false; |
- |
- switch (static_cast<CryptoKeyAlgorithmTag>(rawId)) { |
- case AesCbcTag: |
- id = WebCryptoAlgorithmIdAesCbc; |
- return true; |
- case HmacTag: |
- id = WebCryptoAlgorithmIdHmac; |
- return true; |
- case RsaSsaPkcs1v1_5Tag: |
- id = WebCryptoAlgorithmIdRsaSsaPkcs1v1_5; |
- return true; |
- case Sha1Tag: |
- id = WebCryptoAlgorithmIdSha1; |
- return true; |
- case Sha256Tag: |
- id = WebCryptoAlgorithmIdSha256; |
- return true; |
- case Sha384Tag: |
- id = WebCryptoAlgorithmIdSha384; |
- return true; |
- case Sha512Tag: |
- id = WebCryptoAlgorithmIdSha512; |
- return true; |
- case AesGcmTag: |
- id = WebCryptoAlgorithmIdAesGcm; |
- return true; |
- case RsaOaepTag: |
- id = WebCryptoAlgorithmIdRsaOaep; |
- return true; |
- case AesCtrTag: |
- id = WebCryptoAlgorithmIdAesCtr; |
- return true; |
- case AesKwTag: |
- id = WebCryptoAlgorithmIdAesKw; |
- return true; |
- case RsaPssTag: |
- id = WebCryptoAlgorithmIdRsaPss; |
- return true; |
- case EcdsaTag: |
- id = WebCryptoAlgorithmIdEcdsa; |
- return true; |
- } |
- |
- return false; |
- } |
- |
- bool doReadAsymmetricKeyType(WebCryptoKeyType& type) |
- { |
- uint32_t rawType; |
- if (!doReadUint32(&rawType)) |
- return false; |
- |
- switch (static_cast<AssymetricCryptoKeyType>(rawType)) { |
- case PublicKeyType: |
- type = WebCryptoKeyTypePublic; |
- return true; |
- case PrivateKeyType: |
- type = WebCryptoKeyTypePrivate; |
- return true; |
- } |
- |
- return false; |
- } |
- |
- bool doReadNamedCurve(WebCryptoNamedCurve& namedCurve) |
- { |
- uint32_t rawName; |
- if (!doReadUint32(&rawName)) |
- return false; |
- |
- switch (static_cast<NamedCurveTag>(rawName)) { |
- case P256Tag: |
- namedCurve = WebCryptoNamedCurveP256; |
- return true; |
- case P384Tag: |
- namedCurve = WebCryptoNamedCurveP384; |
- return true; |
- case P521Tag: |
- namedCurve = WebCryptoNamedCurveP521; |
- return true; |
- } |
- |
- return false; |
- } |
- |
- bool doReadKeyUsages(WebCryptoKeyUsageMask& usages, bool& extractable) |
- { |
- // Reminder to update this when adding new key usages. |
- COMPILE_ASSERT(EndOfWebCryptoKeyUsage == (1 << 7) + 1, UpdateMe); |
- const uint32_t allPossibleUsages = ExtractableUsage | EncryptUsage | DecryptUsage | SignUsage | VerifyUsage | DeriveKeyUsage | WrapKeyUsage | UnwrapKeyUsage | DeriveBitsUsage; |
- |
- uint32_t rawUsages; |
- if (!doReadUint32(&rawUsages)) |
- return false; |
- |
- // Make sure it doesn't contain an unrecognized usage value. |
- if (rawUsages & ~allPossibleUsages) |
- return false; |
- |
- usages = 0; |
- |
- extractable = rawUsages & ExtractableUsage; |
- |
- if (rawUsages & EncryptUsage) |
- usages |= WebCryptoKeyUsageEncrypt; |
- if (rawUsages & DecryptUsage) |
- usages |= WebCryptoKeyUsageDecrypt; |
- if (rawUsages & SignUsage) |
- usages |= WebCryptoKeyUsageSign; |
- if (rawUsages & VerifyUsage) |
- usages |= WebCryptoKeyUsageVerify; |
- if (rawUsages & DeriveKeyUsage) |
- usages |= WebCryptoKeyUsageDeriveKey; |
- if (rawUsages & WrapKeyUsage) |
- usages |= WebCryptoKeyUsageWrapKey; |
- if (rawUsages & UnwrapKeyUsage) |
- usages |= WebCryptoKeyUsageUnwrapKey; |
- if (rawUsages & DeriveBitsUsage) |
- usages |= WebCryptoKeyUsageDeriveBits; |
- |
- return true; |
- } |
- |
- 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; |
-}; |
- |
- |
-typedef Vector<WTF::ArrayBufferContents, 1> ArrayBufferContentsArray; |
- |
-class Deserializer final : public CompositeCreator { |
-public: |
- Deserializer(Reader& reader, MessagePortArray* messagePorts, ArrayBufferContentsArray* arrayBufferContents) |
- : m_reader(reader) |
- , m_transferredMessagePorts(messagePorts) |
- , m_arrayBufferContents(arrayBufferContents) |
- , m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0) |
- , m_version(0) |
- { |
- } |
- |
- v8::Handle<v8::Value> deserialize() |
- { |
- v8::Isolate* isolate = m_reader.scriptState()->isolate(); |
- if (!m_reader.readVersion(m_version) || m_version > SerializedScriptValue::wireFormatVersion) |
- return v8::Null(isolate); |
- m_reader.setVersion(m_version); |
- v8::EscapableHandleScope scope(isolate); |
- while (!m_reader.isEof()) { |
- if (!doDeserialize()) |
- return v8::Null(isolate); |
- } |
- if (stackDepth() != 1 || m_openCompositeReferenceStack.size()) |
- return v8::Null(isolate); |
- v8::Handle<v8::Value> result = scope.Escape(element(0)); |
- return result; |
- } |
- |
- virtual bool newSparseArray(uint32_t) override |
- { |
- v8::Local<v8::Array> array = v8::Array::New(m_reader.scriptState()->isolate(), 0); |
- openComposite(array); |
- return true; |
- } |
- |
- virtual bool newDenseArray(uint32_t length) override |
- { |
- v8::Local<v8::Array> array = v8::Array::New(m_reader.scriptState()->isolate(), length); |
- openComposite(array); |
- return true; |
- } |
- |
- virtual bool consumeTopOfStack(v8::Handle<v8::Value>* object) override |
- { |
- if (stackDepth() < 1) |
- return false; |
- *object = element(stackDepth() - 1); |
- pop(1); |
- return true; |
- } |
- |
- virtual bool newObject() override |
- { |
- v8::Local<v8::Object> object = v8::Object::New(m_reader.scriptState()->isolate()); |
- if (object.IsEmpty()) |
- return false; |
- openComposite(object); |
- return true; |
- } |
- |
- virtual bool completeObject(uint32_t numProperties, v8::Handle<v8::Value>* value) override |
- { |
- v8::Local<v8::Object> object; |
- if (m_version > 0) { |
- v8::Local<v8::Value> composite; |
- if (!closeComposite(&composite)) |
- return false; |
- object = composite.As<v8::Object>(); |
- } else { |
- object = v8::Object::New(m_reader.scriptState()->isolate()); |
- } |
- if (object.IsEmpty()) |
- return false; |
- return initializeObject(object, numProperties, value); |
- } |
- |
- virtual bool completeSparseArray(uint32_t numProperties, uint32_t length, v8::Handle<v8::Value>* value) override |
- { |
- v8::Local<v8::Array> array; |
- if (m_version > 0) { |
- v8::Local<v8::Value> composite; |
- if (!closeComposite(&composite)) |
- return false; |
- array = composite.As<v8::Array>(); |
- } else { |
- array = v8::Array::New(m_reader.scriptState()->isolate()); |
- } |
- if (array.IsEmpty()) |
- return false; |
- return initializeObject(array, numProperties, value); |
- } |
- |
- virtual bool completeDenseArray(uint32_t numProperties, uint32_t length, v8::Handle<v8::Value>* value) override |
- { |
- v8::Local<v8::Array> array; |
- if (m_version > 0) { |
- v8::Local<v8::Value> composite; |
- if (!closeComposite(&composite)) |
- return false; |
- array = composite.As<v8::Array>(); |
- } |
- if (array.IsEmpty()) |
- return false; |
- if (!initializeObject(array, numProperties, value)) |
- return false; |
- if (length > stackDepth()) |
- return false; |
- for (unsigned i = 0, stackPos = stackDepth() - length; i < length; i++, stackPos++) { |
- v8::Local<v8::Value> elem = element(stackPos); |
- if (!elem->IsUndefined()) |
- array->Set(i, elem); |
- } |
- pop(length); |
- return true; |
- } |
- |
- virtual void pushObjectReference(const v8::Handle<v8::Value>& object) override |
- { |
- m_objectPool.append(object); |
- } |
- |
- virtual bool tryGetTransferredMessagePort(uint32_t index, v8::Handle<v8::Value>* object) override |
- { |
- if (!m_transferredMessagePorts) |
- return false; |
- if (index >= m_transferredMessagePorts->size()) |
- return false; |
- v8::Handle<v8::Object> creationContext = m_reader.scriptState()->context()->Global(); |
- *object = toV8(m_transferredMessagePorts->at(index).get(), creationContext, m_reader.scriptState()->isolate()); |
- return true; |
- } |
- |
- virtual bool tryGetTransferredArrayBuffer(uint32_t index, v8::Handle<v8::Value>* object) override |
- { |
- if (!m_arrayBufferContents) |
- return false; |
- if (index >= m_arrayBuffers.size()) |
- return false; |
- v8::Handle<v8::Object> result = m_arrayBuffers.at(index); |
- if (result.IsEmpty()) { |
- RefPtr<DOMArrayBuffer> buffer = DOMArrayBuffer::create(m_arrayBufferContents->at(index)); |
- v8::Isolate* isolate = m_reader.scriptState()->isolate(); |
- v8::Handle<v8::Object> creationContext = m_reader.scriptState()->context()->Global(); |
- result = toV8Object(buffer.get(), creationContext, isolate); |
- m_arrayBuffers[index] = result; |
- } |
- *object = result; |
- return true; |
- } |
- |
- virtual bool tryGetObjectFromObjectReference(uint32_t reference, v8::Handle<v8::Value>* object) override |
- { |
- if (reference >= m_objectPool.size()) |
- return false; |
- *object = m_objectPool[reference]; |
- return object; |
- } |
- |
- virtual uint32_t objectReferenceCount() override |
- { |
- return m_objectPool.size(); |
- } |
- |
-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_WITH_SECURITY_IMPLICATION(index < m_stack.size()); |
- return m_stack[index]; |
- } |
- |
- void openComposite(const v8::Local<v8::Value>& object) |
- { |
- uint32_t newObjectReference = m_objectPool.size(); |
- m_openCompositeReferenceStack.append(newObjectReference); |
- m_objectPool.append(object); |
- } |
- |
- bool closeComposite(v8::Handle<v8::Value>* object) |
- { |
- if (!m_openCompositeReferenceStack.size()) |
- return false; |
- uint32_t objectReference = m_openCompositeReferenceStack[m_openCompositeReferenceStack.size() - 1]; |
- m_openCompositeReferenceStack.shrink(m_openCompositeReferenceStack.size() - 1); |
- if (objectReference >= m_objectPool.size()) |
- return false; |
- *object = m_objectPool[objectReference]; |
- return true; |
- } |
- |
- Reader& m_reader; |
- Vector<v8::Local<v8::Value> > m_stack; |
- Vector<v8::Handle<v8::Value> > m_objectPool; |
- Vector<uint32_t> m_openCompositeReferenceStack; |
- RawPtrWillBeMember<MessagePortArray> m_transferredMessagePorts; |
- ArrayBufferContentsArray* m_arrayBufferContents; |
- Vector<v8::Handle<v8::Object> > m_arrayBuffers; |
- uint32_t m_version; |
-}; |
- |
-} // namespace |
- |
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(v8::Handle<v8::Value> value, MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, ExceptionState& exceptionState, v8::Isolate* isolate) |
{ |
return adoptRef(new SerializedScriptValue(value, messagePorts, arrayBuffers, 0, exceptionState, isolate)); |
@@ -2933,7 +101,7 @@ PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(const String& da |
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(const String& data, v8::Isolate* isolate) |
{ |
- Writer writer; |
+ SerializedScriptValueInternal::Writer writer; |
writer.writeWebCoreString(data); |
String wireData = writer.takeWireString(); |
return adoptRef(new SerializedScriptValue(wireData)); |
@@ -2946,7 +114,7 @@ PassRefPtr<SerializedScriptValue> SerializedScriptValue::create() |
PassRefPtr<SerializedScriptValue> SerializedScriptValue::nullValue() |
{ |
- Writer writer; |
+ SerializedScriptValueInternal::Writer writer; |
writer.writeNull(); |
String wireData = writer.takeWireString(); |
return adoptRef(new SerializedScriptValue(wireData)); |
@@ -3031,14 +199,14 @@ PassOwnPtr<SerializedScriptValue::ArrayBufferContentsArray> SerializedScriptValu |
SerializedScriptValue::SerializedScriptValue(v8::Handle<v8::Value> value, MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, WebBlobInfoArray* blobInfo, ExceptionState& exceptionState, v8::Isolate* isolate) |
: m_externallyAllocatedMemory(0) |
{ |
- Writer writer; |
- Serializer::Status status; |
+ SerializedScriptValueInternal::Writer writer; |
+ SerializedScriptValueInternal::Serializer::Status status; |
String errorMessage; |
{ |
v8::TryCatch tryCatch; |
- Serializer serializer(writer, messagePorts, arrayBuffers, blobInfo, m_blobDataHandles, tryCatch, ScriptState::current(isolate)); |
+ SerializedScriptValueInternal::Serializer serializer(writer, messagePorts, arrayBuffers, blobInfo, m_blobDataHandles, tryCatch, ScriptState::current(isolate)); |
status = serializer.serialize(value); |
- if (status == Serializer::JSException) { |
+ if (status == SerializedScriptValueInternal::Serializer::JSException) { |
// If there was a JS exception thrown, re-throw it. |
exceptionState.rethrowV8Exception(tryCatch.Exception()); |
return; |
@@ -3046,17 +214,17 @@ SerializedScriptValue::SerializedScriptValue(v8::Handle<v8::Value> value, Messag |
errorMessage = serializer.errorMessage(); |
} |
switch (status) { |
- case Serializer::InputError: |
- case Serializer::DataCloneError: |
+ case SerializedScriptValueInternal::Serializer::InputError: |
+ case SerializedScriptValueInternal::Serializer::DataCloneError: |
exceptionState.throwDOMException(DataCloneError, errorMessage); |
return; |
- case Serializer::Success: |
+ case SerializedScriptValueInternal::Serializer::Success: |
m_data = writer.takeWireString(); |
ASSERT(m_data.impl()->hasOneRef()); |
if (arrayBuffers && arrayBuffers->size()) |
m_arrayBufferContentsArray = transferArrayBuffers(isolate, *arrayBuffers, exceptionState); |
return; |
- case Serializer::JSException: |
+ case SerializedScriptValueInternal::Serializer::JSException: |
ASSERT_NOT_REACHED(); |
break; |
} |
@@ -3078,14 +246,14 @@ v8::Handle<v8::Value> SerializedScriptValue::deserialize(v8::Isolate* isolate, M |
{ |
if (!m_data.impl()) |
return v8::Null(isolate); |
- COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes); |
+ COMPILE_ASSERT(sizeof(SerializedScriptValueInternal::Writer::BufferValueType) == 2, BufferValueTypeIsTwoBytes); |
m_data.ensure16Bit(); |
// FIXME: SerializedScriptValue shouldn't use String for its underlying |
// storage. Instead, it should use SharedBuffer or Vector<uint8_t>. The |
// information stored in m_data isn't even encoded in UTF-16. Instead, |
// unicode characters are encoded as UTF-8 with two code units per UChar. |
- Reader reader(reinterpret_cast<const uint8_t*>(m_data.impl()->characters16()), 2 * m_data.length(), blobInfo, m_blobDataHandles, ScriptState::current(isolate)); |
- Deserializer deserializer(reader, messagePorts, m_arrayBufferContentsArray.get()); |
+ SerializedScriptValueInternal::Reader reader(reinterpret_cast<const uint8_t*>(m_data.impl()->characters16()), 2 * m_data.length(), blobInfo, m_blobDataHandles, ScriptState::current(isolate)); |
+ SerializedScriptValueInternal::Deserializer deserializer(reader, messagePorts, m_arrayBufferContentsArray.get()); |
// deserialize() can run arbitrary script (e.g., setters), which could result in |this| being destroyed. |
// Holding a RefPtr ensures we are alive (along with our internal data) throughout the operation. |