| Index: Source/bindings/core/v8/ScriptValueSerializer.cpp
|
| diff --git a/Source/bindings/core/v8/ScriptValueSerializer.cpp b/Source/bindings/core/v8/ScriptValueSerializer.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..566a39d0bafbdca7b28cd32061884efcffc19c00
|
| --- /dev/null
|
| +++ b/Source/bindings/core/v8/ScriptValueSerializer.cpp
|
| @@ -0,0 +1,2252 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "config.h"
|
| +#include "bindings/core/v8/ScriptValueSerializer.h"
|
| +
|
| +#include "bindings/core/v8/V8ArrayBuffer.h"
|
| +#include "bindings/core/v8/V8ArrayBufferView.h"
|
| +#include "bindings/core/v8/V8Blob.h"
|
| +#include "bindings/core/v8/V8File.h"
|
| +#include "bindings/core/v8/V8FileList.h"
|
| +#include "bindings/core/v8/V8ImageData.h"
|
| +#include "bindings/core/v8/V8MessagePort.h"
|
| +#include "bindings/modules/v8/V8CryptoKey.h"
|
| +#include "bindings/modules/v8/V8DOMFileSystem.h"
|
| +#include "core/dom/DOMDataView.h"
|
| +#include "core/fileapi/Blob.h"
|
| +#include "core/fileapi/File.h"
|
| +#include "core/fileapi/FileList.h"
|
| +#include "public/platform/Platform.h"
|
| +#include "public/platform/WebBlobInfo.h"
|
| +#include "wtf/ArrayBuffer.h"
|
| +#include "wtf/ArrayBufferContents.h"
|
| +#include "wtf/ArrayBufferView.h"
|
| +#include "wtf/text/StringHash.h"
|
| +#include "wtf/text/StringUTF8Adaptor.h"
|
| +
|
| +namespace blink {
|
| +
|
| +namespace SerializedScriptValueInternal {
|
| +
|
| +// 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
|
| +
|
| +// 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();
|
| +};
|
| +
|
| +static const int maxDepth = 20000;
|
| +
|
| +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));
|
| +}
|
| +
|
| +void Writer::writeUndefined()
|
| +{
|
| + append(UndefinedTag);
|
| +}
|
| +
|
| +void Writer::writeNull()
|
| +{
|
| + append(NullTag);
|
| +}
|
| +
|
| +void Writer::writeTrue()
|
| +{
|
| + append(TrueTag);
|
| +}
|
| +
|
| +void Writer::writeFalse()
|
| +{
|
| + append(FalseTag);
|
| +}
|
| +
|
| +void Writer::writeBooleanObject(bool value)
|
| +{
|
| + append(value ? TrueObjectTag : FalseObjectTag);
|
| +}
|
| +
|
| +void Writer::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 Writer::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 Writer::writeStringObject(const char* data, int length)
|
| +{
|
| + ASSERT(length >= 0);
|
| + append(StringObjectTag);
|
| + doWriteString(data, length);
|
| +}
|
| +
|
| +void Writer::writeWebCoreString(const String& string)
|
| +{
|
| + // Uses UTF8 encoding so we can read it back as either V8 or
|
| + // WebCore string.
|
| + append(StringTag);
|
| + doWriteWebCoreString(string);
|
| +}
|
| +
|
| +void Writer::writeVersion()
|
| +{
|
| + append(VersionTag);
|
| + doWriteUint32(SerializedScriptValue::wireFormatVersion);
|
| +}
|
| +
|
| +void Writer::writeInt32(int32_t value)
|
| +{
|
| + append(Int32Tag);
|
| + doWriteUint32(ZigZag::encode(static_cast<uint32_t>(value)));
|
| +}
|
| +
|
| +void Writer::writeUint32(uint32_t value)
|
| +{
|
| + append(Uint32Tag);
|
| + doWriteUint32(value);
|
| +}
|
| +
|
| +void Writer::writeDate(double numberValue)
|
| +{
|
| + append(DateTag);
|
| + doWriteNumber(numberValue);
|
| +}
|
| +
|
| +void Writer::writeNumber(double number)
|
| +{
|
| + append(NumberTag);
|
| + doWriteNumber(number);
|
| +}
|
| +
|
| +void Writer::writeNumberObject(double number)
|
| +{
|
| + append(NumberObjectTag);
|
| + doWriteNumber(number);
|
| +}
|
| +
|
| +void Writer::writeBlob(const String& uuid, const String& type, unsigned long long size)
|
| +{
|
| + append(BlobTag);
|
| + doWriteWebCoreString(uuid);
|
| + doWriteWebCoreString(type);
|
| + doWriteUint64(size);
|
| +}
|
| +
|
| +void Writer::writeDOMFileSystem(int type, const String& name, const String& url)
|
| +{
|
| + append(DOMFileSystemTag);
|
| + doWriteUint32(type);
|
| + doWriteWebCoreString(name);
|
| + doWriteWebCoreString(url);
|
| +}
|
| +
|
| +void Writer::writeBlobIndex(int blobIndex)
|
| +{
|
| + ASSERT(blobIndex >= 0);
|
| + append(BlobIndexTag);
|
| + doWriteUint32(blobIndex);
|
| +}
|
| +
|
| +void Writer::writeFile(const File& file)
|
| +{
|
| + append(FileTag);
|
| + doWriteFile(file);
|
| +}
|
| +
|
| +void Writer::writeFileIndex(int blobIndex)
|
| +{
|
| + append(FileIndexTag);
|
| + doWriteUint32(blobIndex);
|
| +}
|
| +
|
| +void Writer::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 Writer::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 Writer::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 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 Writer::writeArrayBuffer(const ArrayBuffer& arrayBuffer)
|
| +{
|
| + append(ArrayBufferTag);
|
| + doWriteArrayBuffer(arrayBuffer);
|
| +}
|
| +
|
| +void Writer::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 Writer::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 Writer::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 Writer::writeTransferredMessagePort(uint32_t index)
|
| +{
|
| + append(MessagePortTag);
|
| + doWriteUint32(index);
|
| +}
|
| +
|
| +void Writer::writeTransferredArrayBuffer(uint32_t index)
|
| +{
|
| + append(ArrayBufferTransferTag);
|
| + doWriteUint32(index);
|
| +}
|
| +
|
| +void Writer::writeObjectReference(uint32_t reference)
|
| +{
|
| + append(ObjectReferenceTag);
|
| + doWriteUint32(reference);
|
| +}
|
| +
|
| +void Writer::writeObject(uint32_t numProperties)
|
| +{
|
| + append(ObjectTag);
|
| + doWriteUint32(numProperties);
|
| +}
|
| +
|
| +void Writer::writeSparseArray(uint32_t numProperties, uint32_t length)
|
| +{
|
| + append(SparseArrayTag);
|
| + doWriteUint32(numProperties);
|
| + doWriteUint32(length);
|
| +}
|
| +
|
| +void Writer::writeDenseArray(uint32_t numProperties, uint32_t length)
|
| +{
|
| + append(DenseArrayTag);
|
| + doWriteUint32(numProperties);
|
| + doWriteUint32(length);
|
| +}
|
| +
|
| +String Writer::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 Writer::writeReferenceCount(uint32_t numberOfReferences)
|
| +{
|
| + append(ReferenceCountTag);
|
| + doWriteUint32(numberOfReferences);
|
| +}
|
| +
|
| +void Writer::writeGenerateFreshObject()
|
| +{
|
| + append(GenerateFreshObjectTag);
|
| +}
|
| +
|
| +void Writer::writeGenerateFreshSparseArray(uint32_t length)
|
| +{
|
| + append(GenerateFreshSparseArrayTag);
|
| + doWriteUint32(length);
|
| +}
|
| +
|
| +void Writer::writeGenerateFreshDenseArray(uint32_t length)
|
| +{
|
| + append(GenerateFreshDenseArrayTag);
|
| + doWriteUint32(length);
|
| +}
|
| +
|
| +void Writer::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 Writer::doWriteArrayBuffer(const ArrayBuffer& arrayBuffer)
|
| +{
|
| + uint32_t byteLength = arrayBuffer.byteLength();
|
| + doWriteUint32(byteLength);
|
| + append(static_cast<const uint8_t*>(arrayBuffer.data()), byteLength);
|
| +}
|
| +
|
| +void Writer::doWriteString(const char* data, int length)
|
| +{
|
| + doWriteUint32(static_cast<uint32_t>(length));
|
| + append(reinterpret_cast<const uint8_t*>(data), length);
|
| +}
|
| +
|
| +void Writer::doWriteWebCoreString(const String& string)
|
| +{
|
| + StringUTF8Adaptor stringUTF8(string);
|
| + doWriteString(stringUTF8.data(), stringUTF8.length());
|
| +}
|
| +
|
| +void Writer::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 Writer::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 Writer::doWriteRsaHashedKey(const WebCryptoKey& key)
|
| +{
|
| + ASSERT(key.algorithm().rsaHashedParams());
|
| + append(static_cast<uint8_t>(RsaHashedKeyTag));
|
| +
|
| + doWriteAlgorithmId(key.algorithm().id());
|
| +
|
| + switch (key.type()) {
|
| + case WebCryptoKeyTypePublic:
|
| + doWriteUint32(PublicKeyType);
|
| + break;
|
| + case WebCryptoKeyTypePrivate:
|
| + doWriteUint32(PrivateKeyType);
|
| + break;
|
| + case WebCryptoKeyTypeSecret:
|
| + ASSERT_NOT_REACHED();
|
| + }
|
| +
|
| + const WebCryptoRsaHashedKeyAlgorithmParams* params = key.algorithm().rsaHashedParams();
|
| + doWriteUint32(params->modulusLengthBits());
|
| + doWriteUint32(params->publicExponent().size());
|
| + append(params->publicExponent().data(), params->publicExponent().size());
|
| + doWriteAlgorithmId(key.algorithm().rsaHashedParams()->hash().id());
|
| +}
|
| +
|
| +void Writer::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);
|
| + }
|
| + ASSERT_NOT_REACHED();
|
| +}
|
| +
|
| +void Writer::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 Writer::bytesNeededToWireEncode(uint32_t value)
|
| +{
|
| + int bytes = 1;
|
| + while (true) {
|
| + value >>= SerializedScriptValue::varIntShift;
|
| + if (!value)
|
| + break;
|
| + ++bytes;
|
| + }
|
| +
|
| + return bytes;
|
| +}
|
| +
|
| +void Writer::doWriteUint32(uint32_t value)
|
| +{
|
| + doWriteUintHelper(value);
|
| +}
|
| +
|
| +void Writer::doWriteUint64(uint64_t value)
|
| +{
|
| + doWriteUintHelper(value);
|
| +}
|
| +
|
| +void Writer::doWriteNumber(double number)
|
| +{
|
| + append(reinterpret_cast<uint8_t*>(&number), sizeof(number));
|
| +}
|
| +
|
| +void Writer::append(SerializationTag tag)
|
| +{
|
| + append(static_cast<uint8_t>(tag));
|
| +}
|
| +
|
| +void Writer::append(uint8_t b)
|
| +{
|
| + ensureSpace(1);
|
| + *byteAt(m_position++) = b;
|
| +}
|
| +
|
| +void Writer::append(const uint8_t* data, int length)
|
| +{
|
| + ensureSpace(length);
|
| + memcpy(byteAt(m_position), data, length);
|
| + m_position += length;
|
| +}
|
| +
|
| +void Writer::ensureSpace(unsigned extra)
|
| +{
|
| + COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes);
|
| + m_buffer.resize((m_position + extra + 1) / sizeof(BufferValueType)); // "+ 1" to round up.
|
| +}
|
| +
|
| +void Writer::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* Writer::byteAt(int position)
|
| +{
|
| + return reinterpret_cast<uint8_t*>(m_buffer.data()) + position;
|
| +}
|
| +
|
| +int Writer::v8StringWriteOptions()
|
| +{
|
| + return v8::String::NO_NULL_TERMINATION;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::AbstractObjectState::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);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::ObjectState::advance(Serializer& serializer)
|
| +{
|
| + 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);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::ObjectState::objectDone(unsigned numProperties, Serializer& serializer)
|
| +{
|
| + return serializer.writeObject(numProperties, this);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::DenseArrayState::advance(Serializer& serializer)
|
| +{
|
| + 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);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::DenseArrayState::objectDone(unsigned numProperties, Serializer& serializer)
|
| +{
|
| + return serializer.writeDenseArray(numProperties, m_arrayLength, this);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::SparseArrayState::advance(Serializer& serializer)
|
| +{
|
| + return serializeProperties(false, serializer);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::SparseArrayState::objectDone(unsigned numProperties, Serializer& serializer)
|
| +{
|
| + return serializer.writeSparseArray(numProperties, composite().As<v8::Array>()->Length(), this);
|
| +}
|
| +
|
| +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>();
|
| +}
|
| +
|
| +// 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::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);
|
| + }
|
| + }
|
| +}
|
| +
|
| +Serializer::Status Serializer::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;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::doSerialize(v8::Handle<v8::Value> value, Serializer::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;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::doSerializeArrayBuffer(v8::Handle<v8::Value> arrayBuffer, Serializer::StateBase* next)
|
| +{
|
| + return doSerialize(arrayBuffer, next);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::checkException(Serializer::StateBase* state)
|
| +{
|
| + return m_tryCatch.HasCaught() ? handleError(JSException, "", state) : 0;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeObject(uint32_t numProperties, Serializer::StateBase* state)
|
| +{
|
| + m_writer.writeObject(numProperties);
|
| + return pop(state);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeSparseArray(uint32_t numProperties, uint32_t length, Serializer::StateBase* state)
|
| +{
|
| + m_writer.writeSparseArray(numProperties, length);
|
| + return pop(state);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeDenseArray(uint32_t numProperties, uint32_t length, Serializer::StateBase* state)
|
| +{
|
| + m_writer.writeDenseArray(numProperties, length);
|
| + return pop(state);
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::handleError(Serializer::Status errorStatus, const String& message, Serializer::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 Serializer::checkComposite(Serializer::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 Serializer::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 Serializer::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 Serializer::writeNumberObject(v8::Handle<v8::Value> value)
|
| +{
|
| + v8::Handle<v8::NumberObject> numberObject = value.As<v8::NumberObject>();
|
| + m_writer.writeNumberObject(numberObject->ValueOf());
|
| +}
|
| +
|
| +void Serializer::writeBooleanObject(v8::Handle<v8::Value> value)
|
| +{
|
| + v8::Handle<v8::BooleanObject> booleanObject = value.As<v8::BooleanObject>();
|
| + m_writer.writeBooleanObject(booleanObject->ValueOf());
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeBlob(v8::Handle<v8::Value> value, Serializer::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;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::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;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeFile(v8::Handle<v8::Value> value, Serializer::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;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeFileList(v8::Handle<v8::Value> value, Serializer::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 Serializer::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 Serializer::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 Serializer::writeRegExp(v8::Handle<v8::Value> value)
|
| +{
|
| + v8::Handle<v8::RegExp> regExp = value.As<v8::RegExp>();
|
| + m_writer.writeRegExp(regExp->GetSource(), regExp->GetFlags());
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeAndGreyArrayBufferView(v8::Handle<v8::Object> object, Serializer::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;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeArrayBuffer(v8::Handle<v8::Value> value, Serializer::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;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::writeTransferredArrayBuffer(v8::Handle<v8::Value> value, uint32_t index, Serializer::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;
|
| +}
|
| +
|
| +bool Serializer::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;
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::startArrayState(v8::Handle<v8::Array> array, Serializer::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()));
|
| +}
|
| +
|
| +Serializer::StateBase* Serializer::startObjectState(v8::Handle<v8::Object> object, Serializer::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 Serializer::greyObject(const v8::Handle<v8::Object>& object)
|
| +{
|
| + ASSERT(!m_objectPool.contains(object));
|
| + uint32_t objectReference = m_nextObjectReference++;
|
| + m_objectPool.set(object, objectReference);
|
| +}
|
| +
|
| +bool Serializer::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 Serializer::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;
|
| +}
|
| +
|
| +bool Reader::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 Reader::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 Reader::setVersion(uint32_t version)
|
| +{
|
| + m_version = version;
|
| +}
|
| +
|
| +bool Reader::readTag(SerializationTag* tag)
|
| +{
|
| + if (m_position >= m_length)
|
| + return false;
|
| + *tag = static_cast<SerializationTag>(m_buffer[m_position++]);
|
| + return true;
|
| +}
|
| +
|
| +void Reader::undoReadTag()
|
| +{
|
| + if (m_position > 0)
|
| + --m_position;
|
| +}
|
| +
|
| +bool Reader::readArrayBufferViewSubTag(ArrayBufferViewSubTag* tag)
|
| +{
|
| + if (m_position >= m_length)
|
| + return false;
|
| + *tag = static_cast<ArrayBufferViewSubTag>(m_buffer[m_position++]);
|
| + return true;
|
| +}
|
| +
|
| +bool Reader::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 Reader::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 Reader::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 Reader::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 Reader::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 Reader::readUint32(v8::Handle<v8::Value>* value)
|
| +{
|
| + uint32_t rawValue;
|
| + if (!doReadUint32(&rawValue))
|
| + return false;
|
| + *value = v8::Integer::NewFromUnsigned(isolate(), rawValue);
|
| + return true;
|
| +}
|
| +
|
| +bool Reader::readDate(v8::Handle<v8::Value>* value)
|
| +{
|
| + double numberValue;
|
| + if (!doReadNumber(&numberValue))
|
| + return false;
|
| + *value = v8DateOrNaN(numberValue, isolate());
|
| + return true;
|
| +}
|
| +
|
| +bool Reader::readNumber(v8::Handle<v8::Value>* value)
|
| +{
|
| + double number;
|
| + if (!doReadNumber(&number))
|
| + return false;
|
| + *value = v8::Number::New(isolate(), number);
|
| + return true;
|
| +}
|
| +
|
| +bool Reader::readNumberObject(v8::Handle<v8::Value>* value)
|
| +{
|
| + double number;
|
| + if (!doReadNumber(&number))
|
| + return false;
|
| + *value = v8::NumberObject::New(isolate(), number);
|
| + return true;
|
| +}
|
| +
|
| +bool Reader::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> Reader::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 Reader::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 Reader::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 Reader::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 Reader::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 Reader::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 Reader::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 Reader::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 Reader::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;
|
| + 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* Reader::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* Reader::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()));
|
| +}
|
| +
|
| +bool Reader::doReadUint32(uint32_t* value)
|
| +{
|
| + return doReadUintHelper(value);
|
| +}
|
| +
|
| +bool Reader::doReadUint64(uint64_t* value)
|
| +{
|
| + return doReadUintHelper(value);
|
| +}
|
| +
|
| +bool Reader::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> Reader::getOrCreateBlobDataHandle(const String& uuid, const String& type, long long size)
|
| +{
|
| + // 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 Reader::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 Reader::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 Reader::doReadRsaHashedKey(WebCryptoKeyAlgorithm& algorithm, WebCryptoKeyType& type)
|
| +{
|
| + WebCryptoAlgorithmId id;
|
| + if (!doReadAlgorithmId(id))
|
| + return false;
|
| +
|
| + uint32_t rawType;
|
| + if (!doReadUint32(&rawType))
|
| + return false;
|
| +
|
| + switch (static_cast<AssymetricCryptoKeyType>(rawType)) {
|
| + case PublicKeyType:
|
| + type = WebCryptoKeyTypePublic;
|
| + break;
|
| + case PrivateKeyType:
|
| + type = WebCryptoKeyTypePrivate;
|
| + break;
|
| + default:
|
| + 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 Reader::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;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool Reader::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;
|
| +}
|
| +
|
| +v8::Handle<v8::Value> Deserializer::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;
|
| +}
|
| +
|
| +bool Deserializer::newSparseArray(uint32_t)
|
| +{
|
| + v8::Local<v8::Array> array = v8::Array::New(m_reader.scriptState()->isolate(), 0);
|
| + openComposite(array);
|
| + return true;
|
| +}
|
| +
|
| +bool Deserializer::newDenseArray(uint32_t length)
|
| +{
|
| + v8::Local<v8::Array> array = v8::Array::New(m_reader.scriptState()->isolate(), length);
|
| + openComposite(array);
|
| + return true;
|
| +}
|
| +
|
| +bool Deserializer::consumeTopOfStack(v8::Handle<v8::Value>* object)
|
| +{
|
| + if (stackDepth() < 1)
|
| + return false;
|
| + *object = element(stackDepth() - 1);
|
| + pop(1);
|
| + return true;
|
| +}
|
| +
|
| +bool Deserializer::newObject()
|
| +{
|
| + v8::Local<v8::Object> object = v8::Object::New(m_reader.scriptState()->isolate());
|
| + if (object.IsEmpty())
|
| + return false;
|
| + openComposite(object);
|
| + return true;
|
| +}
|
| +
|
| +bool Deserializer::completeObject(uint32_t numProperties, v8::Handle<v8::Value>* value)
|
| +{
|
| + 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);
|
| +}
|
| +
|
| +bool Deserializer::completeSparseArray(uint32_t numProperties, uint32_t length, v8::Handle<v8::Value>* value)
|
| +{
|
| + 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);
|
| +}
|
| +
|
| +bool Deserializer::completeDenseArray(uint32_t numProperties, uint32_t length, v8::Handle<v8::Value>* value)
|
| +{
|
| + 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;
|
| +}
|
| +
|
| +void Deserializer::pushObjectReference(const v8::Handle<v8::Value>& object)
|
| +{
|
| + m_objectPool.append(object);
|
| +}
|
| +
|
| +bool Deserializer::tryGetTransferredMessagePort(uint32_t index, v8::Handle<v8::Value>* object)
|
| +{
|
| + 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;
|
| +}
|
| +
|
| +bool Deserializer::tryGetTransferredArrayBuffer(uint32_t index, v8::Handle<v8::Value>* object)
|
| +{
|
| + 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;
|
| +}
|
| +
|
| +bool Deserializer::tryGetObjectFromObjectReference(uint32_t reference, v8::Handle<v8::Value>* object)
|
| +{
|
| + if (reference >= m_objectPool.size())
|
| + return false;
|
| + *object = m_objectPool[reference];
|
| + return object;
|
| +}
|
| +
|
| +uint32_t Deserializer::objectReferenceCount()
|
| +{
|
| + return m_objectPool.size();
|
| +}
|
| +
|
| +bool Deserializer::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 Deserializer::read(v8::Local<v8::Value>* value)
|
| +{
|
| + return m_reader.read(value, *this);
|
| +}
|
| +
|
| +bool Deserializer::doDeserialize()
|
| +{
|
| + v8::Local<v8::Value> value;
|
| + if (!read(&value))
|
| + return false;
|
| + if (!value.IsEmpty())
|
| + push(value);
|
| + return true;
|
| +}
|
| +
|
| +v8::Local<v8::Value> Deserializer::element(unsigned index)
|
| +{
|
| + ASSERT_WITH_SECURITY_IMPLICATION(index < m_stack.size());
|
| + return m_stack[index];
|
| +}
|
| +
|
| +void Deserializer::openComposite(const v8::Local<v8::Value>& object)
|
| +{
|
| + uint32_t newObjectReference = m_objectPool.size();
|
| + m_openCompositeReferenceStack.append(newObjectReference);
|
| + m_objectPool.append(object);
|
| +}
|
| +
|
| +bool Deserializer::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;
|
| +}
|
| +
|
| +} // SerializedScriptValueInternal
|
| +
|
| +} // namespace blink
|
|
|